Qnet match v upload queue — auto-prepoznavanje pesmi takoj ko izbere\u0161 fajle (parallel POST /api/qnet/match-batch). \u010ce baza prepozna komad, prikaz Artist \u2014 Title z station badge namesto 'Brez razvidnega imena'.
This commit is contained in:
parent
b938d1e4d8
commit
24e1b53aa8
21
app/main.py
21
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)
|
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")
|
@app.post("/api/qnet/sync")
|
||||||
async def qnet_sync(background: BackgroundTasks, user: str = Depends(check_auth)):
|
async def qnet_sync(background: BackgroundTasks, user: str = Depends(check_auth)):
|
||||||
"""Sproži sync (fetch Songs.txt iz vseh playerjev). Async background task."""
|
"""Sproži sync (fetch Songs.txt iz vseh playerjev). Async background task."""
|
||||||
|
|||||||
@ -504,28 +504,45 @@
|
|||||||
const newItems = [];
|
const newItems = [];
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
const [artist, title] = parseArtistTitle(f.name);
|
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";
|
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",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
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);
|
pendingFiles.push(...newItems);
|
||||||
@ -579,7 +596,14 @@
|
|||||||
div.className = "file-queue-item";
|
div.className = "file-queue-item";
|
||||||
const sizeMB = (item.file.size / 1024 / 1024).toFixed(1);
|
const sizeMB = (item.file.size / 1024 / 1024).toFixed(1);
|
||||||
let nameHtml;
|
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 = `<span style="font-size:9px; padding:1px 5px; background:rgba(74,222,128,0.18); border:1px solid rgba(74,222,128,0.5); border-radius:3px; color:#4ade80; font-weight:600; margin-left:6px; vertical-align:middle;">${escapeHtml(m.station)}</span>`;
|
||||||
|
const confLabel = m.confidence >= 0.95 ? "" : ` <span style="color:var(--muted); font-size:10px;">(${Math.round(m.confidence*100)}%)</span>`;
|
||||||
|
nameHtml = `<b>${escapeHtml(m.artist)} — ${escapeHtml(m.title)}</b>${stationBadge}` +
|
||||||
|
`<div style="font-size:10px;color:var(--muted)">🎵 prepoznano iz Qnet baze${confLabel} · ${escapeHtml(item.file.name)}</div>`;
|
||||||
|
} else if (item.artist && item.title) {
|
||||||
nameHtml = `<b>${escapeHtml(item.artist)} — ${escapeHtml(item.title)}</b>` +
|
nameHtml = `<b>${escapeHtml(item.artist)} — ${escapeHtml(item.title)}</b>` +
|
||||||
`<div style="font-size:10px;color:var(--muted)">${escapeHtml(item.file.name)}</div>`;
|
`<div style="font-size:10px;color:var(--muted)">${escapeHtml(item.file.name)}</div>`;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user