Fix NAS playback: Strict HTTP protocol (no HLS), pre-flight checks, and Content-Length

This commit is contained in:
Khoa Vo 2026-01-01 16:21:45 +07:00
parent 7bc230a8fd
commit 2a0a919294
2 changed files with 40 additions and 22 deletions

View file

@ -637,6 +637,21 @@ async def stream_audio(id: str):
'extractor_args': {'youtube': {'player_client': ['ios', 'android']}}, 'extractor_args': {'youtube': {'player_client': ['ios', 'android']}},
} }
if not stream_url:
print(f"DEBUG: Fetching new stream URL for '{id}'")
url = f"https://www.youtube.com/watch?v={id}"
ydl_opts = {
'format': 'bestaudio[ext=m4a][protocol^=http]/bestaudio[protocol^=http]/best[protocol^=http]', # Strictly exclude m3u8/HLS
'quiet': True,
'noplaylist': True,
'nocheckcertificate': True,
'geo_bypass': True,
'socket_timeout': 30,
'retries': 3,
'force_ipv4': True,
'extractor_args': {'youtube': {'player_client': ['android', 'web', 'ios']}}, # Android often gives good progressive streams
}
try: try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl: with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False) info = ydl.extract_info(url, download=False)
@ -649,7 +664,7 @@ async def stream_audio(id: str):
elif ext == 'webm': elif ext == 'webm':
mime_type = "audio/webm" mime_type = "audio/webm"
else: else:
mime_type = "audio/mpeg" # Fallback mime_type = "audio/mpeg"
print(f"DEBUG: Got stream URL format: {info.get('format')}, ext: {ext}, mime: {mime_type}") print(f"DEBUG: Got stream URL format: {info.get('format')}, ext: {ext}, mime: {mime_type}")
except Exception as ydl_error: except Exception as ydl_error:
@ -665,22 +680,35 @@ async def stream_audio(id: str):
print(f"Streaming {id} with Content-Type: {mime_type}") print(f"Streaming {id} with Content-Type: {mime_type}")
# Pre-open the connection to verify it works and get headers
try:
external_req = requests.get(stream_url, stream=True, timeout=30)
external_req.raise_for_status() # Check for 403/404 immediately
except requests.exceptions.HTTPError as http_err:
print(f"DEBUG: Stream Pre-flight HTTP Error: {http_err}")
if http_err.response.status_code == 403:
cache.delete(cache_key)
raise HTTPException(status_code=500, detail="Upstream stream error")
except Exception as e:
print(f"DEBUG: Stream Connection Error: {e}")
raise HTTPException(status_code=500, detail="Stream connection failed")
# Forward Content-Length if available for progress bars
headers = {}
if "Content-Length" in external_req.headers:
headers["Content-Length"] = external_req.headers["Content-Length"]
def iterfile(): def iterfile():
try: try:
with requests.get(stream_url, stream=True, timeout=30) as r: # Use the already open request
r.raise_for_status() for chunk in external_req.iter_content(chunk_size=64*1024):
for chunk in r.iter_content(chunk_size=64*1024): yield chunk
yield chunk external_req.close()
except requests.exceptions.HTTPError as http_err:
print(f"DEBUG: Stream HTTP Error: {http_err}")
if http_err.response.status_code == 403:
cache.delete(cache_key)
raise
except Exception as e: except Exception as e:
print(f"DEBUG: Stream Iterator Error: {e}") print(f"DEBUG: Stream Iterator Error: {e}")
raise pass
return StreamingResponse(iterfile(), media_type=mime_type) return StreamingResponse(iterfile(), media_type=mime_type, headers=headers)
except HTTPException: except HTTPException:
raise raise

View file

@ -9,13 +9,3 @@ services:
volumes: volumes:
- ./data:/app/backend/data - ./data:/app/backend/data
watchtower:
image: containrrr/watchtower
container_name: spotify-watchtower
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 3600 --cleanup
environment:
- WATCHTOWER_INCLUDE_RESTARTING=true