addon: switch play action to resolve_play (yt-dlp combined format)
Realized during M6 packaging that the rustypipe path returns separate audio + video DASH streams (Opus 251 + AV1 401 on the smoke video). Kodi can't sync those without an inputstream.adaptive DASH manifest, which would need server-side manifest generation — M3+ territory. Stopgap for shippable M3: new sidecar op resolve_play that asks yt-dlp for -f best[ext=mp4]/best — one combined audio+video URL Kodi plays as plain HTTP. ~3-5s overhead vs rustypipe but reliable sync. main.py _play() now calls resolve_play. resolve still exists for metadata + browse paths (M4 will use it). Rebuilt aarch64-musl binary, repackaged plugin.video.torttube-0.0.1.zip (38.7MB, md5 f2c08aed130b1c1bd231a9b6cbfac93c). Live at: smb://fileserver/downloads/torttube/plugin.video.torttube-0.0.1.zip
This commit is contained in:
parent
2df2c0622a
commit
55f8fc7c06
3 changed files with 76 additions and 33 deletions
|
|
@ -105,12 +105,15 @@ def _resolved_listitem(stream_url: str, title: str | None) -> xbmcgui.ListItem:
|
|||
|
||||
|
||||
def _play(yt_id: str) -> None:
|
||||
"""Resolve via sidecar, hand the URL to Kodi's player."""
|
||||
"""Resolve via sidecar (yt-dlp combined-format path), hand URL to Kodi."""
|
||||
_log(f"play id={yt_id}")
|
||||
try:
|
||||
resp = _call_sidecar({"op": "resolve", "id": yt_id})
|
||||
# resolve_play returns ONE combined audio+video URL — guaranteed
|
||||
# to play in Kodi without needing inputstream.adaptive/DASH.
|
||||
# ~3-5s overhead vs rustypipe but reliable audio sync.
|
||||
resp = _call_sidecar({"op": "resolve_play", "id": yt_id}, timeout_s=45)
|
||||
except Exception as e:
|
||||
_log(f"sidecar resolve failed: {e}", xbmc.LOGERROR)
|
||||
_log(f"sidecar resolve_play failed: {e}", xbmc.LOGERROR)
|
||||
xbmcgui.Dialog().notification(
|
||||
"torttube", f"resolve failed: {e}", xbmcgui.NOTIFICATION_ERROR, 5000
|
||||
)
|
||||
|
|
@ -127,34 +130,8 @@ def _play(yt_id: str) -> None:
|
|||
xbmcplugin.setResolvedUrl(_HANDLE, False, xbmcgui.ListItem())
|
||||
return
|
||||
|
||||
# Pick a URL. Preference order for M3:
|
||||
# 1. video_stream.url if it has embedded audio (yt-dlp combined formats)
|
||||
# 2. video_stream.url (will need audio mux in a later milestone)
|
||||
# 3. audio_stream.url (audio-only playback)
|
||||
stream_url = None
|
||||
title = None
|
||||
source = resp.get("source", "?")
|
||||
|
||||
if source == "rustypipe":
|
||||
details = resp.get("details") or {}
|
||||
title = details.get("name") or details.get("title")
|
||||
vs = resp.get("video_stream")
|
||||
as_ = resp.get("audio_stream")
|
||||
# rustypipe separates audio + video. For M3 we play video_stream;
|
||||
# M3+ will wire a DASH manifest or merged-format selection for sync.
|
||||
if vs and vs.get("url"):
|
||||
stream_url = vs["url"]
|
||||
elif as_ and as_.get("url"):
|
||||
stream_url = as_["url"]
|
||||
else: # yt-dlp tier 2
|
||||
title = resp.get("title")
|
||||
streams = resp.get("streams") or []
|
||||
# yt-dlp's combined formats come back as entries with both audio + video.
|
||||
combined = [s for s in streams if not s.get("is_audio_only") and not s.get("is_video_only")]
|
||||
candidates = combined or streams
|
||||
if candidates:
|
||||
stream_url = candidates[0].get("url")
|
||||
|
||||
stream_url = resp.get("stream_url")
|
||||
title = resp.get("title")
|
||||
if not stream_url:
|
||||
_log("no usable stream URL in sidecar response", xbmc.LOGERROR)
|
||||
xbmcgui.Dialog().notification(
|
||||
|
|
@ -163,7 +140,7 @@ def _play(yt_id: str) -> None:
|
|||
xbmcplugin.setResolvedUrl(_HANDLE, False, xbmcgui.ListItem())
|
||||
return
|
||||
|
||||
_log(f"resolved via {source}, playing")
|
||||
_log(f"resolved via {resp.get('source')}, playing")
|
||||
xbmcplugin.setResolvedUrl(_HANDLE, True, _resolved_listitem(stream_url, title))
|
||||
|
||||
|
||||
|
|
|
|||
Reference in a new issue