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 catalog
|
||||||
from core import flatten
|
from core import flatten
|
||||||
from core import charts
|
from core import charts
|
||||||
|
from core import royalty_check as royalty
|
||||||
|
|
||||||
|
|
||||||
SPOTIFY_GUIDE_TEXT = """\
|
SPOTIFY_GUIDE_TEXT = """\
|
||||||
@@ -118,6 +119,7 @@ class Api:
|
|||||||
self._dedup_thread: Optional[threading.Thread] = None
|
self._dedup_thread: Optional[threading.Thread] = None
|
||||||
self._catalog_thread: Optional[threading.Thread] = None
|
self._catalog_thread: Optional[threading.Thread] = None
|
||||||
self._flatten_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
|
# Coda usata dal resolve_callback per attendere la scelta utente
|
||||||
# sul modal "match locali multipli" della tab Upgrade.
|
# sul modal "match locali multipli" della tab Upgrade.
|
||||||
self._upgrade_resolve_q: "queue.Queue[dict]" = queue.Queue(1)
|
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(),
|
"cookies_browser": (payload.get("cookies_browser") or "").strip().lower(),
|
||||||
"output_dir": (payload.get("output_dir") or "").strip(),
|
"output_dir": (payload.get("output_dir") or "").strip(),
|
||||||
"theme": payload.get("theme", "dark"),
|
"theme": payload.get("theme", "dark"),
|
||||||
|
"jamendo_client_id": (payload.get("jamendo_client_id") or "").strip(),
|
||||||
})
|
})
|
||||||
save_config(config)
|
save_config(config)
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
@@ -2384,3 +2387,130 @@ class Api:
|
|||||||
self._emit("charts:add_to_queue", payload)
|
self._emit("charts:add_to_queue", payload)
|
||||||
self._log("charts", f"[INFO] {len(tracks)} brani inviati alla coda Scarica")
|
self._log("charts", f"[INFO] {len(tracks)} brani inviati alla coda Scarica")
|
||||||
return {"ok": True, "count": len(tracks)}
|
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
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
VERSION = "v1.13.0"
|
VERSION = "v1.14.0"
|
||||||
|
|
||||||
|
|
||||||
APP_NAME = "MusicTools"
|
APP_NAME = "MusicTools"
|
||||||
@@ -95,6 +95,10 @@ DEFAULTS = {
|
|||||||
"charts_lastfm_genre": "dance", # "" | pop, rock, dance, ...
|
"charts_lastfm_genre": "dance", # "" | pop, rock, dance, ...
|
||||||
"charts_spotify_playlist": "top50_global", # chiave in charts._SPOTIFY_CHARTS
|
"charts_spotify_playlist": "top50_global", # chiave in charts._SPOTIFY_CHARTS
|
||||||
"lastfm_api_key": "", # opzionale: override della chiave demo
|
"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 ----
|
# ---- Licenza ----
|
||||||
"license_key": "", # chiave fornita all'utente via email
|
"license_key": "", # chiave fornita all'utente via email
|
||||||
"license_email": "", # email associata all'acquisto
|
"license_email": "", # email associata all'acquisto
|
||||||
|
|||||||
Reference in new issue
Block a user