diff --git a/Makefile b/Makefile index 19a377002b..3b97c74079 100644 --- a/Makefile +++ b/Makefile @@ -81,9 +81,9 @@ yt-dlp: yt_dlp/*.py yt_dlp/*/*.py mkdir -p zip/$$d ;\ cp -pPR $$d/*.py zip/$$d/ ;\ done - touch -t 200001010101 zip/yt_dlp/*.py zip/yt_dlp/*/*.py zip/yt_dlp/*/*/*.py + touch -t 200001010101 zip/yt_dlp/*.py zip/yt_dlp/*/*.py mv zip/yt_dlp/__main__.py zip/ - cd zip ; zip -q ../yt-dlp yt_dlp/*.py yt_dlp/*/*.py yt_dlp/*/*/*.py __main__.py + cd zip ; zip -q ../yt-dlp yt_dlp/*.py yt_dlp/*/*.py __main__.py rm -rf zip echo '#!$(PYTHON)' > yt-dlp cat yt-dlp.zip >> yt-dlp diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index bc6de49267..53681149e1 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -2426,6 +2426,8 @@ class YoutubeDL: for key in live_keys: if info_dict.get(key) is None: info_dict[key] = (live_status == key) + if live_status == 'post_live': + info_dict['was_live'] = True # Auto generate title fields corresponding to the *_number fields when missing # in order to always have clean titles. This is very common for TV series. @@ -3683,6 +3685,8 @@ class YoutubeDL: if not self.params.get('verbose'): return + from . import _IN_CLI # Must be delayed import + # These imports can be slow. So import them only as needed from .extractor.extractors import _LAZY_LOADER from .extractor.extractors import _PLUGIN_CLASSES as plugin_extractors @@ -3719,6 +3723,7 @@ class YoutubeDL: __version__, f'[{RELEASE_GIT_HEAD}]' if RELEASE_GIT_HEAD else '', '' if source == 'unknown' else f'({source})', + '' if _IN_CLI else 'API', delim=' ')) if not _LAZY_LOADER: if os.environ.get('YTDLP_NO_LAZY_EXTRACTORS'): diff --git a/yt_dlp/downloader/common.py b/yt_dlp/downloader/common.py index ab557a47ac..221b3827c7 100644 --- a/yt_dlp/downloader/common.py +++ b/yt_dlp/downloader/common.py @@ -24,6 +24,7 @@ from ..utils import ( encodeFilename, format_bytes, join_nonempty, + remove_start, sanitize_open, shell_quote, timeconvert, @@ -120,11 +121,11 @@ class FileDownloader: time = timetuple_from_msec(seconds * 1000) if time.hours > 99: return '--:--:--' - if not time.hours: - return ' %02d:%02d' % time[1:-1] return '%02d:%02d:%02d' % time[:-1] - format_eta = format_seconds + @classmethod + def format_eta(cls, seconds): + return f'{remove_start(cls.format_seconds(seconds), "00:"):>8s}' @staticmethod def calc_percent(byte_counter, data_len): @@ -332,6 +333,8 @@ class FileDownloader: return tmpl return default + _formats_bytes = lambda k: f'{format_bytes(s.get(k)):>10s}' + if s['status'] == 'finished': if self.params.get('noprogress'): self.to_screen('[download] Download completed') @@ -339,7 +342,7 @@ class FileDownloader: s.update({ 'speed': speed, '_speed_str': self.format_speed(speed).strip(), - '_total_bytes_str': format_bytes(s.get('total_bytes')), + '_total_bytes_str': _formats_bytes('total_bytes'), '_elapsed_str': self.format_seconds(s.get('elapsed')), '_percent_str': self.format_percent(100), }) @@ -354,15 +357,15 @@ class FileDownloader: return s.update({ - '_eta_str': self.format_eta(s.get('eta')), + '_eta_str': self.format_eta(s.get('eta')).strip(), '_speed_str': self.format_speed(s.get('speed')), '_percent_str': self.format_percent(try_call( lambda: 100 * s['downloaded_bytes'] / s['total_bytes'], lambda: 100 * s['downloaded_bytes'] / s['total_bytes_estimate'], lambda: s['downloaded_bytes'] == 0 and 0)), - '_total_bytes_str': format_bytes(s.get('total_bytes')), - '_total_bytes_estimate_str': format_bytes(s.get('total_bytes_estimate')), - '_downloaded_bytes_str': format_bytes(s.get('downloaded_bytes')), + '_total_bytes_str': _formats_bytes('total_bytes'), + '_total_bytes_estimate_str': _formats_bytes('total_bytes_estimate'), + '_downloaded_bytes_str': _formats_bytes('downloaded_bytes'), '_elapsed_str': self.format_seconds(s.get('elapsed')), }) diff --git a/yt_dlp/extractor/common.py b/yt_dlp/extractor/common.py index 0700b4767b..944b196a11 100644 --- a/yt_dlp/extractor/common.py +++ b/yt_dlp/extractor/common.py @@ -1862,7 +1862,7 @@ class InfoExtractor: alias, field = field, self._get_field_setting(field, 'field') if self._get_field_setting(alias, 'deprecated'): self.ydl.deprecated_feature(f'Format sorting alias {alias} is deprecated and may ' - 'be removed in a future version. Please use {field} instead') + f'be removed in a future version. Please use {field} instead') reverse = match.group('reverse') is not None closest = match.group('separator') == '~' limit_text = match.group('limit') diff --git a/yt_dlp/extractor/spotify.py b/yt_dlp/extractor/spotify.py index 4da24db9e9..55ce36aeaa 100644 --- a/yt_dlp/extractor/spotify.py +++ b/yt_dlp/extractor/spotify.py @@ -16,6 +16,7 @@ from ..utils import ( class SpotifyBaseIE(InfoExtractor): + _WORKING = False _ACCESS_TOKEN = None _OPERATION_HASHES = { 'Episode': '8276d4423d709ae9b68ec1b74cc047ba0f7479059a37820be730f125189ac2bf', diff --git a/yt_dlp/extractor/youtube.py b/yt_dlp/extractor/youtube.py index f73465ba4c..6047f2864a 100644 --- a/yt_dlp/extractor/youtube.py +++ b/yt_dlp/extractor/youtube.py @@ -390,6 +390,8 @@ class YoutubeBaseInfoExtractor(InfoExtractor): 'si', 'th', 'lo', 'my', 'ka', 'am', 'km', 'zh-CN', 'zh-TW', 'zh-HK', 'ja', 'ko' ] + _IGNORED_WARNINGS = {'Unavailable videos will be hidden during playback'} + @functools.cached_property def _preferred_lang(self): """ @@ -692,12 +694,11 @@ class YoutubeBaseInfoExtractor(InfoExtractor): yield alert_type, message def _report_alerts(self, alerts, expected=True, fatal=True, only_once=False): - errors = [] - warnings = [] + errors, warnings = [], [] for alert_type, alert_message in alerts: if alert_type.lower() == 'error' and fatal: errors.append([alert_type, alert_message]) - else: + elif alert_message not in self._IGNORED_WARNINGS: warnings.append([alert_type, alert_message]) for alert_type, alert_message in (warnings + errors[:-1]):