Use Post class attributes in filename-pattern
- Added owner_id and mediaid to Post class properties. - In case of not downloading stories, the attributes of the Post class can now be used in filename-pattern, e.g. {post.owner_id} or {post.mediaid}. Closes #53.
This commit is contained in:
parent
ce13c0c53c
commit
e9207f095f
@ -10,6 +10,6 @@ https://instaloader.readthedocs.io/
|
|||||||
The documentation is created with [Sphinx](http://www.sphinx-doc.org/). To build it, use
|
The documentation is created with [Sphinx](http://www.sphinx-doc.org/). To build it, use
|
||||||
|
|
||||||
```
|
```
|
||||||
pip3 install sphinx
|
pip3 install sphinx sphinx-autodoc-typehints
|
||||||
make html
|
make html
|
||||||
```
|
```
|
||||||
|
@ -96,7 +96,8 @@ pattern, the token ``{target}`` is replaced by the target name, and
|
|||||||
to the target directory. The default is ``--filename-pattern={date}``.
|
to the target directory. The default is ``--filename-pattern={date}``.
|
||||||
The tokens ``{target}`` and ``{profile}`` are replaced like in the
|
The tokens ``{target}`` and ``{profile}`` are replaced like in the
|
||||||
dirname pattern. Further, the tokens ``{date}`` and ``{shortcode}`` are
|
dirname pattern. Further, the tokens ``{date}`` and ``{shortcode}`` are
|
||||||
defined.
|
defined. Additionally, in case of not downloading stories, the attributes of
|
||||||
|
:class:`.Post` can be used, e.g. ``{post.owner_id}`` or ``{post.mediaid}``.
|
||||||
|
|
||||||
For example, encode the poster's profile name in the filenames with:
|
For example, encode the poster's profile name in the filenames with:
|
||||||
|
|
||||||
|
@ -134,8 +134,11 @@ How to Download
|
|||||||
with ``--dirname-pattern``. ``{profile}`` is replaced by the profile name,
|
with ``--dirname-pattern``. ``{profile}`` is replaced by the profile name,
|
||||||
``{target}`` is replaced by the target you specified, i.e. either ``:feed``,
|
``{target}`` is replaced by the target you specified, i.e. either ``:feed``,
|
||||||
``#hashtag`` or the profile name. Also, the fields ``{date}`` and
|
``#hashtag`` or the profile name. Also, the fields ``{date}`` and
|
||||||
``{shortcode}`` can be specified. Defaults to ``{date:%Y-%m-%d_%H-%M-%S}``.
|
``{shortcode}`` can be specified. In case of not downloading stories, the
|
||||||
See :ref:`filename-specification`.
|
attributes of the :class:`.Post` class can be used in addition, e.g.
|
||||||
|
``{post.owner_id}`` or ``{post.mediaid}``.
|
||||||
|
Defaults to
|
||||||
|
``{date:%Y-%m-%d_%H-%M-%S}``. See :ref:`filename-specification`.
|
||||||
|
|
||||||
.. option:: --user-agent USER_AGENT
|
.. option:: --user-agent USER_AGENT
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ def mediaid_to_shortcode(mediaid: int) -> str:
|
|||||||
def format_string_contains_key(format_string: str, key: str) -> bool:
|
def format_string_contains_key(format_string: str, key: str) -> bool:
|
||||||
# pylint:disable=unused-variable
|
# pylint:disable=unused-variable
|
||||||
for literal_text, field_name, format_spec, conversion in string.Formatter().parse(format_string):
|
for literal_text, field_name, format_spec, conversion in string.Formatter().parse(format_string):
|
||||||
if field_name == key:
|
if field_name == key or field_name.startswith(key + '.'):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -176,7 +176,8 @@ class Post:
|
|||||||
|
|
||||||
LOGIN_REQUIRING_PROPERTIES = ["viewer_has_liked"]
|
LOGIN_REQUIRING_PROPERTIES = ["viewer_has_liked"]
|
||||||
|
|
||||||
def __init__(self, instaloader: 'Instaloader', node: Dict[str, Any], profile: Optional[str] = None):
|
def __init__(self, instaloader: 'Instaloader', node: Dict[str, Any],
|
||||||
|
profile: Optional[str] = None, profile_id: Optional[int] = None):
|
||||||
"""Create a Post instance from a node structure as returned by Instagram.
|
"""Create a Post instance from a node structure as returned by Instagram.
|
||||||
|
|
||||||
:param instaloader: :class:`Instaloader` instance used for additional queries if neccessary.
|
:param instaloader: :class:`Instaloader` instance used for additional queries if neccessary.
|
||||||
@ -186,6 +187,7 @@ class Post:
|
|||||||
self._instaloader = instaloader
|
self._instaloader = instaloader
|
||||||
self._node = node
|
self._node = node
|
||||||
self._profile = profile
|
self._profile = profile
|
||||||
|
self._profile_id = profile_id
|
||||||
self._full_metadata_dict = None
|
self._full_metadata_dict = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -206,6 +208,11 @@ class Post:
|
|||||||
"""Media shortcode. URL of the post is instagram.com/p/<shortcode>/."""
|
"""Media shortcode. URL of the post is instagram.com/p/<shortcode>/."""
|
||||||
return self._node['shortcode'] if 'shortcode' in self._node else self._node['code']
|
return self._node['shortcode'] if 'shortcode' in self._node else self._node['code']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mediaid(self) -> int:
|
||||||
|
"""The mediaid is a decimal representation of the media shortcode."""
|
||||||
|
return int(self._node['id'])
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Post {}>'.format(self.shortcode)
|
return '<Post {}>'.format(self.shortcode)
|
||||||
|
|
||||||
@ -252,6 +259,13 @@ class Post:
|
|||||||
self._instaloader.error("Get owner name of {}: {} -- using \'UNKNOWN\'.".format(self, err))
|
self._instaloader.error("Get owner name of {}: {} -- using \'UNKNOWN\'.".format(self, err))
|
||||||
return 'UNKNOWN'
|
return 'UNKNOWN'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def owner_id(self) -> int:
|
||||||
|
"""The ID of the Post's owner."""
|
||||||
|
if self._profile_id:
|
||||||
|
return self._profile_id
|
||||||
|
return int(self._field('owner', 'id'))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def date(self) -> datetime:
|
def date(self) -> datetime:
|
||||||
"""Timestamp when the post was created."""
|
"""Timestamp when the post was created."""
|
||||||
@ -889,7 +903,8 @@ class Instaloader:
|
|||||||
profilename = post.owner_username if needs_profilename else None
|
profilename = post.owner_username if needs_profilename else None
|
||||||
dirname = self.dirname_pattern.format(profile=profilename, target=target.lower())
|
dirname = self.dirname_pattern.format(profile=profilename, target=target.lower())
|
||||||
filename = dirname + '/' + self.filename_pattern.format(profile=profilename, target=target.lower(),
|
filename = dirname + '/' + self.filename_pattern.format(profile=profilename, target=target.lower(),
|
||||||
date=post.date, shortcode=post.shortcode)
|
date=post.date, shortcode=post.shortcode,
|
||||||
|
post=post)
|
||||||
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
||||||
|
|
||||||
# Download the image(s) / video thumbnail and videos within sidecars if desired
|
# Download the image(s) / video thumbnail and videos within sidecars if desired
|
||||||
@ -993,6 +1008,10 @@ class Instaloader:
|
|||||||
:param filename_target: Replacement for {target} in dirname_pattern and filename_pattern
|
:param filename_target: Replacement for {target} in dirname_pattern and filename_pattern
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if format_string_contains_key(self.filename_pattern, 'post'):
|
||||||
|
raise InvalidArgumentException("The \"post\" keyword is not supported in the filename pattern when "
|
||||||
|
"downloading stories.")
|
||||||
|
|
||||||
if not self.is_logged_in:
|
if not self.is_logged_in:
|
||||||
raise LoginRequiredException('Login required to download stories')
|
raise LoginRequiredException('Login required to download stories')
|
||||||
|
|
||||||
@ -1202,7 +1221,9 @@ class Instaloader:
|
|||||||
def get_profile_posts(self, profile_metadata: Dict[str, Any]) -> Iterator[Post]:
|
def get_profile_posts(self, profile_metadata: Dict[str, Any]) -> Iterator[Post]:
|
||||||
"""Retrieve all posts from a profile."""
|
"""Retrieve all posts from a profile."""
|
||||||
profile_name = profile_metadata['user']['username']
|
profile_name = profile_metadata['user']['username']
|
||||||
yield from (Post(self, node, profile=profile_name) for node in profile_metadata['user']['media']['nodes'])
|
profile_id = int(profile_metadata['user']['id'])
|
||||||
|
yield from (Post(self, node, profile=profile_name, profile_id=profile_id)
|
||||||
|
for node in profile_metadata['user']['media']['nodes'])
|
||||||
has_next_page = profile_metadata['user']['media']['page_info']['has_next_page']
|
has_next_page = profile_metadata['user']['media']['page_info']['has_next_page']
|
||||||
end_cursor = profile_metadata['user']['media']['page_info']['end_cursor']
|
end_cursor = profile_metadata['user']['media']['page_info']['end_cursor']
|
||||||
while has_next_page:
|
while has_next_page:
|
||||||
@ -1213,7 +1234,8 @@ class Instaloader:
|
|||||||
'after': end_cursor},
|
'after': end_cursor},
|
||||||
'https://www.instagram.com/{0}/'.format(profile_name))
|
'https://www.instagram.com/{0}/'.format(profile_name))
|
||||||
media = data['data']['user']['edge_owner_to_timeline_media']
|
media = data['data']['user']['edge_owner_to_timeline_media']
|
||||||
yield from (Post(self, edge['node'], profile=profile_name) for edge in media['edges'])
|
yield from (Post(self, edge['node'], profile=profile_name, profile_id=profile_id)
|
||||||
|
for edge in media['edges'])
|
||||||
has_next_page = media['page_info']['has_next_page']
|
has_next_page = media['page_info']['has_next_page']
|
||||||
end_cursor = media['page_info']['end_cursor']
|
end_cursor = media['page_info']['end_cursor']
|
||||||
|
|
||||||
@ -1468,8 +1490,9 @@ def main():
|
|||||||
help='Prefix of filenames. Posts are stored in the directory whose pattern is given with '
|
help='Prefix of filenames. Posts are stored in the directory whose pattern is given with '
|
||||||
'--dirname-pattern. {profile} is replaced by the profile name, '
|
'--dirname-pattern. {profile} is replaced by the profile name, '
|
||||||
'{target} is replaced by the target you specified, i.e. either :feed, #hashtag or the '
|
'{target} is replaced by the target you specified, i.e. either :feed, #hashtag or the '
|
||||||
'profile name. Also, the fields date and shortcode can be specified. Defaults to '
|
'profile name. Also, the fields {date} and {shortcode} can be specified. In case of not '
|
||||||
'\'{date:%%Y-%%m-%%d_%%H-%%M-%%S}\'.')
|
'downloading stories, the attributes of the Post class can be used in addition, e.g. '
|
||||||
|
'{post.owner_id} or {post.mediaid}. Defaults to \'{date:%%Y-%%m-%%d_%%H-%%M-%%S}\'.')
|
||||||
g_how.add_argument('--user-agent',
|
g_how.add_argument('--user-agent',
|
||||||
help='User Agent to use for HTTP requests. Defaults to \'{}\'.'.format(default_user_agent()))
|
help='User Agent to use for HTTP requests. Defaults to \'{}\'.'.format(default_user_agent()))
|
||||||
g_how.add_argument('-S', '--no-sleep', action='store_true', help=SUPPRESS)
|
g_how.add_argument('-S', '--no-sleep', action='store_true', help=SUPPRESS)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user