Skip ffmpeg for extensionless HLS segments

This commit is contained in:
Codex
2026-08-09 12:46:19 +02:00
parent 3018e7aa66
commit 499271c3b8
5 changed files with 62 additions and 9 deletions
+43 -7
View File
@@ -247,6 +247,14 @@ def resolve_hls_input_url(stream):
return best_hls_variant_url(url, data)
def hls_media_playlist(stream, input_url):
playlist = fetch_text(input_url, headers=stream.get("headers") or {})
if "#EXT-X-STREAM-INF" in playlist:
input_url = best_hls_variant_url(input_url, playlist)
playlist = fetch_text(input_url, headers=stream.get("headers") or {})
return input_url, playlist
def ffmpeg_base_command(stream, input_url):
cmd = [
"ffmpeg",
@@ -320,6 +328,17 @@ def parse_hls_segments(playlist_url, playlist_text):
return segments
def hls_segments_need_native_download(segments):
for segment in segments or []:
path = urlparse(segment.get("url") or "").path
name = path.rsplit("/", 1)[-1]
if "." not in name:
return True
if "/ad-site-i18n/" in path:
return True
return False
def decrypt_aes128_segment(data, key, iv_value):
openssl = shutil.which("openssl")
if not openssl:
@@ -343,10 +362,7 @@ def decrypt_aes128_segment(data, key, iv_value):
def download_hls_segments(stream, input_url, target, partial):
playlist = fetch_text(input_url, headers=stream.get("headers") or {})
if "#EXT-X-STREAM-INF" in playlist:
input_url = best_hls_variant_url(input_url, playlist)
playlist = fetch_text(input_url, headers=stream.get("headers") or {})
input_url, playlist = hls_media_playlist(stream, input_url)
segments = parse_hls_segments(input_url, playlist)
if not segments:
return 1
@@ -357,7 +373,7 @@ def download_hls_segments(stream, input_url, target, partial):
segment_dir.mkdir(parents=True, exist_ok=True)
key_cache = {}
try:
print(f"Direct ffmpeg HLS failed; downloading {len(segments)} playlist segments...")
print(f"Downloading {len(segments)} HLS playlist segments directly...")
with ts_file.open("wb") as joined:
for index, segment in enumerate(segments, start=1):
data = fetch_bytes(segment["url"], headers=stream.get("headers") or {}, timeout=60)
@@ -392,7 +408,7 @@ def download_hls_segments(stream, input_url, target, partial):
"mp4",
str(partial),
]
code = subprocess.call(cmd)
code = run_ffmpeg(cmd, timeout=300)
if code == 0 and partial.exists() and partial.stat().st_size > 0:
partial.replace(target)
return 0
@@ -405,6 +421,14 @@ def download_hls_segments(stream, input_url, target, partial):
shutil.rmtree(segment_dir, ignore_errors=True)
def run_ffmpeg(cmd, timeout=180):
try:
return subprocess.run(cmd, check=False, timeout=timeout).returncode
except subprocess.TimeoutExpired:
print(f"ffmpeg timed out after {timeout}s; trying fallback.", file=sys.stderr)
return 124
def download_subtitles(subtitles, output_base):
saved = []
english = [
@@ -430,6 +454,18 @@ def download_episode(stream, target):
path.unlink()
except FileNotFoundError:
pass
if stream.get("isM3U8") or ".m3u8" in str(input_url):
try:
media_url, playlist = hls_media_playlist(stream, input_url)
segments = parse_hls_segments(media_url, playlist)
if hls_segments_need_native_download(segments):
print("HLS playlist uses extensionless provider segments; skipping direct ffmpeg.")
code = download_hls_segments(stream, media_url, target, partial)
if code == 0:
return 0
return code
except Exception as exc:
print(f"Native HLS preflight failed: {exc}", file=sys.stderr)
attempts = [
[
*ffmpeg_base_command(stream, input_url),
@@ -468,7 +504,7 @@ def download_episode(stream, target):
partial.unlink()
except FileNotFoundError:
pass
code = subprocess.call(cmd)
code = run_ffmpeg(cmd)
if code == 0 and partial.exists() and partial.stat().st_size > 0:
partial.replace(target)
return 0