mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-10-31 23:02:40 +00:00
[youtube] Support --download-sections for YT Livestream from start
This commit is contained in:
parent
b83d7526f2
commit
fba1c397b1
3 changed files with 39 additions and 12 deletions
|
@ -26,7 +26,12 @@
|
||||||
from .cache import Cache
|
from .cache import Cache
|
||||||
from .compat import compat_os_name, compat_shlex_quote
|
from .compat import compat_os_name, compat_shlex_quote
|
||||||
from .cookies import load_cookies
|
from .cookies import load_cookies
|
||||||
from .downloader import FFmpegFD, get_suitable_downloader, shorten_protocol_name
|
from .downloader import (
|
||||||
|
DashSegmentsFD,
|
||||||
|
FFmpegFD,
|
||||||
|
get_suitable_downloader,
|
||||||
|
shorten_protocol_name,
|
||||||
|
)
|
||||||
from .downloader.rtmp import rtmpdump_version
|
from .downloader.rtmp import rtmpdump_version
|
||||||
from .extractor import gen_extractor_classes, get_info_extractor
|
from .extractor import gen_extractor_classes, get_info_extractor
|
||||||
from .extractor.common import UnsupportedURLIE
|
from .extractor.common import UnsupportedURLIE
|
||||||
|
@ -3143,7 +3148,7 @@ def existing_video_file(*filepaths):
|
||||||
fd, success = None, True
|
fd, success = None, True
|
||||||
if info_dict.get('protocol') or info_dict.get('url'):
|
if info_dict.get('protocol') or info_dict.get('url'):
|
||||||
fd = get_suitable_downloader(info_dict, self.params, to_stdout=temp_filename == '-')
|
fd = get_suitable_downloader(info_dict, self.params, to_stdout=temp_filename == '-')
|
||||||
if fd is not FFmpegFD and 'no-direct-merge' not in self.params['compat_opts'] and (
|
if not(fd is FFmpegFD or fd is DashSegmentsFD) and 'no-direct-merge' not in self.params['compat_opts'] and (
|
||||||
info_dict.get('section_start') or info_dict.get('section_end')):
|
info_dict.get('section_start') or info_dict.get('section_end')):
|
||||||
msg = ('This format cannot be partially downloaded' if FFmpegFD.available()
|
msg = ('This format cannot be partially downloaded' if FFmpegFD.available()
|
||||||
else 'You have requested downloading the video partially, but ffmpeg is not installed')
|
else 'You have requested downloading the video partially, but ffmpeg is not installed')
|
||||||
|
|
|
@ -33,6 +33,8 @@ def real_download(self, filename, info_dict):
|
||||||
'filename': fmt.get('filepath') or filename,
|
'filename': fmt.get('filepath') or filename,
|
||||||
'live': 'is_from_start' if fmt.get('is_from_start') else fmt.get('is_live'),
|
'live': 'is_from_start' if fmt.get('is_from_start') else fmt.get('is_live'),
|
||||||
'total_frags': fragment_count,
|
'total_frags': fragment_count,
|
||||||
|
'section_start': info_dict.get('section_start'),
|
||||||
|
'section_end': info_dict.get('section_end'),
|
||||||
}
|
}
|
||||||
|
|
||||||
if real_downloader:
|
if real_downloader:
|
||||||
|
|
|
@ -2757,6 +2757,8 @@ def _live_dash_fragments(self, video_id, format_id, live_start_time, mpd_feed, m
|
||||||
|
|
||||||
begin_index = 0
|
begin_index = 0
|
||||||
download_start_time = ctx.get('start') or time.time()
|
download_start_time = ctx.get('start') or time.time()
|
||||||
|
section_start = ctx.get('section_start') or 0
|
||||||
|
section_end = ctx.get('section_end') or math.inf
|
||||||
|
|
||||||
lack_early_segments = download_start_time - (live_start_time or download_start_time) > MAX_DURATION
|
lack_early_segments = download_start_time - (live_start_time or download_start_time) > MAX_DURATION
|
||||||
if lack_early_segments:
|
if lack_early_segments:
|
||||||
|
@ -2797,8 +2799,7 @@ def _extract_sequence_from_mpd(refresh_sequence, immediate):
|
||||||
fragment_base_url = fmt_info['fragment_base_url']
|
fragment_base_url = fmt_info['fragment_base_url']
|
||||||
assert fragment_base_url
|
assert fragment_base_url
|
||||||
|
|
||||||
_last_seq = int(re.search(r'(?:/|^)sq/(\d+)', fragments[-1]['path']).group(1))
|
return True
|
||||||
return True, _last_seq
|
|
||||||
|
|
||||||
self.write_debug(f'[{video_id}] Generating fragments for format {format_id}')
|
self.write_debug(f'[{video_id}] Generating fragments for format {format_id}')
|
||||||
while is_live:
|
while is_live:
|
||||||
|
@ -2818,11 +2819,19 @@ def _extract_sequence_from_mpd(refresh_sequence, immediate):
|
||||||
last_segment_url = None
|
last_segment_url = None
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
should_continue, last_seq = _extract_sequence_from_mpd(True, no_fragment_score > 15)
|
should_continue = _extract_sequence_from_mpd(True, no_fragment_score > 15)
|
||||||
no_fragment_score += 2
|
no_fragment_score += 2
|
||||||
if not should_continue:
|
if not should_continue:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
last_fragment = fragments[-1]
|
||||||
|
last_seq = int(re.search(r'(?:/|^)sq/(\d+)', last_fragment['path']).group(1))
|
||||||
|
|
||||||
|
known_fragment = next(
|
||||||
|
(fragment for fragment in fragments if f'sq/{known_idx}' in fragment['path']), None)
|
||||||
|
if known_fragment and known_fragment['end'] > section_end:
|
||||||
|
break
|
||||||
|
|
||||||
if known_idx > last_seq:
|
if known_idx > last_seq:
|
||||||
last_segment_url = None
|
last_segment_url = None
|
||||||
continue
|
continue
|
||||||
|
@ -2832,20 +2841,31 @@ def _extract_sequence_from_mpd(refresh_sequence, immediate):
|
||||||
if begin_index < 0 and known_idx < 0:
|
if begin_index < 0 and known_idx < 0:
|
||||||
# skip from the start when it's negative value
|
# skip from the start when it's negative value
|
||||||
known_idx = last_seq + begin_index
|
known_idx = last_seq + begin_index
|
||||||
|
|
||||||
if lack_early_segments:
|
if lack_early_segments:
|
||||||
known_idx = max(known_idx, last_seq - int(MAX_DURATION // fragments[-1]['duration']))
|
known_idx = max(known_idx, last_seq - int(MAX_DURATION // last_fragment['duration']))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for idx in range(known_idx, last_seq):
|
for idx in range(known_idx, last_seq):
|
||||||
# do not update sequence here or you'll get skipped some part of it
|
# do not update sequence here or you'll get skipped some part of it
|
||||||
should_continue, _ = _extract_sequence_from_mpd(False, False)
|
should_continue = _extract_sequence_from_mpd(False, False)
|
||||||
if not should_continue:
|
if not should_continue:
|
||||||
known_idx = idx - 1
|
known_idx = idx - 1
|
||||||
raise ExtractorError('breaking out of outer loop')
|
raise ExtractorError('breaking out of outer loop')
|
||||||
|
|
||||||
last_segment_url = urljoin(fragment_base_url, 'sq/%d' % idx)
|
last_segment_url = urljoin(fragment_base_url, 'sq/%d' % idx)
|
||||||
|
frag_duration = last_fragment['duration']
|
||||||
|
frag_start = last_fragment['start'] - (last_seq - idx) * frag_duration
|
||||||
|
frag_end = frag_start + frag_duration
|
||||||
|
|
||||||
|
if frag_start >= section_start and frag_end <= section_end:
|
||||||
yield {
|
yield {
|
||||||
'url': last_segment_url,
|
'url': last_segment_url,
|
||||||
'fragment_count': last_seq,
|
'duration': frag_duration,
|
||||||
|
'start': frag_start,
|
||||||
|
'end': frag_end,
|
||||||
}
|
}
|
||||||
|
|
||||||
if known_idx == last_seq:
|
if known_idx == last_seq:
|
||||||
no_fragment_score += 5
|
no_fragment_score += 5
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in a new issue