mirror of
https://github.com/ytdl-org/youtube-dl
synced 2025-07-20 18:34:14 +09:00
Compare commits
4 Commits
3b7719063e
...
af671b3e3e
Author | SHA1 | Date | |
---|---|---|---|
![]() |
af671b3e3e | ||
![]() |
673277e510 | ||
![]() |
91b1569f68 | ||
![]() |
ffd4b5970d |
@ -57,6 +57,7 @@ class FileDownloader(object):
|
|||||||
|
|
||||||
_TEST_FILE_SIZE = 10241
|
_TEST_FILE_SIZE = 10241
|
||||||
params = None
|
params = None
|
||||||
|
progress_update = time.time()
|
||||||
|
|
||||||
def __init__(self, ydl, params):
|
def __init__(self, ydl, params):
|
||||||
"""Create a FileDownloader object with the given options."""
|
"""Create a FileDownloader object with the given options."""
|
||||||
@ -120,8 +121,8 @@ class FileDownloader(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def format_speed(speed):
|
def format_speed(speed):
|
||||||
if speed is None:
|
if speed is None:
|
||||||
return '%10s' % '---b/s'
|
return '%12s' % '--- B/s'
|
||||||
return '%10s' % ('%s/s' % format_bytes(speed))
|
return '%12s' % ('%s/s' % format_bytes(speed))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def format_retries(retries):
|
def format_retries(retries):
|
||||||
@ -276,32 +277,33 @@ class FileDownloader(object):
|
|||||||
if s['status'] != 'downloading':
|
if s['status'] != 'downloading':
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Show non-final progress updates no more often than once per second
|
||||||
|
tstamp = time.time()
|
||||||
|
if tstamp - self.progress_update < 1:
|
||||||
|
return
|
||||||
|
self.progress_update = tstamp
|
||||||
|
|
||||||
if s.get('eta') is not None:
|
if s.get('eta') is not None:
|
||||||
s['_eta_str'] = self.format_eta(s['eta'])
|
s['_eta_str'] = self.format_eta(s['eta'])
|
||||||
else:
|
else:
|
||||||
s['_eta_str'] = 'Unknown ETA'
|
s['_eta_str'] = 'unknown'
|
||||||
|
|
||||||
if s.get('total_bytes') and s.get('downloaded_bytes') is not None:
|
if s.get('total_bytes') and s.get('downloaded_bytes') is not None:
|
||||||
s['_percent_str'] = self.format_percent(100 * s['downloaded_bytes'] / s['total_bytes'])
|
s['_percent_str'] = self.format_percent(100 * s['downloaded_bytes'] / s['total_bytes'])
|
||||||
elif s.get('total_bytes_estimate') and s.get('downloaded_bytes') is not None:
|
elif s.get('total_bytes_estimate') and s.get('downloaded_bytes') is not None:
|
||||||
s['_percent_str'] = self.format_percent(100 * s['downloaded_bytes'] / s['total_bytes_estimate'])
|
s['_percent_str'] = self.format_percent(100 * s['downloaded_bytes'] / s['total_bytes_estimate'])
|
||||||
else:
|
else:
|
||||||
if s.get('downloaded_bytes') == 0:
|
s['_percent_str'] = self.format_percent(0 if s.get('downloaded_bytes') == 0 else None)
|
||||||
s['_percent_str'] = self.format_percent(0)
|
|
||||||
else:
|
|
||||||
s['_percent_str'] = 'Unknown %'
|
|
||||||
|
|
||||||
if s.get('speed') is not None:
|
# Missing 'speed' is already handled properly in self.format_speed()
|
||||||
s['_speed_str'] = self.format_speed(s['speed'])
|
s['_speed_str'] = self.format_speed(s.get('speed'))
|
||||||
else:
|
|
||||||
s['_speed_str'] = 'Unknown speed'
|
|
||||||
|
|
||||||
if s.get('total_bytes') is not None:
|
if s.get('total_bytes') is not None:
|
||||||
s['_total_bytes_str'] = format_bytes(s['total_bytes'])
|
s['_total_bytes_str'] = format_bytes(s['total_bytes'])
|
||||||
msg_template = '%(_percent_str)s of %(_total_bytes_str)s at %(_speed_str)s ETA %(_eta_str)s'
|
msg_template = '%(_percent_str)s of %(_total_bytes_str)s at %(_speed_str)s, ETA %(_eta_str)s'
|
||||||
elif s.get('total_bytes_estimate') is not None:
|
elif s.get('total_bytes_estimate') is not None:
|
||||||
s['_total_bytes_estimate_str'] = format_bytes(s['total_bytes_estimate'])
|
s['_total_bytes_estimate_str'] = format_bytes(s['total_bytes_estimate'])
|
||||||
msg_template = '%(_percent_str)s of ~%(_total_bytes_estimate_str)s at %(_speed_str)s ETA %(_eta_str)s'
|
msg_template = '%(_percent_str)s of ~%(_total_bytes_estimate_str)s at %(_speed_str)s, ETA %(_eta_str)s'
|
||||||
else:
|
else:
|
||||||
if s.get('downloaded_bytes') is not None:
|
if s.get('downloaded_bytes') is not None:
|
||||||
s['_downloaded_bytes_str'] = format_bytes(s['downloaded_bytes'])
|
s['_downloaded_bytes_str'] = format_bytes(s['downloaded_bytes'])
|
||||||
@ -311,7 +313,7 @@ class FileDownloader(object):
|
|||||||
else:
|
else:
|
||||||
msg_template = '%(_downloaded_bytes_str)s at %(_speed_str)s'
|
msg_template = '%(_downloaded_bytes_str)s at %(_speed_str)s'
|
||||||
else:
|
else:
|
||||||
msg_template = '%(_percent_str)s % at %(_speed_str)s ETA %(_eta_str)s'
|
msg_template = '%(_percent_str)s % at %(_speed_str)s, ETA %(_eta_str)s'
|
||||||
|
|
||||||
self._report_progress_status(msg_template % s)
|
self._report_progress_status(msg_template % s)
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ from ..compat import (
|
|||||||
)
|
)
|
||||||
from ..jsinterp import JSInterpreter
|
from ..jsinterp import JSInterpreter
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
|
bug_reports_message,
|
||||||
clean_html,
|
clean_html,
|
||||||
dict_get,
|
dict_get,
|
||||||
error_to_compat_str,
|
error_to_compat_str,
|
||||||
@ -65,6 +66,7 @@ from ..utils import (
|
|||||||
url_or_none,
|
url_or_none,
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
urljoin,
|
urljoin,
|
||||||
|
variadic,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -460,6 +462,26 @@ class YoutubeBaseInfoExtractor(InfoExtractor):
|
|||||||
'uploader': uploader,
|
'uploader': uploader,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_thumbnails(data, *path_list, **kw_final_key):
|
||||||
|
"""
|
||||||
|
Extract thumbnails from thumbnails dict
|
||||||
|
@param path_list: path list to level that contains 'thumbnails' key
|
||||||
|
"""
|
||||||
|
final_key = kw_final_key.get('final_key', 'thumbnails')
|
||||||
|
|
||||||
|
return traverse_obj(data, ((
|
||||||
|
tuple(variadic(path) + (final_key, Ellipsis)
|
||||||
|
for path in path_list or [()])), {
|
||||||
|
'url': ('url', T(url_or_none),
|
||||||
|
# Sometimes youtube gives a wrong thumbnail URL. See:
|
||||||
|
# https://github.com/yt-dlp/yt-dlp/issues/233
|
||||||
|
# https://github.com/ytdl-org/youtube-dl/issues/28023
|
||||||
|
T(lambda u: update_url(u, query=None) if u and 'maxresdefault' in u else u)),
|
||||||
|
'height': ('height', T(int_or_none)),
|
||||||
|
'width': ('width', T(int_or_none)),
|
||||||
|
}, T(lambda t: t if t.get('url') else None)))
|
||||||
|
|
||||||
def _search_results(self, query, params):
|
def _search_results(self, query, params):
|
||||||
data = {
|
data = {
|
||||||
'context': {
|
'context': {
|
||||||
@ -3183,8 +3205,12 @@ class YoutubeTabIE(YoutubeBaseInfoExtractor):
|
|||||||
expected_type=txt_or_none)
|
expected_type=txt_or_none)
|
||||||
|
|
||||||
def _grid_entries(self, grid_renderer):
|
def _grid_entries(self, grid_renderer):
|
||||||
for item in grid_renderer['items']:
|
for item in traverse_obj(grid_renderer, ('items', Ellipsis, T(dict))):
|
||||||
if not isinstance(item, dict):
|
lockup_view_model = traverse_obj(item, ('lockupViewModel', T(dict)))
|
||||||
|
if lockup_view_model:
|
||||||
|
entry = self._extract_lockup_view_model(lockup_view_model)
|
||||||
|
if entry:
|
||||||
|
yield entry
|
||||||
continue
|
continue
|
||||||
renderer = self._extract_grid_item_renderer(item)
|
renderer = self._extract_grid_item_renderer(item)
|
||||||
if not isinstance(renderer, dict):
|
if not isinstance(renderer, dict):
|
||||||
@ -3268,6 +3294,25 @@ class YoutubeTabIE(YoutubeBaseInfoExtractor):
|
|||||||
continue
|
continue
|
||||||
yield self._extract_video(renderer)
|
yield self._extract_video(renderer)
|
||||||
|
|
||||||
|
def _extract_lockup_view_model(self, view_model):
|
||||||
|
content_id = view_model.get('contentId')
|
||||||
|
if not content_id:
|
||||||
|
return
|
||||||
|
content_type = view_model.get('contentType')
|
||||||
|
if content_type not in ('LOCKUP_CONTENT_TYPE_PLAYLIST', 'LOCKUP_CONTENT_TYPE_PODCAST'):
|
||||||
|
self.report_warning(
|
||||||
|
'Unsupported lockup view model content type "{0}"{1}'.format(content_type, bug_reports_message()), only_once=True)
|
||||||
|
return
|
||||||
|
return merge_dicts(self.url_result(
|
||||||
|
update_url_query('https://www.youtube.com/playlist', {'list': content_id}),
|
||||||
|
ie=YoutubeTabIE.ie_key(), video_id=content_id), {
|
||||||
|
'title': traverse_obj(view_model, (
|
||||||
|
'metadata', 'lockupMetadataViewModel', 'title', 'content', T(compat_str))),
|
||||||
|
'thumbnails': self._extract_thumbnails(view_model, (
|
||||||
|
'contentImage', 'collectionThumbnailViewModel', 'primaryThumbnail',
|
||||||
|
'thumbnailViewModel', 'image'), final_key='sources'),
|
||||||
|
})
|
||||||
|
|
||||||
def _video_entry(self, video_renderer):
|
def _video_entry(self, video_renderer):
|
||||||
video_id = video_renderer.get('videoId')
|
video_id = video_renderer.get('videoId')
|
||||||
if video_id:
|
if video_id:
|
||||||
|
@ -3586,10 +3586,12 @@ def format_bytes(bytes):
|
|||||||
if bytes == 0.0:
|
if bytes == 0.0:
|
||||||
exponent = 0
|
exponent = 0
|
||||||
else:
|
else:
|
||||||
exponent = int(math.log(bytes, 1024.0))
|
# Display user-friendly values, e.g. "1001.45 KiB" is much less
|
||||||
|
# user-friendly than "1.00 MiB"
|
||||||
|
exponent = int(math.log(bytes, 1000.0))
|
||||||
suffix = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'][exponent]
|
suffix = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'][exponent]
|
||||||
converted = float(bytes) / float(1024 ** exponent)
|
converted = float(bytes) / float(1024 ** exponent)
|
||||||
return '%.2f%s' % (converted, suffix)
|
return '%.2f %s' % (converted, suffix)
|
||||||
|
|
||||||
|
|
||||||
def lookup_unit_table(unit_table, s):
|
def lookup_unit_table(unit_table, s):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user