bridge: metodi Api per tab Charts + bump v1.12.0
- api.charts_fetch(payload): dispatcher unico su iTunes/Last.fm/Spotify/
M2O, persiste la sorgente/params scelti in config, ritorna
{ok, source, entries}
- api.charts_add_to_queue(entries): normalizza le entry selezionate in
tracklist "Artista - Titolo" ed emette l'evento `charts:add_to_queue`
che la UI aggancia per popolare la tab Scarica
- config.DEFAULTS: aggiunte chiavi charts_last_source, charts_itunes_
country, charts_lastfm_decade/genre, charts_spotify_playlist,
lastfm_api_key
- VERSION -> v1.12.0
This commit is contained in:
1 parent
75e3da5f82
commit
29c177ff69
2 files changed
+114
-1
No files matched your search
+106
@@ -49,6 +49,7 @@ from core.upgrader import (
|
|||||||
from core import dedup
|
from core import dedup
|
||||||
from core import catalog
|
from core import catalog
|
||||||
from core import flatten
|
from core import flatten
|
||||||
|
from core import charts
|
||||||
|
|
||||||
|
|
||||||
SPOTIFY_GUIDE_TEXT = """\
|
SPOTIFY_GUIDE_TEXT = """\
|
||||||
@@ -2263,3 +2264,108 @@ class Api:
|
|||||||
"n_matched": n_matched,
|
"n_matched": n_matched,
|
||||||
"n_unmatched": n_unmatched,
|
"n_unmatched": n_unmatched,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# ================================================================
|
||||||
|
# CHARTS — classifiche musicali (iTunes / Spotify / M2O / Last.fm)
|
||||||
|
# ================================================================
|
||||||
|
def charts_fetch(self, payload: dict) -> dict:
|
||||||
|
"""Ritorna le entry di una chart per la sorgente/params richiesti.
|
||||||
|
|
||||||
|
Payload:
|
||||||
|
{
|
||||||
|
source: "itunes" | "spotify" | "m2o" | "lastfm",
|
||||||
|
country?: "it"|"us"|"gb"|"ww", # solo itunes
|
||||||
|
playlist_key?: str, # solo spotify (chiave _SPOTIFY_CHARTS)
|
||||||
|
decade?: str, # solo lastfm ("" | "70s"|"80s"|...)
|
||||||
|
genre?: str, # solo lastfm ("" | pop|rock|dance|...)
|
||||||
|
force?: bool, # bypass cache
|
||||||
|
}
|
||||||
|
Persiste anche la scelta corrente in config.
|
||||||
|
"""
|
||||||
|
payload = payload or {}
|
||||||
|
source = (payload.get("source") or "itunes").strip().lower()
|
||||||
|
force = bool(payload.get("force", False))
|
||||||
|
|
||||||
|
# Persist scelta corrente (best-effort, non blocca la fetch)
|
||||||
|
try:
|
||||||
|
cfg = load_config()
|
||||||
|
cfg["charts_last_source"] = source
|
||||||
|
if source == "itunes":
|
||||||
|
cfg["charts_itunes_country"] = (
|
||||||
|
payload.get("country") or cfg.get("charts_itunes_country") or "it"
|
||||||
|
)
|
||||||
|
elif source == "lastfm":
|
||||||
|
cfg["charts_lastfm_decade"] = (
|
||||||
|
payload.get("decade") if payload.get("decade") is not None
|
||||||
|
else cfg.get("charts_lastfm_decade", "90s")
|
||||||
|
)
|
||||||
|
cfg["charts_lastfm_genre"] = (
|
||||||
|
payload.get("genre") if payload.get("genre") is not None
|
||||||
|
else cfg.get("charts_lastfm_genre", "dance")
|
||||||
|
)
|
||||||
|
elif source == "spotify":
|
||||||
|
cfg["charts_spotify_playlist"] = (
|
||||||
|
payload.get("playlist_key")
|
||||||
|
or cfg.get("charts_spotify_playlist")
|
||||||
|
or "top50_global"
|
||||||
|
)
|
||||||
|
save_config(cfg)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
if source == "itunes":
|
||||||
|
country = (payload.get("country") or "it").strip().lower() or "it"
|
||||||
|
entries = charts.fetch_itunes(country, force=force)
|
||||||
|
elif source == "lastfm":
|
||||||
|
decade = payload.get("decade") or ""
|
||||||
|
genre = payload.get("genre") or ""
|
||||||
|
entries = charts.fetch_lastfm(decade, genre, force=force)
|
||||||
|
elif source == "spotify":
|
||||||
|
playlist_key = (payload.get("playlist_key") or "top50_global").strip()
|
||||||
|
entries = charts.fetch_spotify(playlist_key, force=force)
|
||||||
|
elif source == "m2o":
|
||||||
|
entries = charts.fetch_m2o(force=force)
|
||||||
|
else:
|
||||||
|
return {"ok": False, "error": f"Sorgente sconosciuta: {source}"}
|
||||||
|
except Exception as e:
|
||||||
|
self._log("charts", f"[ERRORE] fetch {source}: {e}")
|
||||||
|
return {"ok": False, "error": str(e)}
|
||||||
|
|
||||||
|
self._log("charts", f"[INFO] {source}: {len(entries)} brani ricevuti")
|
||||||
|
return {"ok": True, "source": source, "entries": entries}
|
||||||
|
|
||||||
|
def charts_add_to_queue(self, entries: list) -> dict:
|
||||||
|
"""Riceve una lista di entry `{artist, title}` selezionate dalla UI
|
||||||
|
e le converte in una tracklist compatibile con la tab Scarica.
|
||||||
|
|
||||||
|
Emette l'evento `charts:add_to_queue` col testo formattato
|
||||||
|
(`Artista - Titolo` una per riga) e la lista strutturata. La UI
|
||||||
|
popola l'input della tab Scarica e commuta view.
|
||||||
|
"""
|
||||||
|
if not isinstance(entries, list):
|
||||||
|
return {"ok": False, "error": "entries deve essere una lista"}
|
||||||
|
|
||||||
|
tracks = []
|
||||||
|
lines = []
|
||||||
|
for e in entries:
|
||||||
|
if not isinstance(e, dict):
|
||||||
|
continue
|
||||||
|
artist = (e.get("artist") or "").strip()
|
||||||
|
title = (e.get("title") or "").strip()
|
||||||
|
if not title:
|
||||||
|
continue
|
||||||
|
tracks.append({"artist": artist, "name": title})
|
||||||
|
if artist:
|
||||||
|
lines.append(f"{artist} - {title}")
|
||||||
|
else:
|
||||||
|
lines.append(title)
|
||||||
|
|
||||||
|
if not tracks:
|
||||||
|
return {"ok": False, "error": "Nessun brano valido selezionato"}
|
||||||
|
|
||||||
|
text = "\n".join(lines)
|
||||||
|
payload = {"text": text, "count": len(tracks), "tracks": tracks}
|
||||||
|
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)}
|
||||||
+8
-1
@@ -5,7 +5,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
VERSION = "v1.11.3"
|
VERSION = "v1.12.0"
|
||||||
|
|
||||||
|
|
||||||
APP_NAME = "MusicTools"
|
APP_NAME = "MusicTools"
|
||||||
@@ -88,6 +88,13 @@ DEFAULTS = {
|
|||||||
"catalog_last_target": "",
|
"catalog_last_target": "",
|
||||||
"catalog_recursive": True,
|
"catalog_recursive": True,
|
||||||
"acoustid_app_key": "", # opzionale: se vuoto usa default in core/acoustid.py
|
"acoustid_app_key": "", # opzionale: se vuoto usa default in core/acoustid.py
|
||||||
|
# ---- Charts (iTunes RSS + Last.fm + Spotify + M2O) ----
|
||||||
|
"charts_last_source": "itunes", # "itunes" | "spotify" | "m2o" | "lastfm"
|
||||||
|
"charts_itunes_country": "it", # "it" | "us" | "gb" | "ww"
|
||||||
|
"charts_lastfm_decade": "90s", # "" | "70s" | "80s" | "90s" | "2000s" | "2010s"
|
||||||
|
"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
|
||||||
# ---- 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