- Frontend HTML/CSS/JS in webui/ (stile Spotify-like, hero gradient, modal blur) - API bridge JS<->Python in api/bridge.py - Core invariato: downloader yt-dlp, upgrader, spotify client, config - Build scripts macOS (WebKit) e Windows (EdgeChromium) - .gitignore esclude credenziali (config.json, cookies.txt) e artefatti build Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
429 lines
14 KiB
Bash
Executable File
429 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
# ============================================
|
|
# Upgrade Quality - Cerca versioni HQ dei brani
|
|
# Sovrascrive i file originali
|
|
# Con progress bar e ripresa automatica
|
|
# ============================================
|
|
|
|
# Requisiti: yt-dlp, ffmpeg, ffprobe
|
|
# Installazione: brew install yt-dlp ffmpeg
|
|
|
|
# Percorso script (per trovare cookies.txt nella stessa cartella)
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
COOKIE_FILE="$SCRIPT_DIR/cookies.txt"
|
|
|
|
# Colori
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
GRAY='\033[0;90m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
BOLD='\033[1m'
|
|
|
|
# Soglia qualita (kbps) - sopra questa aggiorna solo copertina
|
|
HQ_THRESHOLD=310
|
|
|
|
# ============================================
|
|
# Funzione progress bar
|
|
# ============================================
|
|
progress_bar() {
|
|
local current=$1
|
|
local total=$2
|
|
local width=30
|
|
local percent=$((current * 100 / total))
|
|
local filled=$((current * width / total))
|
|
local empty=$((width - filled))
|
|
|
|
printf "\r ["
|
|
printf "%${filled}s" | tr ' ' '#'
|
|
printf "%${empty}s" | tr ' ' '-'
|
|
printf "] %3d%% (%d/%d)" "$percent" "$current" "$total"
|
|
}
|
|
|
|
# ============================================
|
|
# Funzione per aggiornare solo la copertina
|
|
# ============================================
|
|
update_cover_only() {
|
|
local FILE="$1"
|
|
local VIDEO_URL="$2"
|
|
local TEMP_DIR="$3"
|
|
local FILENAME="$4"
|
|
|
|
# Scarica thumbnail
|
|
yt-dlp \
|
|
--skip-download \
|
|
--write-thumbnail \
|
|
--convert-thumbnails jpg \
|
|
--no-warnings \
|
|
$COOKIE_OPTS \
|
|
--output "$TEMP_DIR/cover" \
|
|
"$VIDEO_URL" 2>/dev/null
|
|
|
|
local COVER_FILE=$(ls "$TEMP_DIR"/cover*.jpg 2>/dev/null | head -n 1)
|
|
|
|
if [ -n "$COVER_FILE" ] && [ -f "$COVER_FILE" ]; then
|
|
# Applica copertina con ffmpeg
|
|
local TEMP_OUTPUT="$TEMP_DIR/temp_output.mp3"
|
|
|
|
ffmpeg -y -i "$FILE" -i "$COVER_FILE" \
|
|
-map 0:a -map 1:0 \
|
|
-c:a copy \
|
|
-id3v2_version 3 \
|
|
-metadata:s:v title="Album cover" \
|
|
-metadata:s:v comment="Cover (front)" \
|
|
"$TEMP_OUTPUT" 2>/dev/null
|
|
|
|
if [ -f "$TEMP_OUTPUT" ]; then
|
|
mv "$TEMP_OUTPUT" "$FILE"
|
|
rm -f "$COVER_FILE"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
rm -f "$TEMP_DIR"/cover*.jpg 2>/dev/null
|
|
return 1
|
|
}
|
|
|
|
# ============================================
|
|
# Funzione per processare una singola cartella
|
|
# ============================================
|
|
process_folder() {
|
|
local SOURCE_DIR="$1"
|
|
local LOGFILE="$SOURCE_DIR/upgrade.log"
|
|
local DONE_FILE="$SOURCE_DIR/.upgraded_tracks"
|
|
local TEMP_DIR="$SOURCE_DIR/.temp_download"
|
|
|
|
echo ""
|
|
echo -e "${CYAN}========================================${NC}"
|
|
echo -e "${CYAN} Cartella: ${YELLOW}$(basename "$SOURCE_DIR")${NC}"
|
|
echo -e "${CYAN}========================================${NC}"
|
|
|
|
# Crea file done se non esiste
|
|
touch "$DONE_FILE"
|
|
|
|
# Conta file totali e da processare
|
|
local ALL_FILES=$(find "$SOURCE_DIR" -maxdepth 1 -type f \( -iname "*.mp3" -o -iname "*.m4a" -o -iname "*.wav" -o -iname "*.flac" \) | wc -l | tr -d ' ')
|
|
|
|
if [ "$ALL_FILES" -eq 0 ]; then
|
|
echo -e " ${YELLOW}Nessun file audio, salto${NC}"
|
|
return
|
|
fi
|
|
|
|
# Conta quanti sono gia stati convertiti
|
|
local ALREADY_DONE=0
|
|
while IFS= read -r FILE; do
|
|
local BASENAME=$(basename "$FILE")
|
|
local FILENAME="${BASENAME%.*}"
|
|
if grep -qxF "$FILENAME" "$DONE_FILE" 2>/dev/null; then
|
|
((ALREADY_DONE++))
|
|
fi
|
|
done < <(find "$SOURCE_DIR" -maxdepth 1 -type f \( -iname "*.mp3" -o -iname "*.m4a" -o -iname "*.wav" -o -iname "*.flac" \))
|
|
|
|
local TO_PROCESS=$((ALL_FILES - ALREADY_DONE))
|
|
|
|
if [ "$TO_PROCESS" -eq 0 ]; then
|
|
echo -e " ${GREEN}Tutti i $ALL_FILES brani sono gia stati convertiti${NC}"
|
|
return
|
|
fi
|
|
|
|
if [ "$ALREADY_DONE" -gt 0 ]; then
|
|
echo -e " Totale: $ALL_FILES file | ${GREEN}Gia convertiti: $ALREADY_DONE${NC} | Da fare: $TO_PROCESS"
|
|
else
|
|
echo " Trovati $ALL_FILES file audio da processare"
|
|
fi
|
|
|
|
# Crea directory temporanea
|
|
mkdir -p "$TEMP_DIR"
|
|
|
|
local INDEX=0
|
|
local SUCCESS=0
|
|
local FAILED=0
|
|
local SKIPPED=0
|
|
|
|
# Processa ogni file audio
|
|
while IFS= read -r FILE; do
|
|
((INDEX++))
|
|
|
|
# Estrai nome file senza estensione
|
|
local BASENAME=$(basename "$FILE")
|
|
local FILENAME="${BASENAME%.*}"
|
|
|
|
# Controlla se gia convertito
|
|
if grep -qxF "$FILENAME" "$DONE_FILE" 2>/dev/null; then
|
|
((SKIPPED++))
|
|
continue
|
|
fi
|
|
|
|
# Analizza qualita attuale
|
|
local CURRENT_BITRATE=$(ffprobe -v quiet -print_format json -show_format "$FILE" 2>/dev/null | grep -o '"bit_rate": "[^"]*"' | grep -o '[0-9]*')
|
|
local CURRENT_KBPS=$((CURRENT_BITRATE / 1000))
|
|
|
|
# Mostra stato
|
|
echo ""
|
|
local DONE_COUNT=$((SKIPPED + SUCCESS + FAILED))
|
|
echo -e " ${YELLOW}[$((DONE_COUNT + 1))/$ALL_FILES]${NC} $FILENAME"
|
|
echo -e " ${GRAY}Qualita attuale: ${CURRENT_KBPS}kbps${NC}"
|
|
|
|
# Cerca su YouTube
|
|
echo -ne " ${GRAY}Cercando su YouTube...${NC}"
|
|
local VIDEO_INFO=$(yt-dlp "ytsearch1:$FILENAME" \
|
|
--get-id --get-title \
|
|
--no-warnings 2>/dev/null)
|
|
|
|
if [ -z "$VIDEO_INFO" ]; then
|
|
echo -e "\r ${RED}Non trovato su YouTube ${NC}"
|
|
echo "FALLITO: $FILENAME - Non trovato" >> "$LOGFILE"
|
|
# Segna come fatto comunque per non riprovare
|
|
echo "$FILENAME" >> "$DONE_FILE"
|
|
((FAILED++))
|
|
continue
|
|
fi
|
|
|
|
local VIDEO_TITLE=$(echo "$VIDEO_INFO" | head -n 1)
|
|
local VIDEO_ID=$(echo "$VIDEO_INFO" | tail -n 1)
|
|
local VIDEO_URL="https://www.youtube.com/watch?v=$VIDEO_ID"
|
|
|
|
echo -e "\r ${GRAY}Trovato: ${VIDEO_TITLE:0:50}...${NC} "
|
|
|
|
# Se qualita gia alta, aggiorna solo copertina
|
|
if [ "$CURRENT_KBPS" -ge "$HQ_THRESHOLD" ]; then
|
|
echo -ne " ${BLUE}Qualita gia alta, aggiorno solo copertina...${NC}"
|
|
|
|
if update_cover_only "$FILE" "$VIDEO_URL" "$TEMP_DIR" "$FILENAME"; then
|
|
echo -e "\r ${GREEN}Copertina aggiornata (audio ${CURRENT_KBPS}kbps mantenuto) ${NC}"
|
|
echo "COVER: $FILENAME - Copertina aggiornata (${CURRENT_KBPS}kbps)" >> "$LOGFILE"
|
|
else
|
|
echo -e "\r ${YELLOW}Copertina non disponibile (audio ${CURRENT_KBPS}kbps ok) ${NC}"
|
|
echo "SKIP: $FILENAME - Gia HQ ${CURRENT_KBPS}kbps" >> "$LOGFILE"
|
|
fi
|
|
|
|
echo "$FILENAME" >> "$DONE_FILE"
|
|
((SUCCESS++))
|
|
|
|
# Pulisci temp
|
|
rm -f "$TEMP_DIR"/*.jpg 2>/dev/null
|
|
rm -f "$TEMP_DIR"/*.webp 2>/dev/null
|
|
continue
|
|
fi
|
|
|
|
# Scarica con progress
|
|
echo -ne " ${CYAN}Scaricando...${NC} "
|
|
|
|
yt-dlp \
|
|
--extract-audio \
|
|
--audio-format mp3 \
|
|
--audio-quality 0 \
|
|
--embed-thumbnail \
|
|
--add-metadata \
|
|
--no-warnings \
|
|
--progress \
|
|
--newline \
|
|
$COOKIE_OPTS \
|
|
--output "$TEMP_DIR/%(title)s.%(ext)s" \
|
|
"$VIDEO_URL" 2>&1 | while IFS= read -r line; do
|
|
# Estrai percentuale dal output di yt-dlp
|
|
if [[ "$line" =~ ([0-9]+\.[0-9]+)% ]]; then
|
|
pct="${BASH_REMATCH[1]}"
|
|
pct_int=${pct%.*}
|
|
bar_width=20
|
|
filled=$((pct_int * bar_width / 100))
|
|
empty=$((bar_width - filled))
|
|
printf "\r ${CYAN}Scaricando${NC} ["
|
|
printf "%${filled}s" | tr ' ' '#'
|
|
printf "%${empty}s" | tr ' ' '-'
|
|
printf "] %3d%%" "$pct_int"
|
|
fi
|
|
done
|
|
|
|
if [ $? -eq 0 ] || [ -n "$(ls -A "$TEMP_DIR" 2>/dev/null)" ]; then
|
|
# Trova il file appena scaricato
|
|
local NEWEST_FILE=$(ls -t "$TEMP_DIR"/*.mp3 2>/dev/null | head -n 1)
|
|
|
|
if [ -n "$NEWEST_FILE" ] && [ -f "$NEWEST_FILE" ]; then
|
|
local NEW_BITRATE=$(ffprobe -v quiet -print_format json -show_format "$NEWEST_FILE" 2>/dev/null | grep -o '"bit_rate": "[^"]*"' | grep -o '[0-9]*')
|
|
local NEW_KBPS=$((NEW_BITRATE / 1000))
|
|
|
|
# Elimina file originale e sposta quello nuovo
|
|
rm -f "$FILE"
|
|
mv "$NEWEST_FILE" "$SOURCE_DIR/$FILENAME.mp3"
|
|
|
|
# Segna come completato
|
|
echo "$FILENAME" >> "$DONE_FILE"
|
|
|
|
local DIFF=$((NEW_KBPS - CURRENT_KBPS))
|
|
if [ "$DIFF" -gt 0 ]; then
|
|
echo -e "\r ${GREEN}Completato: ${NEW_KBPS}kbps (+${DIFF}kbps) ${NC}"
|
|
else
|
|
echo -e "\r ${GREEN}Completato: ${NEW_KBPS}kbps ${NC}"
|
|
fi
|
|
|
|
echo "OK: $FILENAME - ${CURRENT_KBPS}kbps -> ${NEW_KBPS}kbps" >> "$LOGFILE"
|
|
((SUCCESS++))
|
|
else
|
|
echo -e "\r ${RED}Errore: file non trovato dopo download ${NC}"
|
|
echo "$FILENAME" >> "$DONE_FILE"
|
|
((FAILED++))
|
|
fi
|
|
else
|
|
echo -e "\r ${RED}Errore durante il download ${NC}"
|
|
echo "ERRORE: $FILENAME - Download fallito" >> "$LOGFILE"
|
|
echo "$FILENAME" >> "$DONE_FILE"
|
|
((FAILED++))
|
|
fi
|
|
|
|
# Pulisci temp
|
|
rm -f "$TEMP_DIR"/*.mp3 2>/dev/null
|
|
rm -f "$TEMP_DIR"/*.webm 2>/dev/null
|
|
rm -f "$TEMP_DIR"/*.m4a 2>/dev/null
|
|
|
|
done < <(find "$SOURCE_DIR" -maxdepth 1 -type f \( -iname "*.mp3" -o -iname "*.m4a" -o -iname "*.wav" -o -iname "*.flac" \))
|
|
|
|
# Rimuovi directory temporanea
|
|
rmdir "$TEMP_DIR" 2>/dev/null
|
|
|
|
# Sommario cartella
|
|
echo ""
|
|
echo -e " ${GRAY}---${NC}"
|
|
echo -e " Convertiti: ${GREEN}$SUCCESS${NC} | Falliti: ${RED}$FAILED${NC} | Saltati: ${GRAY}$SKIPPED${NC}"
|
|
}
|
|
|
|
# ============================================
|
|
# MAIN
|
|
# ============================================
|
|
|
|
echo ""
|
|
echo "========================================"
|
|
echo " UPGRADE QUALITY AUDIO"
|
|
echo "========================================"
|
|
|
|
# Verifica cookies YouTube Premium
|
|
if [ -f "$COOKIE_FILE" ]; then
|
|
echo -e "Cookies: ${GREEN}YouTube Premium attivo${NC}"
|
|
COOKIE_OPTS="--cookies $COOKIE_FILE"
|
|
else
|
|
echo -e "Cookies: ${YELLOW}Non trovati (qualita standard)${NC}"
|
|
COOKIE_OPTS=""
|
|
fi
|
|
|
|
# Gestione parametri
|
|
if [ "$1" = "-r" ] || [ "$1" = "--recursive" ]; then
|
|
# Modalita ricorsiva: processa tutte le sottocartelle
|
|
if [ -z "$2" ]; then
|
|
echo -e "${CYAN}Inserisci la cartella principale:${NC}"
|
|
read -r BASE_DIR
|
|
else
|
|
BASE_DIR="$2"
|
|
fi
|
|
|
|
if [ ! -d "$BASE_DIR" ]; then
|
|
echo -e "${RED}Errore: Directory '$BASE_DIR' non trovata${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Trova tutte le sottocartelle con file audio
|
|
echo ""
|
|
echo "Scansione cartelle in: $BASE_DIR"
|
|
|
|
FOLDERS=$(find "$BASE_DIR" -type d | while read -r dir; do
|
|
count=$(find "$dir" -maxdepth 1 -type f \( -iname "*.mp3" -o -iname "*.m4a" -o -iname "*.wav" -o -iname "*.flac" \) 2>/dev/null | wc -l | tr -d ' ')
|
|
if [ "$count" -gt 0 ]; then
|
|
echo "$dir"
|
|
fi
|
|
done)
|
|
|
|
if [ -z "$FOLDERS" ]; then
|
|
echo -e "${RED}Nessuna cartella con file audio trovata${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
FOLDER_COUNT=$(echo "$FOLDERS" | wc -l | tr -d ' ')
|
|
echo ""
|
|
echo "Trovate $FOLDER_COUNT cartelle con file audio:"
|
|
echo "$FOLDERS" | while read -r f; do
|
|
count=$(find "$f" -maxdepth 1 -type f \( -iname "*.mp3" -o -iname "*.m4a" -o -iname "*.wav" -o -iname "*.flac" \) | wc -l | tr -d ' ')
|
|
# Conta gia convertiti
|
|
done_file="$f/.upgraded_tracks"
|
|
if [ -f "$done_file" ]; then
|
|
done_count=$(wc -l < "$done_file" | tr -d ' ')
|
|
remaining=$((count - done_count))
|
|
if [ "$remaining" -le 0 ]; then
|
|
echo -e " ${GREEN}$(basename "$f")${NC} ($count file - tutti convertiti)"
|
|
else
|
|
echo -e " ${YELLOW}$(basename "$f")${NC} ($count file - $remaining da convertire)"
|
|
fi
|
|
else
|
|
echo -e " ${YELLOW}$(basename "$f")${NC} ($count file)"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}I file originali verranno sovrascritti!${NC}"
|
|
echo -e "${GRAY}I brani gia convertiti verranno saltati automaticamente.${NC}"
|
|
read -p "Vuoi continuare? (s/n): " CONFIRM
|
|
if [[ ! "$CONFIRM" =~ ^[sS]$ ]]; then
|
|
echo "Operazione annullata."
|
|
exit 0
|
|
fi
|
|
|
|
# Processa ogni cartella
|
|
FOLDER_INDEX=0
|
|
echo "$FOLDERS" | while read -r folder; do
|
|
((FOLDER_INDEX++))
|
|
echo ""
|
|
echo -e "${BOLD}>>> Cartella $FOLDER_INDEX/$FOLDER_COUNT <<<${NC}"
|
|
process_folder "$folder"
|
|
done
|
|
|
|
else
|
|
# Modalita singola cartella
|
|
if [ -z "$1" ]; then
|
|
echo -e "${CYAN}Inserisci il percorso della cartella da convertire:${NC}"
|
|
read -r SOURCE_DIR
|
|
else
|
|
SOURCE_DIR="$1"
|
|
fi
|
|
|
|
if [ ! -d "$SOURCE_DIR" ]; then
|
|
echo -e "${RED}Errore: Directory '$SOURCE_DIR' non trovata${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Conta file
|
|
TOTAL=$(find "$SOURCE_DIR" -maxdepth 1 -type f \( -iname "*.mp3" -o -iname "*.m4a" -o -iname "*.wav" -o -iname "*.flac" \) | wc -l | tr -d ' ')
|
|
|
|
if [ "$TOTAL" -eq 0 ]; then
|
|
echo -e "${RED}Nessun file audio trovato nella cartella${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Conta gia convertiti
|
|
DONE_FILE="$SOURCE_DIR/.upgraded_tracks"
|
|
if [ -f "$DONE_FILE" ]; then
|
|
DONE_COUNT=$(wc -l < "$DONE_FILE" | tr -d ' ')
|
|
REMAINING=$((TOTAL - DONE_COUNT))
|
|
echo -e "Cartella: ${YELLOW}$SOURCE_DIR${NC}"
|
|
echo -e "Totale: $TOTAL file | ${GREEN}Gia convertiti: $DONE_COUNT${NC} | Da fare: $REMAINING"
|
|
else
|
|
echo -e "Cartella: ${YELLOW}$SOURCE_DIR${NC}"
|
|
echo "Trovati $TOTAL file audio"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}I file originali verranno sovrascritti!${NC}"
|
|
echo -e "${GRAY}I brani gia convertiti verranno saltati automaticamente.${NC}"
|
|
read -p "Vuoi continuare? (s/n): " CONFIRM
|
|
if [[ ! "$CONFIRM" =~ ^[sS]$ ]]; then
|
|
echo "Operazione annullata."
|
|
exit 0
|
|
fi
|
|
|
|
process_folder "$SOURCE_DIR"
|
|
fi
|
|
|
|
echo ""
|
|
echo "========================================"
|
|
echo -e " ${GREEN}COMPLETATO${NC}"
|
|
echo "========================================"
|