bridge: metodi Api per tab Diritti + bump v1.14.0 + config jamendo_client_id
- VERSION v1.13.0 -> v1.14.0
- DEFAULTS: royalty_last_folder, royalty_recursive, jamendo_client_id
- save_settings: persiste jamendo_client_id
- Nuovi metodi Api:
- royalty_pick_folder
- royalty_start_scan {directory, recursive}
- royalty_stop_scan
piu' worker _royalty_worker che emette:
- royalty:progress (per file: idx/total/filename/status + overall%)
- royalty:entry (streaming per ogni risultato)
- royalty:done (summary {ok, total, royalty_free_count,
unknown_count, error_count})
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
1 parent
018d310f53
commit
ddacd050d8
2 files changed
+135
-1
No files matched your search
+130
@@ -50,6 +50,7 @@ from core import dedup
|
||||
from core import catalog
|
||||
from core import flatten
|
||||
from core import charts
|
||||
from core import royalty_check as royalty
|
||||
|
||||
|
||||
SPOTIFY_GUIDE_TEXT = """\
|
||||
@@ -118,6 +119,7 @@ class Api:
|
||||
self._dedup_thread: Optional[threading.Thread] = None
|
||||
self._catalog_thread: Optional[threading.Thread] = None
|
||||
self._flatten_thread: Optional[threading.Thread] = None
|
||||
self._royalty_thread: Optional[threading.Thread] = None
|
||||
# Coda usata dal resolve_callback per attendere la scelta utente
|
||||
# sul modal "match locali multipli" della tab Upgrade.
|
||||
self._upgrade_resolve_q: "queue.Queue[dict]" = queue.Queue(1)
|
||||
@@ -211,6 +213,7 @@ class Api:
|
||||
"cookies_browser": (payload.get("cookies_browser") or "").strip().lower(),
|
||||
"output_dir": (payload.get("output_dir") or "").strip(),
|
||||
"theme": payload.get("theme", "dark"),
|
||||
"jamendo_client_id": (payload.get("jamendo_client_id") or "").strip(),
|
||||
})
|
||||
save_config(config)
|
||||
return {"ok": True}
|
||||
@@ -2384,3 +2387,130 @@ class Api:
|
||||
self._emit("charts:add_to_queue", payload)
|
||||
self._log("charts", f"[INFO] {len(tracks)} brani inviati alla coda Scarica")
|
||||
return {"ok": True, "count": len(tracks)}
|
||||
|
||||
# ================================================================
|
||||
# DIRITTI — verifica royalty-free status (ccMixter + Jamendo)
|
||||
# ================================================================
|
||||
def royalty_pick_folder(self) -> str:
|
||||
"""Folder picker per la cartella da analizzare."""
|
||||
return self.browse_directory()
|
||||
|
||||
def royalty_start_scan(self, payload: dict) -> dict:
|
||||
"""Avvia worker di scansione royalty-free.
|
||||
|
||||
Payload: {directory, recursive}.
|
||||
Persiste `royalty_last_folder` e `royalty_recursive` in config.
|
||||
"""
|
||||
if self._royalty_thread and self._royalty_thread.is_alive():
|
||||
return {"ok": False, "error": "Analisi diritti gia in corso"}
|
||||
|
||||
directory = (payload.get("directory") or "").strip()
|
||||
recursive = bool(payload.get("recursive", True))
|
||||
|
||||
if not directory:
|
||||
return {"ok": False, "error": "Cartella non impostata"}
|
||||
if not os.path.isdir(directory):
|
||||
return {"ok": False, "error": "Cartella non trovata"}
|
||||
|
||||
try:
|
||||
cfg = load_config()
|
||||
cfg["royalty_last_folder"] = directory
|
||||
cfg["royalty_recursive"] = recursive
|
||||
save_config(cfg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._royalty_thread = threading.Thread(
|
||||
target=self._royalty_worker,
|
||||
args=(directory, recursive),
|
||||
daemon=True,
|
||||
)
|
||||
self._royalty_thread.start()
|
||||
return {"ok": True}
|
||||
|
||||
def royalty_stop_scan(self) -> dict:
|
||||
royalty.request_stop()
|
||||
self._log("royalty", "[INFO] Interruzione richiesta...")
|
||||
return {"ok": True}
|
||||
|
||||
def _royalty_worker(self, directory: str, recursive: bool) -> None:
|
||||
"""Esegue scan royalty-free in background, emette streaming."""
|
||||
view = "royalty"
|
||||
royalty.reset_stop()
|
||||
self._log(view,
|
||||
f"[INFO] Analisi diritti: {directory} (recursive={recursive})")
|
||||
|
||||
_last = [0.0]
|
||||
_THROTTLE = 0.05
|
||||
|
||||
def progress_cb(idx: int, total: int, filename: str,
|
||||
status: str, err_msg: str = "") -> None:
|
||||
if status == "checking":
|
||||
now = time.monotonic()
|
||||
if now - _last[0] < _THROTTLE and idx != total:
|
||||
return
|
||||
_last[0] = now
|
||||
payload_evt = {
|
||||
"idx": idx, "total": total,
|
||||
"filename": filename, "status": status,
|
||||
}
|
||||
if err_msg:
|
||||
payload_evt["error_msg"] = err_msg
|
||||
if total > 0:
|
||||
payload_evt["overall"] = min(idx / total, 1.0)
|
||||
if status == "completed":
|
||||
payload_evt["overall"] = 1.0
|
||||
self._log(view, f"[INFO] Analisi completata ({total} file).")
|
||||
elif status == "stopped":
|
||||
self._log(view, "[INFO] Analisi interrotta.")
|
||||
elif status == "error" and filename:
|
||||
detail = f": {err_msg}" if err_msg else ""
|
||||
self._log(view, f"[ERRORE] {filename}{detail}")
|
||||
self._emit("royalty:progress", payload_evt)
|
||||
|
||||
def entry_cb(entry: dict) -> None:
|
||||
"""Streaming: appena un file e' processato, emit alla UI."""
|
||||
try:
|
||||
self._emit("royalty:entry", entry)
|
||||
except Exception:
|
||||
pass
|
||||
# Log per file: solo quando trova un match RF
|
||||
try:
|
||||
st = entry.get("status", "")
|
||||
if st == "royalty_free":
|
||||
matches = entry.get("matches") or []
|
||||
src = matches[0].get("source", "?") if matches else "?"
|
||||
lic = matches[0].get("license", "") if matches else ""
|
||||
from pathlib import Path as _P
|
||||
name = _P(entry.get("path", "")).name
|
||||
self._log(view, f"[OK] {name} → {src} ({lic})")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
entries = royalty.scan_folder(
|
||||
directory,
|
||||
recursive=recursive,
|
||||
progress_callback=progress_cb,
|
||||
entry_callback=entry_cb,
|
||||
)
|
||||
except Exception as e:
|
||||
self._log(view, f"[ERRORE] {e}")
|
||||
self._emit("royalty:done", {"ok": False, "error": str(e),
|
||||
"entries": []})
|
||||
return
|
||||
|
||||
n_rf = sum(1 for e in entries if e.get("status") == "royalty_free")
|
||||
n_unk = sum(1 for e in entries if e.get("status") == "unknown")
|
||||
n_err = sum(1 for e in entries if e.get("status") == "error")
|
||||
self._log(view,
|
||||
f"[INFO] Royalty-free: {n_rf} · sconosciuti: {n_unk} · "
|
||||
f"errori: {n_err} (totale {len(entries)})")
|
||||
self._emit("royalty:done", {
|
||||
"ok": True,
|
||||
"entries": entries,
|
||||
"total": len(entries),
|
||||
"royalty_free_count": n_rf,
|
||||
"unknown_count": n_unk,
|
||||
"error_count": n_err,
|
||||
})
|
||||
+5
-1
@@ -5,7 +5,7 @@ import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
VERSION = "v1.13.0"
|
||||
VERSION = "v1.14.0"
|
||||
|
||||
|
||||
APP_NAME = "MusicTools"
|
||||
@@ -95,6 +95,10 @@ DEFAULTS = {
|
||||
"charts_lastfm_genre": "dance", # "" | pop, rock, dance, ...
|
||||
"charts_spotify_playlist": "top50_global", # chiave in charts._SPOTIFY_CHARTS
|
||||
"lastfm_api_key": "", # opzionale: override della chiave demo
|
||||
# ---- Diritti / Royalty-free check ----
|
||||
"royalty_last_folder": "",
|
||||
"royalty_recursive": True,
|
||||
"jamendo_client_id": "", # opzionale: registrati su devportal.jamendo.com
|
||||
# ---- Licenza ----
|
||||
"license_key": "", # chiave fornita all'utente via email
|
||||
"license_email": "", # email associata all'acquisto
|
||||
|
||||
Reference in new issue
Block a user