ACRCloud auto-recognition: never block uploads, fall back to fingerprinting

Changes:

1. UI: removed blocking prompt() that asked for artist+title on filename
   that didn't match 'Artist - Title' pattern. Upload always proceeds.
   Instead shows yellow warning saying 'server will try to recognize'.

2. Backend: added scripts/acr_recognize.py — extracts 20s audio sample
   from video (at 15s and 60s offsets for robustness), computes ACRCloud
   fingerprint via native binary (3KB payload), sends to identify API.

3. Pipeline: process_job() now runs ACR recognition step before analysis
   IF parsed_artist or parsed_title is missing. Result is saved to job
   metadata and used for download filename + Scribe/Claude filename hint.

4. Credentials: ACR_HOST + ACR_ACCESS_KEY + ACR_SECRET_KEY env vars
   added to Coolify (using existing keys from openclaw fb-agent metka).

5. requirements.txt: added pyacrcloud==1.0.11 for native fingerprinting.

This unblocks future automation/cron upload pipelines — files don't need
to be perfectly named, ACRCloud will identify them automatically.

Fallback chain:
1. Filename parsing (Artist - Title.mp4)
2. ACRCloud audio fingerprint (works even for '12345.mp4', 'IMG_001.mp4')
3. If both fail: download filename uses 'reel_<id>.mp4' (still works)
This commit is contained in:
2026-04-29 14:24:53 +00:00
parent 3877b822ff
commit b543057cee
4 changed files with 241 additions and 32 deletions
+15 -32
View File
@@ -500,39 +500,22 @@
function handleFileSelected(f) {
const [artist, title] = parseArtistTitle(f.name);
if (!artist || !title) {
// Ni razvidno ime — vprašaj uporabnika
const userArtist = prompt(
`❗ Iz imena datoteke ni razviden izvajalec in naslov.\n\n` +
`Datoteka: "${f.name}"\n\n` +
`Vpiši IZVAJALCA (npr. "Lady Gaga"):`,
""
);
if (!userArtist || !userArtist.trim()) {
alert("⛔ Brez izvajalca ne morem nadaljevati.\n\nPoimenuj datoteko v formatu:\n Izvajalec - Naslov.mp4");
fileInput.value = "";
return;
}
const userTitle = prompt(
`Vpiši NASLOV pesmi (npr. "Abracadabra"):`,
""
);
if (!userTitle || !userTitle.trim()) {
alert("⛔ Brez naslova ne morem nadaljevati.");
fileInput.value = "";
return;
}
pendingArtist = userArtist.trim();
pendingTitle = userTitle.trim();
} else {
pendingArtist = artist;
pendingTitle = title;
}
pendingFile = f;
dz.querySelector("div").innerHTML =
`📹 <b>${pendingArtist} — ${pendingTitle}</b>` +
`<div style="font-size: 11px; color: var(--muted); margin-top: 4px;">${f.name} (${(f.size/1024/1024).toFixed(1)} MB)</div>`;
pendingArtist = artist;
pendingTitle = title;
if (artist && title) {
// Razvidno iz filename
dz.querySelector("div").innerHTML =
`📹 <b>${pendingArtist} — ${pendingTitle}</b>` +
`<div style="font-size: 11px; color: var(--muted); margin-top: 4px;">${f.name} (${(f.size/1024/1024).toFixed(1)} MB)</div>`;
} else {
// Ni razvidno — pokaži opozorilo, ampak NE blokiraj (server bo poskusil ACR auto-recognize)
dz.querySelector("div").innerHTML =
`📹 ${f.name}` +
`<div style="font-size: 11px; color: var(--warn); margin-top: 4px;">⚠ Iz imena ni razviden izvajalec — server bo poskusil avto-prepoznati pesem (ACRCloud)</div>` +
`<div style="font-size: 11px; color: var(--muted); margin-top: 2px;">${(f.size/1024/1024).toFixed(1)} MB</div>`;
}
}
// ─── Settings collector ─────────────────────────