ui: tab Charts con tabella streaming + integrazione coda Scarica
Nuova tab "Charts" (nav-item con icona grafico) che espone quattro sorgenti di classifiche: - Del momento (iTunes): dropdown paese Italia/USA/UK/Mondo - Spotify Charts: dropdown 6 playlist editoriali (Top 50 e Viral 50 per Global/Italy/USA/UK) - M2O Chart: chart dance italiana settimanale (nessun filtro) - Storiche per genere (Last.fm): combinazione decade x genere Tabella streaming stile catalog/dedup con # / artista / titolo / album / popolarità, "Seleziona tutti" e footer sticky col contatore selezionati + bottone "Aggiungi N alla coda Scarica". Il bottone invoca api.charts_add_to_queue: il backend emette l'evento charts:add_to_queue che la UI intercetta popolando state.loaded come tracklist (kind: "tracks") - stesso pattern usato da "Carica lista" - poi commuta view su Scarica, mostra il badge "· N tracce" e appende al log Scarica. L'utente preme "Avvia Download" e la coda parte. CSS charts-* riusa i pattern catalog-*/dedup-* (scroll 55vh, footer sticky, badge status).
This commit is contained in:
1 parent
29c177ff69
commit
39e843dc69
3 files changed
+662
No files matched your search
@@ -2116,3 +2116,139 @@ input[type="number"]::-webkit-inner-spin-button {
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
/* ==================================================================
|
||||
* Charts tab (iTunes / Spotify / M2O / Last.fm)
|
||||
* Riusa in gran parte le classi catalog-* per stile e comportamento.
|
||||
* ================================================================== */
|
||||
.charts-source-row {
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.charts-radio {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: var(--text-1);
|
||||
cursor: pointer;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
transition: background 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.charts-radio:hover {
|
||||
background: var(--bg-elev);
|
||||
}
|
||||
|
||||
.charts-radio input[type="radio"] {
|
||||
accent-color: var(--indigo-hover, #6366F1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.charts-radio input[type="radio"]:checked ~ span {
|
||||
color: var(--text-1);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.charts-filters {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 14px;
|
||||
align-items: flex-start;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.charts-actions-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.charts-actions-btns {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.charts-scroll {
|
||||
max-height: 55vh;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
padding-right: 6px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-md);
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
.charts-table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
font-size: 12px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
|
||||
.charts-table thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
background: var(--bg-card-2);
|
||||
color: var(--text-1);
|
||||
font-weight: 700;
|
||||
padding: 10px 8px;
|
||||
text-align: left;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.3px;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid var(--border);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.charts-table tbody td {
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
.charts-table tbody tr:hover {
|
||||
background: var(--bg-elev);
|
||||
}
|
||||
|
||||
.charts-col-check { width: 30px; text-align: center; }
|
||||
.charts-col-rank { width: 42px; text-align: center; font-variant-numeric: tabular-nums; color: var(--text-3); font-weight: 700; }
|
||||
.charts-col-pop { width: 110px; text-align: right; font-variant-numeric: tabular-nums; color: var(--text-3); }
|
||||
|
||||
.charts-check {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
accent-color: var(--indigo-hover, #6366F1);
|
||||
}
|
||||
|
||||
.charts-footer-bar {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
margin-top: 12px;
|
||||
border-radius: var(--r-md);
|
||||
background: var(--bg-card);
|
||||
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.15);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@
|
||||
<span class="nav-icon">📤</span>
|
||||
<span>Appiattisci</span>
|
||||
</button>
|
||||
<button class="nav-item" data-view="charts" data-feature="audio">
|
||||
<span class="nav-icon">📊</span>
|
||||
<span>Charts</span>
|
||||
</button>
|
||||
<button class="nav-item" data-view="upgrade" data-feature="upgrade">
|
||||
<span class="nav-icon">⚡</span>
|
||||
<span>Upgrade</span>
|
||||
@@ -1497,6 +1501,170 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ===== VIEW: CHARTS ===== -->
|
||||
<section class="view" id="view-charts">
|
||||
<header class="hero hero-indigo">
|
||||
<div class="hero-content">
|
||||
<div class="hero-eyebrow indigo">CHARTS</div>
|
||||
<h1 class="hero-title">Esplora le classifiche</h1>
|
||||
<p class="hero-subtitle">Del momento (iTunes/Spotify), dance italiana (M2O) o storiche per genere (Last.fm) — aggiungi in un click alla coda Scarica.</p>
|
||||
</div>
|
||||
<div class="hero-deco">📊</div>
|
||||
</header>
|
||||
|
||||
<h2 class="section-label">Sorgente</h2>
|
||||
<div class="card">
|
||||
<div class="charts-source-row">
|
||||
<label class="charts-radio">
|
||||
<input type="radio" name="charts-source" value="itunes" checked />
|
||||
<span>🎵 Del momento (iTunes)</span>
|
||||
</label>
|
||||
<label class="charts-radio">
|
||||
<input type="radio" name="charts-source" value="spotify" />
|
||||
<span>🟢 Spotify Charts</span>
|
||||
</label>
|
||||
<label class="charts-radio">
|
||||
<input type="radio" name="charts-source" value="m2o" />
|
||||
<span>🔴 M2O Chart (dance italiana)</span>
|
||||
</label>
|
||||
<label class="charts-radio">
|
||||
<input type="radio" name="charts-source" value="lastfm" />
|
||||
<span>📻 Storiche per genere (Last.fm)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="charts-filters" id="charts-filters-itunes">
|
||||
<div class="field inline">
|
||||
<label class="field-label">Paese</label>
|
||||
<select id="charts-itunes-country" class="input pill input-sm">
|
||||
<option value="it">🇮🇹 Italia</option>
|
||||
<option value="us">🇺🇸 USA</option>
|
||||
<option value="gb">🇬🇧 UK</option>
|
||||
<option value="ww">🌐 Mondo</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="charts-filters" id="charts-filters-spotify" hidden>
|
||||
<div class="field inline">
|
||||
<label class="field-label">Playlist chart</label>
|
||||
<select id="charts-spotify-playlist" class="input pill input-sm">
|
||||
<option value="top50_global">Top 50 Global</option>
|
||||
<option value="top50_italy">Top 50 Italy</option>
|
||||
<option value="top50_usa">Top 50 USA</option>
|
||||
<option value="top50_uk">Top 50 UK</option>
|
||||
<option value="viral50_global">Viral 50 Global</option>
|
||||
<option value="viral50_italy">Viral 50 Italy</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="hint" style="margin-top:8px;">
|
||||
<span class="hint-ico">ⓘ</span>
|
||||
<div>
|
||||
Le playlist Spotify richiedono le tue credenziali API (Impostazioni → Spotify).
|
||||
Senza credenziali questa sorgente resta vuota.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="charts-filters" id="charts-filters-m2o" hidden>
|
||||
<div class="hint">
|
||||
<span class="hint-ico">ⓘ</span>
|
||||
<div>
|
||||
Chart dance italiana settimanale di M2O. Nessun filtro: premi "Aggiorna" per caricare la classifica corrente.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="charts-filters" id="charts-filters-lastfm" hidden>
|
||||
<div class="field inline">
|
||||
<label class="field-label">Decade</label>
|
||||
<select id="charts-lastfm-decade" class="input pill input-sm">
|
||||
<option value="">Nessuna</option>
|
||||
<option value="70s">Anni 70</option>
|
||||
<option value="80s">Anni 80</option>
|
||||
<option value="90s" selected>Anni 90</option>
|
||||
<option value="2000s">Anni 2000</option>
|
||||
<option value="2010s">Anni 2010</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field inline">
|
||||
<label class="field-label">Genere</label>
|
||||
<select id="charts-lastfm-genre" class="input pill input-sm">
|
||||
<option value="">Nessuno</option>
|
||||
<option value="pop">Pop</option>
|
||||
<option value="rock">Rock</option>
|
||||
<option value="dance" selected>Dance</option>
|
||||
<option value="house">House</option>
|
||||
<option value="hip hop">Hip Hop</option>
|
||||
<option value="latin">Latin</option>
|
||||
<option value="r&b">R&B</option>
|
||||
<option value="italo disco">Italo Disco</option>
|
||||
<option value="reggae">Reggae</option>
|
||||
<option value="electronic">Electronic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="hint" style="margin-top:8px;">
|
||||
<span class="hint-ico">ⓘ</span>
|
||||
<div>
|
||||
Last.fm richiede una API key: nell'app c'è una chiave demo, se non funziona
|
||||
registrane una gratuita su <code>last.fm/api/account/create</code> e mettila nel campo
|
||||
<code>lastfm_api_key</code> di <code>config.json</code>.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="beatport-header" style="margin-top:14px;gap:12px;">
|
||||
<button id="charts-fetch-btn" class="btn btn-primary pill">
|
||||
<span class="ico">▶</span> Aggiorna classifica
|
||||
</button>
|
||||
<label style="display:flex;gap:6px;align-items:center;color:var(--text-2);font-size:13px;">
|
||||
<input type="checkbox" id="charts-force" />
|
||||
<span>Ignora cache (force refresh)</span>
|
||||
</label>
|
||||
<span id="charts-status" class="beatport-status"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="charts-results-card" class="card" hidden>
|
||||
<div class="charts-actions-row">
|
||||
<span id="charts-summary-line" class="catalog-summary"></span>
|
||||
<div class="charts-actions-btns">
|
||||
<button id="charts-select-all-btn" class="btn btn-ghost pill btn-sm">Seleziona tutti</button>
|
||||
<button id="charts-deselect-all-btn" class="btn btn-ghost pill btn-sm">Deseleziona tutti</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="charts-table-wrap" class="charts-scroll">
|
||||
<table class="charts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="charts-col-check">
|
||||
<input type="checkbox" id="charts-select-all" title="Seleziona/deseleziona tutti" />
|
||||
</th>
|
||||
<th class="charts-col-rank">#</th>
|
||||
<th class="charts-col-artist">Artista</th>
|
||||
<th class="charts-col-title">Titolo</th>
|
||||
<th class="charts-col-album">Album</th>
|
||||
<th class="charts-col-pop">Popolarità</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="charts-table-body"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="charts-footer-bar" id="charts-footer-bar" hidden>
|
||||
<span class="counter" id="charts-selected-count">0 brani selezionati</span>
|
||||
<button id="charts-add-queue-btn" class="btn btn-primary pill" disabled>
|
||||
<span class="ico">➕</span> Aggiungi alla coda Scarica
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h2 class="section-label">Log</h2>
|
||||
<div class="card log-card">
|
||||
<div class="log" id="charts-log"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
|
||||
+358
@@ -411,6 +411,7 @@ const logEls = {
|
||||
dedup: () => $("#dedup-log"),
|
||||
catalog: () => $("#catalog-log"),
|
||||
flatten: () => $("#flatten-log"),
|
||||
charts: () => $("#charts-log"),
|
||||
};
|
||||
|
||||
function classifyLog(msg) {
|
||||
@@ -542,6 +543,9 @@ async function init() {
|
||||
// Cataloga tab (AcoustID -> <Anno>/<Genere>/)
|
||||
await CatalogUI.init();
|
||||
await FlattenUI.init();
|
||||
|
||||
// Charts tab (iTunes / Spotify / M2O / Last.fm)
|
||||
await ChartsUI.init();
|
||||
}
|
||||
|
||||
async function refreshRecDevices() {
|
||||
@@ -3770,6 +3774,360 @@ const FlattenUI = (() => {
|
||||
return { init };
|
||||
})();
|
||||
|
||||
// ============================================================
|
||||
// ChartsUI — classifiche iTunes / Spotify / M2O / Last.fm
|
||||
// ============================================================
|
||||
const ChartsUI = (() => {
|
||||
const cs = {
|
||||
source: "itunes",
|
||||
country: "it",
|
||||
playlist: "top50_global",
|
||||
decade: "90s",
|
||||
genre: "dance",
|
||||
entries: [],
|
||||
selected: new Set(), // Set di rank (int)
|
||||
fetching: false,
|
||||
};
|
||||
|
||||
function setStatus(text, kind) {
|
||||
const el = $("#charts-status");
|
||||
if (!el) return;
|
||||
el.textContent = text || "";
|
||||
el.className = "beatport-status" + (kind ? " " + kind : "");
|
||||
}
|
||||
|
||||
function _showFiltersFor(source) {
|
||||
const map = {
|
||||
itunes: "#charts-filters-itunes",
|
||||
spotify: "#charts-filters-spotify",
|
||||
m2o: "#charts-filters-m2o",
|
||||
lastfm: "#charts-filters-lastfm",
|
||||
};
|
||||
Object.values(map).forEach((sel) => {
|
||||
const el = $(sel);
|
||||
if (el) el.hidden = true;
|
||||
});
|
||||
const activeEl = $(map[source]);
|
||||
if (activeEl) activeEl.hidden = false;
|
||||
}
|
||||
|
||||
function _updateSelectedCount() {
|
||||
const n = cs.selected.size;
|
||||
const cnt = $("#charts-selected-count");
|
||||
if (cnt) {
|
||||
cnt.textContent = n === 1
|
||||
? "1 brano selezionato"
|
||||
: `${n} brani selezionati`;
|
||||
}
|
||||
const btn = $("#charts-add-queue-btn");
|
||||
if (btn) {
|
||||
btn.disabled = n === 0 || cs.fetching;
|
||||
const suffix = n > 0
|
||||
? ` Aggiungi ${n} alla coda Scarica`
|
||||
: " Aggiungi alla coda Scarica";
|
||||
btn.innerHTML = `<span class="ico">➕</span>${suffix}`;
|
||||
}
|
||||
const bar = $("#charts-footer-bar");
|
||||
if (bar) bar.hidden = cs.entries.length === 0;
|
||||
}
|
||||
|
||||
function _updateSummary() {
|
||||
const line = $("#charts-summary-line");
|
||||
if (!line) return;
|
||||
const n = cs.entries.length;
|
||||
if (n === 0) {
|
||||
line.textContent = "";
|
||||
return;
|
||||
}
|
||||
const labelBySource = {
|
||||
itunes: "iTunes",
|
||||
spotify: "Spotify Charts",
|
||||
m2o: "M2O Chart",
|
||||
lastfm: "Last.fm",
|
||||
};
|
||||
const src = labelBySource[cs.source] || cs.source;
|
||||
line.textContent = `${n} brani da ${src}`;
|
||||
}
|
||||
|
||||
function _fmtPopularity(entry) {
|
||||
if (entry.popularity === null || entry.popularity === undefined || entry.popularity === "") {
|
||||
return String(entry.rank || "—");
|
||||
}
|
||||
const n = Number(entry.popularity);
|
||||
if (!isFinite(n)) return String(entry.popularity);
|
||||
if (n >= 1000) {
|
||||
// Formatta grandi numeri con separatore
|
||||
return n.toLocaleString();
|
||||
}
|
||||
return String(n);
|
||||
}
|
||||
|
||||
function _renderRow(entry) {
|
||||
const tr = document.createElement("tr");
|
||||
tr.dataset.rank = String(entry.rank);
|
||||
|
||||
// Checkbox
|
||||
const tdCheck = document.createElement("td");
|
||||
tdCheck.className = "charts-col-check";
|
||||
const cb = document.createElement("input");
|
||||
cb.type = "checkbox";
|
||||
cb.className = "charts-check";
|
||||
cb.checked = cs.selected.has(entry.rank);
|
||||
cb.addEventListener("change", () => {
|
||||
if (cb.checked) cs.selected.add(entry.rank);
|
||||
else cs.selected.delete(entry.rank);
|
||||
_updateSelectedCount();
|
||||
});
|
||||
tdCheck.appendChild(cb);
|
||||
tr.appendChild(tdCheck);
|
||||
|
||||
// Rank
|
||||
const tdRank = document.createElement("td");
|
||||
tdRank.className = "charts-col-rank";
|
||||
tdRank.textContent = String(entry.rank || "—");
|
||||
tr.appendChild(tdRank);
|
||||
|
||||
// Artist
|
||||
const tdArtist = document.createElement("td");
|
||||
tdArtist.className = "charts-col-artist";
|
||||
tdArtist.textContent = entry.artist || "—";
|
||||
tdArtist.title = entry.artist || "";
|
||||
tr.appendChild(tdArtist);
|
||||
|
||||
// Title
|
||||
const tdTitle = document.createElement("td");
|
||||
tdTitle.className = "charts-col-title";
|
||||
tdTitle.textContent = entry.title || "—";
|
||||
tdTitle.title = entry.title || "";
|
||||
tr.appendChild(tdTitle);
|
||||
|
||||
// Album
|
||||
const tdAlbum = document.createElement("td");
|
||||
tdAlbum.className = "charts-col-album";
|
||||
tdAlbum.textContent = entry.album || "—";
|
||||
tdAlbum.title = entry.album || "";
|
||||
tr.appendChild(tdAlbum);
|
||||
|
||||
// Popularity
|
||||
const tdPop = document.createElement("td");
|
||||
tdPop.className = "charts-col-pop";
|
||||
tdPop.textContent = _fmtPopularity(entry);
|
||||
tr.appendChild(tdPop);
|
||||
|
||||
return tr;
|
||||
}
|
||||
|
||||
function _renderTable() {
|
||||
const body = $("#charts-table-body");
|
||||
const card = $("#charts-results-card");
|
||||
if (!body || !card) return;
|
||||
body.innerHTML = "";
|
||||
if (cs.entries.length === 0) {
|
||||
card.hidden = true;
|
||||
const bar = $("#charts-footer-bar");
|
||||
if (bar) bar.hidden = true;
|
||||
return;
|
||||
}
|
||||
for (const entry of cs.entries) {
|
||||
body.appendChild(_renderRow(entry));
|
||||
}
|
||||
card.hidden = false;
|
||||
_updateSummary();
|
||||
_updateSelectedCount();
|
||||
}
|
||||
|
||||
function _params() {
|
||||
if (cs.source === "itunes") return { country: cs.country };
|
||||
if (cs.source === "spotify") return { playlist_key: cs.playlist };
|
||||
if (cs.source === "m2o") return {};
|
||||
if (cs.source === "lastfm") return { decade: cs.decade, genre: cs.genre };
|
||||
return {};
|
||||
}
|
||||
|
||||
async function doFetch() {
|
||||
if (cs.fetching) return;
|
||||
cs.fetching = true;
|
||||
cs.entries = [];
|
||||
cs.selected.clear();
|
||||
_renderTable();
|
||||
|
||||
const btn = $("#charts-fetch-btn");
|
||||
if (btn) btn.disabled = true;
|
||||
setStatus("Caricamento classifica…", "loading");
|
||||
|
||||
const force = $("#charts-force")?.checked || false;
|
||||
const payload = { source: cs.source, force, ...(_params()) };
|
||||
|
||||
let res;
|
||||
try {
|
||||
res = await window.pywebview.api.charts_fetch(payload);
|
||||
} catch (e) {
|
||||
setStatus("Errore: " + ((e && e.message) || e), "error");
|
||||
cs.fetching = false;
|
||||
if (btn) btn.disabled = false;
|
||||
return;
|
||||
}
|
||||
cs.fetching = false;
|
||||
if (btn) btn.disabled = false;
|
||||
|
||||
if (!res || !res.ok) {
|
||||
const msg = (res && res.error) || "Impossibile caricare la classifica";
|
||||
setStatus(msg, "error");
|
||||
toast(msg, "error");
|
||||
return;
|
||||
}
|
||||
|
||||
cs.entries = Array.isArray(res.entries) ? res.entries : [];
|
||||
_renderTable();
|
||||
|
||||
if (cs.entries.length === 0) {
|
||||
setStatus("Nessun risultato. Prova a cambiare filtri o sorgente.", "ok");
|
||||
} else {
|
||||
setStatus(`${cs.entries.length} brani ricevuti`, "ok");
|
||||
}
|
||||
}
|
||||
|
||||
async function addToQueue() {
|
||||
if (cs.selected.size === 0) return;
|
||||
const rankSet = cs.selected;
|
||||
const selectedEntries = cs.entries
|
||||
.filter((e) => rankSet.has(e.rank))
|
||||
.map((e) => ({ artist: e.artist, title: e.title }));
|
||||
if (selectedEntries.length === 0) return;
|
||||
|
||||
let res;
|
||||
try {
|
||||
res = await window.pywebview.api.charts_add_to_queue(selectedEntries);
|
||||
} catch (e) {
|
||||
toast("Errore: " + ((e && e.message) || e), "error");
|
||||
return;
|
||||
}
|
||||
if (!res || !res.ok) {
|
||||
toast((res && res.error) || "Errore aggiunta", "error");
|
||||
return;
|
||||
}
|
||||
// Il bridge emette `charts:add_to_queue` che poi popola la tab Scarica.
|
||||
// Reset selezione qui.
|
||||
cs.selected.clear();
|
||||
// Deseleziona checkbox visibili
|
||||
$$("#charts-table-body .charts-check").forEach((cb) => { cb.checked = false; });
|
||||
const master = $("#charts-select-all");
|
||||
if (master) master.checked = false;
|
||||
_updateSelectedCount();
|
||||
}
|
||||
|
||||
function _selectAll(on) {
|
||||
cs.selected.clear();
|
||||
if (on) {
|
||||
for (const e of cs.entries) cs.selected.add(e.rank);
|
||||
}
|
||||
$$("#charts-table-body .charts-check").forEach((cb) => { cb.checked = on; });
|
||||
const master = $("#charts-select-all");
|
||||
if (master) master.checked = on;
|
||||
_updateSelectedCount();
|
||||
}
|
||||
|
||||
async function init() {
|
||||
// Ripristina scelta da config
|
||||
const cfg = state.config || {};
|
||||
cs.source = cfg.charts_last_source || "itunes";
|
||||
cs.country = cfg.charts_itunes_country || "it";
|
||||
cs.playlist = cfg.charts_spotify_playlist || "top50_global";
|
||||
cs.decade = cfg.charts_lastfm_decade || "90s";
|
||||
cs.genre = cfg.charts_lastfm_genre || "dance";
|
||||
|
||||
// Applica valori ai controlli
|
||||
const radio = document.querySelector(`input[name="charts-source"][value="${cs.source}"]`);
|
||||
if (radio) radio.checked = true;
|
||||
else {
|
||||
// Fallback: seleziona iTunes
|
||||
const fallback = document.querySelector('input[name="charts-source"][value="itunes"]');
|
||||
if (fallback) fallback.checked = true;
|
||||
cs.source = "itunes";
|
||||
}
|
||||
_showFiltersFor(cs.source);
|
||||
|
||||
if ($("#charts-itunes-country")) $("#charts-itunes-country").value = cs.country;
|
||||
if ($("#charts-spotify-playlist")) $("#charts-spotify-playlist").value = cs.playlist;
|
||||
if ($("#charts-lastfm-decade")) $("#charts-lastfm-decade").value = cs.decade;
|
||||
if ($("#charts-lastfm-genre")) $("#charts-lastfm-genre").value = cs.genre;
|
||||
|
||||
// Bind eventi source radio
|
||||
document.querySelectorAll('input[name="charts-source"]').forEach((r) => {
|
||||
r.addEventListener("change", () => {
|
||||
if (!r.checked) return;
|
||||
cs.source = r.value;
|
||||
_showFiltersFor(cs.source);
|
||||
});
|
||||
});
|
||||
|
||||
// Bind filtri
|
||||
$("#charts-itunes-country")?.addEventListener("change", (e) => {
|
||||
cs.country = e.target.value;
|
||||
});
|
||||
$("#charts-spotify-playlist")?.addEventListener("change", (e) => {
|
||||
cs.playlist = e.target.value;
|
||||
});
|
||||
$("#charts-lastfm-decade")?.addEventListener("change", (e) => {
|
||||
cs.decade = e.target.value;
|
||||
});
|
||||
$("#charts-lastfm-genre")?.addEventListener("change", (e) => {
|
||||
cs.genre = e.target.value;
|
||||
});
|
||||
|
||||
// Fetch, seleziona, aggiungi
|
||||
$("#charts-fetch-btn")?.addEventListener("click", doFetch);
|
||||
$("#charts-add-queue-btn")?.addEventListener("click", addToQueue);
|
||||
$("#charts-select-all-btn")?.addEventListener("click", () => _selectAll(true));
|
||||
$("#charts-deselect-all-btn")?.addEventListener("click", () => _selectAll(false));
|
||||
$("#charts-select-all")?.addEventListener("change", (e) => _selectAll(!!e.target.checked));
|
||||
|
||||
// Bridge handler: quando l'utente aggiunge alla coda, popola la tab Scarica.
|
||||
bridgeHandlers["charts:add_to_queue"] = (p) => {
|
||||
if (!p) return;
|
||||
const tracks = Array.isArray(p.tracks) ? p.tracks : null;
|
||||
const text = typeof p.text === "string" ? p.text : "";
|
||||
const count = Number(p.count) || (tracks ? tracks.length : 0);
|
||||
|
||||
// Popola state.loaded come "tracks" (stesso pattern del "Carica lista")
|
||||
if (tracks && tracks.length) {
|
||||
state.loaded = {
|
||||
ok: true,
|
||||
kind: "tracks",
|
||||
tracks: tracks,
|
||||
count: tracks.length,
|
||||
name: "charts",
|
||||
};
|
||||
// Aggiorna il campo URL e il badge sulla tab Scarica
|
||||
const urlInput = $("#urlInput");
|
||||
if (urlInput) {
|
||||
urlInput.value = "[Charts] " + count + " brani";
|
||||
}
|
||||
const badge = $("#urlListBadge");
|
||||
if (badge) {
|
||||
badge.textContent = `· ${count} tracce`;
|
||||
badge.hidden = false;
|
||||
}
|
||||
appendLog("download", `[INFO] Caricati ${count} brani dalla tab Charts`);
|
||||
} else if (text) {
|
||||
// Fallback: nessun tracks strutturati, usa il testo grezzo
|
||||
const urlInput = $("#urlInput");
|
||||
if (urlInput) {
|
||||
urlInput.value = urlInput.value ? urlInput.value + "\n" + text : text;
|
||||
}
|
||||
}
|
||||
|
||||
// Naviga alla tab Scarica
|
||||
showView("download");
|
||||
toast(`${count} brani aggiunti alla coda Scarica`, "success");
|
||||
};
|
||||
|
||||
_updateSelectedCount();
|
||||
}
|
||||
|
||||
return { init };
|
||||
})();
|
||||
|
||||
// ============================================================
|
||||
// Boot
|
||||
// ============================================================
|
||||
|
||||
Reference in new issue
Block a user