aboutsummaryrefslogtreecommitdiffstats
path: root/yt_dlp/downloader
diff options
context:
space:
mode:
Diffstat (limited to 'yt_dlp/downloader')
-rw-r--r--yt_dlp/downloader/fragment.py46
-rw-r--r--yt_dlp/downloader/http.py65
-rw-r--r--yt_dlp/downloader/ism.py4
-rw-r--r--yt_dlp/downloader/mhtml.py3
-rw-r--r--yt_dlp/downloader/youtube_live_chat.py6
5 files changed, 59 insertions, 65 deletions
diff --git a/yt_dlp/downloader/fragment.py b/yt_dlp/downloader/fragment.py
index 95fb2f9e7..6b75dfc62 100644
--- a/yt_dlp/downloader/fragment.py
+++ b/yt_dlp/downloader/fragment.py
@@ -133,19 +133,19 @@ class FragmentFD(FileDownloader):
}
success = ctx['dl'].download(fragment_filename, fragment_info_dict)
if not success:
- return False, None
+ return False
if fragment_info_dict.get('filetime'):
ctx['fragment_filetime'] = fragment_info_dict.get('filetime')
ctx['fragment_filename_sanitized'] = fragment_filename
- try:
- return True, self._read_fragment(ctx)
- except FileNotFoundError:
- if not info_dict.get('is_live'):
- raise
- return False, None
+ return True
def _read_fragment(self, ctx):
- down, frag_sanitized = self.sanitize_open(ctx['fragment_filename_sanitized'], 'rb')
+ try:
+ down, frag_sanitized = self.sanitize_open(ctx['fragment_filename_sanitized'], 'rb')
+ except FileNotFoundError:
+ if ctx.get('live'):
+ return None
+ raise
ctx['fragment_filename_sanitized'] = frag_sanitized
frag_content = down.read()
down.close()
@@ -457,7 +457,7 @@ class FragmentFD(FileDownloader):
def download_fragment(fragment, ctx):
if not interrupt_trigger[0]:
- return False, fragment['frag_index']
+ return
frag_index = ctx['fragment_index'] = fragment['frag_index']
ctx['last_error'] = None
@@ -467,14 +467,12 @@ class FragmentFD(FileDownloader):
headers['Range'] = 'bytes=%d-%d' % (byte_range['start'], byte_range['end'] - 1)
# Never skip the first fragment
- fatal = is_fatal(fragment.get('index') or (frag_index - 1))
- count, frag_content = 0, None
+ fatal, count = is_fatal(fragment.get('index') or (frag_index - 1)), 0
while count <= fragment_retries:
try:
- success, frag_content = self._download_fragment(ctx, fragment['url'], info_dict, headers)
- if not success:
- return False, frag_index
- break
+ if self._download_fragment(ctx, fragment['url'], info_dict, headers):
+ break
+ return
except (compat_urllib_error.HTTPError, http.client.IncompleteRead) as err:
# Unavailable (possibly temporary) fragments may be served.
# First we try to retry then either skip or abort.
@@ -491,13 +489,9 @@ class FragmentFD(FileDownloader):
break
raise
- if count > fragment_retries:
- if not fatal:
- return False, frag_index
+ if count > fragment_retries and fatal:
ctx['dest_stream'].close()
self.report_error('Giving up after %s fragment retries' % fragment_retries)
- return False, frag_index
- return frag_content, frag_index
def append_fragment(frag_content, frag_index, ctx):
if not frag_content:
@@ -520,23 +514,23 @@ class FragmentFD(FileDownloader):
def _download_fragment(fragment):
ctx_copy = ctx.copy()
- frag_content, frag_index = download_fragment(fragment, ctx_copy)
- return fragment, frag_content, frag_index, ctx_copy.get('fragment_filename_sanitized')
+ download_fragment(fragment, ctx_copy)
+ return fragment, fragment['frag_index'], ctx_copy.get('fragment_filename_sanitized')
self.report_warning('The download speed shown is only of one thread. This is a known issue and patches are welcome')
with tpe or concurrent.futures.ThreadPoolExecutor(max_workers) as pool:
- for fragment, frag_content, frag_index, frag_filename in pool.map(_download_fragment, fragments):
+ for fragment, frag_index, frag_filename in pool.map(_download_fragment, fragments):
ctx['fragment_filename_sanitized'] = frag_filename
ctx['fragment_index'] = frag_index
- result = append_fragment(decrypt_fragment(fragment, frag_content), frag_index, ctx)
+ result = append_fragment(decrypt_fragment(fragment, self._read_fragment(ctx)), frag_index, ctx)
if not result:
return False
else:
for fragment in fragments:
if not interrupt_trigger[0]:
break
- frag_content, frag_index = download_fragment(fragment, ctx)
- result = append_fragment(decrypt_fragment(fragment, frag_content), frag_index, ctx)
+ download_fragment(fragment, ctx)
+ result = append_fragment(decrypt_fragment(fragment, self._read_fragment(ctx)), fragment['frag_index'], ctx)
if not result:
return False
diff --git a/yt_dlp/downloader/http.py b/yt_dlp/downloader/http.py
index 10ba61024..8e096b76b 100644
--- a/yt_dlp/downloader/http.py
+++ b/yt_dlp/downloader/http.py
@@ -1,8 +1,7 @@
from __future__ import unicode_literals
-import errno
import os
-import socket
+import ssl
import time
import random
@@ -10,6 +9,7 @@ from .common import FileDownloader
from ..compat import (
compat_str,
compat_urllib_error,
+ compat_http_client
)
from ..utils import (
ContentTooShortError,
@@ -18,11 +18,14 @@ from ..utils import (
parse_http_range,
sanitized_Request,
ThrottledDownload,
+ try_get,
write_xattr,
XAttrMetadataError,
XAttrUnavailableError,
)
+RESPONSE_READ_EXCEPTIONS = (TimeoutError, ConnectionError, ssl.SSLError, compat_http_client.HTTPException)
+
class HttpFD(FileDownloader):
def real_download(self, filename, info_dict):
@@ -53,7 +56,6 @@ class HttpFD(FileDownloader):
ctx.open_mode = 'wb'
ctx.resume_len = 0
- ctx.data_len = None
ctx.block_size = self.params.get('buffersize', 1024)
ctx.start_time = time.time()
ctx.chunk_size = None
@@ -100,6 +102,8 @@ class HttpFD(FileDownloader):
if ctx.is_resume:
self.report_resuming_byte(ctx.resume_len)
ctx.open_mode = 'ab'
+ elif req_start is not None:
+ range_start = req_start
elif ctx.chunk_size > 0:
range_start = 0
else:
@@ -116,23 +120,21 @@ class HttpFD(FileDownloader):
else:
range_end = None
- if range_end and ctx.data_len is not None and range_end >= ctx.data_len:
- range_end = ctx.data_len - 1
- has_range = range_start is not None
- ctx.has_range = has_range
+ if try_get(None, lambda _: range_start > range_end):
+ ctx.resume_len = 0
+ ctx.open_mode = 'wb'
+ raise RetryDownload(Exception(f'Conflicting range. (start={range_start} > end={range_end})'))
+
+ if try_get(None, lambda _: range_end >= ctx.content_len):
+ range_end = ctx.content_len - 1
+
request = sanitized_Request(url, request_data, headers)
+ has_range = range_start is not None
if has_range:
set_range(request, range_start, range_end)
# Establish connection
try:
- try:
- ctx.data = self.ydl.urlopen(request)
- except (compat_urllib_error.URLError, ) as err:
- # reason may not be available, e.g. for urllib2.HTTPError on python 2.6
- reason = getattr(err, 'reason', None)
- if isinstance(reason, socket.timeout):
- raise RetryDownload(err)
- raise err
+ ctx.data = self.ydl.urlopen(request)
# When trying to resume, Content-Range HTTP header of response has to be checked
# to match the value of requested Range HTTP header. This is due to a webservers
# that don't support resuming and serve a whole file with no Content-Range
@@ -151,7 +153,8 @@ class HttpFD(FileDownloader):
or content_range_end == range_end
or content_len < range_end)
if accept_content_len:
- ctx.data_len = content_len
+ ctx.content_len = content_len
+ ctx.data_len = min(content_len, req_end or content_len) - (req_start or 0)
return
# Content-Range is either not present or invalid. Assuming remote webserver is
# trying to send the whole file, resume is not possible, so wiping the local file
@@ -159,8 +162,7 @@ class HttpFD(FileDownloader):
self.report_unable_to_resume()
ctx.resume_len = 0
ctx.open_mode = 'wb'
- ctx.data_len = int_or_none(ctx.data.info().get('Content-length', None))
- return
+ ctx.data_len = ctx.content_len = int_or_none(ctx.data.info().get('Content-length', None))
except (compat_urllib_error.HTTPError, ) as err:
if err.code == 416:
# Unable to resume (requested range not satisfiable)
@@ -202,13 +204,14 @@ class HttpFD(FileDownloader):
# Unexpected HTTP error
raise
raise RetryDownload(err)
- except socket.timeout as err:
+ except compat_urllib_error.URLError as err:
+ if isinstance(err.reason, ssl.CertificateError):
+ raise
+ raise RetryDownload(err)
+ # In urllib.request.AbstractHTTPHandler, the response is partially read on request.
+ # Any errors that occur during this will not be wrapped by URLError
+ except RESPONSE_READ_EXCEPTIONS as err:
raise RetryDownload(err)
- except socket.error as err:
- if err.errno in (errno.ECONNRESET, errno.ETIMEDOUT):
- # Connection reset is no problem, just retry
- raise RetryDownload(err)
- raise
def download():
nonlocal throttle_start
@@ -254,16 +257,8 @@ class HttpFD(FileDownloader):
try:
# Download and write
data_block = ctx.data.read(block_size if not is_test else min(block_size, data_len - byte_counter))
- # socket.timeout is a subclass of socket.error but may not have
- # errno set
- except socket.timeout as e:
- retry(e)
- except socket.error as e:
- # SSLError on python 2 (inherits socket.error) may have
- # no errno set but this error message
- if e.errno in (errno.ECONNRESET, errno.ETIMEDOUT) or getattr(e, 'message', None) == 'The read operation timed out':
- retry(e)
- raise
+ except RESPONSE_READ_EXCEPTIONS as err:
+ retry(err)
byte_counter += len(data_block)
@@ -343,7 +338,7 @@ class HttpFD(FileDownloader):
elif speed:
throttle_start = None
- if not is_test and ctx.chunk_size and ctx.data_len is not None and byte_counter < ctx.data_len:
+ if not is_test and ctx.chunk_size and ctx.content_len is not None and byte_counter < ctx.content_len:
ctx.resume_len = byte_counter
# ctx.block_size = block_size
raise NextFragment()
diff --git a/yt_dlp/downloader/ism.py b/yt_dlp/downloader/ism.py
index 09516abe5..4d5618c83 100644
--- a/yt_dlp/downloader/ism.py
+++ b/yt_dlp/downloader/ism.py
@@ -263,9 +263,11 @@ class IsmFD(FragmentFD):
count = 0
while count <= fragment_retries:
try:
- success, frag_content = self._download_fragment(ctx, segment['url'], info_dict)
+ success = self._download_fragment(ctx, segment['url'], info_dict)
if not success:
return False
+ frag_content = self._read_fragment(ctx)
+
if not extra_state['ism_track_written']:
tfhd_data = extract_box_data(frag_content, [b'moof', b'traf', b'tfhd'])
info_dict['_download_params']['track_id'] = u32.unpack(tfhd_data[4:8])[0]
diff --git a/yt_dlp/downloader/mhtml.py b/yt_dlp/downloader/mhtml.py
index bc86fd1bf..54e711792 100644
--- a/yt_dlp/downloader/mhtml.py
+++ b/yt_dlp/downloader/mhtml.py
@@ -171,9 +171,10 @@ body > figure > img {
assert fragment_base_url
fragment_url = urljoin(fragment_base_url, fragment['path'])
- success, frag_content = self._download_fragment(ctx, fragment_url, info_dict)
+ success = self._download_fragment(ctx, fragment_url, info_dict)
if not success:
continue
+ frag_content = self._read_fragment(ctx)
mime_type = b'image/jpeg'
if frag_content.startswith(b'\x89PNG\r\n\x1a\n'):
diff --git a/yt_dlp/downloader/youtube_live_chat.py b/yt_dlp/downloader/youtube_live_chat.py
index b28d1ec17..cfca686ee 100644
--- a/yt_dlp/downloader/youtube_live_chat.py
+++ b/yt_dlp/downloader/youtube_live_chat.py
@@ -115,9 +115,10 @@ class YoutubeLiveChatFD(FragmentFD):
count = 0
while count <= fragment_retries:
try:
- success, raw_fragment = dl_fragment(url, request_data, headers)
+ success = dl_fragment(url, request_data, headers)
if not success:
return False, None, None, None
+ raw_fragment = self._read_fragment(ctx)
try:
data = ie.extract_yt_initial_data(video_id, raw_fragment.decode('utf-8', 'replace'))
except RegexNotFoundError:
@@ -145,9 +146,10 @@ class YoutubeLiveChatFD(FragmentFD):
self._prepare_and_start_frag_download(ctx, info_dict)
- success, raw_fragment = dl_fragment(info_dict['url'])
+ success = dl_fragment(info_dict['url'])
if not success:
return False
+ raw_fragment = self._read_fragment(ctx)
try:
data = ie.extract_yt_initial_data(video_id, raw_fragment.decode('utf-8', 'replace'))
except RegexNotFoundError: