Carica lista: auto-detect URL list vs tracklist
Adesso 'Carica lista' nella tab Scarica riconosce automaticamente il formato del file .txt: - Se la prima riga e un URL (http/https) -> URL list (come prima) - Altrimenti -> tracklist parsata 'Artista - Titolo (variant) (durata)' e scaricata cercando ogni brano su YouTube (riusa download_playlist) Parser robusto: gestisce numerazione iniziale (1. / 1) / 1: / 1-), qualsiasi tipo di dash (-, en-dash, em-dash, ...) e durata finale '(M:SS)'. Testato sulla Beatport Top 100 Melodic House & Techno (100/100 tracce parsate). api/bridge.py: nuovo start_tracks_download, load_url_list ritorna 'kind: urls|tracks'. webui/js/app.js: badge cambia tra 'X URL' e 'X tracce', avvio chiama start_tracks_download quando appropriato. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
1 parent
e5158d382f
commit
4b1524e5f7
3 files changed
+248
-21
No files matched your search
+34
-19
@@ -7,7 +7,7 @@ const $$ = (sel) => Array.from(document.querySelectorAll(sel));
|
||||
|
||||
const state = {
|
||||
config: {},
|
||||
urlList: [],
|
||||
loaded: null, // { kind: "urls"|"tracks", urls?, tracks?, count }
|
||||
dlOutputDir: "",
|
||||
upDir: "",
|
||||
videoOutputDir: "",
|
||||
@@ -55,7 +55,7 @@ const bridgeHandlers = {
|
||||
|
||||
"download:done": () => {
|
||||
state.downloading = false;
|
||||
state.urlList = [];
|
||||
state.loaded = null;
|
||||
$("#urlListBadge").hidden = true;
|
||||
$("#urlListBadge").textContent = "";
|
||||
$("#downloadBtn").disabled = false;
|
||||
@@ -221,27 +221,23 @@ $("#loadListBtn").addEventListener("click", async () => {
|
||||
return;
|
||||
}
|
||||
if (!res.count) {
|
||||
toast("Il file non contiene URL validi", "error");
|
||||
toast("Il file non contiene voci valide", "error");
|
||||
return;
|
||||
}
|
||||
state.urlList = res.urls;
|
||||
state.loaded = res;
|
||||
$("#urlInput").value = path;
|
||||
const badge = $("#urlListBadge");
|
||||
badge.textContent = `· ${res.count} URL caricati`;
|
||||
if (res.kind === "tracks") {
|
||||
badge.textContent = `· ${res.count} tracce`;
|
||||
appendLog("download", `[INFO] Caricata tracklist: ${res.count} brani`);
|
||||
} else {
|
||||
badge.textContent = `· ${res.count} URL`;
|
||||
appendLog("download", `[INFO] Caricati ${res.count} URL dal file`);
|
||||
}
|
||||
badge.hidden = false;
|
||||
appendLog("download", `[INFO] Caricati ${res.count} URL dal file`);
|
||||
});
|
||||
|
||||
$("#downloadBtn").addEventListener("click", async () => {
|
||||
let urls = state.urlList;
|
||||
if (!urls.length) {
|
||||
const single = $("#urlInput").value.trim();
|
||||
if (!single) {
|
||||
toast("Inserisci un URL o carica una lista", "error");
|
||||
return;
|
||||
}
|
||||
urls = [single];
|
||||
}
|
||||
if (!state.dlOutputDir) {
|
||||
toast("Seleziona una cartella di destinazione", "error");
|
||||
return;
|
||||
@@ -254,10 +250,29 @@ $("#downloadBtn").addEventListener("click", async () => {
|
||||
$("#dlProgressFill").style.width = "0%";
|
||||
$("#dlPercent").textContent = "0%";
|
||||
|
||||
const res = await window.pywebview.api.start_download({
|
||||
urls,
|
||||
output_dir: state.dlOutputDir,
|
||||
});
|
||||
let res;
|
||||
if (state.loaded && state.loaded.kind === "tracks") {
|
||||
res = await window.pywebview.api.start_tracks_download({
|
||||
tracks: state.loaded.tracks,
|
||||
output_dir: state.dlOutputDir,
|
||||
});
|
||||
} else {
|
||||
let urls = state.loaded && state.loaded.kind === "urls" ? state.loaded.urls : [];
|
||||
if (!urls.length) {
|
||||
const single = $("#urlInput").value.trim();
|
||||
if (!single) {
|
||||
toast("Inserisci un URL o carica una lista", "error");
|
||||
bridgeHandlers["download:done"]();
|
||||
return;
|
||||
}
|
||||
urls = [single];
|
||||
}
|
||||
res = await window.pywebview.api.start_download({
|
||||
urls,
|
||||
output_dir: state.dlOutputDir,
|
||||
});
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
toast(res.error || "Errore", "error");
|
||||
bridgeHandlers["download:done"]();
|
||||
|
||||
Reference in new issue
Block a user