From 5432c4cce19de74f48417d9afbceb7b187020be5 Mon Sep 17 00:00:00 2001 From: Alexander Graf <17130992+aandergr@users.noreply.github.com> Date: Wed, 21 Oct 2026 20:47:50 +0000 Subject: [PATCH 1/5] Update copyright years --- LICENSE | 3 ++- docs/conf.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index e620fd1..15a4a78 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ 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 of this software and associated documentation files (the "Software"), to deal diff --git a/docs/conf.py b/docs/conf.py index c9d2b2e..18de48a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,7 +15,7 @@ sys.path.insert(0, os.path.abspath('..')) # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 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' # -- General configuration --------------------------------------------------- From 7d26ad6ab80f8161be2552d321360c63bab2d46c Mon Sep 17 00:00:00 2001 From: Alexander Graf <17130992+aandergr@users.noreply.github.com> Date: Fri, 23 Oct 2026 21:16:11 +0200 Subject: [PATCH 2/5] Minor improvements to contributing guidelines --- .github/pull_request_template.md | 2 ++ docs/contributing.rst | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 265bea2..e258326 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -21,4 +21,6 @@ describe: - Is it based on master (bugfix), upcoming/v4.X (new feature), or another pull request? - Does it preserve backward compatibility and existing behavior? +(also see https://instaloader.github.io/contributing.html) + --> diff --git a/docs/contributing.rst b/docs/contributing.rst index 0588c5f..5cf1817 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -80,7 +80,7 @@ Improvements of the Instaloader source or its documentation can be proposed as a review, validate, or selectively merge them. - Be mindful of backward compatibility: changes should preserve existing intended - behavior whenever possible. + behavior whenever feasible. - All Pull Requests are analyzed by `Pylint `__ for error and syntax checking of the source and From e93795223a25ceb042a2b906cc6b3d3d8e41edfc Mon Sep 17 00:00:00 2001 From: Alexander Graf <17130992+aandergr@users.noreply.github.com> Date: Fri, 23 Oct 2026 21:16:27 +0200 Subject: [PATCH 3/5] Add AI Policy --- .github/pull_request_template.md | 1 + docs/contributing.rst | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e258326..86d9e49 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -20,6 +20,7 @@ describe: - Further considerations - Is it based on master (bugfix), upcoming/v4.X (new feature), or another pull request? - 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) diff --git a/docs/contributing.rst b/docs/contributing.rst index 5cf1817..3894f7d 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -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 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 ------------------ From ca44660707bf1d4b68f2161a3c28060cf90fa989 Mon Sep 17 00:00:00 2001 From: Alexander Graf <17130992+aandergr@users.noreply.github.com> Date: Fri, 23 Oct 2026 22:00:38 +0200 Subject: [PATCH 4/5] Fix Post.get_likes() with api/v1/media/{id}/likers endpoint --- instaloader/structures.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/instaloader/structures.py b/instaloader/structures.py index bf6b1db..d3ceb9d 100644 --- a/instaloader/structures.py +++ b/instaloader/structures.py @@ -879,19 +879,9 @@ class Post: if self.likes == 0: # Avoid doing additional requests if there are no comments return - likes_edges = self._field('edge_media_preview_like', 'edges') - if self.likes == len(likes_edges): - # If the Post's metadata already contains all likes, don't do GraphQL requests to obtain them - 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), - ) + data = self._context.get_json(f"api/v1/media/{self.mediaid}/likers/", {}) + for user in data["users"]: + yield Profile(self._context, user) @property def is_sponsored(self) -> bool: From 5b27110476174af7d436dc981d765dbfdc34fc4c Mon Sep 17 00:00:00 2001 From: Alexander Graf <17130992+aandergr@users.noreply.github.com> Date: Sat, 24 Oct 2026 11:59:42 +0200 Subject: [PATCH 5/5] 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. --- instaloader/nodeiterator.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/instaloader/nodeiterator.py b/instaloader/nodeiterator.py index 244b50a..94decae 100644 --- a/instaloader/nodeiterator.py +++ b/instaloader/nodeiterator.py @@ -99,6 +99,7 @@ class NodeIterator(Iterator[T]): else: self._data = self._query() self._first_node: Optional[Dict] = None + self._first_item: Optional[T] = None self._is_first = is_first def _query(self, after: Optional[str] = None) -> Dict: @@ -151,10 +152,10 @@ class NodeIterator(Iterator[T]): item = self._node_wrapper(node) if self._is_first is not None: if self._is_first(item, self.first_item): - self._first_node = node + self._first_node, self._first_item = node, item else: if self._first_node is None: - self._first_node = node + self._first_node, self._first_item = node, item return item if self._data.get('page_info', {}).get('has_next_page'): query_response = self._query(self._data['page_info']['end_cursor']) @@ -201,7 +202,15 @@ class NodeIterator(Iterator[T]): .. versionchanged:: 4.9.2 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 def page_length() -> int: