This commit is contained in:
2026-03-04 16:50:19 -08:00
parent 818d0c0338
commit 4110c5ec00
6 changed files with 379 additions and 77 deletions

View File

@@ -844,7 +844,6 @@ def _tail_text_file(path: str,
return []
except Exception:
return []
try:
with open(p, "rb") as f:
try:
@@ -864,6 +863,64 @@ def _tail_text_file(path: str,
return []
def _extract_tidal_stream_fallback_url(item: Any) -> Optional[str]:
"""Best-effort HTTP streaming fallback for unresolved tidal:// placeholders."""
def _http_candidate(value: Any) -> Optional[str]:
if isinstance(value, list):
for entry in value:
candidate = _http_candidate(entry)
if candidate:
return candidate
return None
text = str(value or "").strip()
if not text:
return None
if text.lower().startswith(("http://", "https://")):
return text
return None
metadata: Optional[Dict[str, Any]] = None
if isinstance(item, dict):
metadata = item.get("full_metadata") or item.get("metadata")
for key in ("url", "source_url", "target"):
candidate = _http_candidate(item.get(key))
if candidate:
return candidate
else:
try:
metadata = getattr(item, "full_metadata", None) or getattr(item, "metadata", None)
except Exception:
metadata = None
for key in ("url", "source_url", "target"):
try:
candidate = _http_candidate(getattr(item, key, None))
except Exception:
candidate = None
if candidate:
return candidate
if not isinstance(metadata, dict):
return None
for key in (
"_tidal_manifest_url",
"streamUrl",
"audioUrl",
"assetUrl",
"playbackUrl",
"manifestUrl",
"manifestURL",
"url",
):
candidate = _http_candidate(metadata.get(key))
if candidate:
return candidate
return None
def _get_playable_path(
item: Any,
file_storage: Optional[Any],
@@ -950,21 +1007,38 @@ def _get_playable_path(
if manifest_path:
path = manifest_path
else:
# If this is a tidal:// placeholder and we couldn't resolve a manifest, do not fall back.
# If this is a tidal:// placeholder and we couldn't resolve a manifest, do not fall back
# UNLESS the item has already been stored in a backend (store+hash present), in which case
# we clear the tidal:// path so the store-resolution logic below can build a playable URL.
try:
if isinstance(path, str) and path.strip().lower().startswith("tidal:"):
try:
meta = None
if isinstance(item, dict):
meta = item.get("full_metadata") or item.get("metadata")
if store and file_hash and str(file_hash).strip().lower() not in ("", "unknown"):
# Item is stored in a backend — clear the tidal:// placeholder and let
# the hash+store resolution further below build the real playable URL.
path = None
else:
fallback_stream_url = _extract_tidal_stream_fallback_url(item)
if fallback_stream_url:
path = fallback_stream_url
try:
debug(
f"_get_playable_path: using fallback Tidal stream URL {fallback_stream_url}"
)
except Exception:
pass
else:
meta = getattr(item, "full_metadata", None) or getattr(item, "metadata", None)
if isinstance(meta, dict) and meta.get("_tidal_manifest_error"):
print(str(meta.get("_tidal_manifest_error")), file=sys.stderr)
except Exception:
pass
print("Tidal selection has no playable DASH MPD manifest.", file=sys.stderr)
return None
try:
meta = None
if isinstance(item, dict):
meta = item.get("full_metadata") or item.get("metadata")
else:
meta = getattr(item, "full_metadata", None) or getattr(item, "metadata", None)
if isinstance(meta, dict) and meta.get("_tidal_manifest_error"):
print(str(meta.get("_tidal_manifest_error")), file=sys.stderr)
except Exception:
pass
print("Tidal selection has no playable DASH MPD manifest.", file=sys.stderr)
return None
except Exception:
pass