api / core /naming.py
Codebuff
Fix Kick streams by making every rung prove it produced bytes
4144c35
Raw History Blame Contribute Delete
2.2 kB
"""Output filename derivation.
Bugs fixed from the previous version:
* twitch.tv/videos/123 produced "www.twitch.tv_123" (the channel guess matched
the host, because the host is the path element before "videos").
* Kick VOD slugs were truncated to 12 chars, so every VOD of a channel with a
long shared title prefix collided on the same filename.
"""
from __future__ import annotations
import hashlib
import re
import uuid
from urllib.parse import urlparse
_DOMAINS = (
("kick.com", "kick"),
("twitch.tv", "twitch"),
("youtube.com", "youtube"),
("youtu.be", "youtube"),
)
_SAFE_RE = re.compile(r"[^A-Za-z0-9._-]+")
_KEEP_TITLE = 40
_MAX_LEN = 110
def _platform(url: str) -> str:
host = urlparse(url).netloc.lower()
for domain, name in _DOMAINS:
if host == domain or host.endswith("." + domain):
return name
return "stream"
def _channel(url: str, platform: str) -> str:
parts = [p for p in urlparse(url).path.split("/") if p]
if platform == "kick" and len(parts) >= 3 and parts[1] == "videos":
return parts[0]
return ""
def _shorten(slug: str) -> str:
slug = slug.strip()
if len(slug) <= _KEEP_TITLE:
return slug
head = slug[:_KEEP_TITLE]
if "-" in head[9:]:
head = head[: head.rfind("-")]
digest = hashlib.sha1(slug.encode("utf-8")).hexdigest()[:6]
return f"{head}-{digest}"
def name_from_url(url: str, fallback: str | None = None) -> str:
platform = _platform(url)
vod = re.search(r"/videos/([^/?#]+)", url) or re.search(r"/shorts/([^/?#]+)", url)
if vod:
parts = [platform, _channel(url, platform), _shorten(vod.group(1))]
return _sanitize("_".join(p for p in parts if p))
watch = re.search(r"[?&]v=([A-Za-z0-9_-]{6,})", url)
if watch:
return _sanitize(f"{platform}_{watch.group(1)}")
live = re.search(r"youtu\.be/([A-Za-z0-9_-]{6,})", url)
if live:
return _sanitize(f"{platform}_{live.group(1)}")
return fallback or uuid.uuid4().hex[:8]
def _sanitize(name: str) -> str:
name = _SAFE_RE.sub("_", name).strip("._-")
if not name:
name = uuid.uuid4().hex[:8]
return name[:_MAX_LEN]