diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a5da654..9559a9b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -38,7 +38,7 @@ body: id: version attributes: label: 哔哩哔哩漫画下载器版本号 - placeholder: v1.1.1 + placeholder: v1.3.0 validations: required: true - type: dropdown diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ba1c6b8..91abb11 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,4 +8,4 @@ updates: - package-ecosystem: "pip" # See documentation for possible values directory: "/" # Location of package manifests schedule: - interval: "daily" + interval: "weekly" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8bf2f36 --- /dev/null +++ b/.gitignore @@ -0,0 +1,134 @@ +.vscode/ +Report.* + +~$rEPORT.DOCX + +codiga.yml +哔哩哔哩漫画下载器.exe + + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +# *.spec +myPyinstaller.json + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/Pipfile b/Pipfile index 52dfab6..32bc085 100644 --- a/Pipfile +++ b/Pipfile @@ -4,15 +4,17 @@ verify_ssl = true name = "pypi" [packages] -piexif = ">=1.1.3" -py7zr = ">=0.20.4" -pypdf2 = ">=3.0.1" -pyside6 = ">=6.4.2" -requests = ">=2.28.1" -retrying = ">=1.3.4" -pillow = ">=9.4.0" -pypinyin = ">=0.48.0" -qt-material = ">=2.14" +piexif = "*" +py7zr = "*" +pypdf2 = "*" +pyside6 = "*" +requests = "*" +retrying = "*" +pillow = "*" +pypinyin = "*" +qt-material = "*" +beautifulsoup4 = "*" +qrcode = "*" [dev-packages] rich = "*" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 71c032c..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,1137 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "10565c5875da770b09489e4a67116bdfaabc7dd212980f57bd74ee7f87317b98" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.11" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "brotli": { - "hashes": [ - "sha256:02177603aaca36e1fd21b091cb742bb3b305a569e2402f1ca38af471777fb019", - "sha256:11d3283d89af7033236fa4e73ec2cbe743d4f6a81d41bd234f24bf63dde979df", - "sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d", - "sha256:160c78292e98d21e73a4cc7f76a234390e516afcd982fa17e1422f7c6a9ce9c8", - "sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b", - "sha256:19598ecddd8a212aedb1ffa15763dd52a388518c4550e615aed88dc3753c0f0c", - "sha256:1c48472a6ba3b113452355b9af0a60da5c2ae60477f8feda8346f8fd48e3e87c", - "sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70", - "sha256:269a5743a393c65db46a7bb982644c67ecba4b8d91b392403ad8a861ba6f495f", - "sha256:26d168aac4aaec9a4394221240e8a5436b5634adc3cd1cdf637f6645cecbf181", - "sha256:29d1d350178e5225397e28ea1b7aca3648fcbab546d20e7475805437bfb0a130", - "sha256:2aad0e0baa04517741c9bb5b07586c642302e5fb3e75319cb62087bd0995ab19", - "sha256:3148362937217b7072cf80a2dcc007f09bb5ecb96dae4617316638194113d5be", - "sha256:330e3f10cd01da535c70d09c4283ba2df5fb78e915bea0a28becad6e2ac010be", - "sha256:336b40348269f9b91268378de5ff44dc6fbaa2268194f85177b53463d313842a", - "sha256:3496fc835370da351d37cada4cf744039616a6db7d13c430035e901443a34daa", - "sha256:35a3edbe18e876e596553c4007a087f8bcfd538f19bc116917b3c7522fca0429", - "sha256:3b78a24b5fd13c03ee2b7b86290ed20efdc95da75a3557cc06811764d5ad1126", - "sha256:3b8b09a16a1950b9ef495a0f8b9d0a87599a9d1f179e2d4ac014b2ec831f87e7", - "sha256:3c1306004d49b84bd0c4f90457c6f57ad109f5cc6067a9664e12b7b79a9948ad", - "sha256:3ffaadcaeafe9d30a7e4e1e97ad727e4f5610b9fa2f7551998471e3736738679", - "sha256:40d15c79f42e0a2c72892bf407979febd9cf91f36f495ffb333d1d04cebb34e4", - "sha256:44bb8ff420c1d19d91d79d8c3574b8954288bdff0273bf788954064d260d7ab0", - "sha256:4688c1e42968ba52e57d8670ad2306fe92e0169c6f3af0089be75bbac0c64a3b", - "sha256:495ba7e49c2db22b046a53b469bbecea802efce200dffb69b93dd47397edc9b6", - "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438", - "sha256:503fa6af7da9f4b5780bb7e4cbe0c639b010f12be85d02c99452825dd0feef3f", - "sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389", - "sha256:5913a1177fc36e30fcf6dc868ce23b0453952c78c04c266d3149b3d39e1410d6", - "sha256:5b6ef7d9f9c38292df3690fe3e302b5b530999fa90014853dcd0d6902fb59f26", - "sha256:5bf37a08493232fbb0f8229f1824b366c2fc1d02d64e7e918af40acd15f3e337", - "sha256:5cb1e18167792d7d21e21365d7650b72d5081ed476123ff7b8cac7f45189c0c7", - "sha256:61a7ee1f13ab913897dac7da44a73c6d44d48a4adff42a5701e3239791c96e14", - "sha256:622a231b08899c864eb87e85f81c75e7b9ce05b001e59bbfbf43d4a71f5f32b2", - "sha256:68715970f16b6e92c574c30747c95cf8cf62804569647386ff032195dc89a430", - "sha256:6b2ae9f5f67f89aade1fab0f7fd8f2832501311c363a21579d02defa844d9296", - "sha256:6c772d6c0a79ac0f414a9f8947cc407e119b8598de7621f39cacadae3cf57d12", - "sha256:6d847b14f7ea89f6ad3c9e3901d1bc4835f6b390a9c71df999b0162d9bb1e20f", - "sha256:73fd30d4ce0ea48010564ccee1a26bfe39323fde05cb34b5863455629db61dc7", - "sha256:76ffebb907bec09ff511bb3acc077695e2c32bc2142819491579a695f77ffd4d", - "sha256:7bbff90b63328013e1e8cb50650ae0b9bac54ffb4be6104378490193cd60f85a", - "sha256:7cb81373984cc0e4682f31bc3d6be9026006d96eecd07ea49aafb06897746452", - "sha256:7ee83d3e3a024a9618e5be64648d6d11c37047ac48adff25f12fa4226cf23d1c", - "sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761", - "sha256:85f7912459c67eaab2fb854ed2bc1cc25772b300545fe7ed2dc03954da638649", - "sha256:87fdccbb6bb589095f413b1e05734ba492c962b4a45a13ff3408fa44ffe6479b", - "sha256:88c63a1b55f352b02c6ffd24b15ead9fc0e8bf781dbe070213039324922a2eea", - "sha256:8a674ac10e0a87b683f4fa2b6fa41090edfd686a6524bd8dedbd6138b309175c", - "sha256:8ed6a5b3d23ecc00ea02e1ed8e0ff9a08f4fc87a1f58a2530e71c0f48adf882f", - "sha256:93130612b837103e15ac3f9cbacb4613f9e348b58b3aad53721d92e57f96d46a", - "sha256:9744a863b489c79a73aba014df554b0e7a0fc44ef3f8a0ef2a52919c7d155031", - "sha256:9749a124280a0ada4187a6cfd1ffd35c350fb3af79c706589d98e088c5044267", - "sha256:97f715cf371b16ac88b8c19da00029804e20e25f30d80203417255d239f228b5", - "sha256:9bf919756d25e4114ace16a8ce91eb340eb57a08e2c6950c3cebcbe3dff2a5e7", - "sha256:9d12cf2851759b8de8ca5fde36a59c08210a97ffca0eb94c532ce7b17c6a3d1d", - "sha256:9ed4c92a0665002ff8ea852353aeb60d9141eb04109e88928026d3c8a9e5433c", - "sha256:a72661af47119a80d82fa583b554095308d6a4c356b2a554fdc2799bc19f2a43", - "sha256:afde17ae04d90fbe53afb628f7f2d4ca022797aa093e809de5c3cf276f61bbfa", - "sha256:b1375b5d17d6145c798661b67e4ae9d5496920d9265e2f00f1c2c0b5ae91fbde", - "sha256:b336c5e9cf03c7be40c47b5fd694c43c9f1358a80ba384a21969e0b4e66a9b17", - "sha256:b3523f51818e8f16599613edddb1ff924eeb4b53ab7e7197f85cbc321cdca32f", - "sha256:b43775532a5904bc938f9c15b77c613cb6ad6fb30990f3b0afaea82797a402d8", - "sha256:b663f1e02de5d0573610756398e44c130add0eb9a3fc912a09665332942a2efb", - "sha256:b83bb06a0192cccf1eb8d0a28672a1b79c74c3a8a5f2619625aeb6f28b3a82bb", - "sha256:ba72d37e2a924717990f4d7482e8ac88e2ef43fb95491eb6e0d124d77d2a150d", - "sha256:c2415d9d082152460f2bd4e382a1e85aed233abc92db5a3880da2257dc7daf7b", - "sha256:c83aa123d56f2e060644427a882a36b3c12db93727ad7a7b9efd7d7f3e9cc2c4", - "sha256:c8e521a0ce7cf690ca84b8cc2272ddaf9d8a50294fd086da67e517439614c755", - "sha256:cab1b5964b39607a66adbba01f1c12df2e55ac36c81ec6ed44f2fca44178bf1a", - "sha256:cb02ed34557afde2d2da68194d12f5719ee96cfb2eacc886352cb73e3808fc5d", - "sha256:cc0283a406774f465fb45ec7efb66857c09ffefbe49ec20b7882eff6d3c86d3a", - "sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3", - "sha256:db844eb158a87ccab83e868a762ea8024ae27337fc7ddcbfcddd157f841fdfe7", - "sha256:defed7ea5f218a9f2336301e6fd379f55c655bea65ba2476346340a0ce6f74a1", - "sha256:e16eb9541f3dd1a3e92b89005e37b1257b157b7256df0e36bd7b33b50be73bcb", - "sha256:e1abbeef02962596548382e393f56e4c94acd286bd0c5afba756cffc33670e8a", - "sha256:e23281b9a08ec338469268f98f194658abfb13658ee98e2b7f85ee9dd06caa91", - "sha256:e2d9e1cbc1b25e22000328702b014227737756f4b5bf5c485ac1d8091ada078b", - "sha256:e48f4234f2469ed012a98f4b7874e7f7e173c167bed4934912a29e03167cf6b1", - "sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806", - "sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3", - "sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1" - ], - "markers": "platform_python_implementation == 'CPython'", - "version": "==1.0.9" - }, - "certifi": { - "hashes": [ - "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", - "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.7.22" - }, - "charset-normalizer": { - "hashes": [ - "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96", - "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c", - "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710", - "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706", - "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020", - "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252", - "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad", - "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329", - "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a", - "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f", - "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6", - "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4", - "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a", - "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46", - "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2", - "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23", - "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace", - "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd", - "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982", - "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10", - "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2", - "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea", - "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09", - "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5", - "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149", - "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489", - "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9", - "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80", - "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592", - "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3", - "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6", - "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed", - "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c", - "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200", - "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a", - "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e", - "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d", - "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6", - "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623", - "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669", - "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3", - "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa", - "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9", - "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2", - "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f", - "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1", - "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4", - "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a", - "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8", - "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3", - "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029", - "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f", - "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959", - "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22", - "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7", - "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952", - "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346", - "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e", - "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d", - "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299", - "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd", - "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a", - "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3", - "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037", - "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94", - "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c", - "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858", - "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a", - "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449", - "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c", - "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918", - "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1", - "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c", - "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac", - "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa" - ], - "markers": "python_version >= '3.7'", - "version": "==3.2.0" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "inflate64": { - "hashes": [ - "sha256:08c3b03514d4b849901762a32a45eeba7fd5d784fec698eca6975f41cca33672", - "sha256:094ef56a87c7b7398d93af7bfe7f24f830f24b6e55b77426f6516cef43e05460", - "sha256:09dd0f8d6dee0da467c264dbd9bca8b33f9c915860fc3385f2a633640a65bd10", - "sha256:0b0c8aa2fcdb1052d3bc6c5b5b1191b9c708d30e47af98ba0a8117ae1f6c9efc", - "sha256:130dfdca4bd38e588ea4f878bf62635e36f83ddf7f2842d1055d1c16a11890cf", - "sha256:1749da3a02b53035cde1cf95f885e78e0c2c49b201e97d368b3ba97e0f3d42c3", - "sha256:17aaac096f40bd80dd72481831607a0846271d401ba3cd863386b8c244c7ebc1", - "sha256:1d861fed6b2098d1862b64db9df650b9bd41fc41caa9fcaeee399079342aa4a8", - "sha256:26e8319fd032c520203e2c001f1693c1c03774d85915900427e884011718f41d", - "sha256:29946840e6970d68e7739207ca21140c59ffebe7e02d28c7e86348166ce32418", - "sha256:35e24ffd8d6225fbfe26c524b45ace1bb8956811bd79e9f3d523a721d51b0d4e", - "sha256:3a17e1dd1a5a872edfc02bc4a048868ada4865a3f4ee3ad5d224b192f2e53df7", - "sha256:3bacbe9d4b7c185011b59268223a010ed777a28ed8cf40efc74fab1b7262e904", - "sha256:3c270d373ca3717dbeb9b171eea53cbf2c9d7471b9b5de1e57f165e60cf58037", - "sha256:3c913b679f023f5907a54bfa9a6e438407ed4e40eee23ed19b4118128bdd091c", - "sha256:3cf41f82dd4e90e8684c7be4583d7232bd800a561f3ed0241c84e39148861887", - "sha256:41504988023042452d2d84e4110c9ef4ff8ebd33cb90ba83e44b92c9a6753c43", - "sha256:42a6ef375b3e7059bd52993a0938f2bf97725cb5dc380f0c4dbaa9fc3780e025", - "sha256:473e0081c268ffa4b18683586b55170eb96d8b4fc684dd3ed9599c17c512d2e4", - "sha256:48fd2527a462374dc19be06301d6aa30a03190532f2f8bddfbc39b7158561750", - "sha256:4d807cfa9ddad940401ef04502eb367a77f569850f59c2e71670347d558a3830", - "sha256:4e7b0a598adaa11366ffbbb7b3d3110db29edd4b732d9336570891363b22b002", - "sha256:525bc309d8533ef9917e006284996ee7a9a71ac6dd19fb57c0f741ad0c805d4f", - "sha256:553cd992f02af574d2116c74ca48d7cf10894c6b9ba8159f488f3bfac3c201ae", - "sha256:5c5b2eb7e89d550d287774dea7d429ee24ce44ca34499a6cef113a14f108e700", - "sha256:5efd55c21b794601fd44b99b8e2f17498744f573116ce27a745bc5e08f0457e1", - "sha256:6059eaba5044739ad6424588e845bd856f89a1a18f1addc31b97c49f02f68728", - "sha256:664929528047b6b472852a4c0d12b4b9cf6e663059ba64ebd10f08aa56365755", - "sha256:67e37d96ea2ee8257b12cde83a09e4f0276950268a7a2f777aee7de60db5ec72", - "sha256:67efdfd21d7b99f30a43560b22264c1e580ff08ae9831e78c99445575962dbc7", - "sha256:6ff89f94823b2466bae45759fc324bd25bd20c490607a7d8407237cf64ccafa9", - "sha256:74ceb9d172ce06572632bc8070d54b963455421e216013575383f991e722bb7d", - "sha256:7b7966193f1bf23e050af72b4c4720dffa5f33471de7afea37ba0d0f0195adef", - "sha256:7ba954600441eafe8f6f54eadffeac4d1ab2416d5d1a6b0ab403e50284ba457b", - "sha256:7c142fbbbfbe0877fe821ff8bc4cc10f96d344b7400721579b3d17deeae28f59", - "sha256:7f8346e644de449a4a90dcb22971dea456398b6cc788102013675b11256ae47e", - "sha256:80a125dd5cb7b7985c05a78b0bfd7751249d0d84fc330901dbd9faa693e1f53f", - "sha256:82393e46b8ba2f8613d030f38c7c492b0896ff8803f7ff870677f25d3e5e7113", - "sha256:84287d1d09fd879353d3ccadd43f3d8adea75e830476ddfd46d8849d36d25afe", - "sha256:853f3442eceda8035072686533694ab833c4293d10c9d0685147200f0e964356", - "sha256:906a4b57df32f903e847766ca685e44ed3e7ee3a960fa94264d5e68b836d446d", - "sha256:90f95b92d0f672d11151cb964964d1723e2e3ce3a19d32d24aece1acdec1e287", - "sha256:91233b5300bbb7562804c3d07617e9ce2983e8434218991db98ef175491e417f", - "sha256:9297115bf144c585e9d6a746e851c64c81d8f1ce8b62da4885babe66c36f7d29", - "sha256:9f6737a575c6e7e818963d95a998be4c91484374961734cee97265f3c4c3b979", - "sha256:a075b174bace5174828906c7c87019a2af3cc5707025f01ee0395fb4b88fd98e", - "sha256:a1b481343f12641b1ae7a19135a70c44ecf020dccb670e02522c2b02db920851", - "sha256:a2f4aaa02f9a5ada944960428b6528a0a9d773925efc73485882f34bf42654be", - "sha256:a6bec3d2f30f6f2656e1c5a4147181e401c8d7026cd598d86ad5647c616fc618", - "sha256:aa7476129e7f81e67a9253470c3085a9fd75ec77e6fae3de61f7795138ce725e", - "sha256:ab8f9e14ba6495f440101751ba8aa371e4a52941b5e343c6f3e8c61021e2df5e", - "sha256:ac60868745f7bfbcd615329fbdc35997fa36043ce358a1c64d229ef448ebecf0", - "sha256:ad4cae5097bdff7e0bb1ab676d86ad08716597baa3b616e5b710a724f5d5cbc4", - "sha256:ad84ac611eae17a961124c5fbe754b6982291a3945ab2b9c334a08e2e56f9ccc", - "sha256:b52dd8fefd2ba179e5dfa18d6eca7e2fc822584616271c039d5ef1f9ca90c71c", - "sha256:b7aa123c740f2f9798f72873e50d7c6d43664d12cad7a1405296079987bdb04a", - "sha256:c1faf43890dbfff31195f5d59e37e49824f5ff4be77d67f7144a6b953bbde51c", - "sha256:c28cb635ccb9aae399fbc8e82c85b89ea0a7bb2219e7d582bbc007a29fb6e149", - "sha256:c71821f93c931ae379cf9c9bbdd7099738fa00802ccf2a5271e2b68bc67a6ab8", - "sha256:ced0af509a31dcba0cd98ecdd06cb7c9ce66ebde78e0d99ba3515d4e991e34d0", - "sha256:d4e2a337c6c03b0e96ccd79940cbb04fe2063974d56fff6d78f8d57839546c57", - "sha256:d71af8b23ac23bc9e9f776451c125be6320ad4589a7d5bcb5ab5e1fc61b4e58f", - "sha256:d881b605b7448be451f02c59128dc5fac262dbd0dcff4638e702dc8c7bbb8ef0", - "sha256:e1987bbc482aa3e2e7fb72c70b22483cfaed3dbebc5ba6f9ac6f75240794709b", - "sha256:e32a78c81afba5699569c3493066ecb38fb45ccdf4c35b3c2232c9c2585b5257", - "sha256:f2a4dac4ebc4ad58a4ac911e39cf97cd74906c0c82c16333887aa9f287e98d5b", - "sha256:f39b57974db0e85897fff40518da420f4c4012b73515ca6f415a472228fea288", - "sha256:fd04764d0bb830414788cae897d082bf6ad92324e571a5511bd7e1de4a0cdc67", - "sha256:fde3f85864c84badb26f42d95639360e627fd09c529a76c46a06dbd7a5735c51" - ], - "markers": "python_version >= '3.7'", - "version": "==0.3.1" - }, - "jinja2": { - "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.2" - }, - "markupsafe": { - "hashes": [ - "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", - "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", - "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", - "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", - "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", - "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", - "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", - "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", - "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", - "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", - "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", - "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", - "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", - "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", - "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", - "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", - "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", - "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", - "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", - "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", - "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", - "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", - "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", - "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", - "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", - "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", - "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", - "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", - "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", - "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", - "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", - "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", - "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", - "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", - "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", - "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", - "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", - "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", - "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", - "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", - "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", - "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", - "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", - "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", - "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", - "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", - "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", - "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", - "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.3" - }, - "multivolumefile": { - "hashes": [ - "sha256:237f4353b60af1703087cf7725755a1f6fcaeeea48421e1896940cd1c920d678", - "sha256:a0648d0aafbc96e59198d5c17e9acad7eb531abea51035d08ce8060dcad709d6" - ], - "markers": "python_version >= '3.6'", - "version": "==0.2.3" - }, - "piexif": { - "hashes": [ - "sha256:3bc435d171720150b81b15d27e05e54b8abbde7b4242cddd81ef160d283108b6", - "sha256:83cb35c606bf3a1ea1a8f0a25cb42cf17e24353fd82e87ae3884e74a302a5f1b" - ], - "index": "pypi", - "version": "==1.1.3" - }, - "pillow": { - "hashes": [ - "sha256:00e65f5e822decd501e374b0650146063fbb30a7264b4d2744bdd7b913e0cab5", - "sha256:040586f7d37b34547153fa383f7f9aed68b738992380ac911447bb78f2abe530", - "sha256:0b6eb5502f45a60a3f411c63187db83a3d3107887ad0d036c13ce836f8a36f1d", - "sha256:1ce91b6ec08d866b14413d3f0bbdea7e24dfdc8e59f562bb77bc3fe60b6144ca", - "sha256:1f62406a884ae75fb2f818694469519fb685cc7eaff05d3451a9ebe55c646891", - "sha256:22c10cc517668d44b211717fd9775799ccec4124b9a7f7b3635fc5386e584992", - "sha256:3400aae60685b06bb96f99a21e1ada7bc7a413d5f49bce739828ecd9391bb8f7", - "sha256:349930d6e9c685c089284b013478d6f76e3a534e36ddfa912cde493f235372f3", - "sha256:368ab3dfb5f49e312231b6f27b8820c823652b7cd29cfbd34090565a015e99ba", - "sha256:38250a349b6b390ee6047a62c086d3817ac69022c127f8a5dc058c31ccef17f3", - "sha256:3a684105f7c32488f7153905a4e3015a3b6c7182e106fe3c37fbb5ef3e6994c3", - "sha256:3a82c40d706d9aa9734289740ce26460a11aeec2d9c79b7af87bb35f0073c12f", - "sha256:3b08d4cc24f471b2c8ca24ec060abf4bebc6b144cb89cba638c720546b1cf538", - "sha256:3ed64f9ca2f0a95411e88a4efbd7a29e5ce2cea36072c53dd9d26d9c76f753b3", - "sha256:3f07ea8d2f827d7d2a49ecf1639ec02d75ffd1b88dcc5b3a61bbb37a8759ad8d", - "sha256:520f2a520dc040512699f20fa1c363eed506e94248d71f85412b625026f6142c", - "sha256:5c6e3df6bdd396749bafd45314871b3d0af81ff935b2d188385e970052091017", - "sha256:608bfdee0d57cf297d32bcbb3c728dc1da0907519d1784962c5f0c68bb93e5a3", - "sha256:685ac03cc4ed5ebc15ad5c23bc555d68a87777586d970c2c3e216619a5476223", - "sha256:76de421f9c326da8f43d690110f0e79fe3ad1e54be811545d7d91898b4c8493e", - "sha256:76edb0a1fa2b4745fb0c99fb9fb98f8b180a1bbceb8be49b087e0b21867e77d3", - "sha256:7be600823e4c8631b74e4a0d38384c73f680e6105a7d3c6824fcf226c178c7e6", - "sha256:81ff539a12457809666fef6624684c008e00ff6bf455b4b89fd00a140eecd640", - "sha256:88af2003543cc40c80f6fca01411892ec52b11021b3dc22ec3bc9d5afd1c5334", - "sha256:8c11160913e3dd06c8ffdb5f233a4f254cb449f4dfc0f8f4549eda9e542c93d1", - "sha256:8f8182b523b2289f7c415f589118228d30ac8c355baa2f3194ced084dac2dbba", - "sha256:9211e7ad69d7c9401cfc0e23d49b69ca65ddd898976d660a2fa5904e3d7a9baa", - "sha256:92be919bbc9f7d09f7ae343c38f5bb21c973d2576c1d45600fce4b74bafa7ac0", - "sha256:9c82b5b3e043c7af0d95792d0d20ccf68f61a1fec6b3530e718b688422727396", - "sha256:9f7c16705f44e0504a3a2a14197c1f0b32a95731d251777dcb060aa83022cb2d", - "sha256:9fb218c8a12e51d7ead2a7c9e101a04982237d4855716af2e9499306728fb485", - "sha256:a74ba0c356aaa3bb8e3eb79606a87669e7ec6444be352870623025d75a14a2bf", - "sha256:b4f69b3700201b80bb82c3a97d5e9254084f6dd5fb5b16fc1a7b974260f89f43", - "sha256:bc2ec7c7b5d66b8ec9ce9f720dbb5fa4bace0f545acd34870eff4a369b44bf37", - "sha256:c189af0545965fa8d3b9613cfdb0cd37f9d71349e0f7750e1fd704648d475ed2", - "sha256:c1fbe7621c167ecaa38ad29643d77a9ce7311583761abf7836e1510c580bf3dd", - "sha256:c7cf14a27b0d6adfaebb3ae4153f1e516df54e47e42dcc073d7b3d76111a8d86", - "sha256:c9f72a021fbb792ce98306ffb0c348b3c9cb967dce0f12a49aa4c3d3fdefa967", - "sha256:cd25d2a9d2b36fcb318882481367956d2cf91329f6892fe5d385c346c0649629", - "sha256:ce543ed15570eedbb85df19b0a1a7314a9c8141a36ce089c0a894adbfccb4568", - "sha256:ce7b031a6fc11365970e6a5686d7ba8c63e4c1cf1ea143811acbb524295eabed", - "sha256:d35e3c8d9b1268cbf5d3670285feb3528f6680420eafe35cccc686b73c1e330f", - "sha256:d50b6aec14bc737742ca96e85d6d0a5f9bfbded018264b3b70ff9d8c33485551", - "sha256:d5d0dae4cfd56969d23d94dc8e89fb6a217be461c69090768227beb8ed28c0a3", - "sha256:d5db32e2a6ccbb3d34d87c87b432959e0db29755727afb37290e10f6e8e62614", - "sha256:d72e2ecc68a942e8cf9739619b7f408cc7b272b279b56b2c83c6123fcfa5cdff", - "sha256:d737a602fbd82afd892ca746392401b634e278cb65d55c4b7a8f48e9ef8d008d", - "sha256:d80cf684b541685fccdd84c485b31ce73fc5c9b5d7523bf1394ce134a60c6883", - "sha256:db24668940f82321e746773a4bc617bfac06ec831e5c88b643f91f122a785684", - "sha256:dbc02381779d412145331789b40cc7b11fdf449e5d94f6bc0b080db0a56ea3f0", - "sha256:dffe31a7f47b603318c609f378ebcd57f1554a3a6a8effbc59c3c69f804296de", - "sha256:edf4392b77bdc81f36e92d3a07a5cd072f90253197f4a52a55a8cec48a12483b", - "sha256:efe8c0681042536e0d06c11f48cebe759707c9e9abf880ee213541c5b46c5bf3", - "sha256:f31f9fdbfecb042d046f9d91270a0ba28368a723302786c0009ee9b9f1f60199", - "sha256:f88a0b92277de8e3ca715a0d79d68dc82807457dae3ab8699c758f07c20b3c51", - "sha256:faaf07ea35355b01a35cb442dd950d8f1bb5b040a7787791a535de13db15ed90" - ], - "index": "pypi", - "version": "==10.0.0" - }, - "psutil": { - "hashes": [ - "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d", - "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217", - "sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4", - "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c", - "sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f", - "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da", - "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4", - "sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42", - "sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5", - "sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4", - "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9", - "sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f", - "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30", - "sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48" - ], - "markers": "sys_platform != 'cygwin'", - "version": "==5.9.5" - }, - "py7zr": { - "hashes": [ - "sha256:c7cfb7183fb8f48038f1036a116ca89dc8bd57979d05b75567f00e88a5afe698", - "sha256:d036dee11fce69ad8d4fa86025ccfc4a3511ec27ee1c6b5bd8d6759313dbd077" - ], - "index": "pypi", - "version": "==0.20.6" - }, - "pybcj": { - "hashes": [ - "sha256:023082fd677f67ebd36fe96322a4a45ac33a2b340d49010d88e1867c76744c50", - "sha256:05fad9a905772774aacc96cb174571ac1f5afa80b9f54c6ec414d369865d305c", - "sha256:09872b32edad4e3653d5b357b244d267ca58fe52d4e1dd3cdff816d3bb9d9f7c", - "sha256:0eaa90639992b6096afb1485380fae7f084483db6b92867847a3bfdf22cc4efc", - "sha256:10182725b0e6aa944d13a10a4a9cb5208bafe0016b4326253340948153de4bc0", - "sha256:10961ea10ae930b9348132707b9dd3cf3e71a41ef1df7656fbc4f14a71f10747", - "sha256:15edd1786617127ecfda4274bbb04f09ae299c474ada86e369bcf050d5cb88dd", - "sha256:1a5365edcaa82dc47e7757ba2efb48f96b9b352e3811a2aaa90084802479ddbe", - "sha256:1c0e1657c233f9f4070ab578951e03d569f1b645042ce661341091f50e41b541", - "sha256:1e6434a46f852cd3e6929633b43537887bd381bc614dbf5c4a128fdde4966b3a", - "sha256:20fc0d8f67e2d9747e0c31082d5f64b112258ae602a85aa5c7e6bf5a7cad287b", - "sha256:21098001200273c3c9fd90e7bf909fb905a8e1c102c80b604cb7c6a3103ef7e0", - "sha256:225a0addf4b3d580bf4eae583b5168dac0125a703c53ded8b3f120882e1e0312", - "sha256:262f53e27bca6096e3424c63e5e59948b10985eee4b03a5d70c3f3f6161a79e7", - "sha256:2d6b34ec233fcf5a83ccfbf422fef22256947eaa7077aaa012e5961d15aa302c", - "sha256:2e1859d36c073231737956fbeb5bbcfa8dba880e1b66bfbd001466718d6d89dc", - "sha256:358dba3dc39a07cded6897b9f99bb5b951a0ad95d567eda535b44861caa02f5b", - "sha256:3847387b43af47d9677952b8a22d9c2d8a544c2175b6d5304c200669c05d39e1", - "sha256:393d95f83e47976d137bcec7b66986f51282dcb2091933f88983dd7eb89e59c4", - "sha256:39dd836134e261ec769cd5aa9ae7a3a330a7dac81efb66eb5504643abd8235df", - "sha256:421ed75e54ebecd79c80178c1df5bdbe1e0e3e10e7efef5f011b5f0be6a9a12f", - "sha256:421f211fb15aeb836b4ba61174cb409fc82222ab3b2486deb4953ae863e6507b", - "sha256:43e8bc75773ca06ee7a64602b799613171e4edf4d9d8fd38fa5c49f1cdbb4407", - "sha256:46b82fe50eb8171ee2205e935f3fd5900e31beb5e54e10c88f23a5420902467d", - "sha256:4bc8720f3a224c27bd413a930b9bec5f225fda050641258967b1ebb252a053fb", - "sha256:4d10dd75fad48555e9530c5565c7ccf13754adad2fe331feefb263055cdca7b3", - "sha256:534b8b253dbdb746c06bab28383db31d7e2b42aa9b33ed4e7836319622dcd75b", - "sha256:570a3cf4e016dcb0fc561991833e5170a2a0bc6ee88fe5667591f356bd7b7895", - "sha256:5de90f8b6c7fc1d28dbe74c29b1d5053a7a8703cbc2c6f4f112907ffd7529f8e", - "sha256:603daa737579cf69efb368fab716cdce18d0b2615af77bb623f5f42aa546b3d8", - "sha256:6485c6b091504c0e6431a9495309271626eaa9ecb23276903486824f94f4c551", - "sha256:6ca6ddae1302477879099d4c4efc65790f4610d71ceff7fbe8f8b60f6ac6dcff", - "sha256:6df9eccc99a0d7bc091b58cff2f507b89f076d657253975fa2ca9eb42dbb4733", - "sha256:6f589af70286ec6565e3415145a03abc3c14a23ed7ed198ac741de81af332f26", - "sha256:731800acfc6112132aa2b7d08f9d6fe49a0c0071b30985809d084e238af98dac", - "sha256:74d34340323996b70dbd73e9530cca71c05ff7c97e30fe4d32aeea2f877836ca", - "sha256:7801ee9a9fcd47b92d4d90ff9a28cfdc23195cad72bd8032938ab3c794942b43", - "sha256:795dff9229dc024e54bd0f618f5a3adb269ee0cccd7ac9a0bef29df388beed23", - "sha256:7b3773a77ae3b18778c9bf22c7ba6478a0e5416f84b7d2ac6d764001f6d0d985", - "sha256:872697d8bff2572e4225ed8cbce17be338faac28ec1ab3c00419aaef2f56dd3c", - "sha256:8b682ed08caabfb7c042d4be083e28ddc692afb1deff5567111f8855071b75c3", - "sha256:8e846a8272bf02202794fe22beaf389ed27c2d8ebf59aafb43af4935feac0389", - "sha256:8efed581f2ee74f1e0ec04a10e97881b93abc258d13b15ef966aee71732ac152", - "sha256:970dc23ca1c64611d35a3abe76a059cf551da53d62faefd84c5bf3e0af1602d1", - "sha256:9b56eeff51efa556ecc186260ac486a4ddd79ad37bc88d669e96c45190f3c0da", - "sha256:9fc313b1a5547c5416982853f2de1454980704f3ab3dbcad18dacdc565a2eafc", - "sha256:a74e70bf3fd50a413fdce4264e037b8e8f34cb8d9207ac364167b6eb076c14ec", - "sha256:a77796b4c5370cedd4fad2264b6d7a78cb40229c7fa3cbcab24df3adea768962", - "sha256:a81f14f213a75597f9be44feb97740a51adda558465fb159114472dc2ab39ef8", - "sha256:acfc4a02ddf22f6df7184441b39f38c31e95aa8af41de4d2f825821ab1fb85c6", - "sha256:b3861996b06b8238f799b4f1bd9542d1a8ae8e4765adbdde25ed011c3bda11df", - "sha256:b901f12380e988da07f21bb6b75da7f91fd9feffb43fcf70fad698e40a2ef3a7", - "sha256:b99f4291e59dcbe548be5a1e8c6a1a19a860184526c2d14fc374ec687b98ad7d", - "sha256:bb378b0f133e19d437eca4327bac7c3f38e30950c5c604092c72b18cba839bc2", - "sha256:bbb49772fc3896850a704215160df8316db89e5e8876b2d8af6c6c15b4e0f6ea", - "sha256:bc1684b9f7ec92d2ae94a137ec311bd2227f684429521061af7ceed4952c7f72", - "sha256:bdabbe7fd66886943393ecf98318d7801dd40183af80314acd4464bccdd44d53", - "sha256:bf87f2a7f827656bc6e1d9888d47931aa0ae35cdc4ff33b1cec70d8d462590b3", - "sha256:c1e02170f8d358a8ddc716606760c73d55eea6bdb0cca2d97b86447e9524708b", - "sha256:c72ff262613c9a6f20e80bcf1e8bbc000b78b95a7fa301164ab3e3bd23bd936c", - "sha256:c854a206d8c3a5a959b803405760f3627bb4878450e2f36b5d35af09c89152fc", - "sha256:d5b327df02761c42399c878cd6c37f885bf0639befbd4d1ab763cd44ba1e0552", - "sha256:d5c4ca6faff0af4b5f3e7d88d13ec76f8cac36c9bcc814b8c84d9f3f951b2cf9", - "sha256:d61f287f820787d3acf60d113c5ce6e506870d9d3103bc37a74373e72ce9d7a6", - "sha256:dc23f2ac2c1ded250f1aa66fbd1a3d823f76de549978b61eed4fb34affc11338", - "sha256:dc79ed4773cd35328377a8fedbbdcafb3a9d242ee63b96863c0692c81faefab8", - "sha256:df75707f466ab6fa086f164bff2df75fd16543c8d43ca43a268f938c1144e792", - "sha256:e6a74cb618da93ac1322d6a548a4508e76eb4c388ed1c80560bc25d8764cf272", - "sha256:e89a814f1727be7d543ac6910f0d94131f43a337e811ab684606d42dbc22b701", - "sha256:efe75e3b8768c4f9d454d3c1b2b2a67e757f2b00d638146d3a4cddb38460fc3a", - "sha256:f2f950ca403ffaa808a017e40e3371115bcb0b4b1061772b03e7d842555132ac", - "sha256:f4428b6808d781f4b605a27f53fc10a3ca343d1cd901c691b9ba2e4ed85a5fc7", - "sha256:f46ba61c942ee64198444c9562c5cf089eaf97f17b413e15fa1c0614df304734", - "sha256:f472da992a6ba58381c0314b994c01d20e522ff8836417ef1c0975bdae142406", - "sha256:f58e489e43c9a1688c7d5ceb7455b44952d87f183b7b9c915b301478a2b3bfbe", - "sha256:f8576a1dcf445ef064bf8c3b2cdc1d6353e41cb4b366329946883e285dcbcec0", - "sha256:fa787b414c4dc6b6cd75338fac18a7dbb53a09443dd863020a2d2bda76940ca6", - "sha256:fda423836d7d69cba6a6f99e7a34c2e5fe3621e5e945cd25ea9ba60a96223254" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0.1" - }, - "pycryptodomex": { - "hashes": [ - "sha256:160a39a708c36fa0b168ab79386dede588e62aec06eb505add870739329aecc6", - "sha256:192306cf881fe3467dda0e174a4f47bb3a8bb24b90c9cdfbdc248eec5fc0578c", - "sha256:1949e09ea49b09c36d11a951b16ff2a05a0ffe969dda1846e4686ee342fe8646", - "sha256:215be2980a6b70704c10796dd7003eb4390e7be138ac6fb8344bf47e71a8d470", - "sha256:27072a494ce621cc7a9096bbf60ed66826bb94db24b49b7359509e7951033e74", - "sha256:2dc4eab20f4f04a2d00220fdc9258717b82d31913552e766d5f00282c031b70a", - "sha256:302a8f37c224e7b5d72017d462a2be058e28f7be627bdd854066e16722d0fc0c", - "sha256:3d9314ac785a5b75d5aaf924c5f21d6ca7e8df442e5cf4f0fefad4f6e284d422", - "sha256:3e3ecb5fe979e7c1bb0027e518340acf7ee60415d79295e5251d13c68dde576e", - "sha256:4d9379c684efea80fdab02a3eb0169372bca7db13f9332cb67483b8dc8b67c37", - "sha256:50308fcdbf8345e5ec224a5502b4215178bdb5e95456ead8ab1a69ffd94779cb", - "sha256:5594a125dae30d60e94f37797fc67ce3c744522de7992c7c360d02fdb34918f8", - "sha256:58fc0aceb9c961b9897facec9da24c6a94c5db04597ec832060f53d4d6a07196", - "sha256:6421d23d6a648e83ba2670a352bcd978542dad86829209f59d17a3f087f4afef", - "sha256:6875eb8666f68ddbd39097867325bd22771f595b4e2b0149739b5623c8bf899b", - "sha256:6ed3606832987018615f68e8ed716a7065c09a0fe94afd7c9ca1b6777f0ac6eb", - "sha256:71687eed47df7e965f6e0bf3cadef98f368d5221f0fb89d2132effe1a3e6a194", - "sha256:73d64b32d84cf48d9ec62106aa277dbe99ab5fbfd38c5100bc7bddd3beb569f7", - "sha256:75672205148bdea34669173366df005dbd52be05115e919551ee97171083423d", - "sha256:76f0a46bee539dae4b3dfe37216f678769349576b0080fdbe431d19a02da42ff", - "sha256:8ff129a5a0eb5ff16e45ca4fa70a6051da7f3de303c33b259063c19be0c43d35", - "sha256:ac614363a86cc53d8ba44b6c469831d1555947e69ab3276ae8d6edc219f570f7", - "sha256:ba95abd563b0d1b88401658665a260852a8e6c647026ee6a0a65589287681df8", - "sha256:bbdcce0a226d9205560a5936b05208c709b01d493ed8307792075dedfaaffa5f", - "sha256:bec6c80994d4e7a38312072f89458903b65ec99bed2d65aa4de96d997a53ea7a", - "sha256:c2953afebf282a444c51bf4effe751706b4d0d63d7ca2cc51db21f902aa5b84e", - "sha256:d35a8ffdc8b05e4b353ba281217c8437f02c57d7233363824e9d794cf753c419", - "sha256:d56c9ec41258fd3734db9f5e4d2faeabe48644ba9ca23b18e1839b3bdf093222", - "sha256:d84e105787f5e5d36ec6a581ff37a1048d12e638688074b2a00bcf402f9aa1c2", - "sha256:e00a4bacb83a2627e8210cb353a2e31f04befc1155db2976e5e239dd66482278", - "sha256:f237278836dda412a325e9340ba2e6a84cb0f56b9244781e5b61f10b3905de88", - "sha256:f9ab5ef0718f6a8716695dea16d83b671b22c45e9c0c78fd807c32c0192e54b5" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==3.18.0" - }, - "pypdf2": { - "hashes": [ - "sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440", - "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928" - ], - "index": "pypi", - "version": "==3.0.1" - }, - "pypinyin": { - "hashes": [ - "sha256:3791f5b647e446866e6b5d3a63942ecbaeb29b49fed268a9d9a4982241c35fe0", - "sha256:a5d61a79c5f48f6b4a422f010c20d48fcd53c705784df4aa80e329493219a4be" - ], - "index": "pypi", - "version": "==0.49.0" - }, - "pyppmd": { - "hashes": [ - "sha256:024f714ebb8ddf59dae164adc3c220c24555d470f4adb5bd022abc50298cfff3", - "sha256:05950d8a39fd9bf6c64572d69a6dd0a1af3fadf8d4a2a0bb62f5b04c0a618300", - "sha256:075c9bd297e3b0a87dd7aeabca7fee668218acbe69ecc1c6511064558de8840f", - "sha256:07e067e114f05918c8a4ab1fa6a070e2c7a9e497aa73fbf6d87a90e7a6e62a57", - "sha256:09668aa43e4f02b8725e6233dfc66e532c72f0e69fa1b34dd814a9f7200e0496", - "sha256:0e9c001719527dbafdd7fd8709b98bd63c173451c2eddbaa77abf62486a13da0", - "sha256:10c8a41093952cde52b6d89488dc601ee7b10f6c95c430488f68987393777b46", - "sha256:12a783a0e3c76484a1bc93783867a36ab9a60de5b5298d57c9fe7348e848346e", - "sha256:12be01e919a34c6944568592b35451acf7c98ed18e005bb4b1c046ed520aff7f", - "sha256:1658714d012a5f9a8a3e67f3a9ede3519a2558064ccbd3163c39aca0cfd2412b", - "sha256:18d7cf4d0a9ced96ff1fa44de9ee3d65f5b06278c8f9a61c3edeb660f12f146b", - "sha256:18f863d58c4451e00765137be731c2b2150aff829468f59de4169e052429e1fd", - "sha256:1c0fd06aaf782e65b7b5bbc47f8a9dbe050c1ba18474ccbe0a2b37f57a8d8c72", - "sha256:1ec00b07c6b68feb402d6596f3575a7892ad69e4f455deee7b5301df703e60dd", - "sha256:2858471a291b51fab49242d78bd67c2b7719368618a02e4aa995de8c855da73c", - "sha256:2c31e5b331923f3b3b2cfbc66a60ecfd73db1a19a646bd1faf25bfde709a80d0", - "sha256:31a09fd1b10518342ff442b57dd8c890b9bfea6bbdbb785c729f0d139092e42e", - "sha256:385a0b1341ebdfd7cb101c43eea130546830073c01bdb5036bca45c033ee633e", - "sha256:3bc75ed4e969b09fd1a766dd79cb3d5efe56edc11c86ac0b357b5648c7181ce2", - "sha256:3ea6a0d26db17027805a804d013cf761d732df5bce9d6b314cd1c727fe347277", - "sha256:3ecb83e0cc92960f959111518ea208b51a58e8cc303ff959e9cd2cc56dd36a63", - "sha256:47ea218f7dfa94d15286c25d60db3091db1082ba958fa0a32ccaaaeaca7fc712", - "sha256:5331a7780d3444d7029e15e68385c94d6a26f688c1e87a5a9ee2e836ea6e4559", - "sha256:556b6a3af3fca2b41ca25f51c481e5df8df4da842fc5a567da7bb099cfa52423", - "sha256:5805c73590fb8f0ceb3e6cb115774b66a6f4700ae84b31d962ad69667e05dfbd", - "sha256:5847c47127ff9ea323f5910c62b9f136c3fab181a5144bfe72be13f051047357", - "sha256:5d90d87377d83d909eafbf23301057fe16e6662c98ffea738159a234d9000a68", - "sha256:6059ea0c9acc3b52b2961412ac75d1da72656f8b69bb8fc3d92eec6776176011", - "sha256:61acfeee5ed59796037499119edd3159bf6b8c5fcaef17e295a2ca4103112d60", - "sha256:6279f1c4b6aefacb95df49db2f2e232530592d1849c37b73478a4f26eb405d12", - "sha256:62f970173baf80aad9472c7c6edca4a021ae7965174b1c5d6f400c9571e92efc", - "sha256:63ddd5a81d6aaed9373cd9fc4de9529f10fa052aaf064ab283dc6218418cc5b5", - "sha256:68184b7246ea73a92a764e16cc18b74ccf3c8d6bfc438bbace57aeb1914118a7", - "sha256:6a0c524be57698fe61fff893d485a9af21e6bc0aa2d385b71a63ff951921d4b6", - "sha256:6e2f5ff5071e4e43c92065f383753d4ad59778816485a01ee7b29e2a1ff48140", - "sha256:6f8a3b9192714b3e4773fc49c100ca13defa2502cb38e56205eb5a131ccf555d", - "sha256:703c4fbc9b5e1454f403fb1d6b4a6c4c729f72eef14690146deecd2166429d6d", - "sha256:706d33cec3601d894f8a4a158bc652b7a3f01cd9e92c2da5d8711efeb9755835", - "sha256:71f994f281439705cb04c497adc2863551fa5813606af6fb26c673a44a36c4e3", - "sha256:74bd56b165283bb5586ff9ac7a896b217b3c94effe144b768279807840142bb4", - "sha256:7ae419f71afa88784d53dd2449882af982bbd0328fa22a7e6a339221f3143918", - "sha256:7e8d3c309061ae7fb40e4a26d30f8982b367abc562b9b8621cb79932cb3b94d9", - "sha256:7f1e7a1747518b5822eb755f3715d88bd1459e24de828aed86b7c1aa35e3ed76", - "sha256:8049c19af4b78b400b2347bff4514763257b55516c359144e9d8091991ed12e8", - "sha256:8680008b1b1e9e77f3337a1a53c1b32541cac9f93f79ae12d34de050585999ac", - "sha256:8a90b98f9d501eaedaca4d0e82f9e771bd2d780d71effcdeacc9fc6180a00e07", - "sha256:8aafe6fc436a782e6d424a0ac00de08a1559b6d6ddd08031adbe791ff4e54c90", - "sha256:93d0d6ed97046ce25d64427ec493e06c23f32838972258bf11d603c9c998d6b3", - "sha256:a5fbec6f39a307818593508d8623d9328baf494137d191fc98e11f47e058ceee", - "sha256:a63adeeb9dc4afd6d377ac1c9801f9539f9a81430e9c96d332023bf2ad6c04a1", - "sha256:a7240c00083527cf0b1bbdc92f6967e522efb9ad6968c953be174c390b091b3e", - "sha256:a7f83970a057157c88d4a53a40431d07d8d3f38029ad2eae621422f955bd243b", - "sha256:aadc63d0ac83f8c5744eb34ea47a70ff7bfab519b293482d7ccb09946c374dc7", - "sha256:ab4e29f774e064af09baf8478acd967684524e566b78fcc4f6f669757f0a2ab5", - "sha256:ac19ec1b6e3a0aadc1537466f537017189373593e23fe254df050fdd01f4a722", - "sha256:ac1aeba466617cf975cd6719070ca9721bcd83a1a84bd8cf74c3a2808724481e", - "sha256:aee9c52a6f232f3f7c683b87213aa3a9eacd281ab31187e784290ba1c05024fe", - "sha256:b0a87399ade5820f787758449976e758604c7739eb5f79ed9e594b5fa3a6a1bc", - "sha256:b6b6c01e46fcf785ad6c272be400ebcbcb434a1d91150614e10de8cc569b8bff", - "sha256:b8eee08c615ae9edd7bf1f214a377cac3d27417f22112685e581d4bab43029b0", - "sha256:bae08176e0d3ed0a5cbd838ff1ac557dfa088a652af633ab1905ab35bb9d7bc4", - "sha256:c60031de93834e5cd262db4b27272101d04a9a18c4cc49f81d483221211a97c8", - "sha256:ca6a926d229a6dbf2ccdb0d4e692d81ff927459b59a1cec14ef522522df6d757", - "sha256:ca8a842b4ff671642b63ed4edd4e1ff7dc0ba0a7af4135758233f056ab992fca", - "sha256:ccdfc8b2a1d73b2186850b9a5bd96106d5fd4419a620d344b0ab8bf630680cf8", - "sha256:cd227b8c292ac43d3297a91055fab51c27894dba39d04ccc774a72d9e6f85752", - "sha256:cecf0859b461bcf04439f32bcfb6e081016fa6204c92b5950d19d248fd1aad6b", - "sha256:cfb716a4a07ccbef84ed9fc31d012cef3b38404a6510e24d307cf64025999b21", - "sha256:d2c3c16f644afb1b3caa4f6c717682030f7c3f54a12af8b1416b21877f0b5226", - "sha256:d5c6c40f15b9fdea10bf966e5b07ee0a0ebcb8cf188ed9a466029c894816b303", - "sha256:e17b08a5c283faf48b4ee888f8fa53f919cd8afd0930eae4d59f719f6be519fb", - "sha256:ea4b1a326afe2055304740a03a233f7389f615179b9f6377264b306f619bfb11", - "sha256:f488164e8876d213b0627a7a6cb798081eaf84fd9ec6dde5a1668296b15e1a6c", - "sha256:f751882dd529328ca43af8018f79cdd02ed707fcda30a2fa9acb1ee5c48261a6", - "sha256:f79ebcd7312b541d3520e1a0d4c362731e24403e2f9f6761679b2ad819d5c706", - "sha256:f7a1b08612627d5280ef2dad1fadb0b1a10c70df0c484f9091eff5fab5e4c84e", - "sha256:f8dbe3076fe20c4d65cb1d1b51eeb17a1c177402b83100017a55daad888e198e", - "sha256:f9a3782f5accab4186d68c86defc61fcc7d0146e9cdc5b54e18656852c71db16" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" - }, - "pyside6": { - "hashes": [ - "sha256:0356dc73c138c25b980d716e425801a61d4aa1d9f7811cf8a825499edfd4d1ae", - "sha256:ad82cb12f805c5bec7693d6f1c0d6e4ae4b9462eb368a81bc0d3091de8fae76e", - "sha256:b509e4d3ffde4a594d70000f881452643c9aaed800bad2959882075c01f72428", - "sha256:ca259b4377eb74e4c9cb74a2afb18c37658f0c83dc18229d1f325974739f6df2", - "sha256:e6d879ca0f8827a7866856fbedd08857e4bd8f9a858dc998dea10d1913e97938", - "sha256:f0944cb0da34dc3b0bb62c7a20b6618667760eccdaa4262a90ba7f64b19fb783" - ], - "index": "pypi", - "version": "==6.5.2" - }, - "pyside6-addons": { - "hashes": [ - "sha256:15bf592e54b3409853c3db23bdb20fb14725b84d50feee862e8b2a3a8a3a0627", - "sha256:26b55a658fed94a0b3c09927b408663860aca530bf6e83040dcb8fdcc221877d", - "sha256:793a53b7db32f7780fa9571a14f0bce3700604247127cdb2125b380c577a92eb", - "sha256:a31cb7eca2e8cd80332e131124ed9ad197610a2a80eac4f2bd58e9124e38060a", - "sha256:bea98d823179b814109dcf3ac6c8a0eddf886570aa9ef8ef09b6bae37bc227ca", - "sha256:c77e06fd511cc8b9292c01724504d6806c33c1f5904bc59f5a8c560a74c15438" - ], - "markers": "python_version < '3.12' and python_version >= '3.7'", - "version": "==6.5.2" - }, - "pyside6-essentials": { - "hashes": [ - "sha256:149f1db77d82f35c6a88a403f5297645ebe737df28e163ac4dc402dfa0b961ee", - "sha256:1620e82b38714a1570b142c01694d0415a25526517b24620ff9b00c9f76cfca9", - "sha256:4be6ed964cb863823f2788717b9a6ba8f1d94eb3fa2bf1584f4621ab5e021e27", - "sha256:6b21cf78b33554f793d409d79b828d9578d34200c86f813ef1b8dac756589b71", - "sha256:c9606de7afaee57f2c0861cc4b973bd630556dd38ec0d1542560cba81c27ab81", - "sha256:e67b10f96f0ac5ed24d7e3c4985d37c70550a9dee6f38b127faf5e5341151665" - ], - "markers": "python_version < '3.12' and python_version >= '3.7'", - "version": "==6.5.2" - }, - "pyzstd": { - "hashes": [ - "sha256:00c188704141c709da96cc4a79f058d51f5318e839d6f904c7cc9badcf78e98e", - "sha256:013321ddaff083b24e43a8b06303446771978343b488ed73adf56c70a46e2783", - "sha256:0a4334e972109bdd17fb40dbdd9fcca6137648cab416fca505a2dcd186f50533", - "sha256:12668ceb8329aaa908b4d907d3a77bb748ff28b309c3b105c995a8715d535d2b", - "sha256:14121a4d95070f54bdc9a80dab1dd8fd9093907a1e687926447ca69b5b40a4d5", - "sha256:1b9cda5314982d64c856f9298be0d9bf69fbff0ca514d1651037616354b473ff", - "sha256:1cbf212253abd65e6451acdfb608adafe98ad8f05462fb9a054ddab816545caa", - "sha256:1dbe76b6d8fe75f6dbec24793fc07b1d1ae9464de9941138d5b9668f7670e6b0", - "sha256:209a92fbe892bd69cde58ffcb4861468e2c3c2d0626763e16e122bb55cb1fb1a", - "sha256:20f2dd56d46441cd9277077060c34c0b9ce3469412665ea5ccd506dd2708d994", - "sha256:23695dabdfd5081beab25754dc0105b42fbd2085a7c293901bcb45045969c5ec", - "sha256:250dad90140a6faea4cef555f339b6ceaad5cf03ed1127b8d06de214ff0db2e7", - "sha256:289e25871fe232d2482c0985a75a1faa7c92e10a6c3e3914d165f62d005d0aa6", - "sha256:2919afd114fd12309ed2f831ef6e95730ebf13c2a92d258ad055769d00ef4d7a", - "sha256:29e452caaf0de9cc17319225921d8c28cdc7a879948e990ff1e7735e7f976517", - "sha256:305c232462dbe80d0ee5ec91b1b0ec9153ec6ba6393d5348741af5d30b07ef52", - "sha256:31f60f01884350aec24e7a68f3ad089151b7a636490203c41a1a7c8e0cddd9b8", - "sha256:3351ad2feb51dcbb936defd47cab00d6f114214f224636503ed08298f30164c9", - "sha256:346f835e368e1051f8ea187ad9b49759cf6249c9ebf2f2a3861e435a568104b8", - "sha256:370b34a7c2f9c53cee494028daa5a7264690e1756a89c3855fd0be5ad298ec30", - "sha256:3a26df749589d898cd3253d2139eb85b867ddffc49286059c8bdb3cb9ce9b545", - "sha256:3bc0e7e2cccf78e562ab416daf68448b6552a5b6450a1ff3e15cabfc19254883", - "sha256:3f0fe2ef7ebc6e9b347585e414c4fefd32270ba8bdf9eb82496f3030cbdca465", - "sha256:3f72f310b10b730cddfb654006ae497e7706c81e6a7642d3da7fd2439df7d88d", - "sha256:40bdb468281a5cd525e2e990b97344f0974e0589bd1b395501c25471fcd7edda", - "sha256:4358dd80b315c82d760b44c6df7857c9c898d04e7b0c14abb0eb3692354e9379", - "sha256:441078bfd3b508597415338af667c3575980364f1286eedde58291558b9c2832", - "sha256:47c2a4c319300c381f194274203f47b12c433e1fd86b90ecdc7fb258c630f93b", - "sha256:49c57ae18f138a4b66480b2364fe6a0f2345ada919e93fc729c95c6b17ec73a4", - "sha256:4a0dcb32ac4d1d67a77ae6a2d60ea0921af7e682b3427202d8acb8e86642391c", - "sha256:4ed01beb31d5177456ec2c4b66591a0df83dbc72df29f05f40502bfefe47bbe4", - "sha256:50ccbaafee80b4f1c5c55bbe07f80871b9b8fe3499bf7357dde2c23fb1c2ac0e", - "sha256:51607d7d44f94a364ef0e3ccf9a92390def0faf6e7572eef082f15c657b5d03a", - "sha256:5345c7a697327e2fa7c37534bb2968ea84595d8ec7fc8c4a60216ec1be6e65bd", - "sha256:542808d88464d538f5d2c6b48b545a7fe15f0d20c7fa703b469d039a08c9fa10", - "sha256:5819d502dacd54114c30bc24efcb76e723b93f8f528be70851056a396a792c46", - "sha256:5aed5fc86d0bfc5f16e871cbb35ec93df61476d7fde4c1c6081015a075ecfbc1", - "sha256:5d9ec8634ab0cbfbcff535ac07555ebdae0282ad66762f0471fad11c16181e33", - "sha256:5fb00c706d0b59c53124f982bd84b7d46866a8ea2a7670aaaa1ab4dbe6001b50", - "sha256:5fd7cf79949174d1018b896638f88aea1ff2a969f87a6199ea23b25b506e26c5", - "sha256:606b2452d78f0f731566d392f8d83cd012c2ffadb2cb2e2903fdd360c1faac8a", - "sha256:6128cb653d011f3781554b70ce1f1f388cd516820fbaf8fd03ee245ecaa48349", - "sha256:639935b5b3d9ed3911493504581254b76cb578279302f7f340924ac5bfca4090", - "sha256:64564f4c175c5bb8e744de5816d69ee0b940e472160a5e665f30adc412b694f3", - "sha256:69f12ce4866a3725138e97f22f2c4cb21d3ae18cd422906cd57ed12a9ffd86c5", - "sha256:6a60ee6836599363a24367cf780ad45446b07eba49ec72d19bad761d5414aca7", - "sha256:6b9af8d62c087354abd071e01d9445ea51b31779c8a4a0d5c14ee12caee3d18f", - "sha256:6c456882baab2a48a5bfabe458a557af25d0768ff29acbe200461e84c0f697d5", - "sha256:6f281cc2f096530c339f122e0d9866545f5592dd9bffe0fade565c2771130a45", - "sha256:73877eebbdcb8259cf0099665f8c8274d4273b361371405a611fb6bd9f4d64f6", - "sha256:74455bd918e7bc9883e3178a1a8fe796308670f0ee4488c80a0d9514e13807a1", - "sha256:7452ae7e6d80e697d78d3f56d1b4d2a350286eea229afb35f55ab88b934b6acd", - "sha256:77294f0f797c97a46ffb3daff1fe097c9d5aa9f96867333978e6791286963e50", - "sha256:7ac886e04f253960ae82e38ded8352085c61d78de99412d178a94ecf475b5e5f", - "sha256:7c420878726d677da7484f6021dbe7e1f9345a791b155de632c6ce36678fb621", - "sha256:836f1d85a4b5d3689d455aeb1dc6c42acb96aaf8e5282825c00ccf2545ad5630", - "sha256:84aa6eecba967bdac167451501dcaceec548d8b8c4ca7fa41ceda4dbfc279297", - "sha256:866ba6ce85f337fa1677516217b6f10fc25e19acb6e17a501d5822e66396bdd5", - "sha256:86e0e65e205793b337d62d9764700dfd02b5f83b01e26ad345736e7ac0554ebd", - "sha256:87a1a4ca93da414f3b6da8131e61aca6d48a4e837fb0b1cbde05ae9d13332317", - "sha256:8d3a1b6fa71a0ae7abc320d9db91b5a96a71eef1dbee0d62a6232b71c97af962", - "sha256:8f9eb97fb6fd4551ff9d5012b4fcee9abeea9c8af6b9e3ebc3c76cc2bd0a43a7", - "sha256:91453ce9476363d777b2ea2e9c6dccecd2073cf35697e048de2e8d47e1f36c7c", - "sha256:9596aeb8c71192f4fba1ca25cec420da195219398d2df811d5082559efd9561f", - "sha256:960ab83a977a44284c4ffab2820ccd6c9b332571a3d622fefa4b29b0a5de72b0", - "sha256:97e05f66c5847e6889594508298d78ddb84a0115e9234d598415dc5a06d3a4a7", - "sha256:9ac634753f6d26cba503cea7bb5b350aec7c5366f44fa68c79e9c90be9fd0ebc", - "sha256:9e1097d8b57f64878a3f176f4cd6b9a1bbe9fb2d236f1a85a4357722626d8f25", - "sha256:a1b81cc86b69ff530d45e735ed479e14704999f534ad28a39f04be4a8fe2b91f", - "sha256:a4f786f1b1ab39a0908db04ebe5b2c7cbc6f1ce07a27d3a12eb980bffd7fea7d", - "sha256:a594795ef89bd83297c860ff585f2d25580ce9805eb9cc44c831d311e7f1951a", - "sha256:a708b9e6ff1826504940beb6b5c2c9dfd4e3b55c16ab88a4572f5b9dbb64cc56", - "sha256:a90b901ccfd24b028faea19c927ff03f3cfefe82ba0b931fbb8da4ef0664911b", - "sha256:ae3d0575721a372c20130681bfaf873225fd9e1c290b7d56b7e0c14f413318f6", - "sha256:afef9eb882cf3b395eef9c85b737a4acd09528975e6a5d9faedf28874ca65f52", - "sha256:aff1b469187f6c789cdf17cd95c9b24e87396dc86953b1cf38b9a05cea873c80", - "sha256:b2ae8993f3863632d31ca8921c8a5dc9ecc5551c7b88895cefb5a26d17643391", - "sha256:b2dd39e12f7467a7422ce50711524759d4d22016714cbae6a7096b954bc2fa32", - "sha256:b4de7741d542a477387299bf9450e8be3e768c352d6b3438254eb02af1e59462", - "sha256:b5b517fbbc5d223fc36041673e7c2a0d3a82be6a5464a5f0599069330b76f97d", - "sha256:bdc09de97b1b3f6c3d87fec04d6fe29dd4fefe6b354ad2d822fc369b8aa0942b", - "sha256:c249741b10eb714578d765487b767e0e7fcc2ac84a299209a6073566e730dbea", - "sha256:c2b093a74b10232c70b5d29814fcee6544bb6f30e2d922d26db9ab4b4cd00c04", - "sha256:c31f6dd5bd60688d51487a3f5e2ae29ed1948926e44d7a2316b193b083f80d5d", - "sha256:c41e5457f4de5d38a270bc44619873589bbe6fe251225deec583ed20199df0f3", - "sha256:c46e77c2ad614a0399503dc675d72436cbf6332a20d49a0e5bad03058d6cbfad", - "sha256:c9589cb79d4e401630481755c92b072aa7ba5505ec81dec865ef43932ec037e4", - "sha256:ca19213785f864781848e0216cba07e97f563f60a50bbc7885b54461d8c64873", - "sha256:cbfdde6c5768ffa5d2f14127bbc1d7c3c2d03c0ceaeb0736946197e06275ccc7", - "sha256:cd6a8d43a0c294918e3afb7e4b1d8c04d2e4c3ea9ddf05475fdaf366c7e5b3a6", - "sha256:cffaab46f9e04856dc3daa6097bfb3d3bea0b1771237e869c57b13f3dcc2c238", - "sha256:d0929302d187bfeca335b7f710f774f1b2ea3f610b2a80e8a1ac2da216cd9766", - "sha256:d44a7d4586f02b630658298c089ff755e74d0677b93c71e09d33dd35bdd4987a", - "sha256:d7ddbf234c9adc72189bb552d830e9a0c2c4401b5baf7b003eacd5c552ddcc00", - "sha256:dca286c6c1ca5febf13f5f2ae7e8aa7536e49bd07f4232796651a43ff741ceca", - "sha256:dcb2172ca8b62f82af9d1f8db80c21c64c5ba3991935caefde88bb378f0afb51", - "sha256:e4e00c1600022b47ef0e9e1f893cb0c2322209ec6c1581a3e3f63ed78330ddf0", - "sha256:e789e19095b818f7126180b4387c0f01700c3ad2378a4e7649b2ddf4bf47ffbc", - "sha256:e79babb67b415aa54abb213897ceaa011515a5f3e146a2a97f4e6486b9743af4", - "sha256:e8f75e839ee253af60b03d9957182fdd069dfaebb62b4e999bd74016f4e120bb", - "sha256:e9934277abdddf9c733267e4dcc4886de8a3302d28f390237d447e215e8ce47d", - "sha256:ef3399e0544b46d31c2a8ff14ae1fb3c3571ae1153bbbc5ddf0d242c67bde624", - "sha256:f169e166774587227255f6ffe71f5b3303ea73cde0e2c6d52e53b9e12c03d787", - "sha256:f1d8b58f00137ccbe8b828a5ede92be3f0115cef75e6bed88d4d0bd1e7a0b1fc", - "sha256:f2839c13e486e4a23b19b1d2dc4624565cec6c228bbf803c066be1106515966b", - "sha256:f66790e4b2dcfcabc0aa54dd89317ea5671cabf06aa93cbef7cbdd4d2fdb7ee3", - "sha256:f6d8a881b50bb2015e9bdba5edb0331e85d41ff44ab33cde551047480b98d748", - "sha256:f7cfc683d320402d61205a196ace77f15dcfd16b5771f8b9ffaf406868c98e78", - "sha256:f9c5fc29a5b9d61a8f0a3494172107e0e6cf23d0cb800d6285c6722ba7fc3535", - "sha256:fc92a718bccb8ce5c9eb63fca743c38f3fa4c4e47f58f0c4ada51b2474668184" - ], - "markers": "python_version >= '3.5'", - "version": "==0.15.9" - }, - "qt-material": { - "hashes": [ - "sha256:b5dbb5ade97217cf7ae336bb7c047c90beb39dc7f2048a498c916ad5f2f7ae23", - "sha256:ceddd9b3aca167e5d620644ce350c3df4c0220c5df457ab6059f634d7ac747e3" - ], - "index": "pypi", - "version": "==2.14" - }, - "requests": { - "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" - ], - "index": "pypi", - "version": "==2.31.0" - }, - "retrying": { - "hashes": [ - "sha256:345da8c5765bd982b1d1915deb9102fd3d1f7ad16bd84a9700b85f64d24e8f3e", - "sha256:8cc4d43cb8e1125e0ff3344e9de678fefd85db3b750b81b2240dc0183af37b35" - ], - "index": "pypi", - "version": "==1.3.4" - }, - "shiboken6": { - "hashes": [ - "sha256:3fbc35ff3c19e7d39433671bfc1be3d7fa9d071bfdd0ffe1c2a4d27acd6cf6a5", - "sha256:4e073b40734a7f9cc6ac289c735362967bb45c34f591f7c0fab7cef1d62e6a66", - "sha256:558bdb3b0ccc8aac5f9029eabfd6ad8d41f93a87cd7f54395e07732fce39280d", - "sha256:ba7af130d34ea03e329baebd80982d2635aee7e58dbb07a07085509a501ed03f", - "sha256:cc89759aabafce49b629b189ed7c81986fedaac695714a4eed1aaeb16783c6a9", - "sha256:e60d8dd3292e3f87662b9e37ae5c558c519f8250da627c473b68bd8d24ed3243" - ], - "markers": "python_version < '3.12' and python_version >= '3.7'", - "version": "==6.5.2" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "texttable": { - "hashes": [ - "sha256:290348fb67f7746931bcdfd55ac7584ecd4e5b0846ab164333f0794b121760f2", - "sha256:b7b68139aa8a6339d2c320ca8b1dc42d13a7831a346b446cb9eb385f0c76310c" - ], - "version": "==1.6.7" - }, - "urllib3": { - "hashes": [ - "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11", - "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.4" - } - }, - "develop": { - "altgraph": { - "hashes": [ - "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd", - "sha256:c8ac1ca6772207179ed8003ce7687757c04b0b71536f81e2ac5755c6226458fe" - ], - "version": "==0.17.3" - }, - "astroid": { - "hashes": [ - "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c", - "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd" - ], - "markers": "python_full_version >= '3.7.2'", - "version": "==2.15.6" - }, - "dill": { - "hashes": [ - "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e", - "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03" - ], - "markers": "python_version >= '3.11'", - "version": "==0.3.7" - }, - "isort": { - "hashes": [ - "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504", - "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==5.12.0" - }, - "lazy-object-proxy": { - "hashes": [ - "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382", - "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82", - "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9", - "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494", - "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46", - "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30", - "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63", - "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4", - "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae", - "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be", - "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701", - "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd", - "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006", - "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a", - "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586", - "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8", - "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821", - "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07", - "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b", - "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171", - "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b", - "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2", - "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7", - "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4", - "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8", - "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e", - "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f", - "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda", - "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4", - "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e", - "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671", - "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11", - "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455", - "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734", - "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb", - "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==1.9.0" - }, - "markdown-it-py": { - "hashes": [ - "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", - "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==3.0.0" - }, - "mccabe": { - "hashes": [ - "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", - "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" - ], - "markers": "python_version >= '3.6'", - "version": "==0.7.0" - }, - "mdurl": { - "hashes": [ - "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", - "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==0.1.2" - }, - "platformdirs": { - "hashes": [ - "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d", - "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.10.0" - }, - "pygments": { - "hashes": [ - "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692", - "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==2.16.1" - }, - "pyinstaller": { - "hashes": [ - "sha256:0df43697c4914285ecd333be968d2cd042ab9b2670124879ee87931d2344eaf5", - "sha256:1fde4381155f21d6354dc450dcaa338cd8a40aaacf6bd22b987b0f3e1f96f3ee", - "sha256:24009eba63cfdbcde6d2634e9c87f545eb67249ddf3b514e0cd3b2cdaa595828", - "sha256:28d9742c37e9fb518444b12f8c8ab3cb4ba212d752693c34475c08009aa21ccf", - "sha256:2d03419904d1c25c8968b0ad21da0e0f33d8d65716e29481b5bd83f7f342b0c5", - "sha256:3a331951f9744bc2379ea5d65d36f3c828eaefe2785f15039592cdc08560b262", - "sha256:5e446df41255e815017d96318e39f65a3eb807e74a796c7e7ff7f13b6366a2e9", - "sha256:78975043edeb628e23a73fb3ef0a273cda50e765f1716f75212ea3e91b09dede", - "sha256:7fdd319828de679f9c5e381eff998ee9b4164bf4457e7fca56946701cf002c3f", - "sha256:9fc27c5a853b14a90d39c252707673c7a0efec921cd817169aff3af0fca8c127", - "sha256:cd7d5c06f2847195a23d72ede17c60857d6f495d6f0727dc6c9bc1235f2eb79c", - "sha256:e5fb17de6c325d3b2b4ceaeb55130ad7100a79096490e4c5b890224406fa42f4" - ], - "index": "pypi", - "version": "==5.13.0" - }, - "pyinstaller-hooks-contrib": { - "hashes": [ - "sha256:596a72009d8692b043e0acbf5e1b476d93149900142ba01845dded91a0770cb5", - "sha256:aa6d7d038814df6aa7bec7bdbebc7cb4c693d3398df858f6062957f0797d397b" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==2023.6" - }, - "pylint": { - "hashes": [ - "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413", - "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252" - ], - "index": "pypi", - "version": "==2.17.5" - }, - "rich": { - "hashes": [ - "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808", - "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39" - ], - "index": "pypi", - "version": "==13.5.2" - }, - "setuptools": { - "hashes": [ - "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f", - "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==68.0.0" - }, - "tomlkit": { - "hashes": [ - "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86", - "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==0.12.1" - }, - "wrapt": { - "hashes": [ - "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", - "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", - "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", - "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", - "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", - "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", - "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", - "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", - "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", - "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", - "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", - "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", - "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", - "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", - "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", - "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", - "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", - "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", - "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", - "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", - "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", - "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", - "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", - "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", - "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", - "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", - "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", - "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", - "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", - "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", - "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", - "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", - "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", - "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", - "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", - "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", - "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", - "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", - "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", - "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", - "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", - "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", - "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", - "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", - "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", - "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", - "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", - "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", - "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", - "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", - "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", - "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", - "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", - "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", - "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", - "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", - "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", - "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", - "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", - "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", - "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", - "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", - "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", - "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", - "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", - "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", - "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", - "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", - "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", - "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", - "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", - "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", - "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", - "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", - "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" - ], - "markers": "python_version >= '3.11'", - "version": "==1.15.0" - } - } -} diff --git a/README.md b/README.md index a4f5819..e5ad06b 100644 --- a/README.md +++ b/README.md @@ -16,19 +16,15 @@ **由于作者某天实在是受不了B漫网页版的观看体验 ~~(时而混入漫画中的广告,无法便捷快速的放大图片,进度栏作死一样的反复横跳挡视线等等...)~~,再加上作者的仓鼠属性 😛** **因此 将将将~ 🎉 一个好用的哔哩哔哩漫画下载器就此诞生!** -
- - -
## :sparkles: 主要功能 / 特性 +- **~~(白嫖)~~ 黑科技下载未解锁章节!** - **已打包成单个可执行文件,双击即用!** - **易操作的图形界面!~~(不用再费劲的部署环境跑命令行)~~** - **无需漫画ID,可直接关键词搜索漫画!并附带搜索词高亮!** -- **丰富的漫画详情信息,并提供元数据下载!** - **可配置的多线程下载,速度拉满!** - **实现了应对网络波动等情况的异常重试,以及应用了指数级退让来避免在短时间大量重试被拉黑名单** -- **本地漫画管理功能,一键检查更新!** +- **丰富的漫画详情信息,本地漫画管理功能,一键检查更新!** - **通过正则匹配过滤重复的章节名称内容,以及过滤非法字符!** - **提供多种可选的保存格式:** - PDF @@ -41,23 +37,48 @@ - **一键清空用户数据,妈妈再也不用担心我删不干净软件了!~~(bushi)~~** - **多种主题选择** +## 📸 相关截图 +**使用哔站解析** +
+ +
+ +**使用BiliPlus解析** +
+ +
+ +**下载进度界面** +
+ +
+ ## 📝 使用指南 -- **本软件的正式版只能下载免费章节和用户已解锁的章节** - - 不过呢有需要的可以尝试预发布版本 (~~黑科技~~实验性功能) 详情请见 [v1.2.0-alpha](https://github.com/BiliBili-Manga-Downloader-Dev-Team/BiliBili-Manga-Downloader/releases/tag/v1.2.0-alpha) +- **本软件有两种下载解析方法:** + - **B站解析** + - 只能下载免费章节和用户已解锁的章节 + - **首先获得你的Cookie** + - 方法一 - B漫手机客户端扫码登入 + - 方法二 - 手动获取Cookie: + 1. 以谷歌浏览器为例,打开B漫首页并且登入 + 2. 点击 `F12` 打开开发者工具 + 3. 点击 `应用` 标签 + 4. 在边栏中找到 `Cookie` ,点击 `https://manga.bilibili.com` + 5. 在右侧的详情中找到 `SESSDATA`,复制 `值` 粘贴到程序设置选项中的 `我的Cookie` ,回车确认 + 6. 如果提示 `Cookie有效!` 那么就成功了! + 7. 否则请再次确认上述步骤,检查是否正确复制内容不含空格,还有疑问的话欢迎联系作者或提 `Issues` + - **BiliPlus解析** + - 利用 [biliplus](https://www.biliplus.com/) 提供的 [ComicWebReader](https://www.biliplus.com/manga/) 在线漫画平台的api来尝试获取未解锁的漫画章节 + - 该网站现有 4w+ 已关联 `Bilibili` 帐号的访客,也就是说很有概率你想看的漫画已经有人购买了,所以你可以白嫖 + - **特别提示: 毕竟是要提供 Cookie 给第三方网站托管,因此可能会有潜在的安全风险。敏感人群请不要使用自己主账号的 Cookie** + - `BiliPlus` 的 `Cookie` 获取方法跟上述一致,在 [ComicWebReader](https://www.biliplus.com/manga/) 登入后在开发者工具中找到 `access_key` 粘贴到程序设置选项中的 `BiliPlus Cookie` 即可 + - **兼容性:目前只在64位的Winodw 10上测试通过,不过其他>=windows 10的版本应该都能运行,发现问题的欢迎提Issues** -- **首先获得你的Cookie** - 1. 以谷歌浏览器为例,打开B漫首页并且登入 - 2. 点击顶部地址栏左侧的🔒图标 - 3. 点击Cookie选项 - 4. 在弹出的界面中依次展开"bilibili.com" -> "Cookie" - 5. 找到"SESSDATA"值,复制"内容"粘贴到程序设置选项中的"我的Cookie",回车确认 - 6. 如果提示"Cookie有效!"那么就成功了! - 7. 否则请再次确认上述步骤,检查是否正确复制内容不含空格,还有疑问的话欢迎联系作者或提Issues - **搜索 / 选择章节 / 下载 的功能介绍我想已经不言而喻了,这就是图形化界面的好处!** - **值得注意的是:本软件不支持断点续传和下载任务缓存的功能 ~~(毕竟一章漫画太小了,好像也没什么必要,断了不如重下)~~,所以请确保不要在下载中途关闭!** - 如果程序意外中断,可以选择把下了一半的文件都删掉(一般在目标漫画文件夹的根目录下),重新下载 - **程序缓存和日志历史文件存在 `C:\Users\AppData\Roaming\BiliBili-Manga-Downloader\` 目录下,可以通过"清空用户数据"功能一键删除** -- **如果想用"本地库存"功能,需要注意的是:下载好的漫画的文件夹名以及章节名都不能更改,否则将会无法正确读取漫画数据** +- **如果想用"本地库存"功能,需要注意的是:下载好的漫画章节名以及保存的 `元数据.json` 都不能更改,否则将会无法正确读取漫画数据** - **🔥 下面我要隆重的推荐一款搭配本软件使用的漫画浏览器 ~~(可以说就是为了这点儿醋 我才包的这顿饺子)~~** -
- **NeeView** 是一款 Windows 下开源的图片浏览器,其特色是可以像翻书一样同时浏览两张照片,还支持压缩包看图、鼠标手势、触摸操作、多线程和超前查看、支持 PDF / 视频。 原生支持中文 @@ -73,11 +94,11 @@ ## 💡 TODO List ~~(在可见的未来...)~~ - **PS: 也欢迎小伙伴们多多的在Issues里提意见,不管是Bug还是操作逻辑,界面优化等等作者统统笑纳~** - 🟦 缓存更多资源,减少网络请求 - - 🟦 添加二维码扫码登入功能 - 🟦 添加一个启动程序加载进度条 - 🟦 添加我的追漫界面,以及追漫功能 - 🟦 对于有特典的漫画,提供特典下载界面 - **已解决** + - ✅ ~~添加二维码扫码登入功能~~ - ✅ ~~添加不同的界面主题~~ - ✅ ~~添加检测cookie无效或者过期功能,并且弹窗~~ - ✅ ~~鼠标移动到漫画封面改变鼠标图标,提示用户可以点击跳转~~ @@ -103,9 +124,8 @@ 2. 这一步可能会花费一定时间,中途需要手动确认安全漏洞检查 2. 打包好的程序会被移动到项目的根目录 "哔哩哔哩漫画下载器.exe" - **彻底清除项目 ~~(删库跑路)~~** - 1. 执行 `pipenv uninstall --all` - 2. 执行 `pipenv --rm ` - 3. 执行 `cd .. && rm -rf BiliBili-Manga-Downloader/` + 1. 执行 `pipenv --rm ` + 2. 执行 `cd .. && rm -rf BiliBili-Manga-Downloader/` ## 🔨 PR 格式 - 遵循项目已有代码的 python doc 格式 @@ -118,10 +138,21 @@ ## ⚰️ 更新记录 -![Alt](https://repobeats.axiom.co/api/embed/da4fa229ddede4560beefbe2dee47490257186e5.svg "Repobeats analytics image") -### v1.2.0-alpha - *2022-07-4* -- 非生产准备就绪的实验性版本 -- 新增功能: 利用 [biliplus](https://www.biliplus.com/) 提供的 [ComicWebReader](https://www.biliplus.com/manga/) 在线漫画平台的api来尝试获取未解锁的漫画章节 +![Alt](https://repobeats.axiom.co/api/embed/cc62fded834eb06fc9b30cf7ffd54eeb53d700fc.svg "Repobeats analytics image") + +### v1.3.0 - *2022-08-11* +- 新增功能: + - 二维码登入 + - 利用 [biliplus](https://www.biliplus.com/) 提供的 [ComicWebReader](https://www.biliplus.com/manga/) 在线漫画平台的api来尝试获取未解锁的漫画章节 +- 优化配置: 移除保存文件夹名里的漫画ID信息;元数据现在默认保存,并且以此来初始化我的库存 + - 老用户需要重新下载一章漫画,然后把以前下载好的移动到新文件夹中 +- 修复bug: + - 修复个别`png`保存为`jpg`的情况 + - 修复`BiliPlus Cookie`检测可能出现的隐藏`bug` ([#61][i61]) + - 修复`BiliPlus`可以看未解锁的漫画章节,软件无法下载 ([#52][i52]) + +[i52]: https://github.com/Zeal-L/BiliBili-Manga-Downloader/issues/52 +[i61]: https://github.com/Zeal-L/BiliBili-Manga-Downloader/issues/61 ### v1.2.0 - *2022-06-20* - 新增功能: 现在可以一键保存漫画的元数据了,包括漫画封面,漫画信息, 等等 (json格式) ([#39][i39]) @@ -129,6 +160,7 @@ [i39]: https://github.com/Zeal-L/BiliBili-Manga-Downloader/issues/39 + ### v1.1.0 - *2022-05-19* - 新增功能: 添加了多种主题选择 - 新增功能: 一键检查软件更新 diff --git a/setup.sh b/setup.sh index 0ec5c3e..f9e54bd 100644 --- a/setup.sh +++ b/setup.sh @@ -12,10 +12,12 @@ echo -e "\033[34m\n 重新编译UI文件 ... \n\033[0m" pipenv run pyside6-rcc src/ui/PySide_src/resource.qrc -o src/ui/PySide_src/resource_rc.py pipenv run pyside6-uic src/ui/PySide_src/mainWindow.ui -o src/ui/PySide_src/mainWindow_ui.py pipenv run pyside6-uic src/ui/PySide_src/myAbout.ui -o src/ui/PySide_src/myAbout_ui.py +pipenv run pyside6-uic src/ui/PySide_src/qrCode.ui -o src/ui/PySide_src/qrCode_ui.py echo -e "\033[34m\n 修复UI文件中的导入问题 ... \n\033[0m" sed -i 's/resource_rc/src.ui.PySide_src.resource_rc/' src/ui/PySide_src/mainWindow_ui.py sed -i 's/resource_rc/src.ui.PySide_src.resource_rc/' src/ui/PySide_src/myAbout_ui.py +sed -i 's/resource_rc/src.ui.PySide_src.resource_rc/' src/ui/PySide_src/qrCode_ui.py echo -e "\033[34m\n 显示项目目录 ... \n\033[0m" pipenv run pipenv --where diff --git a/src/BiliPlus.py b/src/BiliPlus.py new file mode 100644 index 0000000..b715ddf --- /dev/null +++ b/src/BiliPlus.py @@ -0,0 +1,223 @@ +from __future__ import annotations + +import typing + +import requests +from retrying import retry +from bs4 import BeautifulSoup + +from src.Comic import Comic +from src.Episode import Episode +from src.utils import logger, MAX_RETRY_SMALL, RETRY_WAIT_EX, TIMEOUT_SMALL + +if typing.TYPE_CHECKING: + from ui.MainGUI import MainGUI + + +class BiliPlusComic(Comic): + """BiliPlus 单本漫画 综合信息类""" + + def __init__(self, comic_id: int, mainGUI: MainGUI) -> None: + super().__init__(comic_id, mainGUI) + self.access_key = mainGUI.getConfig("biliplus_cookie") + self.headers = { + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36", + "cookie": f"manga_pic_format=jpg-full;login=2;access_key={self.access_key}", + } + + ############################################################ + def getEpisodesInfo(self) -> list[Episode]: + """获取章节信息,但是得到的解锁章节和资源是BiliPlus的数据 + + Returns: + list: 章节信息列表 + """ + if self.episodes: + return self.episodes + if not self.data: + return [] + + # ?########################################################### + # ? 解析章节 + ep_list = self.data["ep_list"] + for episode in reversed(ep_list): + epi = BiliPlusEpisode( + episode, + self.sessdata, + self.headers, + self.comic_id, + self.data, + self.mainGUI, + ) + self.episodes.append(epi) + if epi.isDownloaded(): + self.num_downloaded += 1 + + self.retrieveAvailableEpisode(self.episodes, self.comic_id, self.mainGUI) + + return self.episodes + + ############################################################ + def retrieveAvailableEpisode( + self, episodes: list[BiliPlusEpisode], comic_id: str, mainGUI: MainGUI + ): + """从BiliPlus重新获取解锁状态""" + biliplus_detail_url = ( + f"https://www.biliplus.com/manga/?act=detail_preview&mangaid={comic_id}" + ) + biliplus_html = "" + + @retry( + stop_max_delay=MAX_RETRY_SMALL, wait_exponential_multiplier=RETRY_WAIT_EX + ) + def _(url: str = biliplus_detail_url) -> dict: + try: + res = requests.post( + url, + headers=self.headers, + timeout=TIMEOUT_SMALL, + ) + except requests.RequestException as e: + logger.warning(f"漫画id:{self.comic_id} 在BiliPlus获取漫画信息失败! 重试中...\n{e}") + raise e + if "未登录" in res.text: + mainGUI.message_box.emit("请先在设置界面填写正确的BiliPlus Cookie!") + return + if res.status_code != 200: + logger.warning( + f"漫画id:{self.comic_id} 在BiliPlus爬取漫画信息失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中..." + ) + raise requests.HTTPError() + return res.text + + try: + biliplus_html = _() + except requests.RequestException as e: + logger.error(f"漫画id:{self.comic_id} 在BiliPlus重复获取漫画信息多次后失败!\n{e}") + logger.exception(e) + + # ?########################################################### + # ? 解析BiliPlus解锁章节信息 + try: + document = BeautifulSoup(biliplus_html, "html.parser") + ep_items = document.find_all("div", {"class": "episode-item"}) + ep_available = [] + for ep in ep_items: + if ep.img["src"] != "about:blank": + ep_available.append(ep.a["href"].split("epid=")[1]) + total_ep_element = document.select_one("center p") + if total_ep_element: + total_ep = total_ep_element.contents[0].split("/")[1] + total_pages = int(int(total_ep) / 200) + 1 + for pages in range(2, total_pages + 1): + mainGUI.resolve_status.emit(f"正在解析漫画章节({pages}/{total_pages})...") + page_html = _(f"{biliplus_detail_url}&page={pages}") + document = BeautifulSoup(page_html, "html.parser") + ep_items = document.find_all("div", {"class": "episode-item"}) + for ep in ep_items: + if ep.img["src"] != "about:blank": + ep_available.append(ep.a["href"].split("epid=")[1]) + for ep in episodes: + if str(ep.id) in ep_available: + ep.available = True + except Exception as e: + logger.error(f"漫画id:{self.comic_id} 在处理BiliPlus解锁章节数据时失败!\n{e}") + logger.exception(e) + mainGUI.message_box.emit( + f"漫画id:{self.comic_id} 在处理BiliPlus解锁章节数据时失败!\n\n更多详细信息请查看日志文件, 或联系开发者!" + ) + + +############################################################ +class BiliPlusEpisode(Episode): + """BiliPlus漫画章节类,用于管理漫画章节的详细信息""" + + def __init__( + self, + episode: dict, + sessData: str, + headers: str, + comic_id: str, + comic_info: dict, + mainGUI: MainGUI, + ) -> None: + super().__init__(episode, sessData, comic_id, comic_info, mainGUI) + self.headers = headers + self.comic_id = comic_id + + ############################################################ + def init_imgsList(self, mainGUI: MainGUI) -> bool: + """重写用于初始化从BiliPlus获取的章节内所有图片的列表(自带token) + + Returns + bool: 是否初始化成功 + """ + # ?########################################################### + # ? 获取图片列表 + biliplus_img_url = f"https://www.biliplus.com/manga/?act=read&mangaid={self.comic_id}&epid={self.id}" + biliplus_html = "" + + @retry( + stop_max_delay=MAX_RETRY_SMALL, wait_exponential_multiplier=RETRY_WAIT_EX + ) + def _() -> list[dict]: + try: + res = requests.post( + biliplus_img_url, + headers=self.headers, + timeout=TIMEOUT_SMALL, + ) + except requests.RequestException as e: + logger.warning( + f"《{self.comic_name}》章节:{self.title},从BiliPlus获取图片列表失败! 重试中...\n{e}" + ) + raise e + if res.status_code != 200: + logger.warning( + f"《{self.comic_name}》章节:{self.title} 从BiliPlus获取图片列表失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中..." + ) + raise requests.HTTPError() + return res.text + + try: + biliplus_html = _() + except requests.RequestException as e: + logger.error( + f"《{self.comic_name}》章节:{self.title} 从BiliPlus重复获取图片列表多次后失败!,跳过!\n{e}" + ) + logger.exception(e) + mainGUI.message_box.emit( + f"《{self.comic_name}》章节:{self.title} 从BiliPlus重复获取图片列表多次后失败!\n已暂时跳过此章节!\n请检查网络连接或者重启软件!\n\n更多详细信息请查看日志文件, 或联系开发者!" + ) + return False + + # ?########################################################### + # ? 解析BiliPlus解锁章节图片地址 + try: + biliplus_imgs_token = [] + document = BeautifulSoup(biliplus_html, "html.parser") + images = document.find_all("img", {"class": "comic-single"}) + for img in images: + img_url = img["_src"] + url, token = img_url.split("?token=") + biliplus_imgs_token.append({"url": url, "token": token}) + self.imgs_token = biliplus_imgs_token + if not biliplus_imgs_token: + logger.error( + f"《{self.comic_name}》章节:{self.title} 在处理BiliPlus地址时因Cookie有误导致失败!" + ) + mainGUI.message_box.emit( + f"《{self.comic_name}》章节:{self.title} 在处理BiliPlus解锁章节图片地址时因Cookie有误导致失败!" + ) + return False + except Exception as e: + logger.error( + f"《{self.comic_name}》章节:{self.title} 在处理BiliPlus解锁章节图片地址时失败!\n{e}" + ) + logger.exception(e) + mainGUI.message_box.emit( + f"《{self.comic_name}》章节:{self.title} 在处理BiliPlus解锁章节图片地址时失败!\n\n更多详细信息请查看日志文件, 或联系开发者!" + ) + return False + + return True diff --git a/src/BiliQrCode.py b/src/BiliQrCode.py new file mode 100644 index 0000000..0d789d1 --- /dev/null +++ b/src/BiliQrCode.py @@ -0,0 +1,134 @@ +from __future__ import annotations +import io +import time + +import typing + +import requests +import qrcode +from retrying import retry +from PySide6.QtCore import SignalInstance +from PySide6.QtWidgets import QMessageBox + +from src.utils import logger, MAX_RETRY_SMALL, RETRY_WAIT_EX, TIMEOUT_SMALL + +if typing.TYPE_CHECKING: + from ui.MainGUI import MainGUI + + +class QrCode: + def __init__(self, mainGUI: MainGUI) -> None: + self.mainGUI = mainGUI + self.generate_url = ( + "https://passport.bilibili.com/x/passport-login/web/qrcode/generate" + ) + self.poll_url = "https://passport.bilibili.com/x/passport-login/web/qrcode/poll" + self.code_url = None + self.qrcode_key = None + self.close_flag = False + + def generate(self) -> str | None: + """生成登入二维码 + + Returns: + str: 二维码内容, 二维码图片 + + """ + + @retry( + stop_max_delay=MAX_RETRY_SMALL, wait_exponential_multiplier=RETRY_WAIT_EX + ) + def _() -> dict: + try: + res = requests.get(self.generate_url, timeout=TIMEOUT_SMALL) + except requests.RequestException as e: + logger.warning(f"获取登入二维码失败! 重试中...\n {e}") + raise e + if res.status_code != 200: + logger.warning( + f"获取登入二维码失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中..." + ) + raise requests.HTTPError() + return res.json()["data"] + + try: + data = _() + self.code_url = data["url"] + self.qrcode_key = data["qrcode_key"] + except requests.RequestException as e: + logger.error(f"重复获取登入二维码多次后失败! {e}") + logger.exception(e) + QMessageBox.warning( + self.mainGUI, "警告", "重复获取登入二维码多次后失败!\n请检查网络连接或者重启软件!\n\n更多详细信息请查看日志文件" + ) + return None + + img = qrcode.make(self.code_url) + + image_bytes = io.BytesIO() + img.save(image_bytes, format="PNG") + image_bytes = image_bytes.getvalue() + + return image_bytes + + def confirm(self) -> dict | None: + """确认登入 + + Returns: + str: SESSDATA cookie + """ + + @retry( + stop_max_delay=MAX_RETRY_SMALL, wait_exponential_multiplier=RETRY_WAIT_EX + ) + def _() -> dict: + try: + res = requests.get( + self.poll_url, + params={ + "qrcode_key": self.qrcode_key, + }, + timeout=TIMEOUT_SMALL, + ) + except requests.RequestException as e: + logger.warning(f"确认二维码登入失败! 重试中...\n {e}") + raise e + if res.status_code != 200: + logger.warning( + f"确认二维码登入失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中..." + ) + raise requests.HTTPError() + return res.json()["data"] + + try: + data = _() + except requests.RequestException as e: + logger.error(f"重复确认登入多次后失败! {e}") + logger.exception(e) + QMessageBox.warning( + self.mainGUI, "警告", "重复确认登入多次后失败!\n请检查网络连接或者重启软件!\n\n更多详细信息请查看日志文件" + ) + return None + + return data + + def get_cookie(self, qr_res: SignalInstance) -> None: + """获取cookie + + Args: + qr_res (SignalInstance): 信号槽,用于传递二维码扫描结果 + + """ + + while not self.close_flag: + data = self.confirm() + + # 扫码登录成功或者二维码过期或者请求失败 + if data["code"] in [0, 86038] or data is None: + qr_res.emit(data) + break + + qr_res.emit(data) + + # 每隔1秒确认一次 + time.sleep(1) diff --git a/src/Comic.py b/src/Comic.py index 0abcb28..0b6c275 100644 --- a/src/Comic.py +++ b/src/Comic.py @@ -3,11 +3,17 @@ import typing import requests -from retrying import retry +from retrying import RetryError, retry import re from src.Episode import Episode -from src.utils import logger, MAX_RETRY_SMALL, RETRY_WAIT_EX, TIMEOUT_SMALL +from src.utils import ( + logger, + MAX_RETRY_SMALL, + RETRY_WAIT_EX, + TIMEOUT_SMALL, + isCheckSumValid, +) if typing.TYPE_CHECKING: from ui.MainGUI import MainGUI @@ -82,10 +88,51 @@ def _() -> dict: self.data["styles"] = ",".join(self.data["styles"]) self.data[ "save_path" - ] = f"{self.save_path}/《{self.data['title']}》 作者:{self.data['author_name']} ID-{self.comic_id}" + ] = f"{self.save_path}/《{self.data['title']}》 作者:{self.data['author_name']}" return self.data + ############################################################ + def getComicCover(self, data: dict) -> int: + """获取漫画封面图片 + + Returns: + QPixmap: 漫画封面图片 + """ + + @retry( + stop_max_delay=MAX_RETRY_SMALL, wait_exponential_multiplier=RETRY_WAIT_EX + ) + def _() -> bytes: + try: + res = requests.get(data["vertical_cover"], timeout=TIMEOUT_SMALL) + except requests.RequestException() as e: + logger.warning(f"获取封面图片失败! 重试中...\n{e}") + raise e + if res.status_code != 200: + logger.warning( + f"获取封面图片失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中..." + ) + raise requests.HTTPError() + isValid, md5 = isCheckSumValid(res.headers["Etag"], res.content) + if not isValid: + logger.warning( + f"图片内容 Checksum 不正确! 重试中...\n\t{res.headers['Etag']} ≠ {md5}" + ) + raise requests.HTTPError() + return res.content + + logger.info(f"获取《{data['title']}》的封面图片中...") + try: + img = _() + return img + except RetryError as e: + logger.error(f"获取封面图片多次后失败,跳过!\n{e}") + self.mainGUI.message_box.emit( + f"获取封面图片多次后失败!\n请检查网络连接或者重启软件!\n\n更多详细信息请查看日志文件, 或联系开发者!" + ) + return open(":/imgs/fail_img.jpg") + ############################################################ def getEpisodesInfo(self) -> list[Episode]: """获取章节信息 diff --git a/src/Episode.py b/src/Episode.py index 4fe1fb7..55fce47 100644 --- a/src/Episode.py +++ b/src/Episode.py @@ -1,12 +1,12 @@ from __future__ import annotations -import hashlib import json import os import re import shutil import typing + import piexif import requests from PIL import Image @@ -25,6 +25,7 @@ __copyright__, __version__, logger, + isCheckSumValid, ) if typing.TYPE_CHECKING: @@ -301,6 +302,7 @@ def _(): append_images=temp_imgs[1:], quality=95, ) + # 在pdf文件属性中记录章节标题作者和软件版本以及版权信息 with open(self.epi_path_pdf, "rb") as f: pdf = PdfReader(f) @@ -342,24 +344,30 @@ def saveToFolder(self, mainGUI: MainGUI, imgs_path: list[str]) -> None: def _(): try: for index, path in enumerate(imgs_path, start=1): - # 在图片文件属性中记录章节标题作者和软件版本以及版权信息 - info = {} - info[ - piexif.ImageIFD.ImageDescription - ] = f"《{self.comic_name}》 - {self.title}".encode("utf-8") - info[piexif.ImageIFD.Artist] = self.author.encode("utf-8") - info[ - piexif.ImageIFD.Software - ] = f"{__app_name__} {__version__}".encode("utf-8") - info[piexif.ImageIFD.Copyright] = __copyright__.encode("utf-8") - exif_bytes = piexif.dump({"0th": info}) + + def jpg_exif(): + # 在jpg文件属性中记录章节标题作者和软件版本以及版权信息 + exif_data = { + "0th": { + piexif.ImageIFD.ImageDescription: f"《{self.comic_name}》 - {self.title}".encode( + "utf-8" + ), + piexif.ImageIFD.Artist: self.author.encode("utf-8"), + piexif.ImageIFD.Software: f"{__app_name__} {__version__}".encode( + "utf-8" + ), + piexif.ImageIFD.Copyright: __copyright__, + } + } + exif_bytes = piexif.dump(exif_data) + piexif.insert(exif_bytes, path) + + img_format = path.split(".")[-1] # 将 exif 数据插入到图像文件中, 如果插入失败则跳过 try: - piexif.insert( - exif_bytes, - path, - ) + if img_format == "jpg": + jpg_exif() except piexif.InvalidImageDataError as e: logger.warning(f"Failed to insert exif data for {path}: {e}") logger.exception(e) @@ -368,7 +376,7 @@ def _(): shutil.copy2( path, os.path.join( - self.epi_path_folder, f"{str(index).zfill(3)}.jpg" + self.epi_path_folder, f"{str(index).zfill(3)}.{img_format}" ), ) @@ -458,9 +466,10 @@ def _() -> bytes: f"《{self.comic_name}》章节:{self.title} - {index} - {img_url} 获取图片 header 失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中..." ) raise requests.HTTPError() - if res.headers["Etag"] != hashlib.md5(res.content).hexdigest(): + isValid, md5 = isCheckSumValid(res.headers["Etag"], res.content) + if not isValid: logger.warning( - f"《{self.comic_name}》章节:{self.title} - {index} - {img_url} - 下载内容Checksum不正确! 重试中...\n\t{res.headers['Etag']} ≠ {hashlib.md5(res.content).hexdigest()}" + f"《{self.comic_name}》章节:{self.title} - {index} - {img_url} - 下载内容Checksum不正确! 重试中...\n\t{res.headers['Etag']} ≠ {md5}" ) raise requests.HTTPError() return res.content @@ -479,7 +488,8 @@ def _() -> bytes: # ?########################################################### # ? 保存图片 - path_to_save = os.path.join(self.save_path, f"{self.ord}_{index}.jpg") + img_format = img_url.split(".")[-1].split("?")[0].lower() + path_to_save = os.path.join(self.save_path, f"{self.ord}_{index}.{img_format}") @retry(stop_max_attempt_number=5) def _() -> None: diff --git a/src/ui/DownloadUI.py b/src/ui/DownloadUI.py index 0a2740b..23092b8 100644 --- a/src/ui/DownloadUI.py +++ b/src/ui/DownloadUI.py @@ -1,5 +1,6 @@ from __future__ import annotations +import json import os from concurrent.futures import ThreadPoolExecutor from typing import TYPE_CHECKING @@ -110,31 +111,27 @@ def addFinished(self, mainGUI: MainGUI, label_title: QWidget, path: str) -> None """ # ?########################################################### # ? 添加到已完成列表 - h_layout_donwlowd_list = QHBoxLayout() - h_layout_donwlowd_list.addWidget(label_title) - h_layout_donwlowd_list.addStretch(1) + h_layout_download_list = QHBoxLayout() + h_layout_download_list.addWidget(label_title) + h_layout_download_list.addStretch(1) # ?########################################################### # ? 超链接打开保存路径 label_file_path = QLabel("打开文件夹") label_file_path.linkActivated.connect(lambda: openFolderAndSelectItems(path)) - h_layout_donwlowd_list.addWidget(label_file_path) + h_layout_download_list.addWidget(label_file_path) widget = QWidget() - widget.setLayout(h_layout_donwlowd_list) + widget.setLayout(h_layout_download_list) mainGUI.verticalLayout_finished.addWidget(widget) ############################################################ def addTask(self, mainGUI: MainGUI, epi: Episode) -> None: - """添加下载任务 + """添加漫画下载任务 Args: epi (Episode): 漫画章节类实例 """ - # ?########################################################### - # ? 初始化储存文件夹 - if not os.path.exists(epi.save_path): - os.makedirs(epi.save_path) # ?########################################################### # ? 创建任务 @@ -149,8 +146,8 @@ def addTask(self, mainGUI: MainGUI, epi: Episode) -> None: # ?########################################################### # ? 添加任务组件到正在下载列表 - h_layout_donwlowd_list = QHBoxLayout() - h_layout_donwlowd_list.addWidget( + h_layout_download_list = QHBoxLayout() + h_layout_download_list.addWidget( QLabel( f"{epi.comic_name} > {epi.title}" ) @@ -159,11 +156,11 @@ def addTask(self, mainGUI: MainGUI, epi: Episode) -> None: bar.setTextVisible(True) self.all_tasks[task_id]["bar"] = bar - h_layout_donwlowd_list.addWidget(bar) - h_layout_donwlowd_list.setStretch(0, 1) - h_layout_donwlowd_list.setStretch(1, 1) + h_layout_download_list.addWidget(bar) + h_layout_download_list.setStretch(0, 1) + h_layout_download_list.setStretch(1, 1) widget = QWidget() widget.setObjectName(task_id) - widget.setLayout(h_layout_donwlowd_list) + widget.setLayout(h_layout_download_list) mainGUI.verticalLayout_processing.addWidget(widget) self.id_count += 1 diff --git a/src/ui/MainGUI.py b/src/ui/MainGUI.py index 95cda35..f6b79d7 100644 --- a/src/ui/MainGUI.py +++ b/src/ui/MainGUI.py @@ -20,8 +20,9 @@ class MainGUI(QMainWindow, Ui_MainWindow, QtStyleTools): # ? 主要是为了 Episode 类里面的提示框准备的, # ? 因为 Episode 类是在另一个线程里面运行的,而只有主线程才能修改 GUI message_box = Signal(str) - # ? 用于多线程更新我的库存 - my_library_add_widget = Signal(dict) + + # ? 用于多线程报告程序详情 + resolve_status = Signal(str) def __init__(self, app): super().__init__() @@ -30,6 +31,9 @@ def __init__(self, app): self.setWindowTitle(f"哔哩哔哩漫画下载器 v{__version__}") self.setFont(QFont("Microsoft YaHei", 10)) self.message_box.connect(lambda msg: QMessageBox.warning(None, "警告", msg)) + self.resolve_status.connect( + lambda status: self.label_resolve_status.setText(status) + ) logger.info("\n\n\t\t\t------------------- 程序启动,初始化主窗口 -------------------\n") diff --git a/src/ui/MangaUI.py b/src/ui/MangaUI.py index 81cde91..e105c20 100644 --- a/src/ui/MangaUI.py +++ b/src/ui/MangaUI.py @@ -4,13 +4,11 @@ import json from concurrent.futures import ThreadPoolExecutor, as_completed from functools import partial -from hashlib import md5 -from re import search, sub +from re import sub from typing import TYPE_CHECKING -import requests from pypinyin import lazy_pinyin -from PySide6.QtCore import QEvent, QPoint, QSize, Qt, QUrl +from PySide6.QtCore import QObject, QEvent, QPoint, QSize, Qt, QUrl, Signal from PySide6.QtGui import QColor, QDesktopServices, QImage, QPixmap from PySide6.QtWidgets import ( QHBoxLayout, @@ -20,25 +18,43 @@ QMessageBox, QWidget, ) -from retrying import RetryError, retry from src.Comic import Comic +from src.BiliPlus import BiliPlusComic from src.searchComic import SearchComic -from src.utils import MAX_RETRY_SMALL, RETRY_WAIT_EX, TIMEOUT_SMALL, logger +from src.utils import logger if TYPE_CHECKING: from src.ui.MainGUI import MainGUI -class MangaUI: +class MangaUI(QObject): + """漫画UI类,用于搜索、下载、管理漫画""" + + # ?########################################################### + # ? 用于多线程更新我的库存 + my_library_add_widget = Signal(dict) + + # ? 用于多线程更新漫画详情 + my_comic_detail_widget = Signal(dict) + + # ? 用于多线程更新封面图 + my_cover_update_widget = Signal(dict) + + # ? 用于多线程刷新漫画章节列表 + my_comic_list_widget = Signal(dict) + def __init__(self, mainGUI: MainGUI): + super().__init__() self.search_info = None self.num_selected = 0 self.epi_list = None + self.present_comic_id = 0 self.init_mangaSearch(mainGUI) self.init_mangaDetails(mainGUI) self.init_myLibrary(mainGUI) self.init_episodesDetails(mainGUI) + self.executor = ThreadPoolExecutor() ############################################################ @@ -87,21 +103,45 @@ def _() -> None: ############################################################ def init_mangaDetails(self, mainGUI: MainGUI) -> None: - """绑定双击显示漫画详情事件 + """绑定漫画详情点击事件 Args: mainGUI (MainGUI): 主窗口类实例 """ + # ?########################################################### + # ? 双击获取选中漫画详情绑定 def _(item: QListWidgetItem) -> None: index = mainGUI.listWidget_manga_search.indexFromItem(item).row() - comic = Comic(self.search_info[index]["id"], mainGUI) - self.updateComicInfo(mainGUI, comic) + self.present_comic_id = self.search_info[index]["id"] + self.resolveEnable(mainGUI, "resolving") + comic = Comic(self.present_comic_id, mainGUI) + self.updateComicInfoEvent(mainGUI, comic, "bilibili") mainGUI.listWidget_manga_search.itemDoubleClicked.connect(_) + + # ?########################################################### + # ? 单击修改当前选择id绑定 + def _(item: QListWidgetItem) -> None: + index = mainGUI.listWidget_manga_search.indexFromItem(item).row() + self.present_comic_id = self.search_info[index]["id"] + + mainGUI.listWidget_manga_search.itemClicked.connect(_) # 鼠标移动到图片上的时候更改鼠标样式, 提示用户可以用鼠标点击 mainGUI.label_manga_image.setCursor(Qt.PointingHandCursor) + # ?########################################################### + # ? 漫画封面图更新触发函数绑定 + self.my_cover_update_widget.connect(self.updateComicCover) + + # ?########################################################### + # ? 漫画解析触发函数绑定 + self.my_comic_detail_widget.connect(self.updateComicInfo) + + # ?########################################################### + # ? 漫画章节列表更新触发函数绑定 + self.my_comic_list_widget.connect(self.updateComicList) + ############################################################ def init_myLibrary(self, mainGUI: MainGUI) -> None: """绑定更新我的库存事件 @@ -111,7 +151,7 @@ def init_myLibrary(self, mainGUI: MainGUI) -> None: """ # 布局对齐 mainGUI.v_Layout_myLibrary.setAlignment(Qt.AlignTop) - mainGUI.my_library_add_widget.connect(self.updateMyLibrarySingleAdd) + self.my_library_add_widget.connect(self.updateMyLibrarySingleAdd) if mainGUI.getConfig("cookie"): self.updateMyLibrary(mainGUI) @@ -151,11 +191,15 @@ def updateMyLibrary(self, mainGUI: MainGUI) -> bool: if os.path.exists(path): for item in os.listdir(path): - if search(r"ID-\d+", item): - my_library[int(search(r"ID-(\d+)", item)[1])] = { - "comic_name": search(r"(《.*》)", item)[1], - "comic_path": os.path.join(path, item), - } + if os.path.exists(os.path.join(path, item, "元数据.json")): + with open( + os.path.join(path, item, "元数据.json"), "r", encoding="utf-8" + ) as f: + data = json.load(f) + my_library[data["id"]] = { + "comic_name": data["title"], + "comic_path": os.path.join(path, item), + } else: mainGUI.lineEdit_save_path.setText(os.getcwd()) mainGUI.updateConfig("save_path", os.getcwd()) @@ -213,7 +257,7 @@ def updateMyLibrarySingle( "comic_path": comic_path, } - mainGUI.my_library_add_widget.emit(info) + self.my_library_add_widget.emit(info) return None ############################################################ @@ -245,7 +289,8 @@ def updateMyLibrarySingleAdd(self, info: dict) -> None: # ?########################################################### # ? 绑定列表内漫画被点击事件:当前点击变色,剩余恢复 - def _(_event: QEvent, widget: QWidget) -> None: + def _(_event: QEvent, widget: QWidget, comic: Comic) -> None: + self.present_comic_id = comic.comic_id for i in range(mainGUI.v_Layout_myLibrary.count()): temp = mainGUI.v_Layout_myLibrary.itemAt(i).widget() temp.setStyleSheet("font-size: 10pt;") @@ -253,8 +298,10 @@ def _(_event: QEvent, widget: QWidget) -> None: "background-color:rgb(200, 200, 255); font-size: 10pt;" ) - widget.mousePressEvent = partial(_, widget=widget) - widget.mouseDoubleClickEvent = partial(self.updateComicInfo, mainGUI, comic) + widget.mousePressEvent = partial(_, widget=widget, comic=comic) + widget.mouseDoubleClickEvent = partial( + self.updateComicInfoEvent, mainGUI, comic, "bilibili" + ) widget.setLayout(h_layout_my_library) # ?########################################################### @@ -280,28 +327,74 @@ def myMenu_openFolder(widget: QWidget, comic_path: str, pos: QPoint) -> None: .findChild(QLabel) .text() ) - left_titile: str = left[left.find(">") + 1 : left.rfind("<")] + left_title: str = left[left.find(">") + 1 : left.rfind("<")] if i == mainGUI.v_Layout_myLibrary.count() - 1: mainGUI.v_Layout_myLibrary.addWidget(widget) break - if lazy_pinyin(data["title"]) <= lazy_pinyin(left_titile): + if lazy_pinyin(data["title"]) <= lazy_pinyin(left_title): mainGUI.v_Layout_myLibrary.insertWidget(i, widget) break ############################################################ + # 以下三个函数是为了获取漫画信息详情 + ############################################################ - def updateComicInfo( - self, mainGUI: MainGUI, comic: Comic, _event: QEvent = None + ############################################################ + def updateComicInfoEvent( + self, mainGUI: MainGUI, comic: Comic, resolve_type: str, _event: QEvent = None ) -> None: """更新漫画信息详情界面 Args: comic (Comic): 漫画类实例 mainGUI (MainGUI): 主窗口类实例 + resolve_type (str): 更新的解析类型 """ - # ?########################################################### - # ? 更新漫画信息 + + # 用多线程更新漫画信息,避免卡顿 + self.executor.submit( + self.getComicInfo, + mainGUI, + comic, + resolve_type, + ) + + ############################################################ + def getComicInfo(self, mainGUI: MainGUI, comic: Comic, resolve_type: str) -> None: + """更新封面的执行函数 + + Args: + mainGUI (MainGUI): 主窗口类实例 + comic (Comic): 获取的漫画实例 + resolve_type (str): 更新的解析类型 + + """ + mainGUI.resolve_status.emit("正在解析漫画详情...") data = comic.getComicInfo() + mainGUI.resolve_status.emit("解析漫画详情完毕") + self.my_comic_detail_widget.emit( + { + "mainGUI": mainGUI, + "comic": comic, + "data": data, + "resolve_type": resolve_type, + } + ) + + ############################################################ + def updateComicInfo(self, info: dict) -> None: + """更新漫画信息详情回调函数 + + Args: + info (dict): 执行更新漫画信息详情后返回的数据 + """ + + mainGUI: MainGUI = info["mainGUI"] + comic: Comic = info["comic"] + data: dict = info["data"] + resolve_type: str = info["resolve_type"] + + self.present_comic_id = comic.comic_id # ? 获取漫画信息失败直接跳过 if not data: mainGUI.message_box.emit( @@ -324,41 +417,12 @@ def updateComicInfo( f"概要:{data['evaluate'] or '无'}" ) - self.init_save_mata(mainGUI, data) + # ?########################################################### + # ? 用多线程获取封面,避免卡顿 + self.executor.submit(self.getComicCover, mainGUI, comic, data) # ?########################################################### - # ? 加载图片,以及绑定双击和悬停事件 - @retry( - stop_max_delay=MAX_RETRY_SMALL, wait_exponential_multiplier=RETRY_WAIT_EX - ) - def _() -> bytes: - try: - res = requests.get(data["vertical_cover"], timeout=TIMEOUT_SMALL) - except requests.RequestException() as e: - logger.warning(f"获取封面图片失败! 重试中...\n{e}") - raise e - if res.status_code != 200: - logger.warning( - f"获取封面图片失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中..." - ) - raise requests.HTTPError() - if res.headers["Etag"] != md5(res.content).hexdigest(): - logger.warning( - f"图片内容 Checksum 不正确! 重试中...\n\t{res.headers['Etag']} ≠ {md5(res.content).hexdigest()}" - ) - raise requests.HTTPError() - return res.content - - logger.info(f"获取《{data['title']}》的封面图片中...") - try: - img = _() - label_img = QPixmap.fromImage(QImage.fromData(img)) - except RetryError as e: - logger.error(f"获取封面图片多次后失败,跳过!\n{e}") - label_img = QPixmap(":/imgs/fail_img.jpg") - QMessageBox.warning( - mainGUI, "警告", "获取封面图片多次后失败!\n请检查网络连接或者重启软件!\n\n更多详细信息请查看日志文件, 或联系开发者!" - ) + # ? 封面的绑定双击和悬停事件 mainGUI.label_manga_image.mouseDoubleClickEvent = ( lambda _event: QDesktopServices.openUrl( @@ -370,7 +434,45 @@ def _() -> bytes: ) # ?########################################################### - # ? 重写图片大小改变事件,使图片不会变形 + # ? 用多线程更新漫画章节详情界面显示,避免卡顿 + self.executor.submit(self.getComicList, mainGUI, comic, resolve_type) + + ############################################################ + # 以下两个函数是为了获取漫画封面 + ############################################################ + + ############################################################ + def getComicCover(self, mainGUI: MainGUI, comic: Comic, data: dict) -> None: + """更新封面的执行函数 + + Args: + mainGUI (MainGUI): 主窗口类实例 + comic (Comic): 获取的漫画实例 + data (dict): 漫画实例的数据 + + """ + img_byte = comic.getComicCover(data) + self.my_cover_update_widget.emit( + { + "mainGUI": mainGUI, + "img_byte": img_byte, + } + ) + + ############################################################ + def updateComicCover(self, info: dict) -> None: + """更新封面的回调函数 + + Args: + info (dict): 执行更新封面后返回的数据 + + """ + mainGUI: MainGUI = info["mainGUI"] + img_byte: bytes = info["img_byte"] + + # 重写图片大小改变事件,使图片不会变形 + label_img = QPixmap.fromImage(QImage.fromData(img_byte)) + def _(event: QEvent = None) -> None: new_size = event.size() if event else mainGUI.label_manga_image.size() if new_size.width() < 200: @@ -384,13 +486,50 @@ def _(event: QEvent = None) -> None: mainGUI.label_manga_image.resizeEvent = _ _() - # ?########################################################### - # ? 更新漫画章节详情 - mainGUI.listWidget_chp_detail.clear() + ############################################################ + # 以下两个函数是为了刷新漫画详情界面 + ############################################################ + + ############################################################ + def getComicList(self, mainGUI: MainGUI, comic: Comic, resolve_type: str) -> None: + """更新详情界面的执行函数 + + Args: + mainGUI (MainGUI): 主窗口类实例 + comic (Comic): 获取的漫画实例 + + """ + mainGUI.resolve_status.emit("正在解析漫画章节...") self.num_selected = 0 num_unlocked = 0 if comic: self.epi_list = comic.getEpisodesInfo() + mainGUI.resolve_status.emit("解析漫画章节完毕") + self.my_comic_list_widget.emit( + { + "mainGUI": mainGUI, + "comic": comic, + "resolve_type": resolve_type, + "num_unlocked": num_unlocked, + } + ) + + ############################################################ + def updateComicList(self, info: dict) -> None: + """更新漫画详情界面的回调函数 + + Args: + info (dict): 执行更新封面后返回的数据 + + """ + mainGUI: MainGUI = info["mainGUI"] + comic: Comic = info["comic"] + resolve_type: str = info["resolve_type"] + num_unlocked: int = info["num_unlocked"] + + # ?########################################################### + # ? 更新漫画章节详情 + mainGUI.listWidget_chp_detail.clear() for epi in self.epi_list: temp = QListWidgetItem(epi.title) temp.setCheckState(Qt.Unchecked) @@ -415,51 +554,10 @@ def _(event: QEvent = None) -> None: f"已下载:{comic.getNumDownloaded()}" ) mainGUI.label_chp_detail_num_selected.setText(f"已选中:{self.num_selected}") + self.resolveEnable(mainGUI, resolve_type) + mainGUI.resolve_status.emit("") ############################################################ - - def init_save_mata(self, mainGUI: MainGUI, data: dict) -> None: - """保存元数据 - - Args: - mainGUI (MainGUI): 主窗口类实例 - data (dict): 漫画元数据 - - """ - - def _(data: dict) -> None: - mata = {} - - # 过滤信息 - mata['id'] = data['id'] - mata['title'] = data['title'] - mata['horizontal_cover'] = data['horizontal_cover'] - mata['square_cover'] = data['square_cover'] - mata['vertical_cover'] = data['vertical_cover'] - mata['author_name'] = data['author_name'] - mata['styles'] = data['styles'] - mata['evaluate'] = data['evaluate'] - mata['renewal_time'] = data['renewal_time'] - mata['hall_icon_text'] = data['hall_icon_text'] - mata['tags'] = data['tags'] - - # ? 如果文件夹不存在,创建文件夹 - if not os.path.exists(data['save_path']): - os.makedirs(data['save_path']) - - with open(os.path.join(data['save_path'], "元数据.json"), "w", encoding='utf-8') as f: - json.dump(mata, f, indent=4, ensure_ascii=False) - - # ? 弹出提示框 - QMessageBox.information(mainGUI, "提示", "保存成功!") - mainGUI.pushButton_save_mata.setEnabled(False) - mainGUI.pushButton_save_mata.clicked.disconnect() - - mainGUI.pushButton_save_mata.setEnabled(True) - mainGUI.pushButton_save_mata.clicked.connect(partial(_, data)) - - ############################################################ - def init_episodesDetails(self, mainGUI: MainGUI) -> None: """绑定章节界面的多选以及右键菜单事件 @@ -560,18 +658,33 @@ def _() -> None: # ?########################################################### # ? 更新章节详情界面 - num_num_downloaded = ( + num_downloaded = ( int(mainGUI.label_chp_detail_num_downloaded.text().split(":")[1]) + self.num_selected ) self.num_selected = 0 - mainGUI.label_chp_detail_num_downloaded.setText(f"已下载:{num_num_downloaded}") + mainGUI.label_chp_detail_num_downloaded.setText(f"已下载:{num_downloaded}") mainGUI.label_chp_detail_num_selected.setText(f"已选中:{self.num_selected}") + # ?########################################################### + # ? 初始化储存文件夹 + save_path = self.epi_list[0].save_path + if not os.path.exists(save_path): + os.makedirs(save_path) + + # ?########################################################### + # ? 保存元数据 + if not os.path.exists(os.path.join(save_path, "元数据.json")): + comic = Comic(self.present_comic_id, mainGUI) + self.save_meta(comic.getComicInfo()) + + # ?########################################################### + # ? 开始下载选中章节 mainGUI.listWidget_chp_detail.itemChanged.disconnect() for i in range(mainGUI.listWidget_chp_detail.count()): item = mainGUI.listWidget_chp_detail.item(i) if item.flags() != Qt.NoItemFlags and item.checkState() == Qt.Checked: + comic = Comic(self.present_comic_id, mainGUI) mainGUI.downloadUI.addTask(mainGUI, self.epi_list[i]) item.setFlags(Qt.NoItemFlags) item.setBackground(QColor(0, 255, 0, 50)) @@ -583,7 +696,7 @@ def _() -> None: temp = mainGUI.v_Layout_myLibrary.itemAt(i).widget().layout() if self.epi_list[0].comic_name in temp.itemAt(0).widget().text(): temp.itemAt(2).widget().setText( - f"{num_num_downloaded}/{len(self.epi_list)}" + f"{num_downloaded}/{len(self.epi_list)}" ) break @@ -593,3 +706,88 @@ def _() -> None: mainGUI.tabWidget_download_list.setCurrentIndex(0) mainGUI.pushButton_chp_detail_download_selected.clicked.connect(_) + mainGUI.pushButton_biliplus_detail_download_selected.clicked.connect(_) + + # ?########################################################### + # ? 绑定B站解析按钮事件 + def _() -> None: + if self.present_comic_id == 0: + QMessageBox.critical(mainGUI, "警告", "请先在搜索或库存列表选择一个漫画!") + return + if not mainGUI.getConfig("cookie"): + QMessageBox.critical(mainGUI, "警告", "请先在设置界面填写自己的Cookie!") + return + self.resolveEnable(mainGUI, "resolving") + comic = Comic(self.present_comic_id, mainGUI) + self.updateComicInfoEvent(mainGUI, comic, "bilibili") + + mainGUI.pushButton_resolve_detail.clicked.connect(_) + + # ?########################################################### + # ? 绑定BiliPlus解析按钮事件 + def _() -> None: + if self.present_comic_id == 0: + QMessageBox.critical(mainGUI, "警告", "请先在搜索或库存列表选择一个漫画!") + return + if not mainGUI.getConfig("biliplus_cookie"): + QMessageBox.critical(mainGUI, "警告", "请先在设置界面填写自己的BiliPlus Cookie!") + return + self.resolveEnable(mainGUI, "resolving") + comic = BiliPlusComic(self.present_comic_id, mainGUI) + self.updateComicInfoEvent(mainGUI, comic, "biliplus") + + mainGUI.pushButton_biliplus_resolve_detail.clicked.connect(_) + + ########################################################### + def resolveEnable(self, mainGUI: MainGUI, resolve_type: str) -> None: + """根据解析状态对按钮进行允许和禁用状态的改变 + + Args: + mainGUI (MainGUI): 主窗口类实例 + resolve_type (str): 解析状态 + """ + if resolve_type == "resolving": + mainGUI.pushButton_resolve_detail.setEnabled(False) + mainGUI.pushButton_biliplus_resolve_detail.setEnabled(False) + mainGUI.pushButton_chp_detail_download_selected.setEnabled(False) + mainGUI.pushButton_biliplus_detail_download_selected.setEnabled(False) + else: + mainGUI.pushButton_resolve_detail.setEnabled(True) + mainGUI.pushButton_biliplus_resolve_detail.setEnabled(True) + + if resolve_type == "bilibili": + mainGUI.pushButton_chp_detail_download_selected.setEnabled(True) + mainGUI.pushButton_biliplus_detail_download_selected.setEnabled(False) + elif resolve_type == "biliplus": + mainGUI.pushButton_chp_detail_download_selected.setEnabled(False) + mainGUI.pushButton_biliplus_detail_download_selected.setEnabled(True) + + ############################################################ + + def save_meta(self, data: dict) -> None: + """保存元数据 + + Args: + mainGUI (MainGUI): 主窗口类实例 + data (dict): 漫画元数据 + + """ + + meta = { + "id": data["id"], + "title": data["title"], + "horizontal_cover": data["horizontal_cover"], + "square_cover": data["square_cover"], + "vertical_cover": data["vertical_cover"], + "author_name": data["author_name"], + "styles": data["styles"], + "evaluate": data["evaluate"], + "renewal_time": data["renewal_time"], + "hall_icon_text": data["hall_icon_text"], + "tags": [tag["name"] for tag in data["tags"]], + } + + with open( + os.path.join(data["save_path"], "元数据.json"), "w", encoding="utf-8" + ) as f: + json.dump(meta, f, indent=4, ensure_ascii=False) diff --git a/src/ui/MyAbout.py b/src/ui/MyAboutUI.py similarity index 90% rename from src/ui/MyAbout.py rename to src/ui/MyAboutUI.py index f80df89..96af3cb 100644 --- a/src/ui/MyAbout.py +++ b/src/ui/MyAboutUI.py @@ -3,7 +3,7 @@ from src.ui.PySide_src.myAbout_ui import Ui_My_about -class MyAbout(QWidget, Ui_My_about): +class MyAboutUI(QWidget, Ui_My_about): """关于窗口类""" def __init__(self): diff --git a/src/ui/PySide_src/imgs/Scan-qr-code.512.png b/src/ui/PySide_src/imgs/Scan-qr-code.512.png new file mode 100644 index 0000000..55022cf Binary files /dev/null and b/src/ui/PySide_src/imgs/Scan-qr-code.512.png differ diff --git a/src/ui/PySide_src/mainWindow.ui b/src/ui/PySide_src/mainWindow.ui index d2f00ea..a2c6bb8 100644 --- a/src/ui/PySide_src/mainWindow.ui +++ b/src/ui/PySide_src/mainWindow.ui @@ -153,8 +153,8 @@ 0 0 - 98 - 28 + 517 + 191 @@ -210,7 +210,7 @@ - + 6 @@ -252,39 +252,6 @@ - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - - - - 100 - 0 - - - - 保存元数据 - - - - - @@ -297,7 +264,7 @@ 章节详情 (右键确认鼠标框选内容) - + @@ -378,6 +345,116 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + true + + + + 80 + 25 + + + + B站解析 + + + + + + + true + + + + 80 + 25 + + + + BiliPlus解析 + + + + + + + Qt::Vertical + + + + + + + + 0 + 0 + + + + IBeamCursor + + + + + + Qt::AlignCenter + + + + + + + + + + + + + + Qt::Vertical + + + + + + + false + + + + 130 + 25 + + + + 下载BiliPlus选中章节 + + + + + + + + @@ -443,7 +520,7 @@ true - 100 + 0 true @@ -502,8 +579,8 @@ 0 0 - 98 - 28 + 1068 + 554 @@ -531,8 +608,8 @@ 0 0 - 98 - 28 + 61 + 18 @@ -581,7 +658,7 @@ 设置 - + @@ -605,12 +682,6 @@ 0 - - - 16777215 - 16777215 - - 我的Cookie: @@ -619,6 +690,29 @@ + + + + + + + + :/imgs/Scan-qr-code.512.png:/imgs/Scan-qr-code.512.png + + + + 25 + 25 + + + + false + + + false + + + @@ -628,6 +722,54 @@ + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 110 + 0 + + + + BiliPlus Cookie: + + + + + + + + + + 确认 + + + + + + + + @@ -712,7 +854,7 @@ - 110 + 120 0 diff --git a/src/ui/PySide_src/mainWindow_ui.py b/src/ui/PySide_src/mainWindow_ui.py index 4aa3ca6..c86aa14 100644 --- a/src/ui/PySide_src/mainWindow_ui.py +++ b/src/ui/PySide_src/mainWindow_ui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'mainWindow.ui' ## -## Created by: Qt User Interface Compiler version 6.5.1 +## Created by: Qt User Interface Compiler version 6.5.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -123,7 +123,7 @@ def setupUi(self, MainWindow): self.scrollArea_myLibrary.setWidgetResizable(True) self.scrollAreaWidgetContents_myLibrary = QWidget() self.scrollAreaWidgetContents_myLibrary.setObjectName(u"scrollAreaWidgetContents_myLibrary") - self.scrollAreaWidgetContents_myLibrary.setGeometry(QRect(0, 0, 98, 28)) + self.scrollAreaWidgetContents_myLibrary.setGeometry(QRect(0, 0, 517, 191)) self.v_Layout_myLibrary = QVBoxLayout(self.scrollAreaWidgetContents_myLibrary) self.v_Layout_myLibrary.setObjectName(u"v_Layout_myLibrary") self.scrollArea_myLibrary.setWidget(self.scrollAreaWidgetContents_myLibrary) @@ -187,22 +187,6 @@ def setupUi(self, MainWindow): self.v_Layout_manga_detail.addWidget(self.label_manga_outline, 0, Qt.AlignTop) - self.horizontalLayout_3 = QHBoxLayout() - self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") - self.horizontalSpacer_7 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) - - self.horizontalLayout_3.addItem(self.horizontalSpacer_7) - - self.pushButton_save_mata = QPushButton(self.groupBox_my_manga) - self.pushButton_save_mata.setObjectName(u"pushButton_save_mata") - self.pushButton_save_mata.setEnabled(False) - self.pushButton_save_mata.setMinimumSize(QSize(100, 0)) - - self.horizontalLayout_3.addWidget(self.pushButton_save_mata) - - - self.v_Layout_manga_detail.addLayout(self.horizontalLayout_3) - self.v_Layout_manga_detail.setStretch(4, 1) self.horizontalLayout_8.addLayout(self.v_Layout_manga_detail) @@ -286,6 +270,71 @@ def setupUi(self, MainWindow): self.verticalLayout_4.addLayout(self.h_Layout_chp_detail) + self.widget_biliplus_detail = QWidget(self.groupBox_chp_detail) + self.widget_biliplus_detail.setObjectName(u"widget_biliplus_detail") + self.h_Layout_widget_biliplus_detail = QHBoxLayout(self.widget_biliplus_detail) + self.h_Layout_widget_biliplus_detail.setObjectName(u"h_Layout_widget_biliplus_detail") + self.h_Layout_widget_biliplus_detail.setContentsMargins(0, 0, 0, 0) + self.h_Layout_biliplus_detail = QHBoxLayout() + self.h_Layout_biliplus_detail.setObjectName(u"h_Layout_biliplus_detail") + self.pushButton_resolve_detail = QPushButton(self.widget_biliplus_detail) + self.pushButton_resolve_detail.setObjectName(u"pushButton_resolve_detail") + self.pushButton_resolve_detail.setEnabled(True) + self.pushButton_resolve_detail.setMaximumSize(QSize(80, 25)) + + self.h_Layout_biliplus_detail.addWidget(self.pushButton_resolve_detail) + + self.pushButton_biliplus_resolve_detail = QPushButton(self.widget_biliplus_detail) + self.pushButton_biliplus_resolve_detail.setObjectName(u"pushButton_biliplus_resolve_detail") + self.pushButton_biliplus_resolve_detail.setEnabled(True) + self.pushButton_biliplus_resolve_detail.setMaximumSize(QSize(80, 25)) + + self.h_Layout_biliplus_detail.addWidget(self.pushButton_biliplus_resolve_detail) + + self.line_10 = QFrame(self.widget_biliplus_detail) + self.line_10.setObjectName(u"line_10") + self.line_10.setFrameShape(QFrame.VLine) + self.line_10.setFrameShadow(QFrame.Sunken) + + self.h_Layout_biliplus_detail.addWidget(self.line_10) + + self.label_resolve_status = QLabel(self.widget_biliplus_detail) + self.label_resolve_status.setObjectName(u"label_resolve_status") + sizePolicy1 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) + sizePolicy1.setHorizontalStretch(0) + sizePolicy1.setVerticalStretch(0) + sizePolicy1.setHeightForWidth(self.label_resolve_status.sizePolicy().hasHeightForWidth()) + self.label_resolve_status.setSizePolicy(sizePolicy1) + self.label_resolve_status.setCursor(QCursor(Qt.IBeamCursor)) + self.label_resolve_status.setAlignment(Qt.AlignCenter) + + self.h_Layout_biliplus_detail.addWidget(self.label_resolve_status) + + self.label_biliplus_empty = QLabel(self.widget_biliplus_detail) + self.label_biliplus_empty.setObjectName(u"label_biliplus_empty") + + self.h_Layout_biliplus_detail.addWidget(self.label_biliplus_empty) + + self.line_11 = QFrame(self.widget_biliplus_detail) + self.line_11.setObjectName(u"line_11") + self.line_11.setFrameShape(QFrame.VLine) + self.line_11.setFrameShadow(QFrame.Sunken) + + self.h_Layout_biliplus_detail.addWidget(self.line_11) + + self.pushButton_biliplus_detail_download_selected = QPushButton(self.widget_biliplus_detail) + self.pushButton_biliplus_detail_download_selected.setObjectName(u"pushButton_biliplus_detail_download_selected") + self.pushButton_biliplus_detail_download_selected.setEnabled(False) + self.pushButton_biliplus_detail_download_selected.setMaximumSize(QSize(130, 25)) + + self.h_Layout_biliplus_detail.addWidget(self.pushButton_biliplus_detail_download_selected) + + + self.h_Layout_widget_biliplus_detail.addLayout(self.h_Layout_biliplus_detail) + + + self.verticalLayout_4.addWidget(self.widget_biliplus_detail) + self.listWidget_chp_detail = QListWidget(self.groupBox_chp_detail) self.listWidget_chp_detail.setObjectName(u"listWidget_chp_detail") self.listWidget_chp_detail.setAutoFillBackground(True) @@ -321,7 +370,7 @@ def setupUi(self, MainWindow): self.progressBar_total_progress = QProgressBar(self.tab_download) self.progressBar_total_progress.setObjectName(u"progressBar_total_progress") self.progressBar_total_progress.setEnabled(True) - self.progressBar_total_progress.setValue(100) + self.progressBar_total_progress.setValue(0) self.progressBar_total_progress.setTextVisible(True) self.h_Layout_total_progress.addWidget(self.progressBar_total_progress) @@ -366,7 +415,7 @@ def setupUi(self, MainWindow): self.scrollArea_processing.setWidgetResizable(True) self.scrollAreaWidgetContents_processing = QWidget() self.scrollAreaWidgetContents_processing.setObjectName(u"scrollAreaWidgetContents_processing") - self.scrollAreaWidgetContents_processing.setGeometry(QRect(0, 0, 98, 28)) + self.scrollAreaWidgetContents_processing.setGeometry(QRect(0, 0, 1068, 554)) self.verticalLayout_processing = QVBoxLayout(self.scrollAreaWidgetContents_processing) self.verticalLayout_processing.setObjectName(u"verticalLayout_processing") self.scrollArea_processing.setWidget(self.scrollAreaWidgetContents_processing) @@ -388,7 +437,7 @@ def setupUi(self, MainWindow): self.scrollArea_finished.setWidgetResizable(True) self.scrollAreaWidgetContents_finished = QWidget() self.scrollAreaWidgetContents_finished.setObjectName(u"scrollAreaWidgetContents_finished") - self.scrollAreaWidgetContents_finished.setGeometry(QRect(0, 0, 98, 28)) + self.scrollAreaWidgetContents_finished.setGeometry(QRect(0, 0, 61, 18)) self.verticalLayout_finished = QVBoxLayout(self.scrollAreaWidgetContents_finished) self.verticalLayout_finished.setObjectName(u"verticalLayout_finished") self.scrollArea_finished.setWidget(self.scrollAreaWidgetContents_finished) @@ -431,7 +480,6 @@ def setupUi(self, MainWindow): self.label_my_cookie = QLabel(self.tab_setting) self.label_my_cookie.setObjectName(u"label_my_cookie") self.label_my_cookie.setMinimumSize(QSize(110, 0)) - self.label_my_cookie.setMaximumSize(QSize(16777215, 16777215)) self.h_Layout_my_cookie.addWidget(self.label_my_cookie) @@ -440,6 +488,17 @@ def setupUi(self, MainWindow): self.h_Layout_my_cookie.addWidget(self.lineEdit_my_cookie) + self.pushButton_qrcode = QPushButton(self.tab_setting) + self.pushButton_qrcode.setObjectName(u"pushButton_qrcode") + icon1 = QIcon() + icon1.addFile(u":/imgs/Scan-qr-code.512.png", QSize(), QIcon.Normal, QIcon.Off) + self.pushButton_qrcode.setIcon(icon1) + self.pushButton_qrcode.setIconSize(QSize(25, 25)) + self.pushButton_qrcode.setAutoRepeat(False) + self.pushButton_qrcode.setAutoExclusive(False) + + self.h_Layout_my_cookie.addWidget(self.pushButton_qrcode) + self.pushButton_my_cookie = QPushButton(self.tab_setting) self.pushButton_my_cookie.setObjectName(u"pushButton_my_cookie") @@ -448,6 +507,36 @@ def setupUi(self, MainWindow): self.verticalLayout_5.addLayout(self.h_Layout_my_cookie) + self.widget_biliplus_cookie = QWidget(self.tab_setting) + self.widget_biliplus_cookie.setObjectName(u"widget_biliplus_cookie") + self.h_Layout_widget_biliplus_cookie = QHBoxLayout(self.widget_biliplus_cookie) + self.h_Layout_widget_biliplus_cookie.setSpacing(6) + self.h_Layout_widget_biliplus_cookie.setObjectName(u"h_Layout_widget_biliplus_cookie") + self.h_Layout_widget_biliplus_cookie.setContentsMargins(0, 0, 0, 0) + self.h_Layout_biliplus_cookie = QHBoxLayout() + self.h_Layout_biliplus_cookie.setObjectName(u"h_Layout_biliplus_cookie") + self.label_biliplus_cookie = QLabel(self.widget_biliplus_cookie) + self.label_biliplus_cookie.setObjectName(u"label_biliplus_cookie") + self.label_biliplus_cookie.setMinimumSize(QSize(110, 0)) + + self.h_Layout_biliplus_cookie.addWidget(self.label_biliplus_cookie) + + self.lineEdit_biliplus_cookie = QLineEdit(self.widget_biliplus_cookie) + self.lineEdit_biliplus_cookie.setObjectName(u"lineEdit_biliplus_cookie") + + self.h_Layout_biliplus_cookie.addWidget(self.lineEdit_biliplus_cookie) + + self.pushButton_biliplus_cookie = QPushButton(self.widget_biliplus_cookie) + self.pushButton_biliplus_cookie.setObjectName(u"pushButton_biliplus_cookie") + + self.h_Layout_biliplus_cookie.addWidget(self.pushButton_biliplus_cookie) + + + self.h_Layout_widget_biliplus_cookie.addLayout(self.h_Layout_biliplus_cookie) + + + self.verticalLayout_5.addWidget(self.widget_biliplus_cookie) + self.h_Layout_save_path = QHBoxLayout() self.h_Layout_save_path.setObjectName(u"h_Layout_save_path") self.label_save_path = QLabel(self.tab_setting) @@ -494,7 +583,7 @@ def setupUi(self, MainWindow): self.h_Layout_num_thread.setObjectName(u"h_Layout_num_thread") self.label_num_thread_count = QLabel(self.groupBox) self.label_num_thread_count.setObjectName(u"label_num_thread_count") - self.label_num_thread_count.setMinimumSize(QSize(110, 0)) + self.label_num_thread_count.setMinimumSize(QSize(120, 0)) self.label_num_thread_count.setMaximumSize(QSize(16777215, 16777215)) self.h_Layout_num_thread.addWidget(self.label_num_thread_count) @@ -684,13 +773,17 @@ def retranslateUi(self, MainWindow): self.label_manga_style.setText("") self.label_manga_isFinish.setText("") self.label_manga_outline.setText("") - self.pushButton_save_mata.setText(QCoreApplication.translate("MainWindow", u"\u4fdd\u5b58\u5143\u6570\u636e", None)) self.groupBox_chp_detail.setTitle(QCoreApplication.translate("MainWindow", u"\u7ae0\u8282\u8be6\u60c5 (\u53f3\u952e\u786e\u8ba4\u9f20\u6807\u6846\u9009\u5185\u5bb9)", None)) self.label_chp_detail_total_chp.setText(QCoreApplication.translate("MainWindow", u"\u603b\u7ae0\u6570\uff1a", None)) self.label_chp_detail_num_unlocked.setText(QCoreApplication.translate("MainWindow", u"\u5df2\u89e3\u9501\uff1a", None)) self.label_chp_detail_num_downloaded.setText(QCoreApplication.translate("MainWindow", u"\u5df2\u4e0b\u8f7d\uff1a", None)) self.label_chp_detail_num_selected.setText(QCoreApplication.translate("MainWindow", u"\u5df2\u9009\u4e2d\uff1a", None)) self.pushButton_chp_detail_download_selected.setText(QCoreApplication.translate("MainWindow", u"\u4e0b\u8f7d\u9009\u4e2d\u7ae0\u8282", None)) + self.pushButton_resolve_detail.setText(QCoreApplication.translate("MainWindow", u"B\u7ad9\u89e3\u6790", None)) + self.pushButton_biliplus_resolve_detail.setText(QCoreApplication.translate("MainWindow", u"BiliPlus\u89e3\u6790", None)) + self.label_resolve_status.setText("") + self.label_biliplus_empty.setText("") + self.pushButton_biliplus_detail_download_selected.setText(QCoreApplication.translate("MainWindow", u"\u4e0b\u8f7dBiliPlus\u9009\u4e2d\u7ae0\u8282", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_my_manga), QCoreApplication.translate("MainWindow", u"\u6211\u7684\u6f2b\u753b", None)) self.label_total_progress.setText(QCoreApplication.translate("MainWindow", u"\u603b\u8fdb\u5ea6", None)) self.label_total_progress_speed.setText(QCoreApplication.translate("MainWindow", u"\u603b\u4e0b\u8f7d\u901f\u5ea6:", None)) @@ -700,7 +793,10 @@ def retranslateUi(self, MainWindow): self.tabWidget_download_list.setTabText(self.tabWidget_download_list.indexOf(self.tab_finished), QCoreApplication.translate("MainWindow", u"\u5df2\u5b8c\u6210", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_download), QCoreApplication.translate("MainWindow", u"\u4e0b\u8f7d\u5217\u8868", None)) self.label_my_cookie.setText(QCoreApplication.translate("MainWindow", u"\u6211\u7684Cookie\uff1a", None)) + self.pushButton_qrcode.setText("") self.pushButton_my_cookie.setText(QCoreApplication.translate("MainWindow", u"\u786e\u8ba4", None)) + self.label_biliplus_cookie.setText(QCoreApplication.translate("MainWindow", u"BiliPlus Cookie\uff1a", None)) + self.pushButton_biliplus_cookie.setText(QCoreApplication.translate("MainWindow", u"\u786e\u8ba4", None)) self.label_save_path.setText(QCoreApplication.translate("MainWindow", u"\u6f2b\u753b\u4fdd\u5b58\u8def\u5f84\uff1a", None)) self.pushButton_save_path.setText(QCoreApplication.translate("MainWindow", u"\u6d4f\u89c8...", None)) self.groupBox.setTitle(QCoreApplication.translate("MainWindow", u"\u6ce8\u610f\uff1a\u4ee5\u4e0b\u8bbe\u7f6e\u53ea\u5728\u4e0b\u6b21\u542f\u52a8\u65f6\u751f\u6548\uff01", None)) diff --git a/src/ui/PySide_src/myAbout.ui b/src/ui/PySide_src/myAbout.ui index cfbe068..8fc8875 100644 --- a/src/ui/PySide_src/myAbout.ui +++ b/src/ui/PySide_src/myAbout.ui @@ -33,7 +33,7 @@ - <html><head/><body><p align="center"><span style=" font-size:16pt; font-weight:700; color:#00aaff;">哔哩哔哩</span><span style=" font-size:16pt; font-weight:700;">漫画下载器 </span><span style=" font-size:16pt; font-weight:700; color:#aa00ff;">v1.2.0</span></p><p align="center"><span style=" font-size:14pt; font-weight:700;">作者:Zeal-L</span></p><p align="center"><span style=" font-size:12pt; font-weight:700;">项目地址:</span><a href="https://github.com/Zeal-L/BiliBili-Manga-Downloader"><span style=" font-size:12pt; text-decoration: underline; color:#0000ff;">https://github.com/Zeal-L/BiliBili-Manga-Downloader</span></a></p><p align="center"><span style=" font-size:12pt; font-weight:700;">本程序仅供学习交流使用,严禁用于商业用途</span></p><p align="center"><span style=" font-size:16pt; font-weight:700; color:#00aa00;">联系方式:</span><span style=" font-size:12pt; font-weight:700;">Q群号:</span><span style=" font-size:12pt; font-weight:700; color:#aa00ff;">244029317</span><span style=" font-size:12pt; font-weight:700;"><br/>欢迎进群讨论程序,漫画,资源分享, 提交问题等等</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt; font-weight:700;">-- LICENSE --</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">BiliBili-Manga-Downloader</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">Copyright (C) 2023 Zeal L</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">You should have received a copy of the GNU Affero General Public License along with this program. If not, see </span><a href="https://www.gnu.org/licenses"><span style=" font-size:12pt; text-decoration: underline; color:#0000ff;">https://www.gnu.org/licenses</span></a><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">.</span></p><p><br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:16pt; font-weight:700; color:#00aaff;">哔哩哔哩</span><span style=" font-size:16pt; font-weight:700;">漫画下载器 </span><span style=" font-size:16pt; font-weight:700; color:#aa00ff;">v1.3.0</span></p><p align="center"><span style=" font-size:14pt; font-weight:700;">作者:Zeal-L</span></p><p align="center"><span style=" font-size:12pt; font-weight:700;">项目地址:</span><a href="https://github.com/Zeal-L/BiliBili-Manga-Downloader"><span style=" font-size:12pt; text-decoration: underline; color:#0000ff;">https://github.com/Zeal-L/BiliBili-Manga-Downloader</span></a></p><p align="center"><span style=" font-size:12pt; font-weight:700;">本程序仅供学习交流使用,严禁用于商业用途</span></p><p align="center"><span style=" font-size:16pt; font-weight:700; color:#00aa00;">联系方式:</span><span style=" font-size:12pt; font-weight:700;">Q群号:</span><span style=" font-size:12pt; font-weight:700; color:#aa00ff;">244029317</span><span style=" font-size:12pt; font-weight:700;"><br/>欢迎进群讨论程序,漫画,资源分享, 提交问题等等</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt; font-weight:700;">-- LICENSE --</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">BiliBili-Manga-Downloader</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">Copyright (C) 2023 Zeal L</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.</span></p><p align="center"><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">You should have received a copy of the GNU Affero General Public License along with this program. If not, see </span><a href="https://www.gnu.org/licenses"><span style=" font-size:12pt; text-decoration: underline; color:#0000ff;">https://www.gnu.org/licenses</span></a><span style=" font-family:'HYWenHei-85W'; font-size:12pt;">.</span></p><p><br/></p></body></html> true diff --git a/src/ui/PySide_src/myAbout_ui.py b/src/ui/PySide_src/myAbout_ui.py index 22c1668..0d74a6d 100644 --- a/src/ui/PySide_src/myAbout_ui.py +++ b/src/ui/PySide_src/myAbout_ui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'myAbout.ui' ## -## Created by: Qt User Interface Compiler version 6.5.1 +## Created by: Qt User Interface Compiler version 6.5.2 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -55,7 +55,7 @@ def setupUi(self, My_about): def retranslateUi(self, My_about): My_about.setWindowTitle(QCoreApplication.translate("My_about", u"Form", None)) self.label.setText("") - self.outline.setText(QCoreApplication.translate("My_about", u"

\u54d4\u54e9\u54d4\u54e9\u6f2b\u753b\u4e0b\u8f7d\u5668 v1.2.0

\u4f5c\u8005\uff1aZeal-L

\u9879\u76ee\u5730\u5740\uff1ahttps://github.com/Zeal-L/BiliBili-Manga-Downloader

\u672c\u7a0b\u5e8f\u4ec5\u4f9b\u5b66\u4e60\u4ea4\u6d41\u4f7f\u7528\uff0c\u4e25\u7981\u7528\u4e8e\u5546\u4e1a\u7528\u9014

\u8054\u7cfb\u65b9" + self.outline.setText(QCoreApplication.translate("My_about", u"

\u54d4\u54e9\u54d4\u54e9\u6f2b\u753b\u4e0b\u8f7d\u5668 v1.3.0

\u4f5c\u8005\uff1aZeal-L

\u9879\u76ee\u5730\u5740\uff1ahttps://github.com/Zeal-L/BiliBili-Manga-Downloader

\u672c\u7a0b\u5e8f\u4ec5\u4f9b\u5b66\u4e60\u4ea4\u6d41\u4f7f\u7528\uff0c\u4e25\u7981\u7528\u4e8e\u5546\u4e1a\u7528\u9014

\u8054\u7cfb\u65b9" "\u5f0f\uff1aQ\u7fa4\u53f7\uff1a244029317
\u6b22\u8fce\u8fdb\u7fa4\u8ba8\u8bba\u7a0b\u5e8f\uff0c\u6f2b\u753b\uff0c\u8d44\u6e90\u5206\u4eab, \u63d0\u4ea4\u95ee\u9898\u7b49\u7b49

-- LICENSE --

BiliBili-Manga-Downloader

Copyright (C) 2023 Zeal L

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (" "at your option) any later version.

This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses.


", None)) # retranslateUi diff --git a/src/ui/PySide_src/qrCode.ui b/src/ui/PySide_src/qrCode.ui new file mode 100644 index 0000000..f13cdaa --- /dev/null +++ b/src/ui/PySide_src/qrCode.ui @@ -0,0 +1,62 @@ + + + QrCode + + + + 0 + 0 + 418 + 447 + + + + 二维码登入窗口 + + + + :/imgs/BiliBili_favicon.ico:/imgs/BiliBili_favicon.ico + + + + QLayout::SetFixedSize + + + + + Qt::LeftToRight + + + false + + + ## 请使用BiliBili手机客户端扫描二维码登入 + + + Qt::MarkdownText + + + Qt::AlignCenter + + + -1 + + + + + + + + + + :/imgs/fail_img.jpg + + + + + + + + + + diff --git a/src/ui/PySide_src/qrCode_ui.py b/src/ui/PySide_src/qrCode_ui.py new file mode 100644 index 0000000..dfca7d9 --- /dev/null +++ b/src/ui/PySide_src/qrCode_ui.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'qrCode.ui' +## +## Created by: Qt User Interface Compiler version 6.5.2 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QLabel, QLayout, QSizePolicy, + QVBoxLayout, QWidget) +import src.ui.PySide_src.resource_rc + +class Ui_QrCode(object): + def setupUi(self, QrCode): + if not QrCode.objectName(): + QrCode.setObjectName(u"QrCode") + QrCode.resize(418, 447) + icon = QIcon() + icon.addFile(u":/imgs/BiliBili_favicon.ico", QSize(), QIcon.Normal, QIcon.Off) + QrCode.setWindowIcon(icon) + self.verticalLayout = QVBoxLayout(QrCode) + self.verticalLayout.setObjectName(u"verticalLayout") + self.verticalLayout.setSizeConstraint(QLayout.SetFixedSize) + self.label = QLabel(QrCode) + self.label.setObjectName(u"label") + self.label.setLayoutDirection(Qt.LeftToRight) + self.label.setAutoFillBackground(False) + self.label.setTextFormat(Qt.MarkdownText) + self.label.setAlignment(Qt.AlignCenter) + self.label.setIndent(-1) + + self.verticalLayout.addWidget(self.label) + + self.label_img = QLabel(QrCode) + self.label_img.setObjectName(u"label_img") + self.label_img.setPixmap(QPixmap(u":/imgs/fail_img.jpg")) + + self.verticalLayout.addWidget(self.label_img) + + self.verticalLayout.setStretch(1, 1) + + self.retranslateUi(QrCode) + + QMetaObject.connectSlotsByName(QrCode) + # setupUi + + def retranslateUi(self, QrCode): + QrCode.setWindowTitle(QCoreApplication.translate("QrCode", u"\u4e8c\u7ef4\u7801\u767b\u5165\u7a97\u53e3", None)) + self.label.setText(QCoreApplication.translate("QrCode", u"## \u8bf7\u4f7f\u7528BiliBili\u624b\u673a\u5ba2\u6237\u7aef\u626b\u63cf\u4e8c\u7ef4\u7801\u767b\u5165", None)) + self.label_img.setText("") + # retranslateUi + diff --git a/src/ui/PySide_src/resource.qrc b/src/ui/PySide_src/resource.qrc index edf9ca3..d0ead14 100644 --- a/src/ui/PySide_src/resource.qrc +++ b/src/ui/PySide_src/resource.qrc @@ -1,5 +1,6 @@ + imgs/Scan-qr-code.512.png imgs/fail_img.jpg imgs/blinblin.png imgs/BiliBili_favicon.ico diff --git a/src/ui/PySide_src/resource_rc.py b/src/ui/PySide_src/resource_rc.py index bc99d93..404b227 100644 --- a/src/ui/PySide_src/resource_rc.py +++ b/src/ui/PySide_src/resource_rc.py @@ -1,6 +1,6 @@ # Resource object code (Python 3) # Created by: object code -# Created by: The Resource Compiler for Qt version 6.5.1 +# Created by: The Resource Compiler for Qt version 6.5.2 # WARNING! All changes made in this file will be lost! from PySide6 import QtCore @@ -7118,6 +7118,344 @@ \xcd\xc2.G\x88\x85+D\xb4\x81\x1b\xe9\xe1q\xba\xc6\ J\xe3\xb2F\x99\x94\xbd\xb6f\xfd\xc4Y\xf8\x89\x1b\xf9\ \x09\x1d\xfd\xb5O\xde\x1f~\x98lA\ +\x00\x00\x14\xf1\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x02\x00\x00\x00\x02\x00\x08\x03\x00\x00\x00\xc3\xa6$\xc8\ +\x00\x00\x00\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x01\ +\xb3PLTE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00U;AI\x00\x00\x00\x90\ +tRNS\x00\xf4$\x99W\xca#\xfa\xf9\x0d\xfb\xea\ +\xd2\x9a\xeb\x08\x01\xfc\xe9\xfe\x8b1\x09\xd0\xf8\x89\x0e\x8d\ +\xd34\xf090\x8a6k\xee\x19\x9b\x0bH\xe1fT\ +\xe4(7\x22\xc0%\xd5\x06!?\xab\xec\x93\xc5\xa4I\ +\x15\xed}\xa3\xad|\xc9G\x86o\xba\xae\xde.3\xc4\ +\x90\x16\xc2\x0c;=X\x12J\x82]c\xbf\xd9F\xf1\ +\xb1E\xb2\xa6\xaf\xb5\xf2h\x1b\x8c\xef\xcf\x1a\x5c\xce\x04\ +MNZ\x03\x7f\xc1\xcc\x10\xe7B\x98m\xa2\xe2\xcb/\ +\xddjn\xe0\xb9\xc3\x8e\xda\x92\xb8\xb3\x91i\xb7\x14\x11\ +\x8f\x87\xac\xdc\xbe\xc7\xd9\xac\x00\x00\x12@IDAT\ +x\xda\xed\x9diW\xdb\xc8\xb6@\x05\xd8\xd8\x18\x1b\xec\ +\x980\x85\xd0\x19\x80\x00\x0f\x08p\x1b\x02$d\x1e\xbb\ +\xd3\xdd\x99\xe7y\x9e\xe7t\xf7\xed\xb9\xef<_\xfd\xe4\ +\xd7+\xeb\xad\xf5Bb\xa9\xca:*\xcbU\xda\xfb{\ +\xd59:\xde\xb6\xe4\x92T\xc7\xf3\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\x22:2m\xd9\xae\ +\x9c/$\xd7\x95m\xcbtP?\xeb\xea\xd7Zj\xf1\ +c\xa3\xa5\xd4\x9a\xb6\x8f\xdf\xf2\xfau\x0f\xe4\xfdX\xc9\ +\xf7\xf4\xa6\xe9\xe3\xb7\xbd~\xedE?v\x8a\xed\xe9\xf9\ +\xfcm\xaf\xdf\xa6u\xbe\x01\xba\xd6\xa7\xe5\xf3\xb7\xbd~\ +\xedF\xf2\xf7\xfd\xce\x94\xfc\x06\xd8^\xbf\xee\xa2o\x88\ +\xa1\xeeT\x9c\xffm\xaf\xdff\xdf\x18=i\x10\xc0\xf6\ +\xfa\xb5\xe6\xcd\x1d@>\x05\xff\x06\xad\xaf\xdf\x98o\x90\ +1\xf7\x05\xb0\xbd~\x1d-&\x0f\xa0\xc5\xf95A\xeb\ +\xeb\x97\xf1\x8d\x92q]\x00\xeb\xebW2{\x00m\xae\ +\x0b`}\xfd\xb2f\x0f \xeb\xba\x00\xd6\xd7\xaf\xd3\xec\ +\x01t\xba.\x80\xf5\xf5\xcb\x99=\x80\x9c\xeb\x02X_\ +?\x1f\x01\x9aY\x00\xdfv\x018\x05\xa4\x5c\x80\xfd\x5c\ +\x04\xa6[\x00\xe7\xff\x06\xb6!\x00\x0bA\x08\xc0R0\ +\x02\xd4\xa6\xe4y\x9c\x03R,\x00\xb7\x83S.\x00\x0f\ +\x84\xa4[\x80b:\x1e\x09\xebC\x00\x1e\x0aE\x80$\ +\x1fkN\x9aM\xeb\x10\xa0\xc6\xef?/\x86\xa4Y\x00\ +^\x0dK\xb5\x00\xbc\x1c\x9a^\x01x=\xdc1\x01<\ +p\xbb\xfe\x08\x80\x00\x08\x80\x00\x08\x80\x00\x08\x80\x00\x08\ +\x80\x00\x08\x80\x00\x08\x80\x00\x08\x80\x00\x08\x80\x00\x08\x80\ +\x00\x08\x80\x00\x80\x00\x80\x00\x80\x00\x80\x00\x80\x00\x80\x00\ +\x80\x00\x80\x00\x80\x00\x80\x00\x80\x00\x80\x00\x80\x00\x80\x00\ +\x80\x00\x80\x00\x80\x00\x80\x00\x80\x00\x90^\x01\xbe\xda\xb9\ +{\xcfw\x13#\x05\xbd=\x10\x0a#\x13\xdf\xad\xec\x1e\ +\xdf&<\x92\xc7\x076\xbc\x9c\xea\xaf\x0c\x17W&G\ +\x85S50\x7f\x07\x05\xf8\xc77\x9fG\xda'#\xf7\ +\xeb!\xc1\x07w\xfa\xec{[uU\xae\x9d\x88>S\ +c\xf3wN\x80\x8dG\x04\xbb\xa4\xe4\x8e|\x12\xed(\ +\xca\x93\xabkg\xaa.F\x94\xa9\xd1\xf9;&\xc0\xb1\ +\xc3U\xd9\x968\xd5_6F8\x88\xc1+5\xb6\xa9\ +|\x1ba\xa2\xc6\xe7\xef\x94\x00\x83\xc7\x0b\xbe\x98\xca\x9f\ +\x06\xeb=\x86]\x8fj~\x1dO\xd5\xedQ\x02\xf9\xbb\ +$\xc0\xc6\xff\xc6\xb31\xd6\xc4\x96\xfa\x0e\xa1\xe3U\xc0\ +\x0f\xf2\xce:\x7f\xfd\x93\xc8\xdf!\x01\xb6\xf6\xc7\xb6\xcb\ +\xe4\xd6\xba\x0ea1h\x9e\xe1\xed\xf5L\x93L\xfe\xee\ +\x08p0\xce\xdd\xf16\x97\xf5\x8f\xe0|\xf04\xb3u\ +\x14\x22\xa1\xfc\x9d\x11\xe0\xebx\xf7G,iW\xf0\xf5\ +\x8e\x90i\xfe\xc7k\xf6\xfc]\x11` \xee\x1d2\x07\ +t\x0f\xe0E\xd8,}^\xb3\xe7\xef\x88\x00[\xfd\xd8\ +\xd1<\x8f>\x7f\x13:\xcb'\xcd\x9e\xbf\x1b\x02l\xec\ +\x8f\xbf\x80y\xbdk\xe9\xab\xe1\xb3lh\xf6\xfc\x9d\x10\ +`\xf0\xa8o\x80\x1f\xb4\xfeO\xdf\x0d\x9f\xa4\xaf\xd9\xf3\ +wB\x80\xbf\xfaF8\xae\x13{V\xb18\xfb\xbb&\ +\xcf\xdf\x05\x016\x15\xcc\x14\xb0\xf0\xbdF\xf0)\xc5$\ +\x9f6y\xfe.\x08p\xd87\xc4\x9f5\x82\xab\xce\xde\ +\xeb\x9b<\xff\xc4\x05P\xdc\xfb\xca\xa9g\xd8R5U\ +\xc0\xaa\xc6\x8d\x95\x8aX\x80d\xf3\x97\xd7_Hgx\ +\x02\x9d\xea\x19\x8e\xf8\xc6\xd8\xab\x8e>,>\x05$\x9b\ +\xbf\xbc\xfeB\xb2\xe1\x09\xecWN\xf0E\xce\x5c\x01s\ +\xbb\x94\xe1\x87\xa4\x17\x81\x09\xe7/\xae\xbf\x14E\xef\xe3\ +6\xe5\x04\x87|\x83\xfc\xa4\x0c\x7f=|\x82\xfb\xcd\x9e\ +\xbf\xb8\xfeR2\xe1\x09d\x94\x13|n\xb2\x80_*\ +\xc3\xdf\x96.\x04%\x9c\xbf\xb8\xfeR:B\xbb\x9d\xb5\ +(\xfb\x7f}\x953Y\xc0\xbc\xf2I\xcb;\x15\xd9R\ +p\xd2\xf9K\xebo\xf8\x1cPR\x0e\x1f\xf7\x8d2\xae\ +L\xe0\xa6l!0\xf1\xfc\x85\xf5\x97\xd3\x1a\xd2\xf32\ +\xaf\xee\xff\xb8\xdbl\x01\xf7)\x138Y\x15\xdd\x0eN\ +<\x7fa\xfdc`sp\x02=\xea\xd1+f\x0b\xb8\ +G\x9d\xc1\x8d\xe0\xd1\x17l\xc8_V\xff\x18\xe8\xee\x0b\ +\x8a_\xec\x16\xff\x8d\x94\x92Ug\xf0 \xf0\x89\x90{\ +\xdbm\xc8_V\xff8h\x0f\xe8~\xde\xa9\xd3\xff\xfb\ +\xf7f\x0b\xf8\xb3F\x0a\x97\x02\xae\xe3rg\xec\xc8_\ +T\xffX\xd8T3\x83\xae\xf5:cG\xcc\x16pU\ +'\x87g5\xef\xe6\x14\x1e\xda\x92\xbf\xa4\xfe1\xfd\x06\ +\x14k\xfc\xfe\xe8\xf9W0[@\xbd\xb5\xf0'\x97?\ +\x1ey\xeb\xa9=\xf9\x0b\xea\x1f\xd7u\xc0\xc0\x07\xd7\xa2\ +\xf9\x9e\xdex\xee&J\xd1\xcbbn\xf9\xc3q\xcbs\ +6\xe5\x1f\xbd\xfe\xf1\xfd\x1b,\xbd\xb7\x22\xd1R\xd2\xfe\ +\xff\xd1\x1c\x05\xf4\xca\x9957\x05.\x9e+\xdb\x95\x7f\ +\xe4\xfa\xc7\xb9&\x98i\xcbv\xe5r]\xd9\xb6L\x1d\ +\xebO\xcdR@\xaf\xbc\xb04\xffnU\xb02\xbf\xb4\ +P\xb6/\xff\x88\xf5O\x1ci\x01b} \xa2wz\ +ff\xba\xd7\xde\xfcm\xc4\xf6\x02\x22\x00\x02 \x00\x02\ + \x00\x02 \x00\x02 \x00\x02 \x00\x02 \x00\x02 \ +\x00\x02 \x00\x02 \x00\x02 \x00\x02 \x00\x02 \x00\ +\x02 \x00\x02 \x00\x02 \x00\x02 \x00\x02 \x00\x02\ + \x00\x02 \x00\x02 \x00\x02 \x00\x02 \x00\x02 \ +\x00\x02 \x00\x02 \x00\x02 \x00\x02 \x00\x02 \x00\ +\x02 \x00\x02 @\x837Xx|`\xc3\xcb\xa9\xfe\ +\xcapqer\xd4\xc6\xfc\x11@T\xc0\xd3g\xdf\xdb\ +j\xabr\xed\x04\x02\xa4J\x80\xf2\xe4\xea\xdaa\xd5\xc5\ +Q\x04h\x08\x86\xb7Y+h%1x\xe5\xe3\x91\x9d\ +o\xed\xc9\xdff\x0co\xb48\xa2\x93\xc3\xaeG\xb5\x86\ +\xe6N\xd9\x92\xbf\xd5\x18\xdejuB#\x85\x8eW\xb5\ +\xc7\xe6v\xda\x91\xbf\xdd\x18\xdelY\xa7g\xceb\xd0\ +\xe0\xe1\xedV\xe4o7{\xcc\x16\xf0[u\x06\xe7\x83\ +G\xcf\xda\x90\xbf\xe5$\xdep\xe1\xf5\x8e\x90\xe1\x164\ +\x8c\xb0\x9d\x9df\x0bx@\x99\xc0\x8b\xb0\xe1}\xcd\x9f\ +\xbf\xed$\xddt\xe9\xf9\x9b\xd0\x09\x9a\xbei\x94\xfd$\ +\xdcv\xedj\xf8\x04M\xdf6\xce~~2Y\xc0o\ +\x94\xe1\xef\x86O\xd0\xd7\xec\xf9\xdbO\xc2\xadWg\x15\ +34{\xebX\x07\xd8k\xae\x80{\xd5\xd1\xa7\x14S\ +|\xda\xe4\xf9;@\xb2\xed\xd7\xfb\x15s4{\xfbx\ +\x178l\xaa\x80\x7f\xd7\x08^\x11\x0b\x90l\xfe.\xf0\ +\xbd\xa1;j\x85c\x1a\xc1\x87\xc5\xa7\x80d\xf3w\x82\ +\xe3f\x0a\xf8\x07\x9d\xd8C\xd2\x8b\xc0\x84\xf3w\x82\xc1\ +\xa3&\xea\xf7\x83V\xe7\x9c\xeb\xe1\x93\xdco\xf6\xfc\xdd\ +`c\x7f\xfc\xf5\xcbo\xd1\x0a}[\xba\x10\x94p\xfe\ +\x8e\xf0\xb7\xf8\x0b\xb8U/\xf2\x9d\x8al)8\xe9\xfc\ +]\xe1\xdfq\xd7\xef\xa0n\xe4\x9b\xb2\x85\xc0\xc4\xf3w\ +\x85\xaf\xe3\xad_I\xbb\xf3\xdf\xc9\xaa\xe8vp\xe2\xf9\ +;\xc3\xc18\xeb7PG\xfdn\x04Os\xc1\x86\xfc\ +\x9d\xe1\xc7\xd8\xae\xa4\xf2u\x9d?\x1f\x04>\x11ro\ +\xbb\x0d\xf9\xbb\xc3g\x7f\x8c\xa7~G\xeb\xbc~\xbe\x14\ +p;'w\xc6\x8e\xfc\x1dZ\x0f\xd8\x10\xc3\x9aZ\xe1\ +_u\xff\x7f~V3l\xe1\xa1-\xf9;\xc4\xb1\xbf\ +\x08\xef\xacT\x7f\xf9,B\xd8'\x97?\x9e\xe9\xd6S\ +{\xf2w\xea<\xf0\x1f\xc1\xfd\xf5\xdc\xdeO\xa2E\x9d\ +[\xfep\xaa\xe59\x9b\xf2w\x8a\x7f\xfe\xf8k>\xd2\ +\xa5\xd3\x97\x87\xbe\x88\x1c\xb4\x9cYsS\xe0\xe2\xb9\xb2\ +]\xf9;\xc6\xb6\xf1}\xdff'F4\xcf\xa8\x85\xd5\ +\x9f\xb3{\xf6\x8d\x0b\x9f\x9f,/,\xcd\xbf[\x15\xac\ +\xcc/-\x94\xed\xcb\x1fb\xa0wzff\xba\x97:\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\ +\xd2\x91i\xcbv\x89\xfb\xaf\xe4\xba\xb2m\x99H\xdb,\ +=>\xb0\xe1\xe5T\x7fe\xb8\xb829\x1a=\xff\xe4\ +\xe2[Nk\xa9%\xbe}\x16[J\xad\xf5\xc6?}\ +v\xdd\xff\x8f\xaf\x5c;!\xca?\x81\xf8\x96\xd3=\x90\ +\xf7c%\xdfS\xd7F\x1f\xe5\xc9\xd5\x0fv\xebZ\x1c\ +\x15\xe5\xdf\xe0\xf8\xb6\xd3^\x8c\x7f\xb7\xedb\xbb~\xfc\ +\xc1+\x1f\x8f\xef|+\xcb\xbf\x91\xf1mg\xd3:\x13\ +\x0d\x17\xba\xd6\xeb\xc6\xdf\xf5\xa8\xe6\xd5\xc4)Y\xfe\x8d\ +\x8bo\xfd\xf7\xdf\xc8\xe7\xff\xdbwH\xf3;\xd8\xf1*\ +\xe0zr\xa7,\xffF\xc5\xb7\xfe\xfc_\xf4\x0d1\xd4\ +\xad\x95\xc0b\xd0\xf8\xe1\xed\xb2\xfc\x1b\x13\xdfz6\x9b\ +k\xbc\xd8\xa3\x13\xff|\xf0\xf8Ya\xfe\x0d\x89o\xfd\ +\xff\xbf\xbc9\x01\xf2\x1a\xff\xc6^\xef\x905\x8c\x08\xcb\ +\xbf\x11\xf1\xadg\xcc7\xc8\x98:\xfe\x0ba\xcb\x98\xb1\ +\x84\xe3[\xbf\xfe\xd7bR\x80\x16\xe5\x9a\xdc\xf37\xb2\ +\xa6Q\xe1\xf9\x9b\x8fo=\x19\xdf(\x19U\xfc\xab\xc2\ +\xb6q\x99\x84\xe3[O\xc9\xac\x00m\xaa\xf8w\xc3\xc7\ +\xf7\x09\xf37\x1e\xdfz\xb2f\x05\xc8\xaa\xe2\xcf\x0a[\ +\xc7f\x13\x8eo=\x9df\x05\xe8T\xc5\x9fRL\xf0\ +\xa9,\x7f\xe3\xf1\xad'gV\x80\x9c*\xbe\xaa\xd5\xd7\ +zY\xfe\xc6\xe3#\x80\xf0\x03\xa8\x08?\x00U\x02\xa6\ +\xe3s\x0a\x10\xfe\x04\x0f\x0b\x7f\x82\xa5\x02\x0c\xa7\xfd\x14\ +`\xf8\x22p\xbf*\xfe\x90\xf0\x22L*\xc0P\xda/\ +\x02\xdb\x12\xfe\x1bx=|\xfc}\xcf\xb0\x00\xd2\xf8,\ +\x04\x09\x17bn\x0b\x17b\xa4\x02H\xe3\xb3\x14,\x5c\ +\x0a\xbeS\x91-\xc5J\x05\x90\xc6\xe7\x1c\x10JI\x1d\ +\xff\xa6l!N*\x804\xbe\xfd$};\xf8dU\ +t;V,\x800\xbe\x03$\xfd@\xc8\x8d\xe0\xf1\x17\ +<\xf3\x02\x08\xe3;@w\x9f\xa9\xcf\xbf\xa8\xf5H\xd6\ +\x83\xc0'2\xeemo\x84\x00\xb2\xf8.\x90\xf4C\xa1\ +\x97\x02V#sg\xbcF\x08 \x8b\xef\x04I?\x16\ +\xfe\xacf\x93\xdf\xc2C\xaf1\x02\x88\xe2;\xf2\x1b\x90\ +\xf0\x8b!O.\x7f<\xfe\xd6S\xafQ\x02H\xe2\xbb\ +r\x1d\x90\xf0\xabas\xcb\x1fN\xb0<\xe75N\x00\ +A|w\xfe\x0d&\xfbrh9\xb3fQ\xfe\xe29\ +\xdd\x06\xf2\xf1\x08\x10=\xbeKk\x82\xc9\xbe\x1e^^\ +X\x9a\x7f\xb7*W\x99_Z\xd0/\x7fL\x02D\x8e\ +\x0fq\xd2;=33]\xd7\xb9#>\x01\x22\xc6\x87\ +\x84\x89U\x00@\x00@\x00@\x00@\x00@\x00@\x00\ +@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00\ +@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00\ +@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00\ +\xcb\xf8j\xe7\xee=\xdfM\x8c\x14\xf4\xf6\x84(\x8cL\ +|\xb7\xb2{|\x9b0\xe8\xe3\x03\x1b^N\xf5W\x86\ +\x8b+\x93\xa3I\x08\x10-\xbe\x83\xfc\xe3\x9b\xcf#\xed\ +\x13\x92\xfb\xf5\x90\xa0p\xa7\xcf\xbe\xb7UY\xe5\xda\x89\ +F\x0b\x105\xbesl<\x22\xd8%&w$\xe2\xe6\ +\xca\xe5\xc9\xd5\xb53U\x17G\x1b)@\xf4\xf8\x8eq\ +\xecpU\xb6EP\xf5\x97\x8d\x11\xc2\x0e^\xa9\xb1\xcd\ +\xe4\xdb\xc6\x09 \x88\xef\x14\x83\xc7\x0b\xbe\x98\xca\x9f\x06\ +\xeb\x8d\xbb\xebQ\xcd\x9f\x93S\x8d\x12@\x12\xdf\xa9_\ +\xff\xff\xc6\xb3I\xdc\xc4\x96\xfa\xe2v\xbc\x0a8\xa1\xec\ +l\x8c\x00\xa2\xf8\x0e\xb1\xb5\xdf\x8f\x89\xfc\xd6\xba\x02/\ +\x06\xcd3\xdc\x90\xcd\xa2e\xf1\xdd\xe1`\x9c\x1b\x85n\ +\xaec\xa3\xbd\xf3\xc1\xd3\xcc6B\x00Y|g\xf8:\ +\xe6V!\xda\x06\xbc\xde\x91l\xc3\x08a|W\x18\x88\ +{\xaf\xe8\x01\xdd\xc8/\x12n\x19#\x8c\xef\xca\xf9?\ +\xfe\xdd\xc25\xaf\x03\x9e\xbfI\xb6i\x944\xbe#\xd7\ +\xff\xfd\xf1\x0b\x90\xd7\xfb/p5|\x16\xe3m\xe3\xa4\ +\xf1\xdd\xf8\xff\x7f\xd4D\xc3\x88\x1f\xb4\xd6\x03\xee\x86O\ +\xd2gZ\x00i|'\xf8\xab\x99\x961\xc7ub\xcf\ +*\x16\x97M\xb7\x8e\x95\xc6w\x81M\x053\x02\x14\xbe\ +\xd7\x08>\xa5\x98\xc4t\xf3hi|\x178l\xaak\ +\xd8\x9f5\x82\xab\xae>L\xb7\x8f\x97\xc6w\x80-U\ +S\x02T5n\x0cU\x84\x1f\x80\xe2\xdee\xcet|\ +\x078\xe2\x1bc\xaf:\xfa\xb0\xf0'\xb83|x\xa7\ +\xe9\xf8\xf6\xf3E\xce\x9c\x00\xb9]\xca\xf0C\xc2\x8b\xb0\ +l\xf8\xf8\xfd\xa6\xe3\xdb\xcf!\xdf ?)\xc3_\x0f\ +\x9f\xe0\xbej\xbc\xa2\xf7u\x9b\xe9\xf8\xf6\xf3\xb9I\x01\ +\xbeT\x86\xbf-\x5c\x88\xc9\x84\x8f\xcf\x98\x8eo=_\ +\xe5L\x0a\x90W>)z\xa7\x22[\x8a\xed\x08\xedv\ +\xd7\xd2a:\xbe\xf5\x8c\xfbF\x19W&pS\xb8\x10\ +\x17z\x0e(y\xc6\xe3\xdb\xcen\xb3\x02\xecS&p\ +\xb2*\xbb\x1d\xdb\x1a\xd2\xf34\xaf\xd1\xbfR\x1a\xdfv\ +V\xcc\x0a\xb0G\x9d\xc1\x8d\xe0\xd1\x17t\x8e`s\xf0\ +\xf8\x1e\x9d\xf1\xd2\xf8\x96\x935+@V\x9d\xc1\x83\xc0\ +'2\xeei=\x92\xd5\xdd\x174\xbe\xd8\xad3^\x1a\ +\xdfr~oV\x80\x9f5R\xb8\x14p\x1d\x9a;\xa3\ +w\x08\xed\xeb\x02\x16\x814\xfb\x97K\xe3\xdb\xcd\x88Y\ +\x01VurxV\xf3nT\xe1\xa1\xee1l\xaai\ +@\x97\xf6*\xae4\xbe\xd5\x14\xcc\x0a\x90\xd3J\xe2\xc9\ +\xe5\x8fG\xdez\xaa\x7f\x10\xed\xc5\x1a\xbf\xff\xed\xfa\xe3\ +\xa5\xf1m\xc67\x8c^\x16s\xcb\x1f\x8e[\x9e\xab\xe7\ +(\xba\x07>\xf8/\x90\xef\xa9\xab\x07\xb44>\x02\x08\ +\x05\xf0\xca\x995\x8b\xf2\x17\xcf\xd5\xdb\xc0\xbd\xb5\xf4\xde\ +\x8aPK\xa9\xb5\xce\xe1\xe2\xf8\x08 \x13\xe0\xb7\x8f`\ +ai\xfe\xdd\xaa\x5ce~i!J\xf9;2m\xd9\ +\xae\x5c\xae+\xdb\x96\xe9\x880\x5c\x1c\xdfQ\x01L\x8f\ +_C\xef\xf4\xcc\xcctor\xc5H:~\xea\x05\x00\ +\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\ +\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\ +\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\ +\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\x04H\x03\xd16H\ +h\x9a\x0d\x22\xfe/\x7f\xf1\xa6DQ7\x88\xf0\x1e\x1f\ +\xd8\xf0r\xaa\xbf2\x5c\x5c\x99\x1cm\x5c\xfdc#\xea\ +\x16)\xcd\x22\xc0\x9a\xfc\xa5\xd4\xbfE\x8cw\xfa\xec{\ +[\x8dU\xae\x9dhT\xfdc\x22\xfa&I\xcd!\xc0\ +G\xf9\x8bw\xa9\xaeo\x93\xa8\xf2\xe4\xea\xda\xf1\xd5\xc5\ +\xd1\xc6\xd4?\x1e\x04\xdb\xa4\x19\xde&\xae\x109\x7f)\ +\xf5l\x137x\xa5\xc66\x93o\x1bR\xffX\x90l\ +\x94hx\xa3\xc8\x91\xe8\xf9K\xd1\xdf(r\xd7\xa3\x9a\ +W\x13\xa7\x1aQ\xffX\xbe\xff\x92\xadR\x0do\x15;\ +!\xc8_\x8a\xeeV\xb1\x1d\xaf\x02\xae'w6\xa0\xfe\ +q\x9c\xff\x03\x7f?\x8746K6\xbcY\xf4~I\ +\xfeR\x86\xb46\x8b\xf6\x16\x83\xc6\x0fo7_\xff\x18\ +\x90m\x97\xbe\xc7\xac\x00\xdf\x8a\xf2\x97\xa2\xb5]\xfc\xf9\ +\xe0\xf1\xb3\xe6\xeb\x1f\xc3\xff?Y\xc3\x84\xc4\x1bF\xb4\ +\xe6\xcdE\xd7i\x18\xf1zG\xc8\x04\x0dhX!f\ +,\xac\x02c\xca\xe1;\xcd\x0ap@\x96\xbf\x14\xf5\xf1\ +{/\xc2\xc6\xf7\x19\xaf\xbf|\xfdO\xd84)\xe9\xa6\ +Q\x1d-&\xe3\xab\x8f\xff\xf9\x9b\xd0\x09\x8c7\xad\x12\ +#m\x9b\x96t\xdb\xb8\x8c\xd9_ \xe5\xf1_\x0d\x1f\ +o\xbcm\x9d\x98Rx\x02\xca\xc6\x89\xdeO&\xeb\xff\ +\x8d4\x7f)\xca\xe3\xbf\x1b>\xbe\xcfx\xfd\xa5(\xfe\ +\xc6\xa9{\xf6$\xdc:6\xe9\x9eE\xb3\x8a#\xf8\x9d\ +\xe9\xfaK\x916O\xf6\xbc\xbd\xe6\xca\xbfW\x9c\xbfx\ +1H\x15\x7fJ1\xc1\xa7\xc6\xeb/D\xda>=\xe9\ +\xf6\xf19\xb3\x02(\x8f\xbf_1\xc1z\xe3\xf5\x17\x12\ +\xc3\xdd\xb8\xc3\xa6\xaa\xffw\xaf\xf9\x05\xa8\x08\x05\x88\xef\ +y\x88\xe4\x04\xf8\xde\xd0\x1d\xc1\xc21\xaf\xf9O\x01\xc3\ +\xc2S\x80\x0b\x02x\xc7\xcd\x14\xff\x0f1\x5c\xc4\x1a\xbf\ +\x171$\xbc\x08tB\x80\xc1\xa3&j\xff\x83\xd6\x22\ +H[\xc2\x7f\x03\xaf\x87\x8f\xbf\xef\xa5A\x00oc\x7f\ +\xfc\xa5\xcfo\x89c!\xcb\xf8B\xd0m\xe1B\x90\x1b\ +\x02x\x7f\x8b\xbf\xf4[\xe3X\xca6\xbf\x14|\xa7\x22\ +[\x0avD\x00\xef\xdfq\x97\xfe\xa0\xee\x01\x18=\x07\ +\x94\xd4\xf1o\xcan\x06\xb9\x22\x80\xf7u\xcc\x95\xd7\xee\ +\xbc\x97\xf4\xed\xe0\x93U\xd1\xed`g\x04\xf0\x0e\xc6Y\ +\xf9\x81::/&\xfd@\xc8\x8d\xe0\xf1\x17\xbc\x14\x09\ +\xe0\xfd\x18\xdb\x95`~k=G\xd0\xddg\xea\xf3/\ +j=\x92\xf5 \xf0\x89\x90{\xdbS%\x80\xf7\xd9\x1f\ +\xe3\xa9\xfb\xd1-\xf5\x1dB\xd2\x0f\x85^\x0aX\x8d\xcc\ +\x9d\xf1\xd2%\x807\xb8!\x865\xc1\xc2\xbf\xea~\x08\ +\x22\xe9\xc7\xc2\x9f\xd5<\xec\xc2C/m\x02x\xde\xb1\ +\xbf\x08\xef\x0cU\x7f\xf9,\xc2A$\xfdb\xc8\x93\xcb\ +\x1f\x8f\xbf\xf5\xd4K\xa1\x00\xbf\x9d\x07\xfe#\xb8=\x93\ +\xdb\xfbI\xb4\xa3H\xfa\xd5\xb0\xb9\xe5\x0f'X\x9e\xf3\ +\xd2)\x80\xe7\xfd\xf3\xc7_#}\x1a\xf9/\x0f}\x11\ +\xfd8\x12~9\xb4\x9cYsS\xe0\xe2\xb9\xb2\x97Z\ +\x01~c\xdb\xf8\xbeo\xb3\x13#\x9aW\x04\x85\xd5\x9f\ +\xb3{\xf6\x8do\x13\x1eI\xc2\xaf\x87\x97\x17\x96\xe6\xdf\ +\xad\x0aV\xe6\x97\x16\xca\xc9\xd6?q\x01\xd2J\xef\xf4\ +\xcc\xcct\xaf]\xf5G\x80dA\x00\x04@\x00\x04@\ +\x00\x04@\x00\x04@\x00\x04@\x00\x04@\x00\x04@\x00\ +\x04@\x00\x04@\x00\x04@\x00\x04@\x00\x04@\x00\x04\ +@\x00\x04@\x00\x04@\x00\x04@\x00\x04@\x00\x04@\ +\x00\x04@\x00\x04@\x00\x04@\x00\x04@\x00\x04@\x00\ +\x04@\x00\x04@\x00\x04@\x00\x04@\x00\x04@\x00\x04\ +@\x00\x04\x88)\x81\x86m\xb0`=\xf1lPa\xbd\ +\x00\x11\xb7X\xb1\x9eX\xb7\xa8\xb1^\x80\xba7Y\xb2\ +\x9d\xd87\xa9\xb2^\x80\xfa\xb6Y\xb3\x9dvc\xfd\xab\ +-\x16\xa0\x8e\x8d\x16mg\xd3:\x1f\x01\x04[\xadZ\ +\xff\xfd7\xf8\xf9[-\x80?\xd4\x9d\x86\xcf\xbf\xbb\xe8\ +#@\x00=i\x10`\xb3\x8f\x00\x81\xff\x05R\xf0o\ +\xd0d\xc3\x0a\xeb\x05\xf0\xc7\xdc\x17`\xccG\x80\x90\x15\ +!\xe7\xd7\x04\xcd6\xad\xb2^\x00u\xdb5\xdb1\xdc\ +\xb6\xcez\x01\xda\x5c\x17\xa0\x84\x00\xa1d]\x17 \x8b\ +\x00\xe1\x8bA\xae\x0b\xd0\x89\x00\xe1w\x87]\x17 g\ +\xbb\x009\x04Hw\xfd:9\x05\xa4\xbb~\x86/b\ +\xf6s\x11\xd8\xe4\xf5k3{\x00\xce\xff\x0d\xb4\xbe~\ +\x86\x172X\x08j\xf6\xfa\x99]\xcad)\xb8\xf9\xeb\ +g\xf47\xac\xe4y\x9c\x03\x9a\xbc~&ogr;\ +\xd8\x86\xfa\x19|\xa0\x81\x07Bl\xa8_w\x9f\xb1\x07\ +\x83\xd3\xf1H\x98\xf5\xf53\xf5P#\x0f\x85\xdaR?\ +3\x8f5\xf3X\xb8=\xf53\xf1b\x03/\x86\xd8T\ +\xbf\xd8_m\xe2\xd50\xdb\xea\x17\xeb\xcb\x8d\xbc\x1cj\ +c\xfd\xe2y\xbd\x99\xd7\xc3\xa9\x1f\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00@\xda\xf8_R\xe0|\xe2\ +s\x91\xb0G\x00\x00\x00\x00IEND\xaeB`\x82\ +\ " qt_resource_name = b"\ @@ -7138,19 +7476,26 @@ \x00B\ \x00i\x00l\x00i\x00B\x00i\x00l\x00i\x00_\x00f\x00a\x00v\x00i\x00c\x00o\x00n\x00.\ \x00i\x00c\x00o\ +\x00\x14\ +\x09\x0b\x12\xe7\ +\x00S\ +\x00c\x00a\x00n\x00-\x00q\x00r\x00-\x00c\x00o\x00d\x00e\x00.\x005\x001\x002\x00.\ +\x00p\x00n\x00g\ " qt_resource_struct = b"\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x02\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x02\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x01\x87\xb2\x02\xd0\x5c\ +\x00\x00\x01\x89\xdf\xf3dm\ \x00\x00\x00J\x00\x01\x00\x00\x00\x01\x00\x01\xba\x82\ -\x00\x00\x01\x87\xb2\x02\xd0Z\ +\x00\x00\x01\x89\xdf\xf3dk\ +\x00\x00\x00x\x00\x00\x00\x00\x00\x01\x00\x01\xbc\x12\ +\x00\x00\x01\x89\xdf\xf3\x9f3\ \x00\x00\x00,\x00\x00\x00\x00\x00\x01\x00\x00\xa0<\ -\x00\x00\x01\x87\xb2\x02\xd0[\ +\x00\x00\x01\x89\xdf\xf3dl\ " def qInitResources(): diff --git a/src/ui/QrCodeUI.py b/src/ui/QrCodeUI.py new file mode 100644 index 0000000..1389fc4 --- /dev/null +++ b/src/ui/QrCodeUI.py @@ -0,0 +1,12 @@ +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QWidget +from src.ui.PySide_src.qrCode_ui import Ui_QrCode + + +class QrCodeUI(QWidget, Ui_QrCode): + """二维码窗口""" + + def __init__(self): + super().__init__() + self.setupUi(self) + self.setWindowModality(Qt.ApplicationModal) diff --git a/src/ui/SettingUI.py b/src/ui/SettingUI.py index e72e036..d592f5f 100644 --- a/src/ui/SettingUI.py +++ b/src/ui/SettingUI.py @@ -2,14 +2,19 @@ import os from functools import partial +import threading from typing import TYPE_CHECKING +from PySide6.QtCore import QObject, Signal from PySide6.QtWidgets import QFileDialog, QMessageBox, QRadioButton +from PySide6.QtGui import QPixmap, QImage +from urllib.parse import urlparse, parse_qs, quote import requests from retrying import retry -from src.ui.MyAbout import MyAbout +from src.ui.MyAboutUI import MyAboutUI +from src.ui.QrCodeUI import QrCodeUI from src.utils import ( log_path, logger, @@ -18,17 +23,26 @@ TIMEOUT_SMALL, check_new_version, ) +from src.BiliQrCode import QrCode if TYPE_CHECKING: from src.ui.MainGUI import MainGUI -class SettingUI: +class SettingUI(QObject): """设置窗口类,用于管理设置UI""" + # ?########################################################### + # ? 用于多线程更新我的库存 + qr_res = Signal(dict) + def __init__(self, mainGUI: MainGUI): + super().__init__() + self.mainGUI = mainGUI self.clearUserData = False + self.init_qrCode(mainGUI) self.init_cookie(mainGUI) + self.init_biliplus_cookie(mainGUI) self.init_savePath(mainGUI) self.init_num_thread(mainGUI) self.init_openLog(mainGUI) @@ -37,6 +51,74 @@ def __init__(self, mainGUI: MainGUI): self.init_saveMethod(mainGUI) self.init_checkUpdate(mainGUI) self.init_theme(mainGUI) + self.qr_ui = QrCodeUI() + + ############################################################ + def qrCodeCallBack(self, data: dict) -> None: + # sourcery skip: extract-method + + if data is None: + self.qr_ui.close() + return + + # 0:扫码登录成功 + if data["code"] == 0: + self.qr_ui.close() + + parsed_url = urlparse(data["url"]) + query_params = parse_qs(parsed_url.query) + sessdata = quote(query_params["SESSDATA"][0]) + + self.mainGUI.updateConfig("cookie", sessdata) + self.mainGUI.lineEdit_my_cookie.setText(sessdata) + self.qr_ui.label.setText("## 请使用BiliBili手机客户端扫描二维码登入") + + QMessageBox.information( + self.mainGUI, + "提示", + f"扫码登录成功!\n新Cookie为: {sessdata}\n已自动保存!", + ) + + # 86038:二维码已失效 + elif data["code"] == 86038: + self.qr_ui.close() + QMessageBox.warning(self.mainGUI, "警告", "二维码已超时失效!请重新获取二维码!") + + # 86090:二维码已扫码未确认 + elif data["code"] == 86090: + self.qr_ui.label.setText("## 扫码成功!请在手机上确认登录!") + + ############################################################ + def init_qrCode(self, mainGUI: MainGUI) -> None: + """绑定二维码按钮 + + Args: + mainGUI (MainGUI): 主窗口类实例 + """ + + def _(): + qr = QrCode(mainGUI) + img = qr.generate() + if img is None: + return + + self.qr_ui.label_img.setPixmap( + QPixmap.fromImage(QImage.fromData(img)).scaled(400, 400) + ) + self.qr_ui.show() + + # 开一个线程去检测二维码是否扫描成功 + thread = threading.Thread( + target=qr.get_cookie, + args=(self.qr_res,), + ) + thread.start() + + # 如果用户把二维码窗口关了,就把线程也关了 + self.qr_ui.closeEvent = lambda _: setattr(qr, "close_flag", True) + + self.qr_res.connect(self.qrCodeCallBack) + mainGUI.pushButton_qrcode.clicked.connect(partial(_)) ############################################################ def init_cookie(self, mainGUI: MainGUI) -> None: @@ -52,6 +134,9 @@ def init_cookie(self, mainGUI: MainGUI) -> None: def _(): new_cookie = mainGUI.lineEdit_my_cookie.text() + if new_cookie == "": + QMessageBox.information(mainGUI, "提示", "请输入Cookie!") + return mainGUI.updateConfig("cookie", new_cookie) mainGUI.lineEdit_my_cookie.clearFocus() if self.is_cookie_valid(mainGUI, new_cookie): @@ -109,6 +194,90 @@ def _() -> None: return False return True + ############################################################ + def init_biliplus_cookie(self, mainGUI: MainGUI) -> None: + """绑定BiliPlus Cookie值 + + Args: + mainGUI (MainGUI): 主窗口类实例 + """ + stored_cookie = mainGUI.getConfig("biliplus_cookie") + if stored_cookie: + mainGUI.lineEdit_biliplus_cookie.setText(stored_cookie) + self.is_biliplus_cookie_valid(mainGUI, stored_cookie) + + def _(): + new_cookie = mainGUI.lineEdit_biliplus_cookie.text() + if new_cookie == "": + QMessageBox.information(mainGUI, "提示", "请输入Cookie!") + return + mainGUI.updateConfig("biliplus_cookie", new_cookie) + mainGUI.lineEdit_biliplus_cookie.clearFocus() + if self.is_biliplus_cookie_valid(mainGUI, new_cookie): + QMessageBox.information(mainGUI, "提示", "Cookie有效!") + + mainGUI.lineEdit_biliplus_cookie.returnPressed.connect(_) + mainGUI.pushButton_biliplus_cookie.clicked.connect(_) + + ############################################################ + def is_biliplus_cookie_valid(self, mainGUI: MainGUI, cookie: str) -> bool: + """判断BiliPlus Cookie是否有效 + + Args: + mainGUI (MainGUI): 主窗口类实例 + cookie (str): Cookie值 + Returns: + bool: Cookie是否有效 + """ + # 此处对Cookie是否有效验证使用了硬编码,如果该漫画或该章节变更,需要修改才能继续正常验证 + main_url = "https://www.biliplus.com/manga/?act=read&mangaid=26551&epid=316882" + headers = { + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36", + "cookie": f"login=2;access_key={cookie}", + } + payload = {} + + @retry( + stop_max_delay=MAX_RETRY_SMALL, wait_exponential_multiplier=RETRY_WAIT_EX + ) + def _() -> None: + try: + res = requests.post( + main_url, data=payload, headers=headers, timeout=TIMEOUT_SMALL + ) + except requests.RequestException as e: + logger.warning(f"测试BiliPlus Cookie是否有效失败! 重试中...\n{e}") + raise e + if res.status_code != 200: + logger.warning( + f"测试BiliPlus Cookie是否有效失败! 状态码:{res.status_code}, 理由: {res.reason} 重试中..." + ) + raise requests.HTTPError() + if "hoz-container" not in res.text: + logger.warning("BiliPlus Cookie检测出现故障,暂时无法检测是否有效...") + raise ReferenceError + elif 'class="comic-single"' not in res.text: + logger.warning("BiliPlus Cookie无效!重试中...") + raise requests.HTTPError() + + try: + _() + except requests.RequestException as e: + logger.error(f"重复测试Cookie是否有效多次后失败!\n{e}") + logger.exception(e) + QMessageBox.warning( + mainGUI, + "警告", + "重复测试biliplus Cookie是否有效多次后失败!\n请核对输入的biliplus Cookie值或者检查网络连接!\n\n更多详细信息请查看日志文件", + ) + return False + except ReferenceError: + QMessageBox.warning( + mainGUI, "警告", "BiliPlus Cookie检测功能出现故障!\n暂时无法检测是否有效!\n请自行判断或联系开发者" + ) + return False + return True + ############################################################ def init_savePath(self, mainGUI: MainGUI) -> None: """绑定漫画保存路径设置 @@ -180,7 +349,7 @@ def init_about(self, mainGUI: MainGUI) -> None: Args: mainGUI (MainGUI): 主窗口类实例 """ - about_window = MyAbout() + about_window = MyAboutUI() mainGUI.pushButton_about.clicked.connect(partial(about_window.show)) ############################################################ @@ -249,7 +418,7 @@ def init_checkUpdate(self, mainGUI: MainGUI) -> None: ) ############################################################ - + def init_theme(self, mainGUI: MainGUI) -> None: """绑定主题相关设置 diff --git a/src/utils.py b/src/utils.py index 5683af6..062cec7 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,4 +1,5 @@ from __future__ import annotations +import hashlib import typing import requests @@ -17,7 +18,7 @@ from ui.MainGUI import MainGUI __app_name__ = "BiliBili-Manga-Downloader" -__version__ = "1.2.0" +__version__ = "1.3.0" __author__ = "Zeal L" __copyright__ = "Copyright (C) 2023 Zeal L" @@ -72,6 +73,19 @@ ############################################################ # Helper Functions ############################################################ + + +def isCheckSumValid(etag, content) -> tuple[bool, str]: + """判断MD5是否有效 + + Returns: + tuple[bool, str]: (是否有效, MD5) + """ + md5 = hashlib.md5(content).hexdigest() + return etag == md5, md5 + +############################################################ + def openFolderAndSelectItems(path: str) -> None: """读取一个文件的父目录, 如果可能的话,选择该文件。 @@ -332,9 +346,7 @@ def check_new_version(mainGUI: MainGUI): mainGUI (MainGUI): 主窗口类实例 """ - url = ( - "https://api.github.com/repos/Zeal-L/BiliBili-Manga-Downloader/releases/latest" - ) + url = "https://api.github.com/repos/Zeal-L/BiliBili-Manga-Downloader/releases/latest" @retry(stop_max_delay=MAX_RETRY_SMALL, wait_exponential_multiplier=RETRY_WAIT_EX) def _() -> dict: @@ -354,22 +366,22 @@ def _() -> dict: logger.error(f"重复更新信息多次后失败!\n{e}") logger.exception(e) QMessageBox.warning( - mainGUI, "警告", "重复获取软件版本更新信息多次后失败!\n请检查网络连接或者重启软件!\n因需要访问github,所以请确认拥有外网访问权限(VPN)\n\n更多详细信息请查看日志文件" + mainGUI, + "警告", + "重复获取软件版本更新信息多次后失败!\n请检查网络连接或者重启软件!\n因需要访问github,所以请确认拥有外网访问权限(VPN)\n\n更多详细信息请查看日志文件", ) return if data["tag_name"][1:] != __version__: - msgBox = QMessageBox() - msgBox.setWindowTitle("更新小助手") - msgBox.setText( + message_box = QMessageBox() + message_box.setWindowTitle("更新小助手") + message_box.setText( f"您当前使用的版本为 v{__version__},最新版本为 {data['tag_name']}
请前往 Github 下载最新版本" ) - msgBox.setTextFormat(Qt.RichText) - msgBox.setIcon(QMessageBox.Information) - msgBox.setWindowIcon(QIcon(":/imgs/BiliBili_favicon.ico")) - msgBox.exec() + message_box.setTextFormat(Qt.RichText) + message_box.setIcon(QMessageBox.Information) + message_box.setWindowIcon(QIcon(":/imgs/BiliBili_favicon.ico")) + message_box.exec() else: - QMessageBox.information( - mainGUI, "更新小助手", f"您当前使用的版本为 v{__version__},已经是最新版本了" - ) + QMessageBox.information(mainGUI, "更新小助手", f"您当前使用的版本为 v{__version__},已经是最新版本了") diff --git a/version.txt b/version.txt index bccc83a..9d0fa54 100644 --- a/version.txt +++ b/version.txt @@ -7,8 +7,8 @@ VSVersionInfo( # filevers 和 prodvers 应该总是一个包含四项的元组:(1, 2, 3, 4) # 将不需要的项目设置为零。 # 文件版本号 - filevers=(1, 2, 0, 0), - prodvers=(1, 2, 0, 0), + filevers=(1, 3, 0, 0), + prodvers=(1, 3, 0, 0), # 包含指定有效位“flags”的位掩码 mask=0x3f, # 包含指定文件布尔属性的位掩码。 @@ -32,9 +32,9 @@ VSVersionInfo( u'040904B0', # 文件说明 [StringStruct(u'FileDescription', u'哔哩哔哩漫画下载器'), - StringStruct(u'FileVersion', u'1, 2, 0'), + StringStruct(u'FileVersion', u'1, 3, 0'), # 内部名称 - StringStruct(u'InternalName', u'哔哩哔哩漫画下载器 v1.2.0'), + StringStruct(u'InternalName', u'哔哩哔哩漫画下载器 v1.3.0'), # 版权 StringStruct(u'LegalCopyright', u'Copyright (C) 2023 Zeal L'), # 原始文件名 @@ -42,7 +42,7 @@ VSVersionInfo( # 产品名称 StringStruct(u'ProductName', u'BiliBili-Manga-Downloader'), # 产品版本号 - StringStruct(u'ProductVersion', u'1.2.0')]) + StringStruct(u'ProductVersion', u'1.3.0')]) ]), VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) ]