1
0
mirror of https://github.com/instaloader/instaloader.git synced 2024-11-04 17:32:30 +01:00

Merge branch 'master' into upcoming/v4.8

This commit is contained in:
Alexander Graf 2021-08-04 18:24:46 +02:00
commit 87dfdff335
2 changed files with 66 additions and 8 deletions

View File

@ -529,6 +529,28 @@ class InstaloaderContext:
:raises ConnectionException: When download repeatedly failed.""" :raises ConnectionException: When download repeatedly failed."""
self.write_raw(self.get_raw(url), filename) self.write_raw(self.get_raw(url), filename)
def head(self, url: str, allow_redirects: bool = False) -> requests.Response:
"""HEAD a URL anonymously.
:raises QueryReturnedNotFoundException: When the server responds with a 404.
:raises QueryReturnedForbiddenException: When the server responds with a 403.
:raises ConnectionException: When request failed.
.. versionadded:: 4.7.6
"""
with self.get_anonymous_session() as anonymous_session:
resp = anonymous_session.head(url, allow_redirects=allow_redirects)
if resp.status_code == 200:
return resp
else:
if resp.status_code == 403:
# suspected invalid URL signature
raise QueryReturnedForbiddenException("403 when accessing {}.".format(url))
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))
@property @property
def root_rhx_gis(self) -> Optional[str]: def root_rhx_gis(self) -> Optional[str]:
"""rhx_gis string returned in the / query.""" """rhx_gis string returned in the / query."""

View File

@ -5,7 +5,7 @@ from base64 import b64decode, b64encode
from collections import namedtuple from collections import namedtuple
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Iterable, Iterator, List, Optional, Union from typing import Any, Dict, Iterable, Iterator, List, Optional, Tuple, Union
from . import __version__ from . import __version__
from .exceptions import * from .exceptions import *
@ -377,13 +377,28 @@ class Post:
def video_url(self) -> Optional[str]: def video_url(self) -> Optional[str]:
"""URL of the video, or None.""" """URL of the video, or None."""
if self.is_video: if self.is_video:
version_urls = [self._field('video_url')]
if self._context.iphone_support and self._context.is_logged_in: if self._context.iphone_support and self._context.is_logged_in:
version_urls.extend(version['url'] for version in self._iphone_struct['video_versions'])
else:
return version_urls[0]
url_candidates: List[Tuple[int, str]] = []
for idx, version_url in enumerate(version_urls):
if any(url_candidate[1] == version_url for url_candidate in url_candidates):
# Skip duplicates
continue
try: try:
url = self._iphone_struct['video_versions'][0]['url'] url_candidates.append((
return url int(self._context.head(version_url, allow_redirects=True).headers.get('Content-Length', 0)),
version_url
))
except (InstaloaderException, KeyError, IndexError) as err: except (InstaloaderException, KeyError, IndexError) as err:
self._context.error('{} Unable to fetch high quality video version of {}.'.format(err, self)) self._context.error(f"Video URL candidate {idx+1}/{len(version_urls)} for {self}: {err}")
return self._field('video_url') if not url_candidates:
# All candidates fail: Fallback to default URL and handle errors later at the actual download attempt
return version_urls[0]
url_candidates.sort()
return url_candidates[-1][1]
return None return None
@property @property
@ -556,8 +571,8 @@ class Post:
return None return None
location_id = int(loc['id']) location_id = int(loc['id'])
if any(k not in loc for k in ('name', 'slug', 'has_public_page', 'lat', 'lng')): if any(k not in loc for k in ('name', 'slug', 'has_public_page', 'lat', 'lng')):
loc = self._context.get_json("explore/locations/{0}/".format(location_id), loc.update(self._context.get_json("explore/locations/{0}/".format(location_id),
params={'__a': 1})['graphql']['location'] params={'__a': 1})['native_location_data']['location_info'])
self._location = PostLocation(location_id, loc['name'], loc['slug'], loc['has_public_page'], self._location = PostLocation(location_id, loc['name'], loc['slug'], loc['has_public_page'],
loc['lat'], loc['lng']) loc['lat'], loc['lng'])
return self._location return self._location
@ -1110,7 +1125,28 @@ class StoryItem:
def video_url(self) -> Optional[str]: def video_url(self) -> Optional[str]:
"""URL of the video, or None.""" """URL of the video, or None."""
if self.is_video: if self.is_video:
return self._node['video_resources'][-1]['src'] version_urls = [self._node['video_resources'][-1]['src']]
if self._context.iphone_support and self._context.is_logged_in:
version_urls.extend(version['url'] for version in self._iphone_struct['video_versions'])
else:
return version_urls[0]
url_candidates: List[Tuple[int, str]] = []
for idx, version_url in enumerate(version_urls):
if any(url_candidate[1] == version_url for url_candidate in url_candidates):
# Skip duplicates
continue
try:
url_candidates.append((
int(self._context.head(version_url, allow_redirects=True).headers.get('Content-Length', 0)),
version_url
))
except (InstaloaderException, KeyError, IndexError) as err:
self._context.error(f"Video URL candidate {idx+1}/{len(version_urls)} for {self}: {err}")
if not url_candidates:
# All candidates fail: Fallback to default URL and handle errors later at the actual download attempt
return version_urls[0]
url_candidates.sort()
return url_candidates[-1][1]
return None return None