From 4003e5edba576aa5c67659ed9b1a2d9bfe243962 Mon Sep 17 00:00:00 2001 From: Rushaway Date: Sat, 5 Sep 2026 15:23:27 +0200 Subject: [PATCH] refactor(ffmpeg): resolve per-domain request headers from a registry The MyInstants-specific Referer/Sec-Fetch-* headers were applied by an `if needs_cf_bypass and "myinstants.com" in uri` check buried in the generic streaming path, so every additional host would have meant another special case in shared transport code. Replaced it with a DOMAIN_HEADER_PROFILES registry keyed by domain and a get_domain_headers() lookup, so a new host is a data entry. The lookup matches the URI's hostname (exact or subdomain) instead of substring-matching the whole URI. That is a deliberate behavior change: the old check also fired for URLs that merely contained the string, such as https://evil.com/redirect?to=myinstants.com/x.mp3 or https://notmyinstants.com/x.mp3, leaking a myinstants.com Referer to unrelated hosts. Real MyInstants URLs (apex, www and cdn subdomains) still match. Bumps VERSION to 1.8.21. Closes #156 Co-Authored-By: Claude Opus 5 Co-Authored-By: Dolly132 <109222243+Dolly132@users.noreply.github.com> --- VERSION | 2 +- src/torchlight/FFmpegAudioPlayer.py | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/VERSION b/VERSION index 59009bc..2fef18a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.8.13 +1.8.21 diff --git a/src/torchlight/FFmpegAudioPlayer.py b/src/torchlight/FFmpegAudioPlayer.py index 9316ceb..18135c4 100644 --- a/src/torchlight/FFmpegAudioPlayer.py +++ b/src/torchlight/FFmpegAudioPlayer.py @@ -20,6 +20,26 @@ SAMPLEBYTES = 2 +# Extra request headers some hosts require, keyed by domain. Matched against the URI's +# hostname (exact or subdomain), so a new host means adding an entry here rather than +# another special case inside the streaming path. +DOMAIN_HEADER_PROFILES: dict[str, dict[str, str]] = { + "myinstants.com": { + "Referer": MYINSTANTS_URL, + "Sec-Fetch-Dest": "audio", + "Sec-Fetch-Mode": "no-cors", + "Sec-Fetch-Site": "same-origin", + }, +} + + +def get_domain_headers(uri: str) -> dict[str, str]: + hostname = (urlparse(uri).hostname or "").lower() + for domain, headers in DOMAIN_HEADER_PROFILES.items(): + if hostname == domain or hostname.endswith(f".{domain}"): + return headers + return {} + class FFmpegAudioPlayer: VALID_CALLBACKS = ["Play", "Stop", "Update"] @@ -120,11 +140,8 @@ async def _stream_url_to_ffmpeg(self, uri: str, ffmpeg_command: list[str], needs "Accept": "*/*", "Accept-Language": "en-US,en;q=0.9", } - if needs_cf_bypass and "myinstants.com" in uri: - headers["Referer"] = MYINSTANTS_URL - headers["Sec-Fetch-Dest"] = "audio" - headers["Sec-Fetch-Mode"] = "no-cors" - headers["Sec-Fetch-Site"] = "same-origin" + if needs_cf_bypass: + headers.update(get_domain_headers(uri)) if needs_cf_bypass: queue: asyncio.Queue[bytes | None] = asyncio.Queue(maxsize=10)