diff --git a/app/main.py b/app/main.py index ea96e91..765f289 100644 --- a/app/main.py +++ b/app/main.py @@ -1772,6 +1772,27 @@ async def qnet_match_filename(filename: str, user: str = Depends(check_auth)): return qnet_match.match_filename(filename) +class QnetBatchMatchRequest(BaseModel): + filenames: list[str] + min_confidence: float = 0.85 + + +@app.post("/api/qnet/match-batch") +async def qnet_match_batch(payload: QnetBatchMatchRequest, user: str = Depends(check_auth)): + """Batch match — kliče client ob izbiri datotek za live preview v queue.""" + results = {} + for fn in payload.filenames: + if not fn: + continue + r = qnet_match.match_filename(fn) + # Vrni samo zadetke nad minimum confidence; sicer null + if r["matched"] and r["confidence"] >= payload.min_confidence: + results[fn] = r + else: + results[fn] = None + return {"results": results} + + @app.post("/api/qnet/sync") async def qnet_sync(background: BackgroundTasks, user: str = Depends(check_auth)): """Sproži sync (fetch Songs.txt iz vseh playerjev). Async background task.""" diff --git a/templates/index.html b/templates/index.html index 847120f..3bfe3f7 100644 --- a/templates/index.html +++ b/templates/index.html @@ -504,28 +504,45 @@ const newItems = []; for (const f of files) { const [artist, title] = parseArtistTitle(f.name); - newItems.push({ file: f, artist, title, dedup: null }); + newItems.push({ file: f, artist, title, dedup: null, qnetMatch: null }); } - // Dedup check pred dodanjem v queue + const filenames = newItems.map(i => i.file.name); const tvStation = $("#tv-station-input").value || "FOLX SLOVENIJA"; - try { - const r = await fetch("/api/dedup/check", { + + // Vzporedno: dedup check + Qnet match (oba endpointa, neodvisna) + const [dedupRes, qnetRes] = await Promise.all([ + fetch("/api/dedup/check", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ filenames: newItems.map(i => i.file.name), tv_station: tvStation }), + body: JSON.stringify({ filenames, tv_station: tvStation }), + }).then(r => r.ok ? r.json() : null).catch(e => { console.warn("Dedup check failed:", e); return null; }), + fetch("/api/qnet/match-batch", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ filenames, min_confidence: 0.85 }), + }).then(r => r.ok ? r.json() : null).catch(e => { console.warn("Qnet match failed:", e); return null; }), + ]); + + // Apply dedup + if (dedupRes && dedupRes.results) { + newItems.forEach(item => { + const m = dedupRes.results[item.file.name]; + if (m) item.dedup = m; + }); + } + + // Apply Qnet match — če baza prepozna komad, prepiši artist+title + if (qnetRes && qnetRes.results) { + newItems.forEach(item => { + const m = qnetRes.results[item.file.name]; + if (m && m.matched) { + item.qnetMatch = m; + // Auto-fill artist+title iz Qnet baze (clean podatki) + item.artist = m.artist; + item.title = m.title; + } }); - if (r.ok) { - const data = await r.json(); - newItems.forEach(item => { - const match = data.results[item.file.name]; - if (match) { - item.dedup = match; // {normalized_name, tv_station, filename_orig, job_id, nextcloud_url, file_size_mb, uploaded_at} - } - }); - } - } catch (e) { - console.warn("Dedup check failed:", e); } pendingFiles.push(...newItems); @@ -579,7 +596,14 @@ div.className = "file-queue-item"; const sizeMB = (item.file.size / 1024 / 1024).toFixed(1); let nameHtml; - if (item.artist && item.title) { + if (item.qnetMatch) { + // Prepoznano iz Qnet player baze — clean artist+title + station badge + const m = item.qnetMatch; + const stationBadge = `${escapeHtml(m.station)}`; + const confLabel = m.confidence >= 0.95 ? "" : ` (${Math.round(m.confidence*100)}%)`; + nameHtml = `${escapeHtml(m.artist)} — ${escapeHtml(m.title)}${stationBadge}` + + `
🎵 prepoznano iz Qnet baze${confLabel} · ${escapeHtml(item.file.name)}
`; + } else if (item.artist && item.title) { nameHtml = `${escapeHtml(item.artist)} — ${escapeHtml(item.title)}` + `
${escapeHtml(item.file.name)}
`; } else {