Files
musicdownload/webui/index.html
T
luzadevandClaude Opus 4.7 850d3f4b6c Rename: l'app si chiama MusicTools (non piu MusicDownload)
- main.py: titolo finestra pywebview -> 'MusicTools'
- core/config.py: APP_NAME='MusicTools', cartella config bundled
  ora ~/Library/Application Support/MusicTools/ e %APPDATA%/MusicTools/.
  Migrazione one-shot: se non esiste la nuova ma esiste la legacy
  'MusicDownload', il config.json viene copiato cosi' le impostazioni
  non vanno perse dopo l'aggiornamento
- core/recorder.py: messaggio errore permesso microfono
- api/bridge.py: User-Agent update check + guida Spotify menzionano
  MusicTools
- build_macos.py / build_windows.py / .github/workflows/build.yml:
  prodotti rinominati (MusicTools.app, MusicTools.exe,
  MusicTools-macOS.zip, MusicTools-Windows.zip, Release 'Build MusicTools')
- webui: <title>, brand-name sidebar, intestazioni commenti
- README: tutti i riferimenti aggiornati + nota che il repo Git
  conserva il nome storico 'musicdownload' (URL stabili per
  release gia' pubblicate)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 11:15:34 +02:00

555 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>MusicTools</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<div class="app">
<!-- ============== SIDEBAR ============== -->
<aside class="sidebar">
<div class="brand">
<div class="brand-logo">
<span>♫</span>
</div>
<div class="brand-name">MusicTools</div>
</div>
<div class="divider"></div>
<nav class="nav">
<button class="nav-item active" data-view="download">
<span class="nav-icon">⬇</span>
<span>Scarica</span>
</button>
<button class="nav-item" data-view="video">
<span class="nav-icon">🎬</span>
<span>Video</span>
</button>
<button class="nav-item" data-view="upgrade">
<span class="nav-icon">⚡</span>
<span>Upgrade</span>
</button>
<button class="nav-item" data-view="meta">
<span class="nav-icon">🏷</span>
<span>Metadati</span>
</button>
<button class="nav-item" data-view="record">
<span class="nav-icon">●</span>
<span>Registra</span>
</button>
<button class="nav-item" data-view="settings">
<span class="nav-icon">⚙</span>
<span>Impostazioni</span>
</button>
</nav>
<div class="footer">
<div class="footer-by">by LuZa</div>
<div class="footer-version" id="footerVersion">v1.0</div>
</div>
</aside>
<!-- ============== MAIN ============== -->
<main class="main">
<!-- ===== VIEW: DOWNLOAD ===== -->
<section class="view active" id="view-download">
<header class="hero hero-green">
<div class="hero-content">
<div class="hero-eyebrow">DOWNLOAD</div>
<h1 class="hero-title">Scarica la tua musica</h1>
<p class="hero-subtitle">Spotify · YouTube · SoundCloud · qualsiasi link supportato da yt-dlp</p>
</div>
<div class="hero-deco">♫</div>
</header>
<h2 class="section-label">Sorgente</h2>
<div class="card">
<div class="field">
<label class="field-label">
URL
<span class="badge" id="urlListBadge" hidden></span>
</label>
<div class="row">
<input type="text" id="urlInput" class="input pill" placeholder="Incolla qui un link Spotify, YouTube o SoundCloud…" />
<button class="btn btn-ghost pill" id="loadListBtn">
<span>📂</span> Carica lista
</button>
</div>
</div>
<div class="field">
<label class="field-label">Cartella output</label>
<div class="row">
<div class="path-display" id="dlPathDisplay">Nessuna cartella selezionata</div>
<button class="btn btn-ghost pill btn-sm" id="dlBrowseBtn">Sfoglia</button>
</div>
</div>
</div>
<div class="card actions">
<button class="btn btn-primary pill btn-lg" id="downloadBtn">
<span class="ico">▶</span> Avvia Download
</button>
<button class="btn btn-danger pill btn-lg" id="stopDownloadBtn" disabled>
<span class="ico">◼</span> Stop
</button>
<div class="counter" id="dlCounter"></div>
</div>
<div class="card">
<div class="progress-head">
<span class="micro-label">Progresso</span>
<span class="percent green" id="dlPercent">0%</span>
</div>
<div class="progress">
<div class="progress-fill green" id="dlProgressFill"></div>
</div>
</div>
<h2 class="section-label">Log</h2>
<div class="card log-card">
<div class="log" id="dlLog"></div>
</div>
</section>
<!-- ===== VIEW: VIDEO ===== -->
<section class="view" id="view-video">
<header class="hero hero-pink">
<div class="hero-content">
<div class="hero-eyebrow pink">VIDEO</div>
<h1 class="hero-title">Scarica video</h1>
<p class="hero-subtitle">YouTube · TikTok · Instagram · Facebook · qualsiasi link supportato</p>
</div>
<div class="hero-deco">🎬</div>
</header>
<h2 class="section-label">Sorgente</h2>
<div class="card">
<div class="field">
<label class="field-label">URL</label>
<input type="text" id="videoUrlInput" class="input pill" placeholder="https://youtube.com/watch?v=… · https://tiktok.com/… · https://instagram.com/reel/…" />
</div>
<div class="field">
<label class="field-label">Cartella output</label>
<div class="row">
<div class="path-display" id="videoPathDisplay">Nessuna cartella selezionata</div>
<button class="btn btn-ghost pill btn-sm" id="videoBrowseBtn">Sfoglia</button>
</div>
</div>
<div class="field inline">
<label class="field-label">Modalità</label>
<div class="segmented" id="videoMode">
<button type="button" class="seg-btn active" data-mode="video">🎬 Video (MP4)</button>
<button type="button" class="seg-btn" data-mode="audio">🎵 Solo audio (MP3)</button>
</div>
</div>
<div class="field inline" id="videoQualityField">
<label class="field-label">Qualità</label>
<select id="videoQuality" class="input pill input-sm">
<option value="best">Migliore disponibile</option>
<option value="1080p" selected>1080p</option>
<option value="720p">720p</option>
<option value="480p">480p</option>
</select>
</div>
<div class="hint">
<span class="hint-ico">ⓘ</span>
<div>
In modalità <strong>Solo audio</strong> il file viene salvato in MP3 al bitrate impostato in Impostazioni → Download.<br>
Per Instagram/Facebook privati serve un file <strong>cookies.txt</strong> valido (Impostazioni → Percorsi).
</div>
</div>
</div>
<div class="card actions">
<button class="btn btn-pink pill btn-lg" id="videoDownloadBtn">
<span class="ico">▶</span> Avvia Download
</button>
<button class="btn btn-danger pill btn-lg" id="stopVideoBtn" disabled>
<span class="ico">◼</span> Stop
</button>
<div class="counter" id="videoCounter"></div>
</div>
<div class="card">
<div class="progress-head">
<span class="micro-label">Progresso</span>
<span class="percent pink" id="videoPercent">0%</span>
</div>
<div class="progress">
<div class="progress-fill pink" id="videoProgressFill"></div>
</div>
</div>
<h2 class="section-label">Log</h2>
<div class="card log-card">
<div class="log" id="videoLog"></div>
</div>
</section>
<!-- ===== VIEW: UPGRADE ===== -->
<section class="view" id="view-upgrade">
<header class="hero hero-blue">
<div class="hero-content">
<div class="hero-eyebrow blue">UPGRADE QUALITÀ</div>
<h1 class="hero-title">Migliora i tuoi audio</h1>
<p class="hero-subtitle">Riscarica i file a bitrate più alto. Imposta la soglia minima accettabile.</p>
</div>
<div class="hero-deco">⚡</div>
</header>
<h2 class="section-label">Cartella audio</h2>
<div class="card">
<div class="field">
<div class="row">
<div class="path-display" id="upPathDisplay">Nessuna cartella selezionata</div>
<button class="btn btn-ghost pill btn-sm" id="upBrowseBtn">📂 Sfoglia</button>
</div>
</div>
<label class="checkbox">
<input type="checkbox" id="upRecursive" />
<span class="check-box"></span>
<span>Includi sottocartelle</span>
</label>
<div class="info-row" id="upInfo"></div>
<div class="field inline">
<label class="field-label">Soglia HQ (kbps)</label>
<input type="number" id="upThreshold" class="input pill input-sm" value="310" />
</div>
</div>
<div class="card actions">
<button class="btn btn-blue pill btn-lg" id="upgradeBtn">
<span class="ico">▶</span> Avvia Upgrade
</button>
<button class="btn btn-danger pill btn-lg" id="stopUpgradeBtn" disabled>
<span class="ico">◼</span> Stop
</button>
<div class="counter" id="upCounter"></div>
</div>
<div class="card">
<div class="progress-head">
<span class="micro-label">Progresso</span>
<span class="percent blue" id="upPercent">0%</span>
</div>
<div class="progress">
<div class="progress-fill blue" id="upProgressFill"></div>
</div>
</div>
<h2 class="section-label">Log</h2>
<div class="card log-card">
<div class="log" id="upLog"></div>
</div>
</section>
<!-- ===== VIEW: REGISTRA ===== -->
<section class="view" id="view-record">
<header class="hero hero-red">
<div class="hero-content">
<div class="hero-eyebrow red">REGISTRAZIONE</div>
<h1 class="hero-title">Cattura audio</h1>
<p class="hero-subtitle">Registra in MP3 320 kbps da qualsiasi ingresso audio (mixer, scheda, microfono, BlackHole/loopback)</p>
</div>
<div class="hero-deco">●</div>
</header>
<h2 class="section-label">Dispositivo</h2>
<div class="card">
<div class="field">
<label class="field-label">Ingresso audio</label>
<div class="row">
<select id="recDevice" class="input pill">
<option value="">Caricamento…</option>
</select>
<button class="btn btn-ghost pill btn-sm" id="recRefreshBtn">↻ Aggiorna</button>
</div>
</div>
<div class="hint" id="recHint">
<span class="hint-ico">ⓘ</span>
<div>
<strong>Per registrare l'audio di sistema (Spotify/YouTube):</strong>
installa <code>brew install blackhole-2ch</code>, imposta l'uscita audio di macOS su <strong>BlackHole 2ch</strong>, poi seleziona qui sotto <strong>🔄 BlackHole 2ch (loopback)</strong>.
<br><br>
Per sentire l'audio mentre registri serve un <strong>Multi-Output Device</strong> in <em>Configurazione MIDI Audio</em> (BlackHole + altoparlanti) — guida completa nel
<a href="#" id="recOpenGuide">README su GitHub</a>.
</div>
</div>
</div>
<h2 class="section-label">Destinazione</h2>
<div class="card">
<div class="field">
<label class="field-label">Cartella output</label>
<div class="row">
<div class="path-display" id="recPathDisplay">Nessuna cartella selezionata</div>
<button class="btn btn-ghost pill btn-sm" id="recBrowseBtn">Sfoglia</button>
</div>
</div>
<div class="field">
<label class="field-label">Nome file (opzionale)</label>
<input type="text" id="recFilename" class="input pill" placeholder="Registrazione_AAAAMMGG_HHMMSS.mp3" />
</div>
<div class="field inline">
<label class="field-label">Bitrate</label>
<select id="recBitrate" class="input pill input-sm">
<option value="128k">128 kbps</option>
<option value="192k">192 kbps</option>
<option value="256k">256 kbps</option>
<option value="320k" selected>320 kbps</option>
</select>
</div>
</div>
<div class="card actions">
<button class="btn btn-red pill btn-lg" id="recStartBtn">
<span class="ico">●</span> REC
</button>
<button class="btn btn-danger pill btn-lg" id="recStopBtn" disabled>
<span class="ico">◼</span> Stop
</button>
<div class="rec-timer" id="recTimer">00:00:00</div>
<div class="rec-status" id="recStatus"></div>
</div>
<div class="card" id="recOutputCard" hidden>
<div class="micro-label">Ultima registrazione</div>
<div class="rec-result" id="recResult"></div>
</div>
</section>
<!-- ===== VIEW: METADATI ===== -->
<section class="view" id="view-meta">
<header class="hero hero-amber">
<div class="hero-content">
<div class="hero-eyebrow amber">METADATI</div>
<h1 class="hero-title">Modifica tag audio</h1>
<p class="hero-subtitle">Titolo · Artista · Album · Anno · Copertina · BPM · Key · MP3/M4A/FLAC</p>
</div>
<div class="hero-deco">🏷</div>
</header>
<h2 class="section-label">File</h2>
<div class="card">
<div class="row">
<button class="btn btn-amber pill" id="metaPickBtn">📂 Apri file audio</button>
<div class="path-display" id="metaPathDisplay">Nessun file selezionato</div>
</div>
<div class="meta-info" id="metaInfo" hidden></div>
</div>
<div id="metaForm" hidden>
<h2 class="section-label">Tag</h2>
<div class="card">
<div class="form-grid">
<div class="field full">
<label class="field-label">Titolo</label>
<input type="text" id="metaTitle" class="input pill">
</div>
<div class="field full">
<label class="field-label">Artista</label>
<input type="text" id="metaArtist" class="input pill">
</div>
<div class="field">
<label class="field-label">Album</label>
<input type="text" id="metaAlbum" class="input pill">
</div>
<div class="field">
<label class="field-label">Album Artist</label>
<input type="text" id="metaAlbumArtist" class="input pill">
</div>
<div class="field">
<label class="field-label">Anno</label>
<input type="text" id="metaYear" class="input pill">
</div>
<div class="field">
<label class="field-label">Traccia</label>
<input type="text" id="metaTrack" class="input pill" placeholder="1/12">
</div>
<div class="field">
<label class="field-label">Genere</label>
<input type="text" id="metaGenre" class="input pill">
</div>
<div class="field">
<label class="field-label">BPM</label>
<input type="text" id="metaBpm" class="input pill">
</div>
<div class="field">
<label class="field-label">Key</label>
<input type="text" id="metaKey" class="input pill">
</div>
<div class="field full">
<label class="field-label">Commento</label>
<textarea id="metaComment" class="input textarea" rows="3"></textarea>
</div>
<div class="field full" id="metaWhereFromField" hidden>
<label class="field-label">
Origine (macOS · kMDItemWhereFroms)
</label>
<textarea id="metaWhereFrom" class="input textarea" rows="2" placeholder="https://... (uno per riga)"></textarea>
<div class="cover-hint">Extended attribute macOS — una URL per riga. Vuoto = rimuove l'attributo.</div>
</div>
</div>
</div>
<h2 class="section-label">Copertina</h2>
<div class="card cover-card">
<div class="cover-preview" id="metaCoverPreview">
<span class="cover-empty">Nessuna copertina</span>
</div>
<div class="cover-actions">
<button class="btn btn-ghost pill" id="metaCoverBtn">📂 Cambia copertina</button>
<button class="btn btn-ghost pill" id="metaCoverRemoveBtn">✕ Rimuovi</button>
<div class="cover-hint" id="metaCoverHint"></div>
</div>
</div>
<div class="card actions">
<button class="btn btn-amber pill btn-lg" id="metaSaveBtn">💾 Salva tag</button>
<button class="btn btn-ghost pill btn-lg" id="metaReloadBtn">↺ Ricarica</button>
<div class="counter" id="metaStatus"></div>
</div>
</div>
</section>
<!-- ===== VIEW: SETTINGS ===== -->
<section class="view" id="view-settings">
<header class="hero hero-purple">
<div class="hero-content">
<div class="hero-eyebrow purple">IMPOSTAZIONI</div>
<h1 class="hero-title">Configurazione</h1>
<p class="hero-subtitle">Credenziali Spotify, qualità audio, percorsi e aggiornamenti.</p>
</div>
<div class="hero-deco">⚙</div>
</header>
<h2 class="section-label">Spotify API</h2>
<div class="card card-accent-purple">
<div class="row gap">
<button class="btn btn-purple pill" id="showGuideBtn">Come ottenere le credenziali?</button>
<button class="btn btn-ghost pill" id="openDashboardBtn">Apri Spotify Dashboard</button>
</div>
<div class="field">
<label class="field-label">Client ID</label>
<input type="text" id="clientIdInput" class="input pill" placeholder="Inserisci Client ID" />
</div>
<div class="field">
<label class="field-label">Client Secret</label>
<div class="row">
<input type="password" id="clientSecretInput" class="input pill" placeholder="Inserisci Client Secret" />
<button class="btn btn-ghost pill btn-sm" id="toggleSecretBtn">Mostra</button>
</div>
</div>
</div>
<h2 class="section-label">Download</h2>
<div class="card">
<div class="field">
<label class="field-label">Bitrate</label>
<select id="bitrateSelect" class="input pill input-sm">
<option value="128K">128 kbps</option>
<option value="192K">192 kbps</option>
<option value="256K">256 kbps</option>
<option value="320K" selected>320 kbps</option>
</select>
</div>
<div class="field">
<label class="field-label">Soglia HQ (kbps)</label>
<input type="number" id="hqThresholdInput" class="input pill input-sm" placeholder="310" />
</div>
</div>
<h2 class="section-label">Percorsi</h2>
<div class="card">
<div class="field">
<label class="field-label">Percorso cookies.txt</label>
<div class="row">
<input type="text" id="cookiesInput" class="input pill" placeholder="cookies.txt" />
<button class="btn btn-ghost pill btn-sm" id="browseCookiesBtn">Sfoglia</button>
</div>
</div>
<div class="field">
<label class="field-label">Cartella output predefinita</label>
<div class="row">
<input type="text" id="outputInput" class="input pill" placeholder="MUSICA/" />
<button class="btn btn-ghost pill btn-sm" id="browseOutputBtn">Sfoglia</button>
</div>
</div>
</div>
<h2 class="section-label">Aspetto</h2>
<div class="card">
<div class="field">
<label class="field-label">Tema</label>
<select id="themeSelect" class="input pill input-sm">
<option value="dark">Dark</option>
<option value="light">Light</option>
<option value="system">System</option>
</select>
</div>
</div>
<h2 class="section-label">Aggiornamenti</h2>
<div class="card">
<div class="row spread">
<div class="version-line">
Versione corrente: <span class="strong" id="currentVersionLabel">v1.0</span>
</div>
<button class="btn btn-purple pill" id="checkUpdateBtn">Controlla aggiornamenti</button>
</div>
<div class="update-status" id="updateStatus"></div>
<button class="btn btn-success pill" id="downloadUpdateBtn" hidden>Scarica aggiornamento</button>
<div class="update-notes" id="updateNotes"></div>
</div>
<div class="save-row">
<button class="btn btn-primary pill btn-lg" id="saveBtn">Salva Impostazioni</button>
<div class="save-status" id="saveStatus"></div>
</div>
</section>
</main>
</div>
<!-- ============== MODAL: Spotify Guide ============== -->
<div class="modal-backdrop" id="guideModal" hidden>
<div class="modal">
<div class="modal-head">
<h2>Come ottenere le credenziali Spotify</h2>
<button class="modal-close" id="guideCloseBtn">×</button>
</div>
<pre class="modal-body" id="guideBody"></pre>
<div class="modal-foot">
<button class="btn btn-purple pill" id="guideOpenDashBtn">Apri Spotify Dashboard</button>
<button class="btn btn-ghost pill" id="guideCloseBtn2">Chiudi</button>
</div>
</div>
</div>
<!-- ============== TOAST ============== -->
<div class="toast" id="toast"></div>
<script src="js/app.js"></script>
</body>
</html>