Metadati tab estesa per audit e cleanup file AI-generated:
- Nuovi campi read/write: source_url (WOAS ID3), lyrics (USLT), plus
\xa9lyr per M4A
- Badge AI con riconoscimento: Suno, Udio, Riffusion, Mubert, AIVA,
Boomy, SoundRAW, Loudly + presenza C2PA manifest (GEOB frame)
- Pannello "Metadati avanzati" collapsable: tabella di tutti i frame
ID3 grezzi (chiave, tipo text/binary, preview, size)
- Rimozione selettiva frame custom via bridge remove_metadata_frames:
ID3.__delitem__() + save() per MP3, MP4.tags per M4A. Frame standard
protetti (TIT2/TPE1/APIC ecc.) marcati 🔒 nella UI, si svuotano dai
campi form
- Rilevamento AI riusabile via core.metadata._detect_ai(comment,
source_url, extra) — case-insensitive, returns (is_ai, source_name)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Charts CSS: .charts-filters[hidden] display:none !important, altrimenti
la regola display:flex sovrascriveva l'attributo hidden e tutti i pannelli
filtro delle 4 sorgenti erano visibili contemporaneamente
- Sidebar riorganizzata in 4 categorie (Scarica/Organizza/Altro/Sistema)
con .nav-section-label uppercase piccole tra i gruppi
- Traxsource: rimossi 10 generi per cui il sito ha rimosso la playlist
canonica "Top 100 <genre> of <month>" (broken-beat-nu-jazz, classic-house,
drum-and-bass, electro-house, electronica, leftfield, lounge-chill-out,
pop-dance, r-and-b-hip-hop, world). Dropdown ora mostra solo 13 generi
con Top 100 attiva verificata
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).
- Sidebar diventata troppo lunga (14+ tab): overflow-y auto sulla nav,
brand e footer restano sticky. Scrollbar 6px tema-coerente
- Nuovo core/applog.py: TimedRotatingFileHandler daily con 7 giorni
di retention. NON tocca sys.stdout/stderr (causa segfault WebKit
su macOS); cattura tracebacks via sys.excepthook + threading.excepthook
- bridge._log mirror su logger persistente (ui.<view>) → tutto quello
che vedi nei log delle tab e' anche su file
- Bridge: get_log_file_path() + open_log_folder() per la nuova sezione
Impostazioni con path visibile e bottone "Apri cartella"
Cataloga:
- Fallback Spotify quando AcoustID non trova (copertura ottima per
dance/electronic mashup/bootleg/remix)
- Dropdown "Struttura cartelle" per scegliere tra <Anno>/<Genere>/ e
<Genere>/<Anno>/
- Chiave AcoustID default sostituita da placeholder invalido a quella
pubblica di MusicBrainz Picard (valida)
Nuova tab Appiattisci:
- Sposta tutti i file audio delle sotto-cartelle nella cartella padre
- Utile come undo della catalogazione o merge di archivi frammentati
- Rimuove sotto-cartelle vuote dopo il move (opzionale)
- Suffisso _1, _2 su nomi duplicati (no overwrite)
Alcuni video YouTube restituiscono 403 anche con yt-dlp aggiornato.
Fix: nuova opzione Impostazioni "Cookies da browser" con dropdown
(chrome/safari/firefox/edge/brave). Priorita': file cookies.txt
esistente -> --cookies-from-browser <name> -> nessuno.
Bonus:
- core/paths.py::_bundle_dirs() ora include <project>/bundle_bin/
anche in dev, cosi' python main.py usa lo stesso yt-dlp del build
invece di Homebrew (spesso obsoleto)
- Helper _cookie_args() in downloader.py per non ripetere la logica
su tutti i punti dove serve --cookies
Iterazioni sul feature Dedup (v1.10.0) da subito-post-scaffolding:
- compute_fingerprint espone il vero errore invece del generico None,
worker propaga l'err_msg via progress_callback esteso
- Retry automatico: -length 30 su "invalid data / decoding frame",
-length 60 + -algorithm 1 su "fingerprint vuoto"
- Nuovo metodo "filename" (Jaccard sui token nome file) con algoritmo
incrementale O(N·K) — utile per file corrotti o per anteprima veloce
- Streaming groups: sia fingerprint che filename emettono `dedup:group`
appena un gruppo raggiunge >=2 file; JS accumula in Map per update
in-place. Progress bar avanza in tempo reale
- Fix shape bug che dava "NaN duplicati" nella summary line
- .dedup-groups-scroll: max-height 55vh + overflow-y auto per non
perdere l'header/footer scrollando molti gruppi
- Bottone "Seleziona tutti i consigliati" nel footer: ripristina la
selezione di default (tutti tranne il TIENI marcati per cancellazione)
- .gitignore: dedup_cache.db (SQLite locale per macchina utente)
Tab Upgrade:
- Checkbox "Scelta automatica" per skippare il modal quando ci sono
multi-match (usa bitrate DESC)
- Anteprima audio nel picker: play button sul source + ogni candidato
via bridge read_audio_data_url (WKWebView blocca file://)
- Bottone "Annulla tutto" nel picker → interrompe l'intera coda
- Fix mtime: shutil.copy (non copy2) così il file upgradato ha
data odierna nel Finder
- Fix stop audio → non triggera più toast di errore
- Modal picker più largo (620-780px) con footer wrap
Extra: campo release_date estratto anche in Beatport (publish_date)
e YouTube (upload_date) — completato scaffold sort colonna Data
Frontend Upgrade:
- state.upArchiveDir + handler Sfoglia/Rimuovi per la seconda path-display
- start_upgrade payload include archive_dir
- bridge handler 'upgrade:candidates_needed' apre openUpgradePicker() con
lista candidati (basename + bitrate + size + similarity + path)
- 3 azioni utente: Usa selezionato / Cerca YouTube / Salta; ESC e click
su backdrop = Salta. Chiusura implicita quando upgrade:done.
- CSS minimale (.upgrade-picker-*) accodato dopo le sezioni esistenti.
Version bump v1.9.2 -> v1.9.3.
Nella tab Upgrade aggiunta seconda coppia path-display + Sfoglia/Rimuovi
per la cartella archivio opzionale (con hint esplicativo).
In fondo al body: nuovo modal upgradePickerModal con lista candidati e
3 bottoni (Usa selezionato / Cerca YouTube / Salta). Riusa le classi
esistenti .modal-backdrop / .modal / .modal-head / .modal-body / .modal-foot.
Impostazioni: il bottone "Salva impostazioni" e' ora sticky in fondo
al viewport, sempre visibile senza dover scrollare in coda alla pagina.
Spotify: aggiornata guida in-app e landing per riflettere il nuovo
requisito Premium per creare le API keys (fino a oggi era gratis con
account Spotify Free; Spotify ha cambiato policy). Singoli brani e
tutte le altre fonti continuano a funzionare senza Premium.
Inclusi anche file gia deployati lato server in sessioni precedenti
(webhook LS, landing icone PNG, download.html Gatekeeper instructions).
Il selettore Impostazioni > Tema salvava il valore ma nessuno applicava
data-theme al <html>, e mancavano gli override CSS per il tema chiaro.
Aggiunte custom properties light + applyTheme() su init/change/save.
Pricing model: subscription mensili (Basic 2.99 / Pro 5.99 / Premium 9.99)
piu' Annual 49.90 one-time. Feature gating per piano (Basic = solo audio,
Pro+ = tutto). Quota giornaliera (10/30/150) con reset mezzanotte Europe/Rome.
Server: tabella daily_usage, endpoint /api/usage/consume e /status,
webhook LS gestisce subscription_* events + mappatura variant_id -> plan
via .env (LS_VARIANT_BASIC|PRO|PREMIUM|ANNUAL).
Client: gating tab in sidebar in base ai features del piano, contatore
"X/Y oggi" + bottone Upgrade, modal blocco con CTA verso landing.
Sul sistema dell'utente il comando ffmpeg che usa l'app funziona
correttamente (testato come subprocess identico), ma il vecchio
reporting era confuso quando l'errore era transitorio (BlackHole
non pronto, clock drift, ecc.) o quando la parola 'abort' non
compariva nello stderr.
Cambiamenti:
core/recorder.py:
- _extract_friendly_error riconosce ora piu casi: input/output error
(BlackHole non pronto -> suggerisce 'sudo killall coreaudiod'),
device not configured, permission denied, no such device
- rc=-6 su macOS ritorna SEMPRE il messaggio sul permesso Microfono
(prima richiedeva 'abort' nello stderr, non sempre presente)
- Fallback finale mostra anche le ultime 3 righe di stderr
- Nuova funzione get_last_stderr() per debugging
api/bridge.py:
- Nuovo metodo get_recorder_log() che ritorna stderr per la UI
webui:
- Nuova card di errore persistente con bottoni
'Mostra log tecnici' / 'Copia log'
- Si resetta automaticamente all'avvio di una nuova registrazione
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
L'input URL ora accetta anche query libere: se non sembra un link
(http/https/spotify:/dominio noto), il testo viene parsato come
{artist, name} e scaricato via download_playlist con una traccia.
Riusa lo stesso flusso ytsearch1: gia in uso per le tracklist da file.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Aggiunta una sezione 'Guida' (icona 📖, hero indaco) con manuale
d'uso interno, organizzato in 10 capitoli con sommario sticky a
sinistra:
- Introduzione, Setup iniziale
- Una sezione per ogni tab (Scarica, Video, Upgrade, Metadati,
Registra, Impostazioni)
- Aggiornamenti
- Troubleshooting con tabella di 8 sintomi/cause/fix
- Risorse esterne (GitHub, yt-dlp FAQ, BlackHole)
UX:
- Layout grid 200px (TOC) + content fluido. Su schermi <900px
il TOC diventa una row in alto
- TOC sticky con highlight del link attivo via IntersectionObserver
- Link esterni (data-ext) aperti nel browser di sistema via
open_external_url
- Hint nella tab Registra ora apre la sezione 'Tab Registra' della
guida invece del README su GitHub
- Box info/success/warn riusabili, stile <code> verde, <pre>
monospace, tabella troubleshooting
CSS:
- Nuova palette indigo + hero-indigo
- Stili .guide-layout, .guide-toc, .g-section, .g-box, .g-table,
.kbd, .toc-link.active
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- 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>
Aggiunto toggle Video/Solo audio nella tab Video. In modalita audio
yt-dlp scarica solo bestaudio ed estrae in MP3 al bitrate impostato
in Impostazioni (default 320 kbps), con embed thumbnail come la tab
Scarica.
- core/downloader.py: download_video() accetta audio_only e
audio_bitrate; quando True usa format 'bestaudio/best' e
--extract-audio --audio-format mp3
- api/bridge.py: start_video_download passa audio_only nel worker;
log mostra 'Solo audio MP3 320K' vs 'Video 1080p'
- webui: segmented control 'Video (MP4)' / 'Solo audio (MP3)' rosa,
selettore qualita nascosto in modalita audio, hint aggiornato
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- README: sezione 'Guida rapida' riscritta con installazione,
permesso microfono macOS, Setup A (output dedicato a BlackHole)
e Setup B (Multi-Output Device con BlackHole + altoparlanti,
Master Device + Drift Correction). Aggiunta tabella
Troubleshooting (5 sintomi tipici con fix)
- webui: hint nella tab Registra arricchito (riassunto a step
inline + link al README su GitHub via window open)
- CSS: .hint passa a align-items flex-start per contenuto
multi-riga, aggiunti stili per <code> e <a> dentro l'hint
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Aggiunta sezione per registrare in MP3 320 kbps da qualsiasi
dispositivo di input audio del sistema (mixer, scheda audio
esterna, microfono interno, BlackHole / driver virtuali per audio
di sistema).
- core/recorder.py: list_input_devices() parsa l'output di
ffmpeg -list_devices su AVFoundation (macOS) e DirectShow (Win),
marca come 'virtual' i driver loopback noti (BlackHole, Soundflower,
Loopback, VB-Cable, Stereo Mix, OBS). start_recording() lancia
ffmpeg in thread e emette eventi tick/started/stopped/error.
- api/bridge.py: list_audio_inputs, start_audio_recording,
stop_audio_recording, is_audio_recording
- webui: hero rosso, dropdown dispositivi con badge 'loopback',
selettore bitrate, timer HH:MM:SS animato, bottone REC con
pulse animation, card 'Ultima registrazione' con durata+path
- Hint visibile: per registrare audio di sistema serve BlackHole
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
kMDItemWhereFroms non e un tag ID3/MP4/Vorbis: e' un extended
attribute filesystem di macOS che Safari/Mail/AirDrop e altri tool
impostano per ricordare l'origine di un file scaricato.
- core/metadata.py: _read_where_from / _write_where_from via subprocess
xattr + plistlib (lettura/scrittura del plist binario embedded
nell'attributo)
- read_metadata espone 'where_from' (lista URL) e 'is_macos'
- write_metadata applica l'xattr se data['where_from'] e fornito;
lista vuota -> rimuove l'attributo
- webui: textarea 'Origine' visibile solo su macOS (is_macos=true),
una URL per riga
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Nuova sezione 'Metadati' nella sidebar con editor completo dei tag:
titolo, artista, album, album artist, anno, traccia, genere, BPM,
key, commento, copertina.
- core/metadata.py: read/write metadati via mutagen, supporto ID3
(MP3), MP4 atoms (M4A/AAC) e Vorbis comments (FLAC)
- api/bridge.py: pick_audio_file, pick_image_file, read_metadata,
save_metadata. Cover passata come base64 al frontend per preview
- webui: hero ambra, form a griglia 2 colonne, anteprima cover 180x180,
bottoni cambia/rimuovi copertina, ricarica file
- requirements.txt: aggiunto mutagen>=1.47.0
- build_macos.py / build_windows.py: --collect-all mutagen
Modifiche correlate (in attesa di decisione utente sulla
distribuzione degli aggiornamenti):
- VERSION bumpato a v1.2.2 in core/config.py
- check_update ora interroga l'API GitHub Releases invece di
djluza.com/version.json (funzionera quando il repo sara pubblico)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Quando si carica un file .txt come tracklist, l'app ora scarica i
brani in <output_dir>/<nome_file>/ anziche direttamente in output_dir.
Es: 'Beatport Top 100 Melodic House & Techno.txt' -> cartella
'Beatport Top 100 Melodic House & Techno/' creata dentro la
cartella output predefinita.
- load_url_list ritorna 'name' (basename senza estensione)
- start_tracks_download accetta 'subfolder' opzionale e fa
os.path.join con sanitizzazione dei separatori
- JS passa state.loaded.name come subfolder
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
- core/downloader.py: download_video() con format selector qualita
(best / 1080p / 720p / 480p), merge MP4, tracking .downloaded_videos
- api/bridge.py: start_video_download / stop_video_download / _video_worker.
Guard _any_job_running() per evitare concorrenza tra audio e video
(condividono lo stesso _stop_event)
- webui: nuova nav item, view con hero rosa (gradient pink->black),
selettore qualita, hint cookies per Instagram/Facebook privati
- CSS: palette pink, .hero-pink, .btn-pink, .progress-fill.pink,
componente .hint riusabile
- JS: handlers tab Video, canale bridge video:progress/video:done
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>