Compare commits

..
23 Commits
Author SHA1 Message Date
Alexander Graf 3338260f5d Release of version 4.16 2026-10-26 22:31:03 +01:00
Alexander Graf 49b0df42b4 Merge branch 'upcoming/v4.16' 2026-10-26 22:30:31 +01:00
Alexander Graf 756f097626 Prevent user_id setter 2026-10-26 22:12:33 +01:00
Vassilis Papadimas 9e0158de54 fix get_iphone_json: send derived bearer token and correct user_id (#2771) 2026-10-26 22:06:39 +01:00
Alexander Graf d035c9b7c7 Update dependencies 2026-10-26 21:54:49 +01:00
Alexander Graf 00a6784029 Fix Profile.from_id() 2026-10-26 21:44:16 +01:00
Alexander Graf 09dea80d2b Release of version 4.15.5 2026-10-24 16:26:56 +02:00
Alexander Graf 56493a8a78 First Release Candidate for Version 4.16 2026-10-24 16:21:33 +02:00
Alexander Graf a7119ef1e3 Merge branch 'master' into upcoming/v4.16 2026-10-24 16:05:19 +02:00
André Koch-Kramer bf3f6e2bfb Remove PolarisFeedShareMenu relay provider from NodeIterator 2026-10-24 15:14:18 +02:00
Alexander Graf 5b27110476 NodeIterator: keep first_item besides first_node to spare wrapper invocations
The NodeIterator is sometimes invocated with node wrappers that are heavy to
invocate, e.g. because they trigger requests. Storing the resulting item
besides the node allows to avoid those extra requests.

In the FrozenNodeIterator, the node is still saved, rather than the item.
2026-10-24 12:03:46 +02:00
Alexander Graf 221cd7f8bc Add versionchanged directive for Profile.mediacount 2026-10-24 11:22:10 +02:00
Alexander Graf 328e93b0bc Annotate Profile.mediacount as Optional 2026-10-23 23:40:53 +02:00
Alexander Graf 653efed311 Query Post metadata through api/graphql when not logged in 2026-10-23 23:40:31 +02:00
Alexander Graf 133e5c442c Fetch clips play count only when Post.video_play_count is accessed 2026-10-23 23:40:13 +02:00
Alexander Graf 28d991f765 Fix Profile.mediacount from the logged-out profile page 2026-10-23 23:39:34 +02:00
Alexander Graf fa6baebc66 Raise LoginRequiredException in Profile.get_posts() for private profiles when not logged in 2026-10-23 23:36:53 +02:00
Alexander Graf c0e47314c2 Fix Profile.get_posts() when not logged in 2026-10-23 23:33:28 +02:00
Alexander Graf e628a8d04a Allow api/graphql queries when not logged in 2026-10-23 23:27:30 +02:00
Alexander Graf ca44660707 Fix Post.get_likes() with api/v1/media/{id}/likers endpoint 2026-10-23 22:03:54 +02:00
Alexander Graf e93795223a Add AI Policy 2026-10-23 21:16:27 +02:00
Alexander Graf 7d26ad6ab8 Minor improvements to contributing guidelines 2026-10-23 21:16:11 +02:00
Alexander Graf 5432c4cce1 Update copyright years 2026-10-21 22:56:21 +02:00
12 changed files with 317 additions and 240 deletions
+3
View File
@@ -20,5 +20,8 @@ describe:
- Further considerations - Further considerations
- Is it based on master (bugfix), upcoming/v4.X (new feature), or another pull request? - Is it based on master (bugfix), upcoming/v4.X (new feature), or another pull request?
- Does it preserve backward compatibility and existing behavior? - Does it preserve backward compatibility and existing behavior?
- If generative AI was involved, ensure it follows our AI policy.
(also see https://instaloader.github.io/contributing.html)
--> -->
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
cache: 'pipenv' cache: 'pipenv'
- name: "Install Dependencies" - name: "Install Dependencies"
run: | run: |
python -m pip install pipenv==2026.8.0 python -m pip install pipenv==2026.10.3
pipenv --python `python --version | grep -Eo '3\.[0-9]+'` sync --dev pipenv --python `python --version | grep -Eo '3\.[0-9]+'` sync --dev
- name: "Build Documentation" - name: "Build Documentation"
run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run make -C docs html SPHINXOPTS="-W -n" run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run make -C docs html SPHINXOPTS="-W -n"
+2 -1
View File
@@ -22,9 +22,10 @@ jobs:
cache: 'pipenv' cache: 'pipenv'
- name: Install Dependencies - name: Install Dependencies
run: | run: |
python -m pip install pipenv==2026.8.0 python -m pip install pipenv==2026.10.3
pipenv --python `python --version | grep -Eo '3\.[0-9]+'` sync --dev pipenv --python `python --version | grep -Eo '3\.[0-9]+'` sync --dev
- name: PyLint - name: PyLint
if: matrix.python-version != '3.10'
run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run pylint instaloader run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run pylint instaloader
- name: MyPy - name: MyPy
run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run mypy -m instaloader run: pipenv --python `python --version | grep -Eo '3\.[0-9]+'` run mypy -m instaloader
+2 -1
View File
@@ -1,6 +1,7 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2016-2020 Alexander Graf and André Koch-Kramer. Copyright (c) 2016-2026 Alexander Graf, André Koch-Kramer and the Instaloader
contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
Generated
+178 -178
View File
@@ -24,11 +24,11 @@
}, },
"certifi": { "certifi": {
"hashes": [ "hashes": [
"sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", "sha256:a9895e6e2758b371e0545e321a3a8fd1fcfd9829ada8824a936dfb16aaaf36cd",
"sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55" "sha256:e9b54de76e530bb6cf0ccd036787103acb58fd42ba930ea8bf948ddbaa2f61d1"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==2026.7.22" "version": "==2026.10.23"
}, },
"charset-normalizer": { "charset-normalizer": {
"hashes": [ "hashes": [
@@ -216,6 +216,14 @@
"markers": "python_version >= '3.9'", "markers": "python_version >= '3.9'",
"version": "==3.20" "version": "==3.20"
}, },
"jeepney": {
"hashes": [
"sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683",
"sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732"
],
"markers": "python_version >= '3.7'",
"version": "==0.9.0"
},
"lz4": { "lz4": {
"hashes": [ "hashes": [
"sha256:0846e6e78f374156ccf21c631de80967e03cc3c01c373c665789dc0c5431e7fc", "sha256:0846e6e78f374156ccf21c631de80967e03cc3c01c373c665789dc0c5431e7fc",
@@ -281,52 +289,52 @@
}, },
"pycryptodomex": { "pycryptodomex": {
"hashes": [ "hashes": [
"sha256:0428f19f13452c6b89bbaf2c530f84f369873811dfa77f0cee0da4f40fb0474f", "sha256:03e2027f81fe6b700e7ff614d79111559bfb05ed8c4a9de9d2152d1a0a0768de",
"sha256:0afd3f4cb2bd690cbf2b24e5d330e883cd5d8dcf3370aafc2aed16f676fff91e", "sha256:09081666ffc599976c0b8b29caf2cf82212c0f05bed233c8f8ed53e4f6e1d356",
"sha256:3b6276fe007464502df796710fb57dcfeb813b6ccac0eabaf85100be1ad358f2", "sha256:147c742f73bbe8791d8c454b9972330d7ea501de94e8172957af375f9bdb4a7f",
"sha256:408fb76e0af11937381532c992d9f7c0dcf987878cbe61d772b0e788343e81c5", "sha256:347c37708688414d9b3ba69e3a60df4c82f3d9f9d0fcc1d645f85252d997207b",
"sha256:42c8221e3a4ca04b070cb388fca004815f6544951bfb2cc6c7958e293b7caf64", "sha256:3bbcc1807502da4b5d66c94357a99589c8547aa9ad2f9ecfa537b2e9a347538b",
"sha256:556e063b52f338bd924fe5c40692cc1f50cb0453bd1d6580b7ffff2eade1a894", "sha256:455596da1c6a1d534051c1883761f9c0122a8126375827dd7cb7f0bf83c63a3a",
"sha256:55f22d72250d7923aac3c2442ec6a1dd8774d2efa3bdcb0f68d5e137d372268e", "sha256:48d9058dd4c8f3af83e048b6ff83d9c6300841c42579f77fdfab19e49156a512",
"sha256:582efdf767d72c12538949b876a64b9a7af30f2cff063c06dd63ece32c4e5c1d", "sha256:514d685de4b0227d35d7114047fbd575bf162bff373e07af665aa41eee089dd0",
"sha256:5be79564e05ed620a63f353ea50e0d474eb598f70cda1b28c2ca8e9e2f02c139", "sha256:569fdbeff936cbd5beb852b3969dc2f58fb255bd221b3f7a8999c035c30ec958",
"sha256:5fa46af1281be707e0ec5878aea0f77d58d8d4b465f63f7385a71ae0080bfff9", "sha256:5966f829f64c833cc72a8694cfee05ad50446a44167842c92dec4ebf6b84d72c",
"sha256:6a62122fb4c85befcc01ec3a1ae5b2ba81689481aba1d383b4e21e34f18900dd", "sha256:5b37b86a3771d6aa21cccf9f8448460e4be11e68bb8e699133ce632cb986f521",
"sha256:72742921e388856044163e711643244b6d5c7062846dc0676b1f9f0743846ab3", "sha256:6159dc74824c591b4c294f8f96b74a71c3b5e9f637dab2f2da14cfa32dc24d47",
"sha256:770db9a5b9d30d8a48486bc163870763f3fe0f983c780403cd15c332618fa51c", "sha256:637b4bbc8165921b13d2b9d706e3697a9c6077d7564bfb61a3b7005182b2a7ac",
"sha256:78bb214b8016aade97a357071d753a5c11687bce2ba693da48218c3472ece287", "sha256:6773f8c65b6e3d7524f4e18542ac2aa8856979785792ca0359d69bca27a269c6",
"sha256:793b9f5ba7e9147a6a6d8e284854e3711e12f43d4ef57a26b3415b2455a9801a", "sha256:77a48a855776101a61b932ddedbf522e1ead9e20b321bec0f64dd7434ac4fcc3",
"sha256:7ffcedf4210d8c206fe9852ccde9f78ec9ce35e8039f0a989900ffe710e7a5db", "sha256:77ef934c2b2eab3c57a431daf858fcbf272134691bcc8d729787d17ad4cbf758",
"sha256:8293a92e6f71d6de57364432ff0ba48244959a81c632574eb5586e7e67f5de59", "sha256:794f32227a480ab3b39971ac4a53dfa8ed444e28c0ddecbdc35f26d038bb46da",
"sha256:88d37b28b450c06a03bd5570467c601fe8c7dbe493ca39771716f56fdbe35d6e", "sha256:7a81e9be7084af3591912475b22c5d0646a9160f3974b6b7300138781fffdc0d",
"sha256:8a2a634c86c0a59b28b0d51bbf7e5d1feed00dc7ebdc789fe968f771f1430b47", "sha256:7f30261641dac5ae60e0af2fb11ad7c87200575b0ce403f7531576b0f38a52be",
"sha256:8d6c5e01e4fb1ddd20cc24d4d8706643dd78b5279453dabcf10ba4e0683bf9b2", "sha256:82eb0dd8a95be97f03527b108ee49f9b86a7c534fa47fa7a510be3eab8ea9acd",
"sha256:908f92fbc307b642b65580d0295698f423b057018d87ae43d441f1d1a00674a2", "sha256:8321c315c4418dfe45a811b792380438ec81185deaaf202dbcd77711b4f51796",
"sha256:96b60c0f36ab7c11a826c116633b08afeac58b9b9c066a5ad7cd373ed996363c", "sha256:8f27725ec41d6722bd4f729a403a38238d844127f39cf462991e693b9c9c2608",
"sha256:99c19dcd9846d6bf9886eb758c6d8e06c5bfd8a0c83802551f1aa56e80fe13dc", "sha256:9a732b9f5603b153dcdc071d6b342f192981907519827f2c1e4aeac41e2b34c2",
"sha256:abf768c285d289c4a0e0678f055663162c9d6257c79f5904fbd2692ade94da1c", "sha256:9b94a8c647c1f80b4c82c7ac9642dda94a71abbb4efa29ad652c410ad7a39879",
"sha256:b4f7f9c737ca1af9a79962d3289eaa6521869915ee9fbb9168e578277126691b", "sha256:9bb353c764c144a9fc03302ef3763ad0c3e75bc7ca41f445cc98455f36da6c59",
"sha256:b5d0afc29fc461d599379f814f5ba378e58c69418588034619d76747bc2b5d5d", "sha256:9dd5104a1d8ccf725a3c1b40b21d5ed47530b6499abba30955c174d895c1be2f",
"sha256:b6690bfae043c81108e28e31dc7e3afa8cb1b1eb0ba590fdbff2b66e4c6913e7", "sha256:9fd3b792942e3b0937f9e39a1c56030cc41bbea058fb5a8af652ff6fd02a553e",
"sha256:bb393480b5845ef4acbc9f713e4c473ad08be81bc3cdb5ef017cd02bdafd36e1", "sha256:a692d2484ca8fa2c69f45c63f2b30cd00b8512834e3a522fc297d981dde5b34c",
"sha256:c2f42f24d7112ebb93d3aed581d011c82c20101da7d7a6248145c530e99bd2eb", "sha256:b1c7a61b0b644e9780aa9f6eee301fb77c813b1e41262f6f4f387de0558e8926",
"sha256:c55e6c42f9eb43d90c15792bf69bc09cb3e13a5e55a30eecb555695a02b0d4c0", "sha256:b299a1c10e45e1b71b34b048d555d3a8f366240942af89be5db379f88edb9542",
"sha256:c7055400f773856cd219680d15525c6fb147237534cbe5394a5f92f57f7efaed", "sha256:b3eda6b9416ef35b232403caa2bc6e25cc87530448d505a45d859210eab74051",
"sha256:c7b8ad51baf3505dd3d94ec974855359d15769e45c58c2b89c6d4612b1f7ed94", "sha256:bd06ab1d9cf90e8b198daae3b364e9ac23a640d210bb8faddad6627cf2e33be7",
"sha256:da9996407d03684221a4e1603b70fad97d8772185e5f75addcd97fcbc4c3281f", "sha256:bf9c87112ee78e21be984c5873c17615d7b3f7305ba01f472931dc77a70ffb26",
"sha256:dc685ab0b420597fe9a3187555c14d5b4d87c1518a99646397018457dc2a70c8", "sha256:c20fb5e8cf874dc182091d6df122b8b588501d23e7b500acf662c49899afdd0f",
"sha256:e42a2e9531c40315290278d9eee93ea5031eda3c83969f5709ada51a1c7491b4", "sha256:cc64f4e1fc07a155ef31eb9815183a6a8eb13dfd6876ab049c01125223aa0c40",
"sha256:e635c7ac056f2a47d79e2bf7042fb3fc6d175f47796a954388a3cec1f2e082a4", "sha256:cdec09935db4cbd74da5b577ea7d9959c73374060b22abf7d505429c0da7ed63",
"sha256:e8a7463d346015b42a0fb9c9179ebb7cccfd86bf40199c6796b546ac7be0b85c", "sha256:e87928d8f37952ba53d215836cb3d89c63c1367f037d055c3a5b3f4403f4c6b6",
"sha256:eb14a078429434b017f1fb26a75a8fcd62c12befb6b3d37ca39353dc2b5843fd", "sha256:edc1deb28fceab6b78e12bae506eaef62ee2fc1b3cabc96f7932f0d51e368acd",
"sha256:ef2bd306328a8447d4d2af6037efddd67ef16630876ed3ec84babd16ca558941", "sha256:eeac2c9acbd2d9f0ca493fdf692ce8245ca37cbebee08c496025868d4b8ef70f",
"sha256:f6af77f15a91bb6c16607067cfd201330cf5dfefe1c715e955be1fb06d368613", "sha256:f3ed97cdde1d96894057778095238bad3dd94d3e46dc07d6d618e6d4b7700cce",
"sha256:f96e10f0edff9d4b6b253e0a78f7c819749521d3ffe6af0139e08fcd35384405", "sha256:f87d74a804ca949f46a6babe70247a789f6401f229566e90174c4b7033b87cc5",
"sha256:fab40542a863cc142a763aa92630d613925fe3e937bea3ba5fb735e0c42d4f96", "sha256:fd487cc20730dc9d294e000126be86a02079485fa6cb723d8f01eddf86e96f5a",
"sha256:ff8c1a125b2a7a2933219e9e523aabca31c920839c3c10968b56ec9a76c81d1e" "sha256:fe19e03b81aefdeaa579afc6262fc03504fce93455129d6ef0da79ddb48ca47d"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", "markers": "python_version >= '2.7' and python_version != '3.0' and python_version != '3.1' and python_version != '3.2' and python_version != '3.3' and python_version != '3.4' and python_version != '3.5' and python_version != '3.6'",
"version": "==3.24.0" "version": "==3.24.1"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
@@ -364,66 +372,66 @@
}, },
"ast-serialize": { "ast-serialize": {
"hashes": [ "hashes": [
"sha256:09cc4d3103c1fc97f6845ba307af1db9cde5226bef47f8843220dde83f2276ba", "sha256:02686b281ec9b8e9614f6f0db27e12e4cac515f336c5d05b678bc9eb73ff2af5",
"sha256:0f93a70fa9826c04ea9f2c3a880f87f4cca09a828144ed5084682abd28110980", "sha256:032b09d86c785c3984e975378b7861f2a68ac44e22ba7b07b93a536b2524eeb8",
"sha256:10b59afc108eb285146acb23d1b5ec0fc58bb3c09cb2ab8876402df06c373c3b", "sha256:0d764913374ba21df8cd618045b371ed104ce55c6276de7c59e554a4f5ba26fd",
"sha256:119d1b0cadaba4a6e475f9bbe79eecc79351e373142eabe7c353f0250d70aebb", "sha256:121e1d08d58b75c15491cc3003609cbad71ac2b0016043c36d30a6b2032b6605",
"sha256:1858887be56a64a2aea899423dfe43787c34c75c18b0d7497de8e618d54b2790", "sha256:1b158f1c9e3a0d386913924c57995d6df78224868a4d0f215bdd41587c97b34a",
"sha256:243054a05a5190f5d087b5c8b16423e7f1cefa4991bac26e9c8ace18074b75b6", "sha256:2015fd0165e463aac3b1aadc6a1fa1e5820c14ca950821f4fe02f8a3f6a388d6",
"sha256:3398e458047d21c9bc1b323fe5aab77c608dc9ddb65b2d44deebaff503a1f1eb", "sha256:23d768369b89d551077dc173da6892fad00443338417523b6732f4e8ba4a7ed5",
"sha256:3d6ed63d4fc1ec867b8cb522d58c36df0e8f05e487bea0ffd102043a37636d72", "sha256:2876444a5f87a7c76f371af3c07c2fda563a08bc8af52568df812e8aba4bf568",
"sha256:410233de149ab8414cb27c6fc73e9d2baa35d6f971672d540d752060d980ffb4", "sha256:29422d7bcd9e7f39411571c36cec45ce8a7a9b3ad4b2e7aa64497c15a9e397f6",
"sha256:45a9e6b700bbd973d49942668a5cdbeffa693f2e250b8a5409abd1fa9d351854", "sha256:2bad302ae2427454ef9dab71bd5c3ad67188860c39d36daee38f72031edb39c4",
"sha256:4655ef993e69e01bb47d2d99647de9bbb74af03938438656832cd010d95de348", "sha256:2f9a9ce8b0994f757b941f7f7fe384a6666b4162b941ebc5c248081baea18e47",
"sha256:50a9eaedf1db4857dad7cc47dd757ed70bfcc40b89d44d516c4a2f0d5033bd76", "sha256:30cf89b6a6844542d116c758725d13de5b97b9410a51ee577715f761270d0c1c",
"sha256:5285a390caf1c44368ae270f037f797b91427d138b7d43cad0f1fda4c83518d9", "sha256:3e129d44b0cffbea24a7f40117ce49e30993c3d119e584d4817ebd6f87246456",
"sha256:536d783c4d91331f094e0a892221e619be5ffbe6fb6640885f14d7f226ec90ca", "sha256:401cc84e8888d4d76f2c8d296fa006ed1a5ff993b6aff90dd777c03973efd025",
"sha256:5bbf582286c9dc6b4c544ef645dc99e4b3aa09db28892bc60344141f6926641f", "sha256:43e8ddd63e80fc280c2f790157386f27f9b9f6ccb9c4f8e803b863cbfb085769",
"sha256:5ef62601db3ce5c23445132262a193075e211fb2fc87b46b7550dd351fac0976", "sha256:48f11d96c9ec71119612f8e79a8c55c12f2b136716d68347433c561d27a25f43",
"sha256:5fc136cd08001b817ad0b3e7426f50a7d2b8982dc7c6491f0af78af4c3dd8672", "sha256:4bcf1d5f4eefecaa325a34ecf079b2741189e7d0821523f92cb343a389a67489",
"sha256:610a41351de68199de9a1434499083b4256c0df7658ec1cfc0a0a7b20b08d317", "sha256:4e385b17d5bde892a6184003a9aff3518387908b73f8f5ea539313c07c8c0ede",
"sha256:6485e681625ed7a094221f16a7ff2ef154946112266a05cf83bde50c959ef345", "sha256:4f98da562e5901d69c7dd30885594c5438f859edf354e9ac7517d188fb026c3a",
"sha256:657a7354ea16ed4d29f8127ed477c6fee3915c111d135f020ca835a991438e90", "sha256:523d45bab16ddaf1399c04c4fe81bbf180f81e33c2f7bcbe76be56b0f7311ab4",
"sha256:6add54b495e37ae3cf3a1f0d5eaba364814eb72e93026adc41b7791e4b0d45d3", "sha256:5d99910e6b9a9b9360bcca2ae99aee36cc912ce058df2a57d1c33099b6cb2593",
"sha256:6e25cd319fb0d7b39fcac666784ec86708ccbc78d07a698b1400cf5ed40c045b", "sha256:5db12d843d931b9d1f1519ce66b9cbfe72d8563b75c1204d255322254d1964dd",
"sha256:6ff65f40f49d5e1a1a043ba366081d59a4e26a9f5c1b07eb1170e172115da7ca", "sha256:638333fb8ae72c62753f9e794fda20290883ddc65b0d85e26e08f8bb227567ad",
"sha256:72e871f6995a066c1b19104f8a6b5832b1163adb9a8267c2aa4711fbb0f4d1f3", "sha256:68f8829d114dab6c9db4a8bbb8d0e342958faee1b5e15a29022678df639960f8",
"sha256:7b1ad06513022cfa1337744959255af0ef16119d2beb1e547b67f37ad9433d4a", "sha256:6eb0be64c9b8f1ed26637eef345abe75ea90351c92c85a8aec7838b3af971011",
"sha256:7d9fbe5a3e8acddfc2fddff3dbbc7ea0e9798b3df3428f851b8abc52a3806f31", "sha256:6ffb0f548a7dc126067fdfb7b2325ab5dfcdeecc29faf5e0f0461c96d6ebfa3d",
"sha256:82866f3523d53ffca8d2a69a750bec52908b69f728012e40959bebce2620453c", "sha256:732aaae9e498c973fc63fd7f308009b57c4afa6ff59d0b674d894e57d0bca01a",
"sha256:89a2bc39a820bc7785b60c53a5742b4e8dd4c1a599294e2dd68fae545883d44a", "sha256:75d7750fe98f6f35a96c43aba8d98a2e623e22c778a0c17ab9ede92eb10f5d82",
"sha256:8a32f184ce3e4b1d0b06d642a1243281cf55b99e0323680b1b8f904029fd7700", "sha256:77a6201b69ce42e919f2fd9a559138d5410b5d8afff7c3a6b1f500b0481ff7d8",
"sha256:8d6711adf11136c77e3a35517de9488a5081d1012874fae99c2876b64f4daace", "sha256:7a5c38bdb93669c596f94fb7204dd6ebe4fadb19058ac9a2faf2948c2a175f96",
"sha256:9649cd903db0dc047906c6dd740784a2ba665d54f7e43ba31457edbce76c9493", "sha256:7f2427bddfca208e84de700044fadf9aef2769a70bae740a1bdabb89ee9e1117",
"sha256:98d91cd3a6cb76a39512ee090a539d1e3206b732ad8150eb38918cffa1ddf515", "sha256:7f3e8a3eff69af640db985360fef35073fb46956dfc3e5c614cf00920d2746dd",
"sha256:99e33c93efb5254a70c525b46038212371dfe5693d48eb2d0d5f17d936a263d7", "sha256:847cad4c22953823f5e76d0b126c6f9da670cfa45b1df41b9324e22189d77611",
"sha256:9a0cbab9796e6ce841197feeba4008faa96b4cc7741129542fd81c882d7a4f01", "sha256:87427913c325adc811e81e7c9a757b061f47ef1e29114ac479aa0953a2687a98",
"sha256:9e855adfa5bb982b2e6fe09056b2d584f6dd4fce085d91a07d1155683751b6b5", "sha256:885a62ec6481ce58ab24c6357360d7a82023b62dd7e5e7ceccf59e9662a3272b",
"sha256:9eb9de7e59621acdb3e66984f374272b33d56d15a04763e2fd0604211e1c8303", "sha256:8899c038b1c15ae8b98e379057fb3ef398ceb0b21d1737a8156dd5a0923b7e51",
"sha256:a1d8267f83c613ea0a31f2518df074bd62e98a4b3a4892f6a529d74e08e02dba", "sha256:88df960e67184a71874e5b2ee014f87d1254a0dbc1f903c7bd7b1ab7733ecaef",
"sha256:a513bc6f60980d01767f7cbe39b17ce0373e722824a74ce28d6cea49ee3c8460", "sha256:8c8ebd419811c435812d242fb2a173e57dfede198c9ac9a0a584666ebc9b9331",
"sha256:a5628a12acc875fe7a167910f18d101dd101c2a7b1e6c2b6f7289ffaff25805c", "sha256:9221e09496fb942901993c1812ee87950cc0ecbb514c7c0f1f5fa6d78f701ed8",
"sha256:a5cac246474d2a703147d1605a6ac5ba0fa9e0a443cf1cf42513adf4df02686f", "sha256:929eda0c419e622ab49df3a4c45f81c57dcc10874bbad55d0470a4a03324dac3",
"sha256:a9cd24a26126088693ca054547ea0a391398a29cf1a3a2bec1009b4b6acc8b82", "sha256:9f99786e0d62082a5b121b3437f3167562133711df22128de1e9886f57f0b226",
"sha256:aa6c17a2b7f07e81fa8cfcc4aa7c832b3e57733853aebea113ab502f9b0963db", "sha256:a01b515b25a03184eff4acef25b981ae007916379682e908fc8785b342a0bf3e",
"sha256:af699e81fd7ce80b8b03945826d8ea23dd36d072f10d4613402da597ba4ee9c6", "sha256:a789cbe14d0a0e9ec9b32a456e5abc81ad8799479c3cfd88a29e3442262aebe6",
"sha256:b3cd105995942cc6163a229674a86161ba1305f646493efd59bd6723a357ee14", "sha256:a8ab2c2585b2e22e48cd4aff2136fce4eab4ced569b85f150ef9a5a5d6f34d49",
"sha256:b4282695f1d51a3c6ef76560351bad5af880eff7d755aefea325bffb9bf68c25", "sha256:aca07de3ca8e3d9f2b716c3c31f4f384fe4bd2d03e09912417cff7a3e08036f7",
"sha256:b9a2310845302f1a6bd45ae8a67d5760211103a8d66410b854bfa440d107e093", "sha256:acf4e9f055d75058ab01536da330648746f2f82b7697a85230547f2d5ebe895c",
"sha256:bac5a99a2c91dd823be9b8c44645694fccbb0750773cc5b27889a9f6f22fce89", "sha256:ad88921f95b92bc144d9ad059a0e16f8c1ae69f5239f2d3c6536fd2774f73dfe",
"sha256:c30b609e8fea426b310543126de876592236a25aa8ebd59f1e2b323dd52a4085", "sha256:bf53a3ba1cbe45399fc1e4ccd684b8f2a11cca450f89706b79122f478534c6f5",
"sha256:c42d2d65f388d1960c5796231eb9bf5a988c46228633eb489605c4549ad16c52", "sha256:c652d9f5d52d8b81eff3ff2c95c9dd0f281de45f6ad5d3fa95527550c396454a",
"sha256:c9e2a592706fd791c2271ce9c8f4e38c98d3ea0b4a86b511e09a4fe3ac44ab37", "sha256:ca9acc67f796aa2554fa3037dccc022a4b08b38b2ad153edfac9c9bb7cb1f3c6",
"sha256:cbe239bee4bd609186daf60b95b7b0f47146c7f7f55f6da83807d747d6fe753f", "sha256:cc34c670747d85d4232834665e247009413eaebe47c69eaf60a5c439aaecaed9",
"sha256:d3d516da3463071d27e64caf54d88cba25cf4ad4afcc807e0bcf67743719f03e", "sha256:d9c1a920b2aa0c1e016bed7721689717034ca0dba64ac3a3ff243b003d4e8dd8",
"sha256:e62126ac2be2d9340ac1b3ee7a0466a883ecbed634cff0929c88ca0b671483b7", "sha256:ddf7cbc9c8abf70102c4a088efe154bb86bfb825b0a10e556d2b15972cf99bd7",
"sha256:e73255c9227fd74eac8a9b55c4049e8ad7b66d1f690bf827c98a86b2e594def7", "sha256:e14c4de98f965647a74e6eaf37e20207124e45451ccd36e84a6f6ebab45e0372",
"sha256:f4ac042e95a575432c1730ca4cb9183066a2074886a599f46c1f1b0955fb8198", "sha256:eb41e1906502ac80d24ff343f45008552a3f5b5158607959fe6a07dbfc6eb037",
"sha256:f896fa38e0af38821e1ab1425c5dee89e359623e165765bdeae7d0eb6909e76d", "sha256:f8b17e2921e56621bcb46792bd3e0b82ca3b85de78d5936e7b8174ba1758b0aa",
"sha256:fafe1471e8aca6c87b4913b7b54ff97197adf702fbe28692284b929dfa62ff96" "sha256:fb8021f4dc3296de18737e3b8b34994411da725374d74a0b9eb32a738f2a23d0"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==0.12.1" "version": "==0.13.1"
}, },
"astroid": { "astroid": {
"hashes": [ "hashes": [
@@ -443,11 +451,11 @@
}, },
"certifi": { "certifi": {
"hashes": [ "hashes": [
"sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", "sha256:a9895e6e2758b371e0545e321a3a8fd1fcfd9829ada8824a936dfb16aaaf36cd",
"sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55" "sha256:e9b54de76e530bb6cf0ccd036787103acb58fd42ba930ea8bf948ddbaa2f61d1"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==2026.7.22" "version": "==2026.10.23"
}, },
"charset-normalizer": { "charset-normalizer": {
"hashes": [ "hashes": [
@@ -638,11 +646,11 @@
}, },
"docutils": { "docutils": {
"hashes": [ "hashes": [
"sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", "sha256:25d013af9bf23bc1c7b2b093dff4208166c53a94786c9e447808335ef1185fea",
"sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de" "sha256:746f5060322511280a1e50eb76846ed6bf2342984b2ac04dc42caa1a8d78799e"
], ],
"markers": "python_version >= '3.9'", "markers": "python_version >= '3.9'",
"version": "==0.22.4" "version": "==0.23"
}, },
"idna": { "idna": {
"hashes": [ "hashes": [
@@ -854,14 +862,6 @@
"markers": "python_version >= '3.9'", "markers": "python_version >= '3.9'",
"version": "==0.16.0" "version": "==0.16.0"
}, },
"macholib": {
"hashes": [
"sha256:da1a3fa8266e30f0ce7e97c6a54eefaae8edd1e5f86f3eb8b95457cae90265ea",
"sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362"
],
"markers": "sys_platform == 'darwin'",
"version": "==1.16.4"
},
"markupsafe": { "markupsafe": {
"hashes": [ "hashes": [
"sha256:007e1ffd9bf65bb6ee96df7b258fc632a4868dd5566037986c64781f35a36e98", "sha256:007e1ffd9bf65bb6ee96df7b258fc632a4868dd5566037986c64781f35a36e98",
@@ -1130,11 +1130,11 @@
}, },
"platformdirs": { "platformdirs": {
"hashes": [ "hashes": [
"sha256:63743c02414e755de4e31b8f68125c1407495b86c5a006e203c01ff8b9924250", "sha256:05831adf87d392760f4b5c3a0a95579ab5a10b18b9b9bd43680dcb47544788a0",
"sha256:78bfb9db2a8471ed7eebe3c3c932da413911042994e699b384fbb4493fa872d7" "sha256:53d3ec4280b815c1e0b02edad7f9ee8fda1ba9c0865dd9c2e6e63849ce82fc1e"
], ],
"markers": "python_version >= '3.10'", "markers": "python_version >= '3.11'",
"version": "==4.12.4" "version": "==4.13.5"
}, },
"prettytable": { "prettytable": {
"hashes": [ "hashes": [
@@ -1196,25 +1196,25 @@
"sha256:f82d0fb89f21c3ed6f482ee16f341bc59e8a31dbabb9bae9ef04e53ade4dc0d9" "sha256:f82d0fb89f21c3ed6f482ee16f341bc59e8a31dbabb9bae9ef04e53ade4dc0d9"
], ],
"index": "pypi", "index": "pypi",
"markers": "python_version < '3.16' and python_version >= '3.8'", "markers": "python_version >= '3.8' and python_version < '3.16'",
"version": "==6.22.3" "version": "==6.22.3"
}, },
"pyinstaller-hooks-contrib": { "pyinstaller-hooks-contrib": {
"hashes": [ "hashes": [
"sha256:4d825786ad7a9b7dbcc52d612748a34562fe273461bcfd88f4d549c594bbf8f9", "sha256:2748767901acb5cecd1304652b6c7add5bffcff1d71d01207fe24a5d42942a56",
"sha256:6d21d65323ea4467753afc21f52d7d0e69416ae8101dd88269bf273ad48db084" "sha256:fb3e67dd64122561724c48ad0366ac9e8c16e638b63bf7d85de03bf3fdbf06c3"
], ],
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.8'",
"version": "==2026.8" "version": "==2026.9"
}, },
"pylint": { "pylint": {
"hashes": [ "hashes": [
"sha256:235f13dc418c0041c649b42a5c35c99f2ffc6ca8b6a7574958eac5335906a68a", "sha256:9928603068edfa0d1a3c167f174b099d4b97c3db75d32d0fcdd029770b4713a9",
"sha256:e34c8636edc7d60a2eaadca4ad6c18a49e202361987a26e81b2a17fa92be28e1" "sha256:a85357cae24f33ad8d86c8f3daaa92c600ae4012b54a57299cee76000e9364cf"
], ],
"index": "pypi", "index": "pypi",
"markers": "python_full_version >= '3.10.0'", "markers": "python_full_version >= '3.10.0'",
"version": "==4.1.2" "version": "==4.1.3"
}, },
"pywin32-ctypes": { "pywin32-ctypes": {
"hashes": [ "hashes": [
@@ -1244,11 +1244,11 @@
}, },
"setuptools": { "setuptools": {
"hashes": [ "hashes": [
"sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", "sha256:3ff8b3b5492a0aa66960f4419365a86b84e013070d4313e4d4a6894f5d64d6bc",
"sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73" "sha256:ca140b45d5325a9478f880e351484f62fef15f336462e449ca0c03c873d589c2"
], ],
"markers": "python_version >= '3.10'", "markers": "python_version >= '3.10'",
"version": "==84.0.0" "version": "==84.2.1"
}, },
"snowballstemmer": { "snowballstemmer": {
"hashes": [ "hashes": [
@@ -1260,21 +1260,21 @@
}, },
"sphinx": { "sphinx": {
"hashes": [ "hashes": [
"sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", "sha256:4c3ec13b612712c0d3ce4cbdd9e3eb91e68577e07a8e31f32f21cf178453578c",
"sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978" "sha256:90470459f3cab15d9dfed146c71cf77694b0445df17b2e0e69059cb9f7fc9ee4"
], ],
"index": "pypi", "index": "pypi",
"markers": "python_version >= '3.12'", "markers": "python_version >= '3.13'",
"version": "==9.1.0" "version": "==9.2.0"
}, },
"sphinx-autodoc-typehints": { "sphinx-autodoc-typehints": {
"hashes": [ "hashes": [
"sha256:f5bac0996e76a2007fa26bc5393bb065ceb6cb4d062283fb5c69830c791c9716", "sha256:b0ad1f96503d69b63c2a1c043f12e2a727c9549e6503d4adbf2125dbf5ff50ed",
"sha256:f5db988060f68a231dbc230df97d81421e54a0860778a63216b9f71958a9afdd" "sha256:de41ed4001f4818bb6d37c22fd10ee44a93e6c8e945c6c7e3984e28a9df87d1c"
], ],
"index": "pypi", "index": "pypi",
"markers": "python_version >= '3.12'", "markers": "python_version >= '3.12'",
"version": "==3.13.9" "version": "==3.17.2"
}, },
"sphinxcontrib-applehelp": { "sphinxcontrib-applehelp": {
"hashes": [ "hashes": [
@@ -1311,11 +1311,11 @@
}, },
"sphinxcontrib-jsmath": { "sphinxcontrib-jsmath": {
"hashes": [ "hashes": [
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", "sha256:15ca92fe0eec82f0baf91a7efcc51ffacdb605924be6327683a52ebdacb6744c",
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8" "sha256:551f780cf9687f9c3c74f0cf9f18aca06e0d6958853453f3803fe4d357c5b924"
], ],
"markers": "python_version >= '3.5'", "markers": "python_version >= '3.9'",
"version": "==1.0.1" "version": "==2.0.0"
}, },
"sphinxcontrib-qthelp": { "sphinxcontrib-qthelp": {
"hashes": [ "hashes": [
@@ -1407,11 +1407,11 @@
}, },
"tomlkit": { "tomlkit": {
"hashes": [ "hashes": [
"sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304", "sha256:05562412163a3037b94497385fb7f9204c637bc605af7a48199e41cf2272f437",
"sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97" "sha256:83e32755ba1d20d9340bd069ee97e9f6232ba2346b63ffcba425de01f7e327a6"
], ],
"markers": "python_version >= '3.9'", "markers": "python_version >= '3.9'",
"version": "==0.15.1" "version": "==0.15.2"
}, },
"types-requests": { "types-requests": {
"hashes": [ "hashes": [
@@ -1440,38 +1440,38 @@
}, },
"wcwidth": { "wcwidth": {
"hashes": [ "hashes": [
"sha256:0a47e03d8293590ecce66c45dc20ff7b4b885e3c78093722239585eca0d77ab2", "sha256:093469e01d881bff3db93ff3f56c364dc87c441ea01f9f41c22de0c0bdf5c6ef",
"sha256:0cd4f7f2e53905dcb110d213a4c8529b6733fa3d232d8c717f946cc69a10349b", "sha256:12786620375dc83fabf4b2b2aa52416fef2bc6094522ea29c8a53c75b0dfe9b6",
"sha256:138e1f8898e431b2f2d7881f8ca8d75591c1d3c21aa53f54e989bd6b39811da2", "sha256:14410eb73178cc492aa9bf22f7c12e3bf930e5470b7220cf6f530efbfa2e03f3",
"sha256:196b47cf32f9df27ccda6dc513237f3c2429c4c659db428d60a5bc443d10f270", "sha256:17619066f95c10f44d7ae08ee456602456bc66cc9d5e91966ec73d799acebc5f",
"sha256:1bf361c8705576760623b4724ae564666d73b016f9a778bcfd1c7345378ef4ec", "sha256:29c0685f5f5f8815b88308fdcbe2cd12a92996a21a5b1e18352d165174ef7a6d",
"sha256:2a9746de704242bd4fdaabb31dd46b82f694a56a8d21081ad89b679a89da9fec", "sha256:2b58fc8cf2de1bf9cdb68b8beaa5be198742414a7c8cd97aa51ec039783ffa49",
"sha256:33df042f96c61ed3cd5fb3742fba427553a635bc578799857a48aa79f774a0b9", "sha256:2cca7b5d0b9d0ae49068d3ce806e965326a987f555b62eaa322223396e10d01b",
"sha256:42dbcb76ce8af39e2c9db410ac3f9bdf4e47eb41d6f44525952f172d3d98f724", "sha256:4831662c396dd4c59a016ec53c0ac96738006d653a00482777d9a2e2ea323d62",
"sha256:48719a9bc76c2f84238693fe5013571fa5beffa3621cf228f1f3a9e30dae84b8", "sha256:79e09e9824c88f5e275ca99e921685567a5c5c1bb6165c16e27ddd1adab33a95",
"sha256:5175609bf8cc7398a5f48aa35207bd64ebf9f45e4c70df65f7fdc7a988041a3c", "sha256:87e4101789415a6cf55ec4c53eceeb4782c2654c88692941cb588f19dca1959f",
"sha256:59dab4049cbd982b478bca098528df2c79a9160636a3a163ffebffcbd7d1b892", "sha256:8f892713ab5795d3abae181e9c02db0eb24de4084f9ee080535369d3d98d7bb5",
"sha256:674b518af28d38ee645ff97b74f5760abee5fad4bac74413bfc4b881ef2ce724", "sha256:901eace6d2dd25ebb917d0ce5db18e5c537e1504c905cf32f0d006c12098b05d",
"sha256:67d901a4ad99249eb775b4ee4769ca97fa405d35a75f46e83166910a47003f04", "sha256:9c4b0dd04cca520792100f6110d10be262c94b285a281d1759c551f159214633",
"sha256:734aa9405b321d1042301aa19c943c4731ee9e3460e4f8feea3299c064c97a14", "sha256:9e5090d253f5df99659cddfd3ed90dd21f6ff51989b0421331a63fb2681cbe27",
"sha256:751bef0ab404b6a1dc028b56b4b85d46486be1c55833f80da533e42dc691f389", "sha256:a1526cd25c6420e5153bb7d764c7509e692215ee3ad9b725db1ad665d47eae09",
"sha256:7ef5a940bd5e30bac6e721f1a48fce0cd7bb3ece19e9c5d139e72c76c35cfd07", "sha256:a8c9ce14c6b4a499e61de6de7e9e456de11be8bc991554e2ab2ed4feb40b9664",
"sha256:89ca642c5bf0101157a09366be69fad0379db1f700ae39a920e103234573670e", "sha256:ab1e5190fb82476e75fbf963e9d59b4fb1c1e1f4ced82069a0c989016aa959f7",
"sha256:8b4e381590b9b7390e07e22b2c0c1bb96ce50e1d2243c866d9387600362d51ed", "sha256:b185c8b927b23af2721be1783499d1cfa5c028b098b0858951b4ae224477f1c2",
"sha256:97b878d1e158da5ed9ac5aac53fa3a55e282103af6a09ec353865613d1a31a76", "sha256:b52f265a694dda6b3ad9050c8bd89158ad519109ef7c5eb110b906cc49f25763",
"sha256:9e542f1f8475b78452a295495d7a5bc3ead565112e9446a64dc93462a41c2a79", "sha256:b7969c9aa258d536df54c8ac8af5cad0c74a6268c9972e1fb60a5f29feffb66d",
"sha256:ae0800c5339423cc53d33a266ad264b42ba8aaa16d4464f6e6b1bee607f50b17", "sha256:befbf46467100e43fd8c8cc0c031a0105f13f5418c3be95fdcf0c47214487967",
"sha256:ae0ef90b90f6af38b54f1fe6d58662ec33b3cb4b8391958a62416d654231727b", "sha256:c36954849aafae1092d6f9de9548c5280c1d6841637a27ca288b2241c802c4ab",
"sha256:b9c6ab615e03723b7f8760ea2f27758d656e7e13b51515c9dca5c3e8b04612fa", "sha256:d68f09093c9bec9226a52edc47487042ed4f64c5df91d06f00419495071c0279",
"sha256:bb08ceb501d6aaf94066c3ee122dd825b152df40ff0bd0df4dc27126233b948e", "sha256:d76b7c715c5ca0103b28915f699f1020ae21f06f1d22cdd85a7a97f7b1536eed",
"sha256:c3d80f39ba4653a595edae9aa46a509d14883790a8fc23c5db221ceb207f64b7", "sha256:d8905f1390d828d745fdd1904374c90386d902d3cfa9ee88e9875099f75a4e17",
"sha256:e5f669ae8c3d969c72032f9cdee019674b666e522d45e1e2099a2e9dda4a341d", "sha256:e05458491759aa42b7b85f36345b3e6df082d6d2d9bf616299668fd598e0209d",
"sha256:eda88ffdc97c0fbf193d407114f2c7a54b379f67f6e52a7531ee3b9fe749eca7", "sha256:f279f32c8e206a9092fa350e53ca0c2bd8072e45b1c9fd31bb6d476b59b591c4",
"sha256:ee1fd0db9d9fd711a70f3e7765e0e04c05d26982fa05361456163062549d7da4", "sha256:f28d77153303450ee13ebbe888297e75cf532c754555b6cabafc9bdda16c421c",
"sha256:f2f7b3bba5a5d5f31fc350fd36ce5b84b693c83b7eb95ee630b720da5a5ce06f" "sha256:fd8d57d3cdfe570e3265c8fab1885c24e34ef40889c0759c0c304a19e9835954"
], ],
"markers": "python_version >= '3.9'", "markers": "python_version >= '3.9'",
"version": "==0.9.2" "version": "==0.9.4"
} }
} }
} }
+1 -1
View File
@@ -53,7 +53,7 @@ bundled_packages = ' '.join(list(locked_packages) + ['psutil', 'pyinstaller'])
os.makedirs('dist/licenses', exist_ok=True) os.makedirs('dist/licenses', exist_ok=True)
# install dependencies, invoke PyInstaller and collect licenses # install dependencies, invoke PyInstaller and collect licenses
commands = ["pip install pipenv==2026.8.0", commands = ["pip install pipenv==2026.10.3",
f"pipenv --python {sys.version_info.major}.{sys.version_info.minor} sync --dev", f"pipenv --python {sys.version_info.major}.{sys.version_info.minor} sync --dev",
"pipenv run pyinstaller --log-level=DEBUG instaloader.spec", "pipenv run pyinstaller --log-level=DEBUG instaloader.spec",
f"pipenv run pip-licenses --packages {bundled_packages} --with-license-file " f"pipenv run pip-licenses --packages {bundled_packages} --with-license-file "
+1 -1
View File
@@ -15,7 +15,7 @@ sys.path.insert(0, os.path.abspath('..'))
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'Instaloader' project = 'Instaloader'
copyright = '2023, Alexander Graf and Andre Koch-Kramer' copyright = '2016-2026, Alexander Graf, Andre Koch-Kramer and the Instaloader contributors'
author = 'Alexander Graf and Andre Koch-Kramer' author = 'Alexander Graf and Andre Koch-Kramer'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
+14 -1
View File
@@ -80,7 +80,7 @@ Improvements of the Instaloader source or its documentation can be proposed as a
review, validate, or selectively merge them. review, validate, or selectively merge them.
- Be mindful of backward compatibility: changes should preserve existing intended - Be mindful of backward compatibility: changes should preserve existing intended
behavior whenever possible. behavior whenever feasible.
- All Pull Requests are analyzed by `Pylint <https://www.pylint.org/>`__ for - All Pull Requests are analyzed by `Pylint <https://www.pylint.org/>`__ for
error and syntax checking of the source and error and syntax checking of the source and
@@ -100,6 +100,19 @@ Improvements of the Instaloader source or its documentation can be proposed as a
agrees that the change might be an improvement, or if you want to discuss agrees that the change might be an improvement, or if you want to discuss
your basic proposal, before working on a pull request. your basic proposal, before working on a pull request.
AI Policy
---------
- If generative AI was used to create notable portions of a contribution, the
author must disclose which parts were AI-assisted (e.g. research, testing,
coding, or documentation).
- The author must carefully review and understand the contribution before
submitting it.
- If the maintainers need more effort to review the contribution than it took
to create it, please do not submit it.
Proposing Features Proposing Features
------------------ ------------------
+1 -1
View File
@@ -1,7 +1,7 @@
"""Download pictures (or videos) along with their captions and other metadata from Instagram.""" """Download pictures (or videos) along with their captions and other metadata from Instagram."""
__version__ = '4.16b1' __version__ = '4.16'
try: try:
+43 -10
View File
@@ -9,6 +9,7 @@ import textwrap
import time import time
import urllib.parse import urllib.parse
import uuid import uuid
from base64 import b64encode
from contextlib import contextmanager, suppress from contextlib import contextmanager, suppress
from datetime import datetime, timedelta from datetime import datetime, timedelta
from functools import partial from functools import partial
@@ -102,7 +103,6 @@ class InstaloaderContext:
self.request_timeout = request_timeout self.request_timeout = request_timeout
self._session = self.get_anonymous_session() self._session = self.get_anonymous_session()
self.username: Optional[str] = None self.username: Optional[str] = None
self.user_id = None
self.sleep = sleep self.sleep = sleep
self.quiet = quiet self.quiet = quiet
self.max_connection_attempts = max_connection_attempts self.max_connection_attempts = max_connection_attempts
@@ -132,11 +132,9 @@ class InstaloaderContext:
def anonymous_copy(self): def anonymous_copy(self):
session = self._session session = self._session
username = self.username username = self.username
user_id = self.user_id
iphone_headers = self.iphone_headers iphone_headers = self.iphone_headers
self._session = self.get_anonymous_session() self._session = self.get_anonymous_session()
self.username = None self.username = None
self.user_id = None
self.iphone_headers = default_iphone_headers() self.iphone_headers = default_iphone_headers()
try: try:
yield self yield self
@@ -144,9 +142,22 @@ class InstaloaderContext:
self._session.close() self._session.close()
self.username = username self.username = username
self._session = session self._session = session
self.user_id = user_id
self.iphone_headers = iphone_headers self.iphone_headers = iphone_headers
@property
def user_id(self) -> Optional[str]:
"""ID of the user this session is logged in as.
Derived from the ``ds_user_id`` cookie of the current session, so it is set for every
logged-in session, and ``None`` if not logged in."""
return self._session.cookies.get_dict().get('ds_user_id') or None
@user_id.setter
def user_id(self, user_id: Optional[str]) -> None:
# The setter is only provided for backwards-compatibility with pre-4.16.
if user_id != self.user_id:
raise AttributeError("user_id cannot be set to a different value.")
@property @property
def is_logged_in(self) -> bool: def is_logged_in(self) -> bool:
"""True, if this Instaloader instance is logged in.""" """True, if this Instaloader instance is logged in."""
@@ -354,7 +365,6 @@ class InstaloaderContext:
session.headers.update({'X-CSRFToken': login.cookies['csrftoken']}) session.headers.update({'X-CSRFToken': login.cookies['csrftoken']})
self._session = session self._session = session
self.username = user self.username = user
self.user_id = resp_json['userId']
def two_factor_login(self, two_factor_code): def two_factor_login(self, two_factor_code):
"""Second step of login if 2FA is enabled. """Second step of login if 2FA is enabled.
@@ -605,7 +615,7 @@ class InstaloaderContext:
return token_from_cookies() or '' return token_from_cookies() or ''
def _web_api_tokens(self) -> Dict[str, str]: def _web_api_tokens(self) -> Dict[str, str]:
"""The tokens that the web app sends along with its api/graphql queries, from the logged-in start page.""" """The tokens that the web app sends along with its api/graphql queries, from the start page."""
sessionid = self._session.cookies.get_dict().get("sessionid", "") sessionid = self._session.cookies.get_dict().get("sessionid", "")
if sessionid not in self._web_api_tokens_cache: if sessionid not in self._web_api_tokens_cache:
html = self._get_page(self._session, "") html = self._get_page(self._session, "")
@@ -616,11 +626,13 @@ class InstaloaderContext:
("av", r'"IG_USER_EIMU":"?(\d+)'), ("av", r'"IG_USER_EIMU":"?(\d+)'),
]: ]:
match = re.search(pattern, html) match = re.search(pattern, html)
if match is None: if match is not None:
tokens[name] = match.group(1)
# Logged out, the start page has no fb_dtsg, and the web app sends its queries without.
elif name != "fb_dtsg" or self.is_logged_in:
raise BadResponseException( raise BadResponseException(
"The start page did not contain the token {}.".format(name) "The start page did not contain the token {}.".format(name)
) )
tokens[name] = match.group(1)
self._web_api_tokens_cache[sessionid] = tokens self._web_api_tokens_cache[sessionid] = tokens
return self._web_api_tokens_cache[sessionid] return self._web_api_tokens_cache[sessionid]
@@ -658,7 +670,7 @@ class InstaloaderContext:
:param variables: Variables for the Query. :param variables: Variables for the Query.
:param referer: HTTP Referer, or None. :param referer: HTTP Referer, or None.
:param web_api: Query the web app's ``api/graphql`` endpoint, as the web app does, rather than :param web_api: Query the web app's ``api/graphql`` endpoint, as the web app does, rather than
``graphql/query``. Requires login. ``graphql/query``.
:return: The server's response dictionary. :return: The server's response dictionary.
.. versionchanged:: 4.16 .. versionchanged:: 4.16
@@ -726,12 +738,33 @@ class InstaloaderContext:
.. versionadded:: 4.2.1""" .. versionadded:: 4.2.1"""
with copy_session(self._session, self.request_timeout) as tempsession: with copy_session(self._session, self.request_timeout) as tempsession:
# Set headers to simulate an API request from iPad # Set headers to simulate an API request from iPad
tempsession.headers['ig-intended-user-id'] = str(self.user_id) # ig-intended-user-id should name the logged-in user; self.user_id is derived
# from the ds_user_id cookie, so it is set for any logged-in session. Without a
# logged-in user, omit the header rather than sending the literal string "None".
if self.user_id is not None:
tempsession.headers['ig-intended-user-id'] = self.user_id
tempsession.headers['x-pigeon-rawclienttime'] = '{:.6f}'.format(time.time()) tempsession.headers['x-pigeon-rawclienttime'] = '{:.6f}'.format(time.time())
# Add headers obtained from previous iPad request # Add headers obtained from previous iPad request
tempsession.headers.update(self.iphone_headers) tempsession.headers.update(self.iphone_headers)
# The mobile API requires an Authorization bearer token for most
# endpoints (reels_media, users/info, ...); without it they answer
# with empty data or a "fail" status. A token is handed out via
# the ig-set-authorization response header, but only by the few
# endpoints that still accept cookie-only requests (e.g.
# media/<id>/info/), so relying on having seen one makes success
# depend on request order. The token is deterministically derived
# from the session cookies, so construct it if none is known yet.
if 'authorization' not in tempsession.headers and self.user_id is not None:
sessionid = tempsession.cookies.get_dict().get('sessionid')
if sessionid:
token = b64encode(json.dumps(
{'ds_user_id': self.user_id,
'sessionid': sessionid},
separators=(',', ':')).encode()).decode()
tempsession.headers['authorization'] = 'Bearer IGT:2:{}'.format(token)
# Extract key information from cookies if we haven't got it already from a previous request # Extract key information from cookies if we haven't got it already from a previous request
header_cookies_mapping = {'x-mid': 'mid', header_cookies_mapping = {'x-mid': 'mid',
'ig-u-ds-user-id': 'ds_user_id', 'ig-u-ds-user-id': 'ds_user_id',
+14 -5
View File
@@ -104,6 +104,7 @@ class NodeIterator(Iterator[T]):
else: else:
self._data = self._query() self._data = self._query()
self._first_node: Optional[Dict] = None self._first_node: Optional[Dict] = None
self._first_item: Optional[T] = None
self._is_first = is_first self._is_first = is_first
def _query(self, after: Optional[str] = None) -> Dict: def _query(self, after: Optional[str] = None) -> Dict:
@@ -114,11 +115,11 @@ class NodeIterator(Iterator[T]):
return self._query_query_hash(self._query_hash, after) return self._query_query_hash(self._query_hash, after)
def _query_doc_id(self, doc_id: str, after: Optional[str] = None) -> Dict: def _query_doc_id(self, doc_id: str, after: Optional[str] = None) -> Dict:
pagination_variables: Dict[str, Any] = {'__relay_internal__pv__PolarisFeedShareMenurelayprovider': False} pagination_variables: Dict[str, Any] = {}
if after is not None: if after is not None:
pagination_variables['after'] = after pagination_variables['after'] = after
pagination_variables['before'] = None pagination_variables['before'] = None
pagination_variables['first'] = 12 pagination_variables['first'] = NodeIterator._graphql_page_length
pagination_variables['last'] = None pagination_variables['last'] = None
data = self._edge_extractor( data = self._edge_extractor(
self._context.doc_id_graphql_query( self._context.doc_id_graphql_query(
@@ -156,10 +157,10 @@ class NodeIterator(Iterator[T]):
item = self._node_wrapper(node) item = self._node_wrapper(node)
if self._is_first is not None: if self._is_first is not None:
if self._is_first(item, self.first_item): if self._is_first(item, self.first_item):
self._first_node = node self._first_node, self._first_item = node, item
else: else:
if self._first_node is None: if self._first_node is None:
self._first_node = node self._first_node, self._first_item = node, item
return item return item
if self._data.get('page_info', {}).get('has_next_page'): if self._data.get('page_info', {}).get('has_next_page'):
query_response = self._query(self._data['page_info']['end_cursor']) query_response = self._query(self._data['page_info']['end_cursor'])
@@ -206,7 +207,15 @@ class NodeIterator(Iterator[T]):
.. versionchanged:: 4.9.2 .. versionchanged:: 4.9.2
What is considered the first item can be overridden. What is considered the first item can be overridden.
""" """
return self._node_wrapper(self._first_node) if self._first_node is not None else None if self._first_item is None:
# Restore _first_item from _first_node. Needed after thawing, since
# FrozenNodeIterator stores the node, not the item.
self._first_item = (
self._node_wrapper(self._first_node)
if self._first_node is not None
else None
)
return self._first_item
@staticmethod @staticmethod
def page_length() -> int: def page_length() -> int:
+57 -40
View File
@@ -279,7 +279,7 @@ class Post:
return None return None
@staticmethod @staticmethod
def _normalize_post_data(media: Dict[str, Any], context: 'InstaloaderContext') -> Dict[str, Any]: def _normalize_post_data(media: Dict[str, Any]) -> Dict[str, Any]:
"""Normalize a Polaris media item to a legacy-compatible node, preserving the source structure.""" """Normalize a Polaris media item to a legacy-compatible node, preserving the source structure."""
media_types = {1: "GraphImage", 2: "GraphVideo", 8: "GraphSidecar"} media_types = {1: "GraphImage", 2: "GraphVideo", 8: "GraphSidecar"}
media_type = media.get("media_type") media_type = media.get("media_type")
@@ -306,10 +306,6 @@ class Post:
pic_json["video_duration"] = media.get("video_duration") pic_json["video_duration"] = media.get("video_duration")
pic_json["video_view_count"] = media.get("view_count") pic_json["video_view_count"] = media.get("view_count")
pic_json["video_play_count"] = media.get("play_count") pic_json["video_play_count"] = media.get("play_count")
if media_type == 2 and pic_json["video_view_count"] is None:
pic_json["video_play_count"] = Post._fetch_play_count_from_clips(
context, media["user"]["pk"], media["code"]
)
caption = media.get("caption") caption = media.get("caption")
caption_text = caption.get("text") if isinstance(caption, dict) else None caption_text = caption.get("text") if isinstance(caption, dict) else None
pic_json["edge_media_to_caption"] = ( pic_json["edge_media_to_caption"] = (
@@ -419,12 +415,14 @@ class Post:
"shortcode": self.shortcode, "shortcode": self.shortcode,
"__relay_internal__pv__PolarisAIGMMediaWebLabelEnabledrelayprovider": False, "__relay_internal__pv__PolarisAIGMMediaWebLabelEnabledrelayprovider": False,
}, },
# As the logged-out web app does.
web_api=not self._context.is_logged_in,
) )
web_info = (resp.get("data") or {}).get("xdt_api__v1__media__shortcode__web_info") or {} web_info = (resp.get("data") or {}).get("xdt_api__v1__media__shortcode__web_info") or {}
items = web_info.get("items") items = web_info.get("items")
if not items: if not items:
raise BadResponseException("Fetching Post metadata failed.") raise BadResponseException("Fetching Post metadata failed.")
self._full_metadata_dict = Post._normalize_post_data(items[0], self._context) self._full_metadata_dict = Post._normalize_post_data(items[0])
if self.shortcode != self._full_metadata_dict['shortcode']: if self.shortcode != self._full_metadata_dict['shortcode']:
self._node.update(self._full_metadata_dict) self._node.update(self._full_metadata_dict)
raise PostChangedException raise PostChangedException
@@ -697,7 +695,11 @@ class Post:
.. versionadded:: 4.14.3""" .. versionadded:: 4.14.3"""
if self.is_video: if self.is_video:
return self._field('video_play_count') play_count = self._field('video_play_count')
# Not logged in, the clips connection is null.
if play_count is None and self.video_view_count is None and self._context.is_logged_in:
play_count = Post._fetch_play_count_from_clips(self._context, str(self.owner_id), self.shortcode)
return play_count
return None return None
@property @property
@@ -879,19 +881,9 @@ class Post:
if self.likes == 0: if self.likes == 0:
# Avoid doing additional requests if there are no comments # Avoid doing additional requests if there are no comments
return return
likes_edges = self._field('edge_media_preview_like', 'edges') data = self._context.get_json(f"api/v1/media/{self.mediaid}/likers/", {})
if self.likes == len(likes_edges): for user in data["users"]:
# If the Post's metadata already contains all likes, don't do GraphQL requests to obtain them yield Profile(self._context, user)
yield from (Profile(self._context, like['node']) for like in likes_edges)
return
yield from NodeIterator(
self._context,
'1cb6ec562846122743b61e492c85999f',
lambda d: d['data']['shortcode_media']['edge_liked_by'],
lambda n: Profile(self._context, n),
{'shortcode': self.shortcode},
'https://www.instagram.com/p/{0}/'.format(self.shortcode),
)
@property @property
def is_sponsored(self) -> bool: def is_sponsored(self) -> bool:
@@ -1035,16 +1027,24 @@ class Profile:
""" """
if profile_id in context.profile_id_cache: if profile_id in context.profile_id_cache:
return context.profile_id_cache[profile_id] return context.profile_id_cache[profile_id]
# Resolve the current username from the user id, then load the full profile.
# The GraphQL user query previously used here started responding with HTTP 400.
try: try:
user = context.get_json( user = context.doc_id_graphql_query(
"api/v1/users/{0}/info/".format(profile_id), params={} '27937681195819736',
).get('user') {
"id": str(profile_id),
"render_surface": "PROFILE",
"__relay_internal__pv__PolarisCannesGuardianExperienceEnabledrelayprovider": True,
"__relay_internal__pv__PolarisCASB976ProfileEnabledrelayprovider": False,
"__relay_internal__pv__PolarisRepostsConsumptionEnabledrelayprovider": False,
"__relay_internal__pv__PolarisWebSchoolsEnabledrelayprovider": False,
"enable_integrity_filters": True,
},
web_api=True,
).get('data', {}).get('user')
except QueryReturnedNotFoundException: except QueryReturnedNotFoundException:
user = None user = None
if user and user.get('username'): if user and user.get('username'):
profile = cls.from_username(context, user['username']) profile = cls(context, cls._normalize_profile_data(user))
else: else:
raise ProfileNotExistsException("No profile found, the user may have blocked you (ID: " + raise ProfileNotExistsException("No profile found, the user may have blocked you (ID: " +
str(profile_id) + ").") str(profile_id) + ").")
@@ -1147,6 +1147,9 @@ class Profile:
normalized['id'] = normalized['pk'] normalized['id'] = normalized['pk']
if 'edge_owner_to_timeline_media' not in normalized and 'media_count' in normalized: if 'edge_owner_to_timeline_media' not in normalized and 'media_count' in normalized:
normalized['edge_owner_to_timeline_media'] = {'count': normalized['media_count']} normalized['edge_owner_to_timeline_media'] = {'count': normalized['media_count']}
# The logged-out profile page has all_media_count instead.
if 'edge_owner_to_timeline_media' not in normalized and 'all_media_count' in normalized:
normalized['edge_owner_to_timeline_media'] = {'count': normalized['all_media_count']}
if 'edge_felix_video_timeline' not in normalized: if 'edge_felix_video_timeline' not in normalized:
normalized['edge_felix_video_timeline'] = {'count': 0} normalized['edge_felix_video_timeline'] = {'count': 0}
if 'edge_followed_by' not in normalized and 'follower_count' in normalized: if 'edge_followed_by' not in normalized and 'follower_count' in normalized:
@@ -1232,7 +1235,11 @@ class Profile:
return self._metadata('followed_by_viewer') return self._metadata('followed_by_viewer')
@property @property
def mediacount(self) -> int: def mediacount(self) -> Optional[int]:
"""Number of posts, or None if unavailable (e.g. when not logged in).
.. versionchanged:: 4.16
Returns None if unavailable (e.g. when not logged in)."""
return self._metadata('edge_owner_to_timeline_media', 'count') return self._metadata('edge_owner_to_timeline_media', 'count')
@property @property
@@ -1385,28 +1392,38 @@ class Profile:
"""Retrieve all posts from a profile. """Retrieve all posts from a profile.
:rtype: NodeIterator[Post] :rtype: NodeIterator[Post]
:raises PrivateProfileNotFollowedException: When logged in and the profile is private and not followed.""" :raises PrivateProfileNotFollowedException: When logged in and the profile is private and not followed.
self._obtain_metadata() :raises LoginRequiredException: When not logged in and the profile is private."""
if not self._context.is_logged_in: if not self._context.is_logged_in:
# As the logged-out web app, take the first posts from the profile page, and query further ones
# (PolarisLoggedOutDesktopWWWProfilePostsTabContentQuery_connection) by the page's "id" of the
# profile, which is not its user ID.
page = self._context.get_page_data(f"{self.username}/")
# The page shows no posts of a private profile, rather than an error.
if any((data.get("xig_user_by_username") or {}).get("is_private") for data in page):
raise LoginRequiredException(f"Login required to access the posts of private profile {self.username}.")
user = next((data["xig_user_by_username"] for data in page
if "polaris_ordered_timeline_connection" in (data.get("xig_user_by_username") or {})),
None)
if user is None:
raise BadResponseException(f"The page of profile {self.username} did not contain its posts.")
return NodeIterator( return NodeIterator(
context=self._context, context=self._context,
edge_extractor=lambda d: d["data"]["user"]["edge_owner_to_timeline_media"], edge_extractor=lambda d: d["data"]["node"]["polaris_ordered_timeline_connection"],
node_wrapper=lambda n: Post(self._context, n, self), # The nodes lack most of the post's metadata, as in get_reels().
node_wrapper=lambda n: Post.from_shortcode(self._context, n["code"]),
query_variables={ query_variables={
"data": { "id": user["id"],
"count": 12, "__relay_internal__pv__PolarisSEOIsCrawlerrelayprovider": False,
"include_relationship_info": True,
"latest_besties_reel_media": True,
"latest_reel_media": True,
},
"id": self.userid,
}, },
query_referer="https://www.instagram.com/{0}/".format(self.username), query_referer="https://www.instagram.com/{0}/".format(self.username),
is_first=Profile._make_is_newest_checker(), is_first=Profile._make_is_newest_checker(),
doc_id="7950326061742207", doc_id="28904005159232689",
query_hash=None, query_hash=None,
first_data=self._metadata("edge_owner_to_timeline_media"), first_data=user["polaris_ordered_timeline_connection"],
web_api=True,
) )
self._obtain_metadata()
# Instagram answers with an empty timeline rather than an error in that case. Unlike followed_by_viewer, # Instagram answers with an empty timeline rather than an error in that case. Unlike followed_by_viewer,
# which defaults to False, friendship_status tells whether the follow state is actually known. # which defaults to False, friendship_status tells whether the follow state is actually known.
if (self.is_private and self.username != (self._context.username or '').lower() and if (self.is_private and self.username != (self._context.username or '').lower() and