feat(titles): normalize video titles for display (feed, search, downloads)
Noisy YouTube titles are cleaned for display + storage, raw kept in videos.original_title: - app/titles.normalize_title: strip emoji/symbols (keep accents), remove trailing SEO hashtag clusters (keep numeric #3 episode markers), context-aware de-shout (mostly-ALL-CAPS titles -> Title Case with an acronym whitelist + function-word lowercasing; otherwise only long all-caps words), collapse repeated punctuation - applied at enrichment (sync/videos.py) and in the download worker (ad-hoc yt-dlp titles); catalog downloads inherit the normalized title automatically - migration 0039: add original_title, preserve raw, rewrite title (generated search_vector regenerates); reversible via original_title Backfill on localdev: 122115/273417 titles normalized in ~2 min. Verified in the feed + on real messy samples (emoji/de-shout/hashtags), accents + acronyms (PS5/AI/USA/PC) preserved.
This commit is contained in:
parent
b200f61642
commit
d55799cbc1
5 changed files with 140 additions and 4 deletions
|
|
@ -30,6 +30,7 @@ from app.config import settings
|
|||
from app.db import SessionLocal
|
||||
from app.downloads import formats, quota, service, storage
|
||||
from app.models import DownloadJob, MediaAsset
|
||||
from app.titles import normalize_title
|
||||
|
||||
log = logging.getLogger("siftlode.worker")
|
||||
|
||||
|
|
@ -190,7 +191,7 @@ def _fill_asset_meta_early(asset_id: int, info: dict) -> None:
|
|||
asset = db.get(MediaAsset, asset_id)
|
||||
if asset is None or asset.title:
|
||||
return
|
||||
asset.title = info.get("title")
|
||||
asset.title = normalize_title(info.get("title"))
|
||||
asset.uploader = info.get("uploader") or info.get("channel")
|
||||
asset.thumbnail_url = info.get("thumbnail")
|
||||
asset.duration_s = int(info["duration"]) if info.get("duration") else None
|
||||
|
|
@ -297,7 +298,7 @@ def _download(job_id: int, asset_id: int, source_kind: str, source_ref: str, spe
|
|||
ext = produced.suffix.lstrip(".").lower()
|
||||
meta = storage.MediaMeta(
|
||||
video_id=info.get("id") or source_ref,
|
||||
title=info.get("title") or source_ref,
|
||||
title=normalize_title(info.get("title")) or source_ref,
|
||||
uploader=info.get("uploader") or info.get("channel") or "Unknown Channel",
|
||||
upload_date=_parse_upload_date(info.get("upload_date")),
|
||||
duration_s=int(info["duration"]) if info.get("duration") else None,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue