): legge
+ i meta tag og:title e og:description della pagina pubblica.
+
+ Per album/playlist non c'e' modo affidabile senza JS rendering -> alza
+ SpotifyAuthRequired.
+
+ Ritorna ("Brano", name, [{name, artist}]).
+ """
+ kind, _ = detect_url_type(url)
+ if kind != "track":
+ raise SpotifyAuthRequired(
+ f"I link a {kind} richiedono le credenziali Spotify (gratuite). "
+ "Vai in Impostazioni -> Spotify API per configurarle in 2 minuti."
+ )
+
+ try:
+ resp = requests.get(
+ url,
+ headers={"User-Agent": "Mozilla/5.0 (MusicTools)"},
+ timeout=15,
+ )
+ resp.raise_for_status()
+ except requests.RequestException as e:
+ raise ValueError(f"Impossibile leggere il brano da Spotify: {e}") from e
+
+ html = resp.text
+ title_m = _OG_TITLE_RE.search(html)
+ desc_m = _OG_DESC_RE.search(html)
+ if not title_m:
+ raise ValueError("Risposta inattesa da Spotify (meta og:title mancante).")
+
+ name = _decode_entities(title_m.group(1)).strip()
+ artist = ""
+ if desc_m:
+ # Formato osservato: "Artist Name · Song Title · Song · YYYY"
+ # oppure con piu' artisti separati da ", ".
+ parts = [_decode_entities(p).strip() for p in desc_m.group(1).split("·")]
+ if parts:
+ artist = parts[0]
+
+ if not artist:
+ raise ValueError(
+ "Impossibile estrarre l'artista dal brano Spotify senza API keys. "
+ "Configura le credenziali in Impostazioni."
+ )
+
+ track = {"name": name, "artist": artist}
+ return "Brano", name, [track]
diff --git a/landing/download.html b/landing/download.html
index 86796b0..03b330d 100644
--- a/landing/download.html
+++ b/landing/download.html
@@ -90,6 +90,24 @@
.dl-support { text-align: center; color: var(--text-muted); font-size: 14px; padding-top: 24px; border-top: 1px solid var(--divider); margin-top: 24px; }
.dl-support a { color: var(--accent); }
+
+ .dl-status {
+ max-width: 460px;
+ margin: -16px auto 24px;
+ padding: 12px 18px;
+ text-align: center;
+ background: rgba(29, 185, 84, 0.08);
+ border: 1px solid rgba(29, 185, 84, 0.25);
+ color: var(--accent);
+ border-radius: 12px;
+ font-size: 14px;
+ font-weight: 600;
+ }
+ .dl-status.err {
+ background: rgba(239, 68, 68, 0.08);
+ border-color: rgba(239, 68, 68, 0.3);
+ color: #f87171;
+ }
@@ -137,7 +155,7 @@
🍎
macOS
macOS 11 Big Sur o successivi · Apple Silicon e Intel
-
+
Scarica per Mac
@@ -146,12 +164,14 @@
🪟
Windows
Windows 10 e 11 · 64-bit
-
+
Scarica per Windows
+
+
Installazione macOS
@@ -184,19 +204,47 @@
return url.searchParams.get(name) || "";
}
- function setDownloadLinks(email, key) {
- const q = "key=" + encodeURIComponent(key) + "&email=" + encodeURIComponent(email);
- document.getElementById("dlMacBtn").href = "/api/download/macos?" + q;
- document.getElementById("dlWinBtn").href = "/api/download/windows?" + q;
+ // Stato corrente — popolato da URL o dal form submit.
+ const dl = { email: "", key: "" };
+
+ function setStatus(text, isError) {
+ const el = document.getElementById("dlStatus");
+ if (!text) { el.hidden = true; el.textContent = ""; return; }
+ el.textContent = text;
+ el.classList.toggle("err", !!isError);
+ el.hidden = false;
+ }
+
+ function showDownloadCards(email, key) {
+ dl.email = email;
+ dl.key = key;
document.getElementById("dlGrid").hidden = false;
document.getElementById("dlForm").hidden = true;
}
- // Se arrivi dall'email con ?key=&email= mostra subito i bottoni
+ // Click sui bottoni: naviga sulla stessa tab verso /api/download/:platform.
+ // Il server fa 302 con Content-Disposition: attachment -> il browser
+ // scarica il file e la pagina /download resta visibile.
+ document.querySelectorAll(".dl-btn").forEach((btn) => {
+ btn.addEventListener("click", (e) => {
+ e.preventDefault();
+ if (!dl.email || !dl.key) {
+ setStatus("Inserisci email e chiave prima di scaricare.", true);
+ return;
+ }
+ const platform = btn.dataset.platform;
+ const q = "key=" + encodeURIComponent(dl.key) +
+ "&email=" + encodeURIComponent(dl.email);
+ setStatus("Avvio del download in corso… il file si apre fra qualche secondo.");
+ window.location.href = "/api/download/" + platform + "?" + q;
+ });
+ });
+
+ // Se arrivi dall'email con ?key=&email= mostra subito i bottoni.
const urlKey = getParam("key");
const urlEmail = getParam("email");
if (urlKey && urlEmail) {
- setDownloadLinks(urlEmail, urlKey);
+ showDownloadCards(urlEmail, urlKey);
}
// Form submit
@@ -204,10 +252,11 @@
const email = document.getElementById("dlEmail").value.trim();
const key = document.getElementById("dlKey").value.trim();
if (!email || !key) {
- alert("Inserisci email e chiave di licenza.");
+ setStatus("Inserisci email e chiave di licenza.", true);
return;
}
- setDownloadLinks(email, key);
+ setStatus("");
+ showDownloadCards(email, key);
});
// Enter dentro i campi