YouTube playlist support: /api/youtube/playlist-preview za pred-confirm, /api/youtube zaznava 'list=' URL in kreira batch (1 job/video). Qnet auto-match na YT naslovu, Confirm dialog v UI z prvih 5 naslovov.
This commit is contained in:
+139
-2
@@ -1345,17 +1345,154 @@ async def upload_video(
|
||||
# ────────────────────────────────────────────────────────────────
|
||||
# YouTube submit
|
||||
# ────────────────────────────────────────────────────────────────
|
||||
class YouTubePlaylistPreviewIn(BaseModel):
|
||||
url: str
|
||||
|
||||
|
||||
@app.post("/api/youtube/playlist-preview")
|
||||
async def youtube_playlist_preview(payload: YouTubePlaylistPreviewIn, user: str = Depends(check_auth)):
|
||||
"""Resolve YouTube URL — vrne listo videov če je playlist, ali en item.
|
||||
|
||||
Frontend uporabi to za preview pred submit-om: če je playlist, user vidi
|
||||
koliko komadov je in lahko potrdi.
|
||||
"""
|
||||
import sys as _sys
|
||||
_sys.path.insert(0, str(SCRIPTS_DIR))
|
||||
try:
|
||||
from yt_download import get_playlist
|
||||
except ImportError:
|
||||
raise HTTPException(500, "yt_download modul ne dostopen")
|
||||
|
||||
try:
|
||||
info = get_playlist(payload.url)
|
||||
except subprocess.TimeoutExpired:
|
||||
raise HTTPException(504, "yt-dlp timeout (>120s) — preveri URL")
|
||||
except Exception as e:
|
||||
raise HTTPException(500, f"yt-dlp napaka: {e}")
|
||||
|
||||
if info.get("error"):
|
||||
raise HTTPException(400, f"yt-dlp: {info['error']}")
|
||||
if not info.get("items"):
|
||||
raise HTTPException(400, "Ni najdenih videov na tem URL-u")
|
||||
|
||||
return info
|
||||
|
||||
|
||||
@app.post("/api/youtube")
|
||||
async def submit_youtube(
|
||||
payload: YouTubeJobIn,
|
||||
background: BackgroundTasks,
|
||||
user: str = Depends(check_auth),
|
||||
):
|
||||
"""Submit YouTube URL — single video ali cela playlist.
|
||||
|
||||
Če URL vsebuje 'list=' parameter, resolve-amo playlist in kreiramo
|
||||
batch jobs (en job za vsak video). Single video kreira en job.
|
||||
"""
|
||||
url = payload.url.strip()
|
||||
is_playlist_url = "list=" in url and "watch?" not in url.split("list=")[0].split("&")[0]
|
||||
# Bolj robustno: če ima list= ampak tudi v= potem je morda single video iz playliste
|
||||
# → tretiraj kot single (default)
|
||||
has_list = "list=" in url
|
||||
has_v = "v=" in url or "/watch?" in url or "youtu.be/" in url
|
||||
treat_as_playlist = has_list and not has_v # samo "playlist?list=..." URLs
|
||||
# Pa tudi če je explicit "/playlist?" v URL-u
|
||||
if "/playlist?" in url:
|
||||
treat_as_playlist = True
|
||||
|
||||
if treat_as_playlist:
|
||||
# Resolve playlist
|
||||
import sys as _sys
|
||||
_sys.path.insert(0, str(SCRIPTS_DIR))
|
||||
try:
|
||||
from yt_download import get_playlist
|
||||
except ImportError:
|
||||
raise HTTPException(500, "yt_download modul ne dostopen")
|
||||
|
||||
try:
|
||||
info = get_playlist(url)
|
||||
except Exception as e:
|
||||
raise HTTPException(500, f"Playlist resolve napaka: {e}")
|
||||
|
||||
items = info.get("items", [])
|
||||
if not items:
|
||||
raise HTTPException(400, "Playlist je prazen ali ni dostopen")
|
||||
|
||||
# Batch ID za grupiranje
|
||||
batch_id = "yt-batch-" + uuid.uuid4().hex[:8]
|
||||
playlist_title = info.get("playlist_title", "YouTube Playlist")
|
||||
created_jobs = []
|
||||
|
||||
for item in items:
|
||||
job_id = uuid.uuid4().hex[:12]
|
||||
# Probaj match proti Qnet bazi (parsa Artist - Title iz YouTube naslova)
|
||||
yt_title = item.get("title", "")
|
||||
qm = qnet_match.match_filename(yt_title)
|
||||
|
||||
job = {
|
||||
"id": job_id,
|
||||
"source_type": "youtube",
|
||||
"youtube_url": item["url"],
|
||||
"youtube_title": yt_title,
|
||||
"youtube_id": item.get("id"),
|
||||
"youtube_uploader": item.get("uploader", ""),
|
||||
"playlist_url": url,
|
||||
"playlist_title": playlist_title,
|
||||
"batch_id": batch_id,
|
||||
"status": "queued",
|
||||
"current_step": "V vrsti za YouTube prenos",
|
||||
"created_at": time.time(),
|
||||
"updated_at": time.time(),
|
||||
"mode": payload.mode,
|
||||
"lang": payload.lang,
|
||||
"auto_chorus": payload.auto_chorus,
|
||||
"start": payload.start,
|
||||
"duration": payload.duration,
|
||||
"no_subs": payload.no_subs,
|
||||
"subtitle_style": payload.subtitle_style,
|
||||
"whisper_model": payload.whisper_model,
|
||||
"quality": payload.quality,
|
||||
"tv_station": payload.tv_station,
|
||||
}
|
||||
|
||||
# Qnet match — auto-fill clean Artist/Title in tv_station če je matched
|
||||
if qm["matched"] and qm["confidence"] >= 0.85:
|
||||
job["parsed_artist"] = qm["artist"]
|
||||
job["parsed_title"] = qm["title"]
|
||||
job["has_clean_name"] = True
|
||||
job["qnet_match"] = {
|
||||
"method": qm["method"],
|
||||
"confidence": qm["confidence"],
|
||||
"matched_file": qm["file"],
|
||||
"matched_station": qm["station"],
|
||||
}
|
||||
# Auto-set tv_station iz Qnet match-a (override default)
|
||||
job["tv_station"] = qm["station"]
|
||||
else:
|
||||
# Fallback: regex parser na YT naslovu
|
||||
a, t = parse_artist_title(yt_title)
|
||||
if a: job["parsed_artist"] = a
|
||||
if t: job["parsed_title"] = t
|
||||
job["has_clean_name"] = bool(a and t)
|
||||
|
||||
save_job(job)
|
||||
created_jobs.append(job)
|
||||
|
||||
print(f"📋 YouTube playlist '{playlist_title}': {len(created_jobs)} jobov v batch {batch_id}", flush=True)
|
||||
return {
|
||||
"is_playlist": True,
|
||||
"batch_id": batch_id,
|
||||
"playlist_title": playlist_title,
|
||||
"count": len(created_jobs),
|
||||
"jobs": created_jobs,
|
||||
}
|
||||
|
||||
# ─── Single video ───
|
||||
job_id = uuid.uuid4().hex[:12]
|
||||
job = {
|
||||
"id": job_id,
|
||||
"source_type": "youtube",
|
||||
"youtube_url": payload.url,
|
||||
"youtube_url": url,
|
||||
"status": "queued",
|
||||
"current_step": "V vrsti za YouTube prenos",
|
||||
"created_at": time.time(),
|
||||
@@ -1369,9 +1506,9 @@ async def submit_youtube(
|
||||
"subtitle_style": payload.subtitle_style,
|
||||
"whisper_model": payload.whisper_model,
|
||||
"quality": payload.quality,
|
||||
"tv_station": payload.tv_station,
|
||||
}
|
||||
save_job(job)
|
||||
# Queue worker bo pograbil
|
||||
return job
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user