mirror of
https://github.com/instaloader/instaloader.git
synced 2026-10-16 21:52:17 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36d9f66e8e | ||
|
|
5e04063002 | ||
|
|
76e8154b58 | ||
|
|
4a969882b5 | ||
|
|
dc1eb2b193 | ||
|
|
2a95589a1d | ||
|
|
0c7398882b | ||
|
|
3b705fe5d1 | ||
|
|
3ac790d30f | ||
|
|
fbfcd5df18 | ||
|
|
b44a006344 | ||
|
|
253144203b | ||
|
|
7d0e46798e | ||
|
|
fd8245a216 | ||
|
|
3e335984f2 | ||
|
|
98791fb0aa | ||
|
|
65b12d650c | ||
|
|
1fb98b066a | ||
|
|
14f1c3cb82 | ||
|
|
7a36512093 | ||
|
|
f423aa1507 | ||
|
|
1875aede82 | ||
|
|
2cae933d66 | ||
|
|
5509ea7daf | ||
|
|
2b594909bb | ||
|
|
1852447825 | ||
|
|
baaa2c2998 | ||
|
|
47235bf2b8 |
@@ -8,22 +8,23 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Checkout Instaloader Repository"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # needed for building docs
|
||||
- name: "Setup Python"
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
cache: 'pipenv'
|
||||
- name: "Install Dependencies"
|
||||
run: |
|
||||
python -m pip install pipenv==2023.10.3
|
||||
python -m pip install pipenv==2023.12.1
|
||||
pipenv --python `python --version | grep -Eo '3\.[0-9]+'` sync --dev
|
||||
- name: "Build Documentation"
|
||||
run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run make -C docs html SPHINXOPTS="-W -n"
|
||||
- name: "Deploy Documentation"
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||
uses: JamesIves/github-pages-deploy-action@v4.2.5
|
||||
uses: JamesIves/github-pages-deploy-action@v4
|
||||
with:
|
||||
branch: master
|
||||
repository-name: instaloader/instaloader.github.io
|
||||
|
||||
@@ -12,14 +12,15 @@ jobs:
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
||||
steps:
|
||||
- name: Checkout Instaloader Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pipenv'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
python -m pip install pipenv==2023.10.3
|
||||
python -m pip install pipenv==2023.12.1
|
||||
pipenv --python `python --version | grep -Eo '3\.[0-9]+'` sync --dev
|
||||
- name: PyLint
|
||||
run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run pylint instaloader
|
||||
|
||||
@@ -11,16 +11,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Checkout Repository"
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: "Setup Python"
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
- name: "Get the tagged version"
|
||||
id: get_version
|
||||
env:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
|
||||
run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
- name: "Install Dependencies"
|
||||
run: python -m pip install setuptools
|
||||
|
||||
@@ -2,7 +2,7 @@ name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "10 14 * * *"
|
||||
- cron: "55 7 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
only-labels: 'question'
|
||||
@@ -21,14 +21,14 @@ jobs:
|
||||
days-before-stale: 21
|
||||
days-before-close: -1
|
||||
remove-stale-when-updated: false
|
||||
- uses: actions/stale@v1
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'There has been no activity on this issue for an extended period of time. This issue will be closed after further 14 days of inactivity.'
|
||||
stale-pr-message: 'There has been no activity on this Pull Request for an extended period of time. This Pull Request will be closed after further 14 days of inactivity.'
|
||||
stale-issue-label: 'stale'
|
||||
stale-pr-label: 'stale'
|
||||
exempt-issue-label: 'leave open'
|
||||
exempt-pr-label: 'leave open'
|
||||
exempt-issue-labels: 'leave open'
|
||||
exempt-pr-labels: 'leave open'
|
||||
days-before-stale: 189
|
||||
days-before-close: 14
|
||||
|
||||
@@ -8,17 +8,18 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout Instaloader repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: 'pipenv'
|
||||
architecture: x64
|
||||
- name: Get the tagged version
|
||||
id: get_version
|
||||
env:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
|
||||
run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
- name: Run custom python script for EXE creation
|
||||
env:
|
||||
|
||||
@@ -6,4 +6,5 @@ Instaloader is written by
|
||||
- Alexander Graf (@aandergr)
|
||||
- André Koch-Kramer (@Thammus)
|
||||
- Lars Lindqvist (@e5150)
|
||||
- Eduardo M Kalinowksi (@ekalin)
|
||||
- ... and many more, see https://github.com/instaloader/instaloader/graphs/contributors
|
||||
|
||||
Generated
+259
-258
@@ -24,11 +24,11 @@
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1",
|
||||
"sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"
|
||||
"sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f",
|
||||
"sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2023.11.17"
|
||||
"version": "==2024.2.2"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
@@ -144,82 +144,83 @@
|
||||
},
|
||||
"lz4": {
|
||||
"hashes": [
|
||||
"sha256:0ca83a623c449295bafad745dcd399cea4c55b16b13ed8cfea30963b004016c9",
|
||||
"sha256:0f5614d8229b33d4a97cb527db2a1ac81308c6e796e7bdb5d1309127289f69d5",
|
||||
"sha256:1c4c100d99eed7c08d4e8852dd11e7d1ec47a3340f49e3a96f8dfbba17ffb300",
|
||||
"sha256:1f25eb322eeb24068bb7647cae2b0732b71e5c639e4e4026db57618dcd8279f0",
|
||||
"sha256:200d05777d61ba1ff8d29cb51c534a162ea0b4fe6d3c28be3571a0a48ff36080",
|
||||
"sha256:31d72731c4ac6ebdce57cd9a5cabe0aecba229c4f31ba3e2c64ae52eee3fdb1c",
|
||||
"sha256:3a85b430138882f82f354135b98c320dafb96fc8fe4656573d95ab05de9eb092",
|
||||
"sha256:4931ab28a0d1c133104613e74eec1b8bb1f52403faabe4f47f93008785c0b929",
|
||||
"sha256:4caedeb19e3ede6c7a178968b800f910db6503cb4cb1e9cc9221157572139b49",
|
||||
"sha256:65d5c93f8badacfa0456b660285e394e65023ef8071142e0dcbd4762166e1be0",
|
||||
"sha256:6b50f096a6a25f3b2edca05aa626ce39979d63c3b160687c8c6d50ac3943d0ba",
|
||||
"sha256:7211dc8f636ca625abc3d4fb9ab74e5444b92df4f8d58ec83c8868a2b0ff643d",
|
||||
"sha256:7a9eec24ec7d8c99aab54de91b4a5a149559ed5b3097cf30249b665689b3d402",
|
||||
"sha256:7c2df117def1589fba1327dceee51c5c2176a2b5a7040b45e84185ce0c08b6a3",
|
||||
"sha256:7e2dc1bd88b60fa09b9b37f08553f45dc2b770c52a5996ea52b2b40f25445676",
|
||||
"sha256:83903fe6db92db0be101acedc677aa41a490b561567fe1b3fe68695b2110326c",
|
||||
"sha256:83acfacab3a1a7ab9694333bcb7950fbeb0be21660d236fd09c8337a50817897",
|
||||
"sha256:86480f14a188c37cb1416cdabacfb4e42f7a5eab20a737dac9c4b1c227f3b822",
|
||||
"sha256:867664d9ca9bdfce840ac96d46cd8838c9ae891e859eb98ce82fcdf0e103a947",
|
||||
"sha256:8df16c9a2377bdc01e01e6de5a6e4bbc66ddf007a6b045688e285d7d9d61d1c9",
|
||||
"sha256:8f00a9ba98f6364cadda366ae6469b7b3568c0cced27e16a47ddf6b774169270",
|
||||
"sha256:926b26db87ec8822cf1870efc3d04d06062730ec3279bbbd33ba47a6c0a5c673",
|
||||
"sha256:a6a46889325fd60b8a6b62ffc61588ec500a1883db32cddee9903edfba0b7584",
|
||||
"sha256:a98b61e504fb69f99117b188e60b71e3c94469295571492a6468c1acd63c37ba",
|
||||
"sha256:ad38dc6a7eea6f6b8b642aaa0683253288b0460b70cab3216838747163fb774d",
|
||||
"sha256:b10b77dc2e6b1daa2f11e241141ab8285c42b4ed13a8642495620416279cc5b2",
|
||||
"sha256:d5ea0e788dc7e2311989b78cae7accf75a580827b4d96bbaf06c7e5a03989bd5",
|
||||
"sha256:e05afefc4529e97c08e65ef92432e5f5225c0bb21ad89dee1e06a882f91d7f5e",
|
||||
"sha256:e1431d84a9cfb23e6773e72078ce8e65cad6745816d4cbf9ae67da5ea419acda",
|
||||
"sha256:ec6755cacf83f0c5588d28abb40a1ac1643f2ff2115481089264c7630236618a",
|
||||
"sha256:edc2fb3463d5d9338ccf13eb512aab61937be50aa70734bcf873f2f493801d3b",
|
||||
"sha256:edd8987d8415b5dad25e797043936d91535017237f72fa456601be1479386c92",
|
||||
"sha256:edda4fb109439b7f3f58ed6bede59694bc631c4b69c041112b1b7dc727fffb23",
|
||||
"sha256:f571eab7fec554d3b1db0d666bdc2ad85c81f4b8cb08906c4c59a8cad75e6e22",
|
||||
"sha256:f7c50542b4ddceb74ab4f8b3435327a0861f06257ca501d59067a6a482535a77"
|
||||
"sha256:01fe674ef2889dbb9899d8a67361e0c4a2c833af5aeb37dd505727cf5d2a131e",
|
||||
"sha256:054b4631a355606e99a42396f5db4d22046a3397ffc3269a348ec41eaebd69d2",
|
||||
"sha256:0a136e44a16fc98b1abc404fbabf7f1fada2bdab6a7e970974fb81cf55b636d0",
|
||||
"sha256:0e9c410b11a31dbdc94c05ac3c480cb4b222460faf9231f12538d0074e56c563",
|
||||
"sha256:222a7e35137d7539c9c33bb53fcbb26510c5748779364014235afc62b0ec797f",
|
||||
"sha256:24b3206de56b7a537eda3a8123c644a2b7bf111f0af53bc14bed90ce5562d1aa",
|
||||
"sha256:2b901c7784caac9a1ded4555258207d9e9697e746cc8532129f150ffe1f6ba0d",
|
||||
"sha256:2f7b1839f795315e480fb87d9bc60b186a98e3e5d17203c6e757611ef7dcef61",
|
||||
"sha256:30e8c20b8857adef7be045c65f47ab1e2c4fabba86a9fa9a997d7674a31ea6b6",
|
||||
"sha256:31ea4be9d0059c00b2572d700bf2c1bc82f241f2c3282034a759c9a4d6ca4dc2",
|
||||
"sha256:337cb94488a1b060ef1685187d6ad4ba8bc61d26d631d7ba909ee984ea736be1",
|
||||
"sha256:33c9a6fd20767ccaf70649982f8f3eeb0884035c150c0b818ea660152cf3c809",
|
||||
"sha256:363ab65bf31338eb364062a15f302fc0fab0a49426051429866d71c793c23394",
|
||||
"sha256:43cf03059c0f941b772c8aeb42a0813d68d7081c009542301637e5782f8a33e2",
|
||||
"sha256:56f4fe9c6327adb97406f27a66420b22ce02d71a5c365c48d6b656b4aaeb7775",
|
||||
"sha256:5d35533bf2cee56f38ced91f766cd0038b6abf46f438a80d50c52750088be93f",
|
||||
"sha256:6756212507405f270b66b3ff7f564618de0606395c0fe10a7ae2ffcbbe0b1fba",
|
||||
"sha256:6cdc60e21ec70266947a48839b437d46025076eb4b12c76bd47f8e5eb8a75dcc",
|
||||
"sha256:abc197e4aca8b63f5ae200af03eb95fb4b5055a8f990079b5bdf042f568469dd",
|
||||
"sha256:b14d948e6dce389f9a7afc666d60dd1e35fa2138a8ec5306d30cd2e30d36b40c",
|
||||
"sha256:b47839b53956e2737229d70714f1d75f33e8ac26e52c267f0197b3189ca6de24",
|
||||
"sha256:b6d9ec061b9eca86e4dcc003d93334b95d53909afd5a32c6e4f222157b50c071",
|
||||
"sha256:b891880c187e96339474af2a3b2bfb11a8e4732ff5034be919aa9029484cd201",
|
||||
"sha256:bca8fccc15e3add173da91be8f34121578dc777711ffd98d399be35487c934bf",
|
||||
"sha256:c81703b12475da73a5d66618856d04b1307e43428a7e59d98cfe5a5d608a74c6",
|
||||
"sha256:d2507ee9c99dbddd191c86f0e0c8b724c76d26b0602db9ea23232304382e1f21",
|
||||
"sha256:e36cd7b9d4d920d3bfc2369840da506fa68258f7bb176b8743189793c055e43d",
|
||||
"sha256:e7d84b479ddf39fe3ea05387f10b779155fc0990125f4fb35d636114e1c63a2e",
|
||||
"sha256:eac9af361e0d98335a02ff12fb56caeb7ea1196cf1a49dbf6f17828a131da807",
|
||||
"sha256:edfd858985c23523f4e5a7526ca6ee65ff930207a7ec8a8f57a01eae506aaee7",
|
||||
"sha256:ee9ff50557a942d187ec85462bb0960207e7ec5b19b3b48949263993771c6205",
|
||||
"sha256:f0e822cd7644995d9ba248cb4b67859701748a93e2ab7fc9bc18c599a52e4604",
|
||||
"sha256:f180904f33bdd1e92967923a43c22899e303906d19b2cf8bb547db6653ea6e7d",
|
||||
"sha256:f1d18718f9d78182c6b60f568c9a9cec8a7204d7cb6fad4e511a2ef279e4cb05",
|
||||
"sha256:f4c7bf687303ca47d69f9f0133274958fd672efaa33fb5bcde467862d6c621f0",
|
||||
"sha256:f76176492ff082657ada0d0f10c794b6da5800249ef1692b35cf49b1e93e8ef7"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.3.2"
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==4.3.3"
|
||||
},
|
||||
"pycryptodomex": {
|
||||
"hashes": [
|
||||
"sha256:09c9401dc06fb3d94cb1ec23b4ea067a25d1f4c6b7b118ff5631d0b5daaab3cc",
|
||||
"sha256:0b2f1982c5bc311f0aab8c293524b861b485d76f7c9ab2c3ac9a25b6f7655975",
|
||||
"sha256:136b284e9246b4ccf4f752d435c80f2c44fc2321c198505de1d43a95a3453b3c",
|
||||
"sha256:1789d89f61f70a4cd5483d4dfa8df7032efab1118f8b9894faae03c967707865",
|
||||
"sha256:2126bc54beccbede6eade00e647106b4f4c21e5201d2b0a73e9e816a01c50905",
|
||||
"sha256:258c4233a3fe5a6341780306a36c6fb072ef38ce676a6d41eec3e591347919e8",
|
||||
"sha256:263de9a96d2fcbc9f5bd3a279f14ea0d5f072adb68ebd324987576ec25da084d",
|
||||
"sha256:50cb18d4dd87571006fd2447ccec85e6cec0136632a550aa29226ba075c80644",
|
||||
"sha256:5b883e1439ab63af976656446fb4839d566bb096f15fc3c06b5a99cde4927188",
|
||||
"sha256:5d73e9fa3fe830e7b6b42afc49d8329b07a049a47d12e0ef9225f2fd220f19b2",
|
||||
"sha256:61056a1fd3254f6f863de94c233b30dd33bc02f8c935b2000269705f1eeeffa4",
|
||||
"sha256:67c8eb79ab33d0fbcb56842992298ddb56eb6505a72369c20f60bc1d2b6fb002",
|
||||
"sha256:6e45bb4635b3c4e0a00ca9df75ef6295838c85c2ac44ad882410cb631ed1eeaa",
|
||||
"sha256:7cb51096a6a8d400724104db8a7e4f2206041a1f23e58924aa3d8d96bcb48338",
|
||||
"sha256:800a2b05cfb83654df80266692f7092eeefe2a314fa7901dcefab255934faeec",
|
||||
"sha256:8df69e41f7e7015a90b94d1096ec3d8e0182e73449487306709ec27379fff761",
|
||||
"sha256:917033016ecc23c8933205585a0ab73e20020fdf671b7cd1be788a5c4039840b",
|
||||
"sha256:a12144d785518f6491ad334c75ccdc6ad52ea49230b4237f319dbb7cef26f464",
|
||||
"sha256:a3866d68e2fc345162b1b9b83ef80686acfe5cec0d134337f3b03950a0a8bf56",
|
||||
"sha256:a588a1cb7781da9d5e1c84affd98c32aff9c89771eac8eaa659d2760666f7139",
|
||||
"sha256:a77b79852175064c822b047fee7cf5a1f434f06ad075cc9986aa1c19a0c53eb0",
|
||||
"sha256:af83a554b3f077564229865c45af0791be008ac6469ef0098152139e6bd4b5b6",
|
||||
"sha256:b801216c48c0886742abf286a9a6b117e248ca144d8ceec1f931ce2dd0c9cb40",
|
||||
"sha256:bfb040b5dda1dff1e197d2ef71927bd6b8bfcb9793bc4dfe0bb6df1e691eaacb",
|
||||
"sha256:c01678aee8ac0c1a461cbc38ad496f953f9efcb1fa19f5637cbeba7544792a53",
|
||||
"sha256:c74eb1f73f788facece7979ce91594dc177e1a9b5d5e3e64697dd58299e5cb4d",
|
||||
"sha256:c9a68a2f7bd091ccea54ad3be3e9d65eded813e6d79fdf4cc3604e26cdd6384f",
|
||||
"sha256:d4dd3b381ff5a5907a3eb98f5f6d32c64d319a840278ceea1dcfcc65063856f3",
|
||||
"sha256:e8e5ecbd4da4157889fce8ba49da74764dd86c891410bfd6b24969fa46edda51",
|
||||
"sha256:eb2fc0ec241bf5e5ef56c8fbec4a2634d631e4c4f616a59b567947a0f35ad83c",
|
||||
"sha256:edbe083c299835de7e02c8aa0885cb904a75087d35e7bab75ebe5ed336e8c3e2",
|
||||
"sha256:ff64fd720def623bf64d8776f8d0deada1cc1bf1ec3c1f9d6f5bb5bd098d034f"
|
||||
"sha256:0daad007b685db36d977f9de73f61f8da2a7104e20aca3effd30752fd56f73e1",
|
||||
"sha256:108e5f1c1cd70ffce0b68739c75734437c919d2eaec8e85bffc2c8b4d2794305",
|
||||
"sha256:19764605feea0df966445d46533729b645033f134baeb3ea26ad518c9fdf212c",
|
||||
"sha256:1be97461c439a6af4fe1cf8bf6ca5936d3db252737d2f379cc6b2e394e12a458",
|
||||
"sha256:25cd61e846aaab76d5791d006497134602a9e451e954833018161befc3b5b9ed",
|
||||
"sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc",
|
||||
"sha256:59af01efb011b0e8b686ba7758d59cf4a8263f9ad35911bfe3f416cee4f5c08c",
|
||||
"sha256:5dcac11031a71348faaed1f403a0debd56bf5404232284cf8c761ff918886ebc",
|
||||
"sha256:62a5ec91388984909bb5398ea49ee61b68ecb579123694bffa172c3b0a107079",
|
||||
"sha256:645bd4ca6f543685d643dadf6a856cc382b654cc923460e3a10a49c1b3832aeb",
|
||||
"sha256:653b29b0819605fe0898829c8ad6400a6ccde096146730c2da54eede9b7b8baa",
|
||||
"sha256:69138068268127cd605e03438312d8f271135a33140e2742b417d027a0539427",
|
||||
"sha256:6e186342cfcc3aafaad565cbd496060e5a614b441cacc3995ef0091115c1f6c5",
|
||||
"sha256:76bd15bb65c14900d98835fcd10f59e5e0435077431d3a394b60b15864fddd64",
|
||||
"sha256:7805830e0c56d88f4d491fa5ac640dfc894c5ec570d1ece6ed1546e9df2e98d6",
|
||||
"sha256:7a710b79baddd65b806402e14766c721aee8fb83381769c27920f26476276c1e",
|
||||
"sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43",
|
||||
"sha256:82ee7696ed8eb9a82c7037f32ba9b7c59e51dda6f105b39f043b6ef293989cb3",
|
||||
"sha256:88afd7a3af7ddddd42c2deda43d53d3dfc016c11327d0915f90ca34ebda91499",
|
||||
"sha256:8af1a451ff9e123d0d8bd5d5e60f8e3315c3a64f3cdd6bc853e26090e195cdc8",
|
||||
"sha256:8ee606964553c1a0bc74057dd8782a37d1c2bc0f01b83193b6f8bb14523b877b",
|
||||
"sha256:91852d4480a4537d169c29a9d104dda44094c78f1f5b67bca76c29a91042b623",
|
||||
"sha256:9c682436c359b5ada67e882fec34689726a09c461efd75b6ea77b2403d5665b7",
|
||||
"sha256:bc3ee1b4d97081260d92ae813a83de4d2653206967c4a0a017580f8b9548ddbc",
|
||||
"sha256:bca649483d5ed251d06daf25957f802e44e6bb6df2e8f218ae71968ff8f8edc4",
|
||||
"sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e",
|
||||
"sha256:cbe71b6712429650e3883dc81286edb94c328ffcd24849accac0a4dbcc76958a",
|
||||
"sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781",
|
||||
"sha256:d3584623e68a5064a04748fb6d76117a21a7cb5eaba20608a41c7d0c61721794",
|
||||
"sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea",
|
||||
"sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b",
|
||||
"sha256:ff5c9a67f8a4fba4aed887216e32cbc48f2a6fb2673bb10a99e43be463e15913"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==3.19.0"
|
||||
"version": "==3.20.0"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
@@ -232,21 +233,21 @@
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3",
|
||||
"sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"
|
||||
"sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d",
|
||||
"sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==2.1.0"
|
||||
"version": "==2.2.1"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"alabaster": {
|
||||
"hashes": [
|
||||
"sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3",
|
||||
"sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"
|
||||
"sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65",
|
||||
"sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.7.13"
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==0.7.16"
|
||||
},
|
||||
"altgraph": {
|
||||
"hashes": [
|
||||
@@ -257,11 +258,11 @@
|
||||
},
|
||||
"astroid": {
|
||||
"hashes": [
|
||||
"sha256:4a61cf0a59097c7bb52689b0fd63717cd2a8a14dc9f1eee97b82d814881c8c91",
|
||||
"sha256:d6e62862355f60e716164082d6b4b041d38e2a8cf1c7cd953ded5108bac8ff5c"
|
||||
"sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819",
|
||||
"sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"
|
||||
],
|
||||
"markers": "python_full_version >= '3.8.0'",
|
||||
"version": "==3.0.2"
|
||||
"version": "==3.1.0"
|
||||
},
|
||||
"babel": {
|
||||
"hashes": [
|
||||
@@ -273,11 +274,11 @@
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1",
|
||||
"sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"
|
||||
"sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f",
|
||||
"sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2023.11.17"
|
||||
"version": "==2024.2.2"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
@@ -377,12 +378,12 @@
|
||||
},
|
||||
"dill": {
|
||||
"hashes": [
|
||||
"sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e",
|
||||
"sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"
|
||||
"sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca",
|
||||
"sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.3.7"
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==0.3.8"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
@@ -418,77 +419,77 @@
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
|
||||
"sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
|
||||
"sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa",
|
||||
"sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.1.2"
|
||||
"version": "==3.1.3"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e",
|
||||
"sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e",
|
||||
"sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431",
|
||||
"sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686",
|
||||
"sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c",
|
||||
"sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559",
|
||||
"sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc",
|
||||
"sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb",
|
||||
"sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939",
|
||||
"sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c",
|
||||
"sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0",
|
||||
"sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4",
|
||||
"sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9",
|
||||
"sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575",
|
||||
"sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba",
|
||||
"sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d",
|
||||
"sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd",
|
||||
"sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3",
|
||||
"sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00",
|
||||
"sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155",
|
||||
"sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac",
|
||||
"sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52",
|
||||
"sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f",
|
||||
"sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8",
|
||||
"sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b",
|
||||
"sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007",
|
||||
"sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24",
|
||||
"sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea",
|
||||
"sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198",
|
||||
"sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0",
|
||||
"sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee",
|
||||
"sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be",
|
||||
"sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2",
|
||||
"sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1",
|
||||
"sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707",
|
||||
"sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6",
|
||||
"sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c",
|
||||
"sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58",
|
||||
"sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823",
|
||||
"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:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc",
|
||||
"sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2",
|
||||
"sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"
|
||||
"sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf",
|
||||
"sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff",
|
||||
"sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f",
|
||||
"sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3",
|
||||
"sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532",
|
||||
"sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f",
|
||||
"sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617",
|
||||
"sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df",
|
||||
"sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4",
|
||||
"sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906",
|
||||
"sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f",
|
||||
"sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4",
|
||||
"sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8",
|
||||
"sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371",
|
||||
"sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2",
|
||||
"sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465",
|
||||
"sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52",
|
||||
"sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6",
|
||||
"sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169",
|
||||
"sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad",
|
||||
"sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2",
|
||||
"sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0",
|
||||
"sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029",
|
||||
"sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f",
|
||||
"sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a",
|
||||
"sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced",
|
||||
"sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5",
|
||||
"sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c",
|
||||
"sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf",
|
||||
"sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9",
|
||||
"sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb",
|
||||
"sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad",
|
||||
"sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3",
|
||||
"sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1",
|
||||
"sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46",
|
||||
"sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc",
|
||||
"sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a",
|
||||
"sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee",
|
||||
"sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900",
|
||||
"sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5",
|
||||
"sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea",
|
||||
"sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f",
|
||||
"sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5",
|
||||
"sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e",
|
||||
"sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a",
|
||||
"sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f",
|
||||
"sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50",
|
||||
"sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a",
|
||||
"sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b",
|
||||
"sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4",
|
||||
"sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff",
|
||||
"sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2",
|
||||
"sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46",
|
||||
"sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b",
|
||||
"sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf",
|
||||
"sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5",
|
||||
"sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5",
|
||||
"sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab",
|
||||
"sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd",
|
||||
"sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.1.3"
|
||||
"version": "==2.1.5"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
@@ -500,37 +501,37 @@
|
||||
},
|
||||
"mypy": {
|
||||
"hashes": [
|
||||
"sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340",
|
||||
"sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49",
|
||||
"sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82",
|
||||
"sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce",
|
||||
"sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb",
|
||||
"sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51",
|
||||
"sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5",
|
||||
"sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e",
|
||||
"sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7",
|
||||
"sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33",
|
||||
"sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9",
|
||||
"sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1",
|
||||
"sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6",
|
||||
"sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a",
|
||||
"sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe",
|
||||
"sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7",
|
||||
"sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200",
|
||||
"sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7",
|
||||
"sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a",
|
||||
"sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28",
|
||||
"sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea",
|
||||
"sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120",
|
||||
"sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d",
|
||||
"sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42",
|
||||
"sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea",
|
||||
"sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2",
|
||||
"sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a"
|
||||
"sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6",
|
||||
"sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913",
|
||||
"sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129",
|
||||
"sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc",
|
||||
"sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974",
|
||||
"sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374",
|
||||
"sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150",
|
||||
"sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03",
|
||||
"sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9",
|
||||
"sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02",
|
||||
"sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89",
|
||||
"sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2",
|
||||
"sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d",
|
||||
"sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3",
|
||||
"sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612",
|
||||
"sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e",
|
||||
"sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3",
|
||||
"sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e",
|
||||
"sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd",
|
||||
"sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04",
|
||||
"sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed",
|
||||
"sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185",
|
||||
"sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf",
|
||||
"sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b",
|
||||
"sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4",
|
||||
"sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f",
|
||||
"sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==1.7.1"
|
||||
"version": "==1.9.0"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
@@ -542,11 +543,11 @@
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
|
||||
"sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"
|
||||
"sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5",
|
||||
"sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==23.2"
|
||||
"version": "==24.0"
|
||||
},
|
||||
"pefile": {
|
||||
"hashes": [
|
||||
@@ -559,34 +560,34 @@
|
||||
},
|
||||
"platformdirs": {
|
||||
"hashes": [
|
||||
"sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380",
|
||||
"sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"
|
||||
"sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068",
|
||||
"sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==4.1.0"
|
||||
"version": "==4.2.0"
|
||||
},
|
||||
"psutil": {
|
||||
"hashes": [
|
||||
"sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340",
|
||||
"sha256:0bd41bf2d1463dfa535942b2a8f0e958acf6607ac0be52265ab31f7923bcd5e6",
|
||||
"sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284",
|
||||
"sha256:1d4bc4a0148fdd7fd8f38e0498639ae128e64538faa507df25a20f8f7fb2341c",
|
||||
"sha256:3c4747a3e2ead1589e647e64aad601981f01b68f9398ddf94d01e3dc0d1e57c7",
|
||||
"sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c",
|
||||
"sha256:44969859757f4d8f2a9bd5b76eba8c3099a2c8cf3992ff62144061e39ba8568e",
|
||||
"sha256:4c03362e280d06bbbfcd52f29acd79c733e0af33d707c54255d21029b8b32ba6",
|
||||
"sha256:5794944462509e49d4d458f4dbfb92c47539e7d8d15c796f141f474010084056",
|
||||
"sha256:b27f8fdb190c8c03914f908a4555159327d7481dac2f01008d483137ef3311a9",
|
||||
"sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68",
|
||||
"sha256:e469990e28f1ad738f65a42dcfc17adaed9d0f325d55047593cb9033a0ab63df",
|
||||
"sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e",
|
||||
"sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414",
|
||||
"sha256:fe361f743cb3389b8efda21980d93eb55c1f1e3898269bc9a2a1d0bb7b1f6508",
|
||||
"sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe"
|
||||
"sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d",
|
||||
"sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73",
|
||||
"sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8",
|
||||
"sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2",
|
||||
"sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e",
|
||||
"sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36",
|
||||
"sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7",
|
||||
"sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c",
|
||||
"sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee",
|
||||
"sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421",
|
||||
"sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf",
|
||||
"sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81",
|
||||
"sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0",
|
||||
"sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631",
|
||||
"sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4",
|
||||
"sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==5.9.7"
|
||||
"version": "==5.9.8"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
@@ -598,39 +599,39 @@
|
||||
},
|
||||
"pyinstaller": {
|
||||
"hashes": [
|
||||
"sha256:0597fb04337695e5cc5250253e0655530bf14f264b7a5b7d219cc65f6889c4bd",
|
||||
"sha256:156b32ba943e0090bcc68e40ae1cb68fd92b7f1ab6fe0bdf8faf3d3cfc4e12dd",
|
||||
"sha256:1eadbd1fae84e2e6c678d8b4ed6a232ec5c8fe3a839aea5a3071c4c0282f98cc",
|
||||
"sha256:41c937fe8f07ae02009b3b5a96ac3eb0800a4f8a97af142d4100060fe2135bb9",
|
||||
"sha256:75a6f2a6f835a2e6e0899d10e60c10caf5defd25aced38b1dd48fbbabc89de07",
|
||||
"sha256:886b3b995b674905a20ad5b720b47cc395897d7b391117831027a4c8c5d67a58",
|
||||
"sha256:914d4c96cc99472e37ac552fdd82fbbe09e67bb592d0717fcffaa99ea74273df",
|
||||
"sha256:96c37a1ee5b2fd5bb25c098ef510661d6d17b6515d0b86d8fc93727dd2475ba3",
|
||||
"sha256:abe91106a3bbccc3f3a27af4325676ecdb6f46cb842ac663625002a870fc503b",
|
||||
"sha256:b721d793a33b6d9946c7dd95d3ea7589c0424b51cf1b9fe580f03c544f1336b2",
|
||||
"sha256:de25beb176f73a944758553caacec46cc665bf3910ad8a174706d79cf6e95340",
|
||||
"sha256:e436fcc0ea87c3f132baac916d508c24c84a8f6d8a06c3154fbc753f169b76c7"
|
||||
"sha256:0dae0edbe6d667b6b0ccd8c97a148f86474a82da7ce582296f9025f4c7242ec6",
|
||||
"sha256:1b3b7d6d3b18d76a833fd5a4d7f4544c5e2c2a4db4a728ea191e62f69d5cc33c",
|
||||
"sha256:5f432f3fdef053989e0a44134e483131c533dab7637e6afd80c3f7c26e6dbcc9",
|
||||
"sha256:61865eee5e0d8f8252722f6d001baec497b7cee79ebe62c33a6ba86ba0c7010d",
|
||||
"sha256:6cfee8a74ea2d3a1dc8e99e732a87b314739dc14363778143caac31f8aee9039",
|
||||
"sha256:6ffd76a0194dac4df5e66dcfccc7b597f3eaa40ef9a3f63548f260aa2c187512",
|
||||
"sha256:7c76bfcb624803c311fa8fb137e4780d0ec86d11b7d90a8f43f185e2554afdcc",
|
||||
"sha256:81ec15c0deb8c7a0f95bea85b49eecc2df1bdeaf5fe487a41d97de6b0ad29dff",
|
||||
"sha256:9d828213aea5401bb33a36ca396f8dc76a59a25bce1d76a13c9ad94ba29fbe42",
|
||||
"sha256:a54968df2228f0128607b1dced41bbff94149d459987fb5cd1a41893e9bb85df",
|
||||
"sha256:b1e55113c5a40cb7041c908a57f212f3ebd3e444dbb245ca2f91d86a76dabec5",
|
||||
"sha256:e1266498893ce1d6cc7337e8d2acbf7905a10ed2b7c8377270117d6b7b922fc4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version < '3.13' and python_version >= '3.8'",
|
||||
"version": "==6.3.0"
|
||||
"version": "==6.5.0"
|
||||
},
|
||||
"pyinstaller-hooks-contrib": {
|
||||
"hashes": [
|
||||
"sha256:4b4a998036abb713774cb26534ca06b7e6e09e4c628196017a10deb11a48747f",
|
||||
"sha256:6dc1786a8f452941245d5bb85893e2a33632ebdcbc4c23eea41f2ee08281b0c0"
|
||||
"sha256:6701752d525e1f4eda1eaec2c2affc206171e15c7a4e188a152fcf3ed3308024",
|
||||
"sha256:d18657c29267c63563a96b8fc78db6ba9ae40af6702acb2f8c871df12c75b60b"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2023.10"
|
||||
"version": "==2024.3"
|
||||
},
|
||||
"pylint": {
|
||||
"hashes": [
|
||||
"sha256:58c2398b0301e049609a8429789ec6edf3aabe9b6c5fec916acd18639c16de8b",
|
||||
"sha256:7a1585285aefc5165db81083c3e06363a27448f6b467b3b0f30dbd0ac1f73810"
|
||||
"sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74",
|
||||
"sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_full_version >= '3.8.0'",
|
||||
"version": "==3.0.3"
|
||||
"version": "==3.1.0"
|
||||
},
|
||||
"pywin32-ctypes": {
|
||||
"hashes": [
|
||||
@@ -652,11 +653,11 @@
|
||||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2",
|
||||
"sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"
|
||||
"sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e",
|
||||
"sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==69.0.2"
|
||||
"version": "==69.2.0"
|
||||
},
|
||||
"snowballstemmer": {
|
||||
"hashes": [
|
||||
@@ -676,36 +677,36 @@
|
||||
},
|
||||
"sphinx-autodoc-typehints": {
|
||||
"hashes": [
|
||||
"sha256:3cabc2537e17989b2f92e64a399425c4c8bf561ed73f087bc7414a5003616a50",
|
||||
"sha256:5ed05017d23ad4b937eab3bee9fae9ab0dd63f0b42aa360031f1fad47e47f673"
|
||||
"sha256:12c0e161f6fe191c2cdfd8fa3caea271f5387d9fbc67ebcd6f4f1f24ce880993",
|
||||
"sha256:7f2cdac2e70fd9787926b6e9e541cd4ded1e838d2b46fda2a1bb0a75ec5b7f3a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==1.25.2"
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"sphinxcontrib-applehelp": {
|
||||
"hashes": [
|
||||
"sha256:094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d",
|
||||
"sha256:39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa"
|
||||
"sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619",
|
||||
"sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==1.0.7"
|
||||
"version": "==1.0.8"
|
||||
},
|
||||
"sphinxcontrib-devhelp": {
|
||||
"hashes": [
|
||||
"sha256:63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212",
|
||||
"sha256:fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f"
|
||||
"sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f",
|
||||
"sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==1.0.5"
|
||||
"version": "==1.0.6"
|
||||
},
|
||||
"sphinxcontrib-htmlhelp": {
|
||||
"hashes": [
|
||||
"sha256:6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a",
|
||||
"sha256:8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9"
|
||||
"sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015",
|
||||
"sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==2.0.4"
|
||||
"version": "==2.0.5"
|
||||
},
|
||||
"sphinxcontrib-jquery": {
|
||||
"hashes": [
|
||||
@@ -726,19 +727,19 @@
|
||||
},
|
||||
"sphinxcontrib-qthelp": {
|
||||
"hashes": [
|
||||
"sha256:62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d",
|
||||
"sha256:bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4"
|
||||
"sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6",
|
||||
"sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==1.0.6"
|
||||
"version": "==1.0.7"
|
||||
},
|
||||
"sphinxcontrib-serializinghtml": {
|
||||
"hashes": [
|
||||
"sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54",
|
||||
"sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1"
|
||||
"sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7",
|
||||
"sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==1.1.9"
|
||||
"version": "==1.1.10"
|
||||
},
|
||||
"tomli": {
|
||||
"hashes": [
|
||||
@@ -751,36 +752,36 @@
|
||||
},
|
||||
"tomlkit": {
|
||||
"hashes": [
|
||||
"sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4",
|
||||
"sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba"
|
||||
"sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b",
|
||||
"sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.12.3"
|
||||
"version": "==0.12.4"
|
||||
},
|
||||
"types-requests": {
|
||||
"hashes": [
|
||||
"sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc",
|
||||
"sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92"
|
||||
"sha256:47872893d65a38e282ee9f277a4ee50d1b28bd592040df7d1fdaffdf3779937d",
|
||||
"sha256:b1c1b66abfb7fa79aae09097a811c4aa97130eb8831c60e47aee4ca344731ca5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.31.0.10"
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==2.31.0.20240311"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783",
|
||||
"sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"
|
||||
"sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475",
|
||||
"sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==4.9.0"
|
||||
"version": "==4.10.0"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3",
|
||||
"sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"
|
||||
"sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d",
|
||||
"sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==2.1.0"
|
||||
"version": "==2.2.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,6 @@ Supporters
|
||||
|
||||
| Instaloader is proudly sponsored by
|
||||
| `@rocketapi-io <https://github.com/rocketapi-io>`__
|
||||
| `@socialmethod <https://github.com/socialmethod>`__
|
||||
|
||||
See `Alex' GitHub Sponsors <https://github.com/sponsors/aandergr>`__ page for
|
||||
how you can sponsor the development of Instaloader!
|
||||
|
||||
@@ -44,7 +44,7 @@ with open('__main__.py', 'w+') as f:
|
||||
f.writelines(lines)
|
||||
|
||||
# install dependencies and invoke PyInstaller
|
||||
commands = ["pip install pipenv==2023.10.3",
|
||||
commands = ["pip install pipenv==2023.12.1",
|
||||
f"pipenv --python {sys.version_info.major}.{sys.version_info.minor} sync --dev",
|
||||
"pipenv run pyinstaller --log-level=DEBUG instaloader.spec"]
|
||||
|
||||
|
||||
+43
-9
@@ -75,7 +75,7 @@ What to Download
|
||||
Instaloader supports the following targets:
|
||||
|
||||
- ``profile``
|
||||
Public profile, or private profile with :option:`--login`.
|
||||
Public profile, or private profile with :ref:`login<login>`.
|
||||
|
||||
If an already-downloaded profile has been renamed, Instaloader automatically
|
||||
finds it by its unique ID and renames the folder accordingly.
|
||||
@@ -102,23 +102,23 @@ Instaloader supports the following targets:
|
||||
Posts tagged with a given location; the location ID is the numerical ID
|
||||
Instagram labels a location with (e.g.
|
||||
\https://www.instagram.com/explore/locations/**362629379**/plymouth-naval-memorial/).
|
||||
Requires :option:`--login`.
|
||||
Requires :ref:`login<login>`.
|
||||
|
||||
.. versionadded:: 4.2
|
||||
|
||||
- ``:stories``
|
||||
The currently-visible **stories** of your followees (requires
|
||||
:option:`--login`).
|
||||
:ref:`login<login>`).
|
||||
|
||||
- ``:feed``
|
||||
Your **feed** (requires :option:`--login`).
|
||||
Your **feed** (requires :ref:`login<login>`).
|
||||
|
||||
- ``:saved``
|
||||
Posts which are marked as **saved** (requires :option:`--login`).
|
||||
Posts which are marked as **saved** (requires :ref:`login<login>`).
|
||||
|
||||
- ``@profile``
|
||||
All profiles that are followed by ``profile``, i.e. the *followees* of
|
||||
``profile`` (requires :option:`--login`).
|
||||
``profile`` (requires :ref:`login<login>`).
|
||||
|
||||
- ``-post``
|
||||
Replace **post** with the post's shortcode to download single post. Must be preceded by ``--`` in
|
||||
@@ -140,7 +140,7 @@ downloads the pictures and videos and their captions. You can specify
|
||||
|
||||
- :option:`--geotags`
|
||||
**download geotags** of each post and save them as
|
||||
Google Maps link (requires :option:`--login`),
|
||||
Google Maps link (requires :ref:`login<login>`),
|
||||
|
||||
For a reference of all supported command line options, see
|
||||
:ref:`command-line-options`.
|
||||
@@ -255,7 +255,7 @@ Id est, the following attributes can be used with both
|
||||
As :option:`--post-filter`, the following attributes can be used additionally:
|
||||
|
||||
- :attr:`~Post.viewer_has_liked` (bool)
|
||||
Whether user (with :option:`--login`) has liked given post. To download the
|
||||
Whether user (with :ref:`login<login>`) has liked given post. To download the
|
||||
pictures from your feed that you have liked::
|
||||
|
||||
instaloader --login=your_username --post-filter=viewer_has_liked :feed
|
||||
@@ -307,13 +307,47 @@ the post's caption::
|
||||
|
||||
instaloader --post-metadata-txt="{likes} likes." <target>
|
||||
|
||||
Note that with this feature, it is possible to easily and fastly extract
|
||||
Note that with this feature, it is possible to easily and quickly extract
|
||||
additional metadata of already-downloaded posts, by reimporting their JSON
|
||||
files. Say, you now also want to export the number of comments the Posts had
|
||||
when they were downloaded::
|
||||
|
||||
instaloader --post-metadata-txt="{likes} likes, {comments} comments." <target>/*.json.xz
|
||||
|
||||
.. _exit_codes:
|
||||
|
||||
Exit codes
|
||||
^^^^^^^^^^
|
||||
|
||||
Different exit codes are used to indicate different kinds of error:
|
||||
|
||||
0
|
||||
No error, all downloads were successful.
|
||||
|
||||
1
|
||||
A non-fatal error happened. One or more posts, or even one or more
|
||||
profiles could not be downloaded, but execution was not stopped. The
|
||||
errors are repeated at the end of the log for easy access.
|
||||
|
||||
2
|
||||
Command-line error. An unrecognized option was passed, or an invalid
|
||||
combination of options, for example. No interaction with Instagram
|
||||
was made.
|
||||
|
||||
3
|
||||
Login error. It was not possible to login. Downloads were not
|
||||
attempted.
|
||||
|
||||
4
|
||||
Fatal download error. Downloads were interrupted and no further
|
||||
attempts were made. Happens when a response with one of the status
|
||||
codes in the :option:`--abort-on` option were passed, or when
|
||||
Instagram logs the user out during downloads.
|
||||
|
||||
5
|
||||
Interrupted by the user. Happens when the user presses Control-C or
|
||||
sends SIGINT to the process.
|
||||
|
||||
.. _instaloader-as-cronjob:
|
||||
|
||||
Instaloader as Cronjob
|
||||
|
||||
+12
-8
@@ -26,7 +26,7 @@ Targets
|
||||
^^^^^^^
|
||||
|
||||
Specify a list of targets. For each of these, Instaloader creates a folder and
|
||||
stores all posts along with the pictures's captions there.
|
||||
stores all posts along with the pictures' captions there.
|
||||
|
||||
.. include:: basic-usage.rst
|
||||
:start-after: targets-start
|
||||
@@ -61,13 +61,13 @@ What to Download of each Post
|
||||
|
||||
Download geotags when available. Geotags are stored as a text file with
|
||||
the location's name and a Google Maps link. This requires an additional
|
||||
request to the Instagram server for each picture. Requires :option:`--login`.
|
||||
request to the Instagram server for each picture. Requires :ref:`login<login>`.
|
||||
|
||||
.. option:: --comments, -C
|
||||
|
||||
Download and update comments for each post. This requires an additional
|
||||
request to the Instagram server for each post, which is why it is disabled by
|
||||
default.
|
||||
default. Requires :ref:`login<login>`.
|
||||
|
||||
.. option:: --no-captions
|
||||
|
||||
@@ -116,12 +116,12 @@ What to Download of each Profile
|
||||
.. option:: --stories, -s
|
||||
|
||||
Also download stories of each profile that is downloaded. Requires
|
||||
:option:`--login`.
|
||||
:ref:`login<login>`.
|
||||
|
||||
.. option:: --highlights
|
||||
|
||||
Also download highlights of each profile that is downloaded. Requires
|
||||
:option:`--login`.
|
||||
:ref:`login<login>`.
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
@@ -183,6 +183,7 @@ Which Posts to Download
|
||||
Do not attempt to download more than COUNT posts. Applies to
|
||||
``#hashtag``, ``%location_id``, ``:feed``, and ``:saved``.
|
||||
|
||||
.. _login:
|
||||
|
||||
Login (Download Private Profiles)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -192,6 +193,9 @@ profiles. To login, pass the :option:`--login` option. Your session cookie (not
|
||||
password!) will be saved to a local file to be reused next time you want
|
||||
Instaloader to login.
|
||||
|
||||
Instead of :option:`--login`, it is possible to use
|
||||
:option:`--load-cookies` to import a session from a browser.
|
||||
|
||||
.. option:: --login YOUR-USERNAME, -l YOUR-USERNAME
|
||||
|
||||
Login name (profile name) for your Instagram account.
|
||||
@@ -202,10 +206,10 @@ Instaloader to login.
|
||||
This feature requires the browser_cookie3 library.
|
||||
Compatible with :option:`--cookiefile` if you want to load cookies from browser profiles.
|
||||
Incompatible with :option:`--login` due to potential username mismatch between user input and browser login.
|
||||
Supported browsers: Chrome, Firefox, Edge, Opera, Safari, Brave.
|
||||
Supported browsers: Brave, Chrome, Chromium, Edge, Firefox, LibreWolf, Opera, Opera_GX, Safari and Vivaldi.
|
||||
|
||||
After loading the cookies run the :option:`--login` option as it is required to download high quality media
|
||||
and to make full use of Instaloader's features.
|
||||
In subsequent runs, you can just use :option:`--login` to reuse the
|
||||
same session, which is saved by Instaloader.
|
||||
|
||||
.. versionadded:: 4.11
|
||||
|
||||
|
||||
@@ -27,16 +27,26 @@ Exceptions
|
||||
|
||||
.. autoexception:: LoginRequiredException
|
||||
|
||||
.. autoexception:: LoginException
|
||||
|
||||
.. versionadded:: 4.12
|
||||
|
||||
.. autoexception:: TwoFactorAuthRequiredException
|
||||
|
||||
.. versionadded:: 4.2
|
||||
|
||||
.. versionchanged:: 4.12
|
||||
Inherits LoginException
|
||||
|
||||
.. autoexception:: InvalidArgumentException
|
||||
|
||||
.. autoexception:: BadResponseException
|
||||
|
||||
.. autoexception:: BadCredentialsException
|
||||
|
||||
.. versionchanged:: 4.12
|
||||
Inherits LoginException
|
||||
|
||||
.. autoexception:: PostChangedException
|
||||
|
||||
.. autoexception:: QueryReturnedNotFoundException
|
||||
|
||||
@@ -33,7 +33,7 @@ happen under normal conditions, consider adjusting the
|
||||
There have been observations that services, that in their nature offer
|
||||
promiscuous IP addresses, such as cloud, VPN and public proxy services, might be
|
||||
subject to significantly stricter limits for anonymous access. However,
|
||||
logged-in accesses (see :option:`--login`) do not seem to be affected.
|
||||
:ref:`logged-in accesses<login>` do not seem to be affected.
|
||||
|
||||
Instaloader allows to adjust the rate controlling behavior by overriding
|
||||
:class:`instaloader.RateController`.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Download pictures (or videos) along with their captions and other metadata from Instagram."""
|
||||
|
||||
|
||||
__version__ = '4.11rc2'
|
||||
__version__ = '4.12'
|
||||
|
||||
|
||||
try:
|
||||
@@ -19,4 +19,5 @@ from .lateststamps import LatestStamps
|
||||
from .nodeiterator import NodeIterator, FrozenNodeIterator, resumable_iteration
|
||||
from .structures import (Hashtag, Highlight, Post, PostSidecarNode, PostComment, PostCommentAnswer, PostLocation,
|
||||
Profile, Story, StoryItem, TopSearchResults, TitlePic,
|
||||
load_structure_from_file, save_structure_to_file, load_structure, get_json_structure)
|
||||
load_structure_from_file, save_structure_to_file,
|
||||
load_structure, get_json_structure)
|
||||
|
||||
+90
-58
@@ -5,12 +5,12 @@ import datetime
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import textwrap
|
||||
from argparse import ArgumentParser, ArgumentTypeError, SUPPRESS
|
||||
from enum import IntEnum
|
||||
from typing import List, Optional
|
||||
|
||||
from . import (AbortDownloadException, BadCredentialsException, Instaloader, InstaloaderException,
|
||||
InvalidArgumentException, Post, Profile, ProfileNotExistsException, StoryItem,
|
||||
InvalidArgumentException, LoginException, Post, Profile, ProfileNotExistsException, StoryItem,
|
||||
TwoFactorAuthRequiredException, __version__, load_structure_from_file)
|
||||
from .instaloader import (get_default_session_filename, get_default_stamps_filename)
|
||||
from .instaloadercontext import default_user_agent
|
||||
@@ -22,6 +22,15 @@ except ImportError:
|
||||
bc3_library = False
|
||||
|
||||
|
||||
class ExitCode(IntEnum):
|
||||
SUCCESS = 0
|
||||
NON_FATAL_ERROR = 1
|
||||
INIT_FAILURE = 2
|
||||
LOGIN_FAILURE = 3
|
||||
DOWNLOAD_ABORTED = 4
|
||||
USER_ABORTED = 5
|
||||
UNEXPECTED_ERROR = 99
|
||||
|
||||
def usage_string():
|
||||
# NOTE: duplicated in README.rst and docs/index.rst
|
||||
argv0 = os.path.basename(sys.argv[0])
|
||||
@@ -75,18 +84,22 @@ def filterstr_to_filterfunc(filter_str: str, item_type: type):
|
||||
|
||||
def get_cookies_from_instagram(domain, browser, cookie_file='', cookie_name=''):
|
||||
supported_browsers = {
|
||||
"chrome": browser_cookie3.chrome,
|
||||
"firefox": browser_cookie3.firefox,
|
||||
"edge": browser_cookie3.edge,
|
||||
"brave": browser_cookie3.brave,
|
||||
"chrome": browser_cookie3.chrome,
|
||||
"chromium": browser_cookie3.chromium,
|
||||
"edge": browser_cookie3.edge,
|
||||
"firefox": browser_cookie3.firefox,
|
||||
"librewolf": browser_cookie3.librewolf,
|
||||
"opera": browser_cookie3.opera,
|
||||
"safari": browser_cookie3.safari
|
||||
"opera_gx": browser_cookie3.opera_gx,
|
||||
"safari": browser_cookie3.safari,
|
||||
"vivaldi": browser_cookie3.vivaldi,
|
||||
}
|
||||
|
||||
if browser not in supported_browsers:
|
||||
print("Loading cookies from the specified browser failed")
|
||||
print("Supported browsers are Chrome, Firefox, Edge, Brave, Opera and Safari")
|
||||
return {}
|
||||
raise InvalidArgumentException("Loading cookies from the specified browser failed\n"
|
||||
"Supported browsers are Brave, Chrome, Chromium, Edge, Firefox, LibreWolf, "
|
||||
"Opera, Opera_GX, Safari and Vivaldi")
|
||||
|
||||
cookies = {}
|
||||
browser_cookies = list(supported_browsers[browser](cookie_file=cookie_file))
|
||||
@@ -98,7 +111,8 @@ def get_cookies_from_instagram(domain, browser, cookie_file='', cookie_name=''):
|
||||
if cookies:
|
||||
print(f"Cookies loaded successfully from {browser}")
|
||||
else:
|
||||
print(f"No cookies found for Instagram in {browser}, Are you logged in succesfully in {browser}?")
|
||||
raise LoginException(f"No cookies found for Instagram in {browser}, "
|
||||
f"Are you logged in succesfully in {browser}?")
|
||||
|
||||
if cookie_name:
|
||||
return cookies.get(cookie_name, {})
|
||||
@@ -112,12 +126,10 @@ def import_session(browser, instaloader, cookiefile):
|
||||
instaloader.context.update_cookies(cookie)
|
||||
username = instaloader.test_login()
|
||||
if not username:
|
||||
raise SystemExit(f"Not logged in. Are you logged in successfully in {browser}?")
|
||||
raise LoginException(f"Not logged in. Are you logged in successfully in {browser}?")
|
||||
instaloader.context.username = username
|
||||
print(f"{username} has been successfully logged in.")
|
||||
next_step_text = (f"Next: Run instaloader --login={username} as it is required to download high quality media "
|
||||
"and to make full use of instaloader's features.")
|
||||
print(textwrap.fill(next_step_text))
|
||||
print(f"Next time use --login={username} to reuse the same session.")
|
||||
|
||||
|
||||
def _main(instaloader: Instaloader, targetlist: List[str],
|
||||
@@ -133,7 +145,7 @@ def _main(instaloader: Instaloader, targetlist: List[str],
|
||||
max_count: Optional[int] = None, post_filter_str: Optional[str] = None,
|
||||
storyitem_filter_str: Optional[str] = None,
|
||||
browser: Optional[str] = None,
|
||||
cookiefile: Optional[str] = None) -> None:
|
||||
cookiefile: Optional[str] = None) -> ExitCode:
|
||||
"""Download set of profiles, hashtags etc. and handle logging in and session files if desired."""
|
||||
# Parse and generate filter function
|
||||
post_filter = None
|
||||
@@ -152,7 +164,7 @@ def _main(instaloader: Instaloader, targetlist: List[str],
|
||||
if browser and bc3_library:
|
||||
import_session(browser.lower(), instaloader, cookiefile)
|
||||
elif browser and not bc3_library:
|
||||
raise SystemExit("browser_cookie3 library is needed to load cookies from browsers")
|
||||
raise InvalidArgumentException("browser_cookie3 library is needed to load cookies from browsers")
|
||||
# Login, if desired
|
||||
if username is not None:
|
||||
if not re.match(r"^[A-Za-z0-9._]+$", username):
|
||||
@@ -181,14 +193,19 @@ def _main(instaloader: Instaloader, targetlist: List[str],
|
||||
print(err, file=sys.stderr)
|
||||
pass
|
||||
else:
|
||||
instaloader.interactive_login(username)
|
||||
try:
|
||||
instaloader.interactive_login(username)
|
||||
except KeyboardInterrupt:
|
||||
print("\nInterrupted by user.", file=sys.stderr)
|
||||
return ExitCode.USER_ABORTED
|
||||
instaloader.context.log("Logged in as %s." % username)
|
||||
# since 4.2.9 login is required for geotags
|
||||
if instaloader.download_geotags and not instaloader.context.is_logged_in:
|
||||
instaloader.context.error("Warning: Use --login to download geotags of posts.")
|
||||
instaloader.context.error("Warning: Login is required to download geotags of posts.")
|
||||
# Try block for KeyboardInterrupt (save session on ^C)
|
||||
profiles = set()
|
||||
anonymous_retry_profiles = set()
|
||||
exit_code = ExitCode.SUCCESS
|
||||
try:
|
||||
# Generate set of profiles, already downloading non-profile targets
|
||||
for target in targetlist:
|
||||
@@ -279,7 +296,7 @@ def _main(instaloader: Instaloader, targetlist: List[str],
|
||||
' '.join([p.username for p in profiles])))
|
||||
if instaloader.context.iphone_support and profiles and (download_profile_pic or download_posts) and \
|
||||
not instaloader.context.is_logged_in:
|
||||
instaloader.context.log("Hint: Use --login to download higher-quality versions of pictures.")
|
||||
instaloader.context.log("Hint: Login to download higher-quality versions of pictures.")
|
||||
instaloader.download_profiles(profiles,
|
||||
download_profile_pic, download_posts, download_tagged, download_igtv,
|
||||
download_highlights, download_stories,
|
||||
@@ -294,8 +311,10 @@ def _main(instaloader: Instaloader, targetlist: List[str],
|
||||
latest_stamps=latest_stamps)
|
||||
except KeyboardInterrupt:
|
||||
print("\nInterrupted by user.", file=sys.stderr)
|
||||
exit_code = ExitCode.USER_ABORTED
|
||||
except AbortDownloadException as exc:
|
||||
print("\nDownload aborted: {}.".format(exc), file=sys.stderr)
|
||||
exit_code = ExitCode.DOWNLOAD_ABORTED
|
||||
# Save session if it is useful
|
||||
if instaloader.context.is_logged_in:
|
||||
instaloader.save_session_to_file(sessionfile)
|
||||
@@ -307,6 +326,8 @@ def _main(instaloader: Instaloader, targetlist: List[str],
|
||||
else:
|
||||
# Instaloader did not do anything
|
||||
instaloader.context.log("usage:" + usage_string())
|
||||
exit_code = ExitCode.INIT_FAILURE
|
||||
return exit_code
|
||||
|
||||
|
||||
def main():
|
||||
@@ -322,17 +343,17 @@ def main():
|
||||
help="Download profile. If an already-downloaded profile has been renamed, Instaloader "
|
||||
"automatically finds it by its unique ID and renames the folder likewise.")
|
||||
g_targets.add_argument('_at_profile', nargs='*', metavar="@profile",
|
||||
help="Download all followees of profile. Requires --login. "
|
||||
help="Download all followees of profile. Requires login. "
|
||||
"Consider using :feed rather than @yourself.")
|
||||
g_targets.add_argument('_hashtag', nargs='*', metavar='"#hashtag"', help="Download #hashtag.")
|
||||
g_targets.add_argument('_location', nargs='*', metavar='%location_id',
|
||||
help="Download %%location_id. Requires --login.")
|
||||
help="Download %%location_id. Requires login.")
|
||||
g_targets.add_argument('_feed', nargs='*', metavar=":feed",
|
||||
help="Download pictures from your feed. Requires --login.")
|
||||
help="Download pictures from your feed. Requires login.")
|
||||
g_targets.add_argument('_stories', nargs='*', metavar=":stories",
|
||||
help="Download the stories of your followees. Requires --login.")
|
||||
help="Download the stories of your followees. Requires login.")
|
||||
g_targets.add_argument('_saved', nargs='*', metavar=":saved",
|
||||
help="Download the posts that you marked as saved. Requires --login.")
|
||||
help="Download the posts that you marked as saved. Requires login.")
|
||||
g_targets.add_argument('_singlepost', nargs='*', metavar="-- -shortcode",
|
||||
help="Download the post with the given shortcode")
|
||||
g_targets.add_argument('_json', nargs='*', metavar="filename.json[.xz]",
|
||||
@@ -363,11 +384,11 @@ def main():
|
||||
help='Download geotags when available. Geotags are stored as a '
|
||||
'text file with the location\'s name and a Google Maps link. '
|
||||
'This requires an additional request to the Instagram '
|
||||
'server for each picture. Requires --login.')
|
||||
'server for each picture. Requires login.')
|
||||
g_post.add_argument('-C', '--comments', action='store_true',
|
||||
help='Download and update comments for each post. '
|
||||
'This requires an additional request to the Instagram '
|
||||
'server for each post, which is why it is disabled by default.')
|
||||
'server for each post, which is why it is disabled by default. Requires login.')
|
||||
g_post.add_argument('--no-captions', action='store_true',
|
||||
help='Do not create txt files.')
|
||||
g_post.add_argument('--post-metadata-txt', action='append',
|
||||
@@ -381,11 +402,11 @@ def main():
|
||||
g_post.add_argument('--no-compress-json', action='store_true',
|
||||
help='Do not xz compress JSON files, rather create pretty formatted JSONs.')
|
||||
g_prof.add_argument('-s', '--stories', action='store_true',
|
||||
help='Also download stories of each profile that is downloaded. Requires --login.')
|
||||
help='Also download stories of each profile that is downloaded. Requires login.')
|
||||
g_prof.add_argument('--stories-only', action='store_true',
|
||||
help=SUPPRESS)
|
||||
g_prof.add_argument('--highlights', action='store_true',
|
||||
help='Also download highlights of each profile that is downloaded. Requires --login.')
|
||||
help='Also download highlights of each profile that is downloaded. Requires login.')
|
||||
g_prof.add_argument('--tagged', action='store_true',
|
||||
help='Also download posts where each profile is tagged.')
|
||||
g_prof.add_argument('--igtv', action='store_true',
|
||||
@@ -417,7 +438,8 @@ def main():
|
||||
'Instaloader can login to Instagram. This allows downloading private profiles. '
|
||||
'To login, pass the --login option. Your session cookie (not your password!) '
|
||||
'will be saved to a local file to be reused next time you want Instaloader '
|
||||
'to login.')
|
||||
'to login. Instead of --login, the --load-cookies option can be used to '
|
||||
'import a session from a browser.')
|
||||
g_login.add_argument('-l', '--login', metavar='YOUR-USERNAME',
|
||||
help='Login name (profile name) for your Instagram account.')
|
||||
g_login.add_argument('-b', '--load-cookies', metavar='BROWSER-NAME',
|
||||
@@ -484,15 +506,15 @@ def main():
|
||||
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
if args.login is None and (args.stories or args.stories_only):
|
||||
print("--login=USERNAME required to download stories.", file=sys.stderr)
|
||||
if (args.login is None and args.load_cookies is None) and (args.stories or args.stories_only):
|
||||
print("Login is required to download stories.", file=sys.stderr)
|
||||
args.stories = False
|
||||
if args.stories_only:
|
||||
raise SystemExit(1)
|
||||
raise InvalidArgumentException()
|
||||
|
||||
if ':feed-all' in args.profile or ':feed-liked' in args.profile:
|
||||
raise SystemExit(":feed-all and :feed-liked were removed. Use :feed as target and "
|
||||
"eventually --post-filter=viewer_has_liked.")
|
||||
raise InvalidArgumentException(":feed-all and :feed-liked were removed. Use :feed as target and "
|
||||
"eventually --post-filter=viewer_has_liked.")
|
||||
|
||||
post_metadata_txt_pattern = '\n'.join(args.post_metadata_txt) if args.post_metadata_txt else None
|
||||
storyitem_metadata_txt_pattern = '\n'.join(args.storyitem_metadata_txt) if args.storyitem_metadata_txt else None
|
||||
@@ -502,18 +524,18 @@ def main():
|
||||
post_metadata_txt_pattern = ''
|
||||
storyitem_metadata_txt_pattern = ''
|
||||
else:
|
||||
raise SystemExit("--no-captions and --post-metadata-txt or --storyitem-metadata-txt given; "
|
||||
"That contradicts.")
|
||||
raise InvalidArgumentException("--no-captions and --post-metadata-txt or --storyitem-metadata-txt "
|
||||
"given; That contradicts.")
|
||||
|
||||
if args.no_resume and args.resume_prefix:
|
||||
raise SystemExit("--no-resume and --resume-prefix given; That contradicts.")
|
||||
raise InvalidArgumentException("--no-resume and --resume-prefix given; That contradicts.")
|
||||
resume_prefix = (args.resume_prefix if args.resume_prefix else 'iterator') if not args.no_resume else None
|
||||
|
||||
if args.no_pictures and args.fast_update:
|
||||
raise SystemExit('--no-pictures and --fast-update cannot be used together.')
|
||||
raise InvalidArgumentException('--no-pictures and --fast-update cannot be used together.')
|
||||
|
||||
if args.login and args.load_cookies:
|
||||
raise SystemExit('--load-cookies and --login cannot be used together.')
|
||||
raise InvalidArgumentException('--load-cookies and --login cannot be used together.')
|
||||
|
||||
# Determine what to download
|
||||
download_profile_pic = not args.no_profile_pic or args.profile_pic_only
|
||||
@@ -538,27 +560,37 @@ def main():
|
||||
iphone_support=not args.no_iphone,
|
||||
title_pattern=args.title_pattern,
|
||||
sanitize_paths=args.sanitize_paths)
|
||||
_main(loader,
|
||||
args.profile,
|
||||
username=args.login.lower() if args.login is not None else None,
|
||||
password=args.password,
|
||||
sessionfile=args.sessionfile,
|
||||
download_profile_pic=download_profile_pic,
|
||||
download_posts=download_posts,
|
||||
download_stories=download_stories,
|
||||
download_highlights=args.highlights,
|
||||
download_tagged=args.tagged,
|
||||
download_igtv=args.igtv,
|
||||
fast_update=args.fast_update,
|
||||
latest_stamps_file=args.latest_stamps,
|
||||
max_count=int(args.count) if args.count is not None else None,
|
||||
post_filter_str=args.post_filter,
|
||||
storyitem_filter_str=args.storyitem_filter,
|
||||
browser=args.load_cookies,
|
||||
cookiefile=args.cookiefile)
|
||||
exit_code = _main(loader,
|
||||
args.profile,
|
||||
username=args.login.lower() if args.login is not None else None,
|
||||
password=args.password,
|
||||
sessionfile=args.sessionfile,
|
||||
download_profile_pic=download_profile_pic,
|
||||
download_posts=download_posts,
|
||||
download_stories=download_stories,
|
||||
download_highlights=args.highlights,
|
||||
download_tagged=args.tagged,
|
||||
download_igtv=args.igtv,
|
||||
fast_update=args.fast_update,
|
||||
latest_stamps_file=args.latest_stamps,
|
||||
max_count=int(args.count) if args.count is not None else None,
|
||||
post_filter_str=args.post_filter,
|
||||
storyitem_filter_str=args.storyitem_filter,
|
||||
browser=args.load_cookies,
|
||||
cookiefile=args.cookiefile)
|
||||
loader.close()
|
||||
if loader.has_stored_errors:
|
||||
exit_code = ExitCode.NON_FATAL_ERROR
|
||||
except InvalidArgumentException as err:
|
||||
print(err, file=sys.stderr)
|
||||
exit_code = ExitCode.INIT_FAILURE
|
||||
except LoginException as err:
|
||||
print(err, file=sys.stderr)
|
||||
exit_code = ExitCode.LOGIN_FAILURE
|
||||
except InstaloaderException as err:
|
||||
raise SystemExit("Fatal error: %s" % err) from err
|
||||
print("Fatal error: %s" % err)
|
||||
exit_code = ExitCode.UNEXPECTED_ERROR
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -33,7 +33,11 @@ class LoginRequiredException(InstaloaderException):
|
||||
pass
|
||||
|
||||
|
||||
class TwoFactorAuthRequiredException(InstaloaderException):
|
||||
class LoginException(InstaloaderException):
|
||||
pass
|
||||
|
||||
|
||||
class TwoFactorAuthRequiredException(LoginException):
|
||||
pass
|
||||
|
||||
|
||||
@@ -45,7 +49,7 @@ class BadResponseException(InstaloaderException):
|
||||
pass
|
||||
|
||||
|
||||
class BadCredentialsException(InstaloaderException):
|
||||
class BadCredentialsException(LoginException):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
+36
-13
@@ -77,7 +77,7 @@ def _requires_login(func: Callable) -> Callable:
|
||||
@wraps(func)
|
||||
def call(instaloader, *args, **kwargs):
|
||||
if not instaloader.context.is_logged_in:
|
||||
raise LoginRequiredException("--login=USERNAME required.")
|
||||
raise LoginRequiredException("Login required.")
|
||||
return func(instaloader, *args, **kwargs)
|
||||
return call
|
||||
|
||||
@@ -643,11 +643,16 @@ class Instaloader:
|
||||
def login(self, user: str, passwd: str) -> None:
|
||||
"""Log in to instagram with given username and password and internally store session object.
|
||||
|
||||
:raises InvalidArgumentException: If the provided username does not exist.
|
||||
:raises BadCredentialsException: If the provided password is wrong.
|
||||
:raises ConnectionException: If connection to Instagram failed.
|
||||
:raises TwoFactorAuthRequiredException: First step of 2FA login done, now call
|
||||
:meth:`Instaloader.two_factor_login`."""
|
||||
:meth:`Instaloader.two_factor_login`.
|
||||
:raises LoginException: An error happened during login (for example, an invalid response was received).
|
||||
Or if the provided username does not exist.
|
||||
|
||||
.. versionchanged:: 4.12
|
||||
Raises LoginException instead of ConnectionException when an error happens.
|
||||
Raises LoginException instead of InvalidArgumentException when the username does not exist.
|
||||
"""
|
||||
self.context.login(user, passwd)
|
||||
|
||||
def two_factor_login(self, two_factor_code) -> None:
|
||||
@@ -991,7 +996,8 @@ class Instaloader:
|
||||
max_count: Optional[int] = None,
|
||||
total_count: Optional[int] = None,
|
||||
owner_profile: Optional[Profile] = None,
|
||||
takewhile: Optional[Callable[[Post], bool]] = None) -> None:
|
||||
takewhile: Optional[Callable[[Post], bool]] = None,
|
||||
possibly_pinned: int = 0) -> None:
|
||||
"""
|
||||
Download the Posts returned by given Post Iterator.
|
||||
|
||||
@@ -1003,6 +1009,9 @@ class Instaloader:
|
||||
.. versionchanged:: 4.8
|
||||
Add `takewhile` parameter.
|
||||
|
||||
.. versionchanged:: 4.10.3
|
||||
Add `possibly_pinned` parameter.
|
||||
|
||||
:param posts: Post Iterator to loop through.
|
||||
:param target: Target name.
|
||||
:param fast_update: :option:`--fast-update`.
|
||||
@@ -1011,6 +1020,8 @@ class Instaloader:
|
||||
:param total_count: Total number of posts returned by given iterator.
|
||||
:param owner_profile: Associated profile, if any.
|
||||
:param takewhile: Expression evaluated for each post. Once it returns false, downloading stops.
|
||||
:param possibly_pinned: Number of posts that might be pinned. These posts do not cause download
|
||||
to stop even if they've already been downloaded.
|
||||
"""
|
||||
displayed_count = (max_count if total_count is None or max_count is not None and max_count < total_count
|
||||
else total_count)
|
||||
@@ -1032,7 +1043,7 @@ class Instaloader:
|
||||
) as (is_resuming, start_index):
|
||||
for number, post in enumerate(posts, start=start_index + 1):
|
||||
should_stop = not takewhile(post)
|
||||
if should_stop and post.is_pinned:
|
||||
if should_stop and number <= possibly_pinned:
|
||||
continue
|
||||
if (max_count is not None and number > max_count) or should_stop:
|
||||
break
|
||||
@@ -1066,7 +1077,7 @@ class Instaloader:
|
||||
except PostChangedException:
|
||||
post_changed = True
|
||||
continue
|
||||
if fast_update and not downloaded and not post_changed and not post.is_pinned:
|
||||
if fast_update and not downloaded and not post_changed and number > possibly_pinned:
|
||||
# disengage fast_update for first post when resuming
|
||||
if not is_resuming or number > 0:
|
||||
break
|
||||
@@ -1454,7 +1465,7 @@ class Instaloader:
|
||||
if tagged or igtv or highlights or posts:
|
||||
if (not self.context.is_logged_in and
|
||||
profile.is_private):
|
||||
raise LoginRequiredException("--login=USERNAME required.")
|
||||
raise LoginRequiredException("Login required.")
|
||||
if (self.context.username != profile.username and
|
||||
profile.is_private and
|
||||
not profile.followed_by_viewer):
|
||||
@@ -1488,7 +1499,7 @@ class Instaloader:
|
||||
posts_to_download = profile.get_posts()
|
||||
self.posts_download_loop(posts_to_download, profile_name, fast_update, post_filter,
|
||||
total_count=profile.mediacount, owner_profile=profile,
|
||||
takewhile=posts_takewhile)
|
||||
takewhile=posts_takewhile, possibly_pinned=3)
|
||||
if latest_stamps is not None and posts_to_download.first_item is not None:
|
||||
latest_stamps.set_last_post_timestamp(profile_name,
|
||||
posts_to_download.first_item.date_local)
|
||||
@@ -1576,11 +1587,16 @@ class Instaloader:
|
||||
def interactive_login(self, username: str) -> None:
|
||||
"""Logs in and internally stores session, asking user for password interactively.
|
||||
|
||||
:raises LoginRequiredException: when in quiet mode.
|
||||
:raises InvalidArgumentException: If the provided username does not exist.
|
||||
:raises ConnectionException: If connection to Instagram failed."""
|
||||
:raises InvalidArgumentException: when in quiet mode.
|
||||
:raises LoginException: If the provided username does not exist.
|
||||
:raises ConnectionException: If connection to Instagram failed.
|
||||
|
||||
.. versionchanged:: 4.12
|
||||
Raises InvalidArgumentException instead of LoginRequiredException when in quiet mode.
|
||||
Raises LoginException instead of InvalidArgumentException when the username does not exist.
|
||||
"""
|
||||
if self.context.quiet:
|
||||
raise LoginRequiredException("Quiet mode requires given password or valid session file.")
|
||||
raise InvalidArgumentException("Quiet mode requires given password or valid session file.")
|
||||
try:
|
||||
password = None
|
||||
while password is None:
|
||||
@@ -1599,3 +1615,10 @@ class Instaloader:
|
||||
except BadCredentialsException as err:
|
||||
print(err, file=sys.stderr)
|
||||
pass
|
||||
|
||||
@property
|
||||
def has_stored_errors(self) -> bool:
|
||||
"""Returns whether any error has been reported and stored to be repeated at program termination.
|
||||
|
||||
.. versionadded: 4.12"""
|
||||
return self.context.has_stored_errors
|
||||
|
||||
@@ -9,7 +9,7 @@ import textwrap
|
||||
import time
|
||||
import urllib.parse
|
||||
import uuid
|
||||
from contextlib import contextmanager
|
||||
from contextlib import contextmanager, suppress
|
||||
from datetime import datetime, timedelta
|
||||
from functools import partial
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Union
|
||||
@@ -151,6 +151,13 @@ class InstaloaderContext:
|
||||
if repeat_at_end:
|
||||
self.error_log.append(msg)
|
||||
|
||||
@property
|
||||
def has_stored_errors(self) -> bool:
|
||||
"""Returns whether any error has been reported and stored to be repeated at program termination.
|
||||
|
||||
.. versionadded: 4.12"""
|
||||
return bool(self.error_log)
|
||||
|
||||
def close(self):
|
||||
"""Print error log and close session"""
|
||||
if self.error_log and not self.quiet:
|
||||
@@ -236,17 +243,26 @@ class InstaloaderContext:
|
||||
|
||||
def test_login(self) -> Optional[str]:
|
||||
"""Not meant to be used directly, use :meth:`Instaloader.test_login`."""
|
||||
data = self.graphql_query("d6f4427fbe92d846298cf93df0b937d3", {})
|
||||
return data["data"]["user"]["username"] if data["data"]["user"] is not None else None
|
||||
try:
|
||||
data = self.graphql_query("d6f4427fbe92d846298cf93df0b937d3", {})
|
||||
return data["data"]["user"]["username"] if data["data"]["user"] is not None else None
|
||||
except (AbortDownloadException, ConnectionException) as err:
|
||||
self.error(f"Error when checking if logged in: {err}")
|
||||
return None
|
||||
|
||||
def login(self, user, passwd):
|
||||
"""Not meant to be used directly, use :meth:`Instaloader.login`.
|
||||
|
||||
:raises InvalidArgumentException: If the provided username does not exist.
|
||||
:raises BadCredentialsException: If the provided password is wrong.
|
||||
:raises ConnectionException: If connection to Instagram failed.
|
||||
:raises TwoFactorAuthRequiredException: First step of 2FA login done, now call
|
||||
:meth:`Instaloader.two_factor_login`."""
|
||||
:meth:`Instaloader.two_factor_login`.
|
||||
:raises LoginException: An error happened during login (for example, and invalid response).
|
||||
Or if the provided username does not exist.
|
||||
|
||||
.. versionchanged:: 4.12
|
||||
Raises LoginException instead of ConnectionException when an error happens.
|
||||
Raises LoginException instead of InvalidArgumentException when the username does not exist.
|
||||
"""
|
||||
# pylint:disable=import-outside-toplevel
|
||||
import http.client
|
||||
# pylint:disable=protected-access
|
||||
@@ -277,7 +293,7 @@ class InstaloaderContext:
|
||||
resp_json = login.json()
|
||||
|
||||
except json.decoder.JSONDecodeError as err:
|
||||
raise ConnectionException(
|
||||
raise LoginException(
|
||||
"Login error: JSON decode fail, {} - {}.".format(login.status_code, login.reason)
|
||||
) from err
|
||||
if resp_json.get('two_factor_required'):
|
||||
@@ -289,31 +305,32 @@ class InstaloaderContext:
|
||||
resp_json['two_factor_info']['two_factor_identifier'])
|
||||
raise TwoFactorAuthRequiredException("Login error: two-factor authentication required.")
|
||||
if resp_json.get('checkpoint_url'):
|
||||
raise ConnectionException("Login: Checkpoint required. Point your browser to "
|
||||
"https://www.instagram.com{} - "
|
||||
"follow the instructions, then retry.".format(resp_json.get('checkpoint_url')))
|
||||
raise LoginException(
|
||||
f"Login: Checkpoint required. Point your browser to {resp_json.get('checkpoint_url')} - "
|
||||
f"follow the instructions, then retry."
|
||||
)
|
||||
if resp_json['status'] != 'ok':
|
||||
if 'message' in resp_json:
|
||||
raise ConnectionException("Login error: \"{}\" status, message \"{}\".".format(resp_json['status'],
|
||||
resp_json['message']))
|
||||
raise LoginException("Login error: \"{}\" status, message \"{}\".".format(resp_json['status'],
|
||||
resp_json['message']))
|
||||
else:
|
||||
raise ConnectionException("Login error: \"{}\" status.".format(resp_json['status']))
|
||||
raise LoginException("Login error: \"{}\" status.".format(resp_json['status']))
|
||||
if 'authenticated' not in resp_json:
|
||||
# Issue #472
|
||||
if 'message' in resp_json:
|
||||
raise ConnectionException("Login error: Unexpected response, \"{}\".".format(resp_json['message']))
|
||||
raise LoginException("Login error: Unexpected response, \"{}\".".format(resp_json['message']))
|
||||
else:
|
||||
raise ConnectionException("Login error: Unexpected response, this might indicate a blocked IP.")
|
||||
raise LoginException("Login error: Unexpected response, this might indicate a blocked IP.")
|
||||
if not resp_json['authenticated']:
|
||||
if resp_json['user']:
|
||||
# '{"authenticated": false, "user": true, "status": "ok"}'
|
||||
raise BadCredentialsException('Login error: Wrong password.')
|
||||
else:
|
||||
# '{"authenticated": false, "user": false, "status": "ok"}'
|
||||
# Raise InvalidArgumentException rather than BadCredentialException, because BadCredentialException
|
||||
# Raise LoginException rather than BadCredentialException, because BadCredentialException
|
||||
# triggers re-asking of password in Instaloader.interactive_login(), which makes no sense if the
|
||||
# username is invalid.
|
||||
raise InvalidArgumentException('Login error: User {} does not exist.'.format(user))
|
||||
raise LoginException('Login error: User {} does not exist.'.format(user))
|
||||
# '{"authenticated": true, "user": true, "userId": ..., "oneTapPrompt": false, "status": "ok"}'
|
||||
session.headers.update({'X-CSRFToken': login.cookies['csrftoken']})
|
||||
self._session = session
|
||||
@@ -351,6 +368,23 @@ class InstaloaderContext:
|
||||
if self.sleep:
|
||||
time.sleep(min(random.expovariate(0.6), 15.0))
|
||||
|
||||
@staticmethod
|
||||
def _response_error(resp: requests.Response) -> str:
|
||||
extra_from_json: Optional[str] = None
|
||||
with suppress(json.decoder.JSONDecodeError):
|
||||
resp_json = resp.json()
|
||||
if "status" in resp_json:
|
||||
extra_from_json = (
|
||||
f"\"{resp_json['status']}\" status, message \"{resp_json['message']}\""
|
||||
if "message" in resp_json
|
||||
else f"\"{resp_json['status']}\" status"
|
||||
)
|
||||
return (
|
||||
f"{resp.status_code} {resp.reason}"
|
||||
f"{f' - {extra_from_json}' if extra_from_json is not None else ''}"
|
||||
f" when accessing {resp.url}"
|
||||
)
|
||||
|
||||
def get_json(self, path: str, params: Dict[str, Any], host: str = 'www.instagram.com',
|
||||
session: Optional[requests.Session] = None, _attempt=1,
|
||||
response_headers: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
||||
@@ -392,7 +426,7 @@ class InstaloaderContext:
|
||||
if (redirect_url.startswith('https://www.instagram.com/accounts/login') or
|
||||
redirect_url.startswith('https://i.instagram.com/accounts/login')):
|
||||
if not self.is_logged_in:
|
||||
raise LoginRequiredException("Redirected to login page. Use --login.")
|
||||
raise LoginRequiredException("Redirected to login page. Use --login or --load-cookies.")
|
||||
raise AbortDownloadException("Redirected to login page. You've been logged out, please wait " +
|
||||
"some time, recreate the session and try again")
|
||||
if redirect_url.startswith('https://{}/'.format(host)):
|
||||
@@ -404,21 +438,17 @@ class InstaloaderContext:
|
||||
response_headers.clear()
|
||||
response_headers.update(resp.headers)
|
||||
if resp.status_code == 400:
|
||||
raise QueryReturnedBadRequestException("400 Bad Request")
|
||||
raise QueryReturnedBadRequestException(self._response_error(resp))
|
||||
if resp.status_code == 404:
|
||||
raise QueryReturnedNotFoundException("404 Not Found")
|
||||
raise QueryReturnedNotFoundException(self._response_error(resp))
|
||||
if resp.status_code == 429:
|
||||
raise TooManyRequestsException("429 Too Many Requests")
|
||||
raise TooManyRequestsException(self._response_error(resp))
|
||||
if resp.status_code != 200:
|
||||
raise ConnectionException("HTTP error code {}.".format(resp.status_code))
|
||||
raise ConnectionException(self._response_error(resp))
|
||||
else:
|
||||
resp_json = resp.json()
|
||||
if 'status' in resp_json and resp_json['status'] != "ok":
|
||||
if 'message' in resp_json:
|
||||
raise ConnectionException("Returned \"{}\" status, message \"{}\".".format(resp_json['status'],
|
||||
resp_json['message']))
|
||||
else:
|
||||
raise ConnectionException("Returned \"{}\" status.".format(resp_json['status']))
|
||||
raise ConnectionException(self._response_error(resp))
|
||||
return resp_json
|
||||
except (ConnectionException, json.decoder.JSONDecodeError, requests.exceptions.RequestException) as err:
|
||||
error_string = "JSON Query to {}: {}".format(path, err)
|
||||
@@ -607,11 +637,11 @@ class InstaloaderContext:
|
||||
else:
|
||||
if resp.status_code == 403:
|
||||
# suspected invalid URL signature
|
||||
raise QueryReturnedForbiddenException("403 when accessing {}.".format(url))
|
||||
raise QueryReturnedForbiddenException(self._response_error(resp))
|
||||
if resp.status_code == 404:
|
||||
# 404 not worth retrying.
|
||||
raise QueryReturnedNotFoundException("404 when accessing {}.".format(url))
|
||||
raise ConnectionException("HTTP error code {}.".format(resp.status_code))
|
||||
raise QueryReturnedNotFoundException(self._response_error(resp))
|
||||
raise ConnectionException(self._response_error(resp))
|
||||
|
||||
def get_and_write_raw(self, url: str, filename: str) -> None:
|
||||
"""Downloads and writes anonymously-requested raw data into a file.
|
||||
@@ -637,11 +667,11 @@ class InstaloaderContext:
|
||||
else:
|
||||
if resp.status_code == 403:
|
||||
# suspected invalid URL signature
|
||||
raise QueryReturnedForbiddenException("403 when accessing {}.".format(url))
|
||||
raise QueryReturnedForbiddenException(self._response_error(resp))
|
||||
if resp.status_code == 404:
|
||||
# 404 not worth retrying.
|
||||
raise QueryReturnedNotFoundException("404 when accessing {}.".format(url))
|
||||
raise ConnectionException("HTTP error code {}.".format(resp.status_code))
|
||||
raise QueryReturnedNotFoundException(self._response_error(resp))
|
||||
raise ConnectionException(self._response_error(resp))
|
||||
|
||||
@property
|
||||
def root_rhx_gis(self) -> Optional[str]:
|
||||
|
||||
@@ -184,6 +184,10 @@ class NodeIterator(Iterator[T]):
|
||||
"""
|
||||
return self._node_wrapper(self._first_node) if self._first_node is not None else None
|
||||
|
||||
@staticmethod
|
||||
def page_length() -> int:
|
||||
return NodeIterator._graphql_page_length
|
||||
|
||||
def freeze(self) -> FrozenNodeIterator:
|
||||
"""Freeze the iterator for later resuming."""
|
||||
remaining_data = None
|
||||
|
||||
+113
-15
@@ -51,6 +51,29 @@ class PostComment:
|
||||
self._answers = answers
|
||||
self._post = post
|
||||
|
||||
@classmethod
|
||||
def from_iphone_struct(
|
||||
cls,
|
||||
context: "InstaloaderContext",
|
||||
media: Dict[str, Any],
|
||||
answers: Iterator["PostCommentAnswer"],
|
||||
post: "Post",
|
||||
):
|
||||
return cls(
|
||||
context=context,
|
||||
node={
|
||||
"id": int(media["pk"]),
|
||||
"created_at": media["created_at"],
|
||||
"text": media["text"],
|
||||
"edge_liked_by": {
|
||||
"count": media["comment_like_count"],
|
||||
},
|
||||
"iphone_struct": media,
|
||||
},
|
||||
answers=answers,
|
||||
post=post,
|
||||
)
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
""" ID number of comment. """
|
||||
@@ -67,9 +90,13 @@ class PostComment:
|
||||
return self._node['text']
|
||||
|
||||
@property
|
||||
def owner(self):
|
||||
def owner(self) -> "Profile":
|
||||
""" Owner :class:`Profile` of the comment. """
|
||||
return Profile(self._context, self._node['owner'])
|
||||
if "iphone_struct" in self._node:
|
||||
return Profile.from_iphone_struct(
|
||||
self._context, self._node["iphone_struct"]["user"]
|
||||
)
|
||||
return Profile(self._context, self._node["owner"])
|
||||
|
||||
@property
|
||||
def likes_count(self):
|
||||
@@ -303,7 +330,7 @@ class Post:
|
||||
if not self._context.iphone_support:
|
||||
raise IPhoneSupportDisabledException("iPhone support is disabled.")
|
||||
if not self._context.is_logged_in:
|
||||
raise LoginRequiredException("--login required to access iPhone media info endpoint.")
|
||||
raise LoginRequiredException("Login required to access iPhone media info endpoint.")
|
||||
if not self._iphone_struct_:
|
||||
data = self._context.get_iphone_json(path='api/v1/media/{}/info/'.format(self.mediaid), params={})
|
||||
self._iphone_struct_ = data['items'][0]
|
||||
@@ -590,16 +617,77 @@ class Post:
|
||||
except KeyError:
|
||||
return self._field('edge_media_to_comment', 'count')
|
||||
|
||||
def _get_comments_via_iphone_endpoint(self) -> Iterable[PostComment]:
|
||||
"""
|
||||
Iterate over all comments of the post via an iPhone endpoint.
|
||||
|
||||
.. versionadded:: 4.10.3
|
||||
fallback for :issue:`2125`.
|
||||
"""
|
||||
def _query(min_id=None):
|
||||
pagination_params = {"min_id": min_id} if min_id is not None else {}
|
||||
return self._context.get_iphone_json(
|
||||
f"api/v1/media/{self.mediaid}/comments/",
|
||||
{
|
||||
"can_support_threading": "true",
|
||||
"permalink_enabled": "false",
|
||||
**pagination_params,
|
||||
},
|
||||
)
|
||||
|
||||
def _answers(comment_node):
|
||||
def _answer(child_comment):
|
||||
return PostCommentAnswer(
|
||||
id=int(child_comment["pk"]),
|
||||
created_at_utc=datetime.utcfromtimestamp(child_comment["created_at"]),
|
||||
text=child_comment["text"],
|
||||
owner=Profile.from_iphone_struct(self._context, child_comment["user"]),
|
||||
likes_count=child_comment["comment_like_count"],
|
||||
)
|
||||
|
||||
child_comment_count = comment_node["child_comment_count"]
|
||||
if child_comment_count == 0:
|
||||
return
|
||||
preview_child_comments = comment_node["preview_child_comments"]
|
||||
if child_comment_count == len(preview_child_comments):
|
||||
yield from (
|
||||
_answer(child_comment) for child_comment in preview_child_comments
|
||||
)
|
||||
return
|
||||
pk = comment_node["pk"]
|
||||
answers_json = self._context.get_iphone_json(
|
||||
f"api/v1/media/{self.mediaid}/comments/{pk}/child_comments/",
|
||||
{"max_id": ""},
|
||||
)
|
||||
yield from (
|
||||
_answer(child_comment) for child_comment in answers_json["child_comments"]
|
||||
)
|
||||
|
||||
def _paginated_comments(comments_json):
|
||||
for comment_node in comments_json.get("comments", []):
|
||||
yield PostComment.from_iphone_struct(
|
||||
self._context, comment_node, _answers(comment_node), self
|
||||
)
|
||||
|
||||
next_min_id = comments_json.get("next_min_id")
|
||||
if next_min_id:
|
||||
yield from _paginated_comments(_query(next_min_id))
|
||||
|
||||
return _paginated_comments(_query())
|
||||
|
||||
def get_comments(self) -> Iterable[PostComment]:
|
||||
r"""Iterate over all comments of the post.
|
||||
"""Iterate over all comments of the post.
|
||||
|
||||
Each comment is represented by a PostComment NamedTuple with fields text (string), created_at (datetime),
|
||||
id (int), owner (:class:`Profile`) and answers (:class:`~typing.Iterator`\ [:class:`PostCommentAnswer`])
|
||||
id (int), owner (:class:`Profile`) and answers (:class:`~typing.Iterator` [:class:`PostCommentAnswer`])
|
||||
if available.
|
||||
|
||||
.. versionchanged:: 4.7
|
||||
Change return type to ``Iterable``.
|
||||
"""
|
||||
if not self._context.is_logged_in:
|
||||
raise LoginRequiredException("Login required to access comments of a post.")
|
||||
|
||||
def _postcommentanswer(node):
|
||||
return PostCommentAnswer(id=int(node['id']),
|
||||
created_at_utc=datetime.utcfromtimestamp(node['created_at']),
|
||||
@@ -641,6 +729,12 @@ class Post:
|
||||
if self.comments == len(comment_edges) + answers_count:
|
||||
# If the Post's metadata already contains all parent comments, don't do GraphQL requests to obtain them
|
||||
return [_postcomment(comment['node']) for comment in comment_edges]
|
||||
|
||||
if self.comments > NodeIterator.page_length():
|
||||
# comments pagination via our graphql query does not work reliably anymore (issue #2125), fallback to an
|
||||
# iphone endpoint if needed.
|
||||
return self._get_comments_via_iphone_endpoint()
|
||||
|
||||
return NodeIterator(
|
||||
self._context,
|
||||
'97b41c52301f77ce508f55e66d17620e',
|
||||
@@ -658,7 +752,7 @@ class Post:
|
||||
Require being logged in (as required by Instagram).
|
||||
"""
|
||||
if not self._context.is_logged_in:
|
||||
raise LoginRequiredException("--login required to access likes of a post.")
|
||||
raise LoginRequiredException("Login required to access likes of a post.")
|
||||
if self.likes == 0:
|
||||
# Avoid doing additional requests if there are no comments
|
||||
return
|
||||
@@ -723,7 +817,11 @@ class Post:
|
||||
|
||||
@property
|
||||
def is_pinned(self) -> bool:
|
||||
"""True if this Post has been pinned by at least one user.
|
||||
"""
|
||||
.. deprecated: 4.10.3
|
||||
This information is not returned by IG anymore
|
||||
|
||||
Used to return True if this Post has been pinned by at least one user, now likely returns always false.
|
||||
|
||||
.. versionadded: 4.9.2"""
|
||||
return 'pinned_for_users' in self._node and bool(self._node['pinned_for_users'])
|
||||
@@ -831,7 +929,7 @@ class Profile:
|
||||
|
||||
.. versionadded:: 4.5.2"""
|
||||
if not context.is_logged_in:
|
||||
raise LoginRequiredException("--login required to access own profile.")
|
||||
raise LoginRequiredException("Login required to access own profile.")
|
||||
return cls(context, context.graphql_query("d6f4427fbe92d846298cf93df0b937d3", {})["data"]["user"])
|
||||
|
||||
def _asdict(self):
|
||||
@@ -885,7 +983,7 @@ class Profile:
|
||||
if not self._context.iphone_support:
|
||||
raise IPhoneSupportDisabledException("iPhone support is disabled.")
|
||||
if not self._context.is_logged_in:
|
||||
raise LoginRequiredException("--login required to access iPhone profile info endpoint.")
|
||||
raise LoginRequiredException("Login required to access iPhone profile info endpoint.")
|
||||
if not self._iphone_struct_:
|
||||
data = self._context.get_iphone_json(path='api/v1/users/{}/info/'.format(self.userid), params={})
|
||||
self._iphone_struct_ = data['user']
|
||||
@@ -1092,7 +1190,7 @@ class Profile:
|
||||
:rtype: NodeIterator[Post]"""
|
||||
|
||||
if self.username != self._context.username:
|
||||
raise LoginRequiredException("--login={} required to get that profile's saved posts.".format(self.username))
|
||||
raise LoginRequiredException(f"Login as {self.username} required to get that profile's saved posts.")
|
||||
|
||||
return NodeIterator(
|
||||
self._context,
|
||||
@@ -1152,7 +1250,7 @@ class Profile:
|
||||
.. versionadded:: 4.10
|
||||
"""
|
||||
if not self._context.is_logged_in:
|
||||
raise LoginRequiredException("--login required to get a profile's followers.")
|
||||
raise LoginRequiredException("Login required to get a profile's followers.")
|
||||
self._obtain_metadata()
|
||||
return NodeIterator(
|
||||
self._context,
|
||||
@@ -1171,7 +1269,7 @@ class Profile:
|
||||
:rtype: NodeIterator[Profile]
|
||||
"""
|
||||
if not self._context.is_logged_in:
|
||||
raise LoginRequiredException("--login required to get a profile's followers.")
|
||||
raise LoginRequiredException("Login required to get a profile's followers.")
|
||||
self._obtain_metadata()
|
||||
return NodeIterator(
|
||||
self._context,
|
||||
@@ -1190,7 +1288,7 @@ class Profile:
|
||||
:rtype: NodeIterator[Profile]
|
||||
"""
|
||||
if not self._context.is_logged_in:
|
||||
raise LoginRequiredException("--login required to get a profile's followees.")
|
||||
raise LoginRequiredException("Login required to get a profile's followees.")
|
||||
self._obtain_metadata()
|
||||
return NodeIterator(
|
||||
self._context,
|
||||
@@ -1209,7 +1307,7 @@ class Profile:
|
||||
.. versionadded:: 4.4
|
||||
"""
|
||||
if not self._context.is_logged_in:
|
||||
raise LoginRequiredException("--login required to get a profile's similar accounts.")
|
||||
raise LoginRequiredException("Login required to get a profile's similar accounts.")
|
||||
self._obtain_metadata()
|
||||
yield from (Profile(self._context, edge["node"]) for edge in
|
||||
self._context.graphql_query("ad99dd9d3646cc3c0dda65debcd266a7",
|
||||
@@ -1288,7 +1386,7 @@ class StoryItem:
|
||||
if not self._context.iphone_support:
|
||||
raise IPhoneSupportDisabledException("iPhone support is disabled.")
|
||||
if not self._context.is_logged_in:
|
||||
raise LoginRequiredException("--login required to access iPhone media info endpoint.")
|
||||
raise LoginRequiredException("Login required to access iPhone media info endpoint.")
|
||||
if not self._iphone_struct_:
|
||||
data = self._context.get_iphone_json(
|
||||
path='api/v1/feed/reels_media/?reel_ids={}'.format(self.owner_id), params={}
|
||||
|
||||
Reference in New Issue
Block a user