- 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>
555 lines
22 KiB
HTML
555 lines
22 KiB
HTML
<!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>
|