Skip to content

Instantly share code, notes, and snippets.

@phnahes
Last active December 1, 2025 16:49
Show Gist options
  • Select an option

  • Save phnahes/6696ec1194768293908ecc387a1b5e04 to your computer and use it in GitHub Desktop.

Select an option

Save phnahes/6696ec1194768293908ecc387a1b5e04 to your computer and use it in GitHub Desktop.
Download Youtube Playlist mp3 files
#!/bin/bash
################################################################################
# YouTube Playlist to MP3 Downloader Script
#
# Description:
# This script automatically downloads YouTube playlists and converts
# them to high-quality MP3 files. Uses yt-dlp for downloading
# and audio conversion.
#
# Dependencies:
# - yt-dlp: video download tool
# - jq: command-line JSON processor
# - ffmpeg: audio/video converter (used by yt-dlp)
#
# Author: Paulo Nahes
# Date: 11/30/2025
################################################################################
# ==================== DEFAULT SETTINGS ====================
# Default playlist URL (can be overridden with -u)
DEFAULT_PLAYLIST_URL="https://www.youtube.com/watch?v=&list=xxxxxxxxxxx"
# Default destination directory (can be overridden with -d)
DEFAULT_DEST_DIR="musicas"
# Default audio format (can be overridden with -f)
DEFAULT_AUDIO_FORMAT="mp3"
# Default audio quality: 0 (best) to 9 (worst)
DEFAULT_AUDIO_QUALITY="0"
# Default delay between downloads in seconds (can be overridden with -w)
DEFAULT_DELAY="5"
# Random delay (0 = disabled, 1 = enabled)
RANDOM_DELAY="1"
# Random delay range (minimum and maximum in seconds)
RANDOM_DELAY_MIN="0"
RANDOM_DELAY_MAX="10"
# Filename sanitization (0 = disabled, 1 = enabled)
SANITIZE_NAMES="1"
# Resume/checkpoint system
ENABLE_RESUME="1"
# Force re-download even if file exists (0 = no, 1 = yes)
FORCE_REDOWNLOAD="0"
# Temporary file for URLs
TEMP_URLS_FILE="urls_temp_$$.txt"
# Download progress log file
DOWNLOAD_LOG_FILE=".download_progress.log"
# Failed downloads log file
FAILED_LOG_FILE=".download_failed.log"
# ==================== OUTPUT COLORS ====================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# ==================== FUNCTIONS ====================
show_help() {
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN}Script de Download de Playlists do YouTube em MP3${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e "${YELLOW}DESCRIÇÃO:${NC}"
echo " Este script baixa automaticamente playlists do YouTube e converte"
echo " os vídeos para arquivos MP3 de alta qualidade. Cada faixa é salva"
echo " com índice da playlist e título do vídeo."
echo ""
echo -e "${YELLOW}USO:${NC}"
echo " $0 [opções]"
echo ""
echo -e "${YELLOW}OPÇÕES:${NC}"
echo " -u URL URL da playlist do YouTube"
echo " (padrão: $DEFAULT_PLAYLIST_URL)"
echo ""
echo " -d DIR Diretório de destino para os arquivos MP3"
echo " (padrão: $DEFAULT_DEST_DIR)"
echo ""
echo " -f FORMAT Formato de áudio (mp3, aac, m4a, opus, vorbis, wav)"
echo " (padrão: $DEFAULT_AUDIO_FORMAT)"
echo ""
echo " -q QUALITY Qualidade de áudio (0-9, onde 0 é a melhor)"
echo " (padrão: $DEFAULT_AUDIO_QUALITY)"
echo ""
echo " -w SECONDS Tempo de espera entre downloads em segundos"
echo " (padrão: $DEFAULT_DELAY)"
echo ""
echo " -r Ativa delay ALEATÓRIO entre downloads"
echo " (entre $RANDOM_DELAY_MIN e $RANDOM_DELAY_MAX segundos, ignora -w)"
echo ""
echo " -s Desabilita sanitização de nomes de arquivos"
echo " (por padrão, caracteres especiais são removidos)"
echo ""
echo " -c Desabilita sistema de checkpoint/resume"
echo " (por padrão, downloads já feitos são pulados)"
echo ""
echo " -F FORÇA re-download de todas as faixas"
echo " (ignora arquivos existentes e checkpoint)"
echo ""
echo " -h Mostra esta mensagem de ajuda"
echo ""
echo -e "${YELLOW}EXEMPLOS:${NC}"
echo " # Usar configurações padrão"
echo " $0"
echo ""
echo " # Baixar playlist específica para diretório customizado"
echo " $0 -u \"https://www.youtube.com/playlist?list=PLxxxxxxxx\" -d \"minhas_musicas\""
echo ""
echo " # Baixar com qualidade média e sem delay"
echo " $0 -q 5 -w 0"
echo ""
echo " # Baixar em formato AAC de alta qualidade"
echo " $0 -f aac -q 0"
echo ""
echo " # Usar delay aleatório entre downloads (0-15 segundos)"
echo " $0 -r"
echo ""
echo " # Combinar delay aleatório com outras opções"
echo " $0 -u \"URL_DA_PLAYLIST\" -d \"musicas\" -r"
echo ""
echo " # Retomar download interrompido (checkpoint automático)"
echo " $0"
echo ""
echo " # Forçar re-download de tudo"
echo " $0 -F"
echo ""
echo -e "${YELLOW}DEPENDÊNCIAS NECESSÁRIAS:${NC}"
echo " - yt-dlp (instalação: brew install yt-dlp ou pip install yt-dlp)"
echo " - jq (instalação: brew install jq ou apt-get install jq)"
echo " - ffmpeg (instalação: brew install ffmpeg ou apt-get install ffmpeg)"
echo ""
echo -e "${YELLOW}NOTAS:${NC}"
echo " - Os arquivos são salvos como: \"001 - Titulo da Musica.mp3\""
echo " - Caracteres especiais são removidos automaticamente (use -s para desabilitar)"
echo " - Índices são formatados com zeros à esquerda (001, 002, etc.)"
echo " - Sistema de checkpoint salva progresso automaticamente"
echo " - Downloads já concluídos são pulados automaticamente"
echo " - O script aguarda entre downloads para evitar sobrecarga"
echo " - URLs inválidas ou downloads com falha são reportados"
echo " - Arquivos temporários são limpos automaticamente"
echo ""
echo -e "${YELLOW}FALHAS COMUNS DE DOWNLOAD:${NC}"
echo " - Vídeos com restrição de idade: Não podem ser baixados"
echo " - Vídeos privados/deletados: Não estão mais disponíveis"
echo " - Conteúdo com restrição geográfica: Bloqueado na sua região"
echo " - Conteúdo premium: Requer YouTube Premium"
echo " - Lives ao vivo: Podem falhar se estiverem transmitindo"
echo " - Limite de requisições: Muitas requisições (use -w para delay)"
echo " - Problemas de rede: Conexão instável ou timeout"
echo ""
echo " Falhas são registradas em: .download_failed.log"
echo " Verifique este arquivo para mensagens de erro detalhadas"
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
exit 0
}
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[✓]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[⚠]${NC} $1"
}
log_error() {
echo -e "${RED}[✗]${NC} $1"
}
sanitize_filename() {
local filename="$1"
# Convert to lowercase if desired (commented out by default)
# filename=$(echo "$filename" | tr '[:upper:]' '[:lower:]')
# Remove problematic special characters
# Removes: / \ : * ? " < > | [ ] { } ! @ # $ % ^ & = + ` ~ ; '
filename=$(echo "$filename" | sed 's/[\/\\:*?"<>|]/ /g')
filename=$(echo "$filename" | sed 's/[\[\]{}!@#$%^&=+`~;'\'']//g')
# Replace parentheses with spaces
filename=$(echo "$filename" | sed 's/[()]/ /g')
# Remove extra underscores and replace with spaces
filename=$(echo "$filename" | sed 's/_/ /g')
# Remove commas and semicolons
filename=$(echo "$filename" | sed 's/[,;]//g')
# Replace multiple spaces with a single space
filename=$(echo "$filename" | sed 's/ */ /g')
# Replace multiple hyphens with a single hyphen
filename=$(echo "$filename" | sed 's/--*/-/g')
# Remove leading and trailing spaces
filename=$(echo "$filename" | sed 's/^ *//;s/ *$//')
# Remove leading and trailing hyphens
filename=$(echo "$filename" | sed 's/^-*//;s/-*$//')
# Remove leading dots (but keep at the end for extensions)
filename=$(echo "$filename" | sed 's/^\.*//g')
# Limit filename length (maximum 150 characters to avoid problems)
if [ ${#filename} -gt 150 ]; then
filename="${filename:0:150}"
# Remove spaces/hyphens that may remain at the end after truncation
filename=$(echo "$filename" | sed 's/[ -]*$//')
fi
# If the name is empty, use a default name
if [ -z "$filename" ]; then
filename="track"
fi
echo "$filename"
}
check_dependencies() {
log_info "Checking required dependencies..."
local missing_deps=0
# Check yt-dlp
if ! command -v yt-dlp &> /dev/null; then
log_error "yt-dlp not found!"
echo " Install with: brew install yt-dlp OR pip install yt-dlp"
missing_deps=1
else
log_success "yt-dlp found: $(command -v yt-dlp)"
fi
# Check jq
if ! command -v jq &> /dev/null; then
log_error "jq not found!"
echo " Install with: brew install jq OR apt-get install jq"
missing_deps=1
else
log_success "jq found: $(command -v jq)"
fi
# Check ffmpeg
if ! command -v ffmpeg &> /dev/null; then
log_error "ffmpeg not found!"
echo " Install with: brew install ffmpeg OR apt-get install ffmpeg"
missing_deps=1
else
log_success "ffmpeg found: $(command -v ffmpeg)"
fi
if [ $missing_deps -eq 1 ]; then
log_error "Missing dependencies. Please install them before continuing."
exit 1
fi
log_success "All dependencies are installed!"
echo ""
}
validate_url() {
local url="$1"
# Check if URL contains youtube.com or youtu.be
if [[ ! "$url" =~ youtube\.com|youtu\.be ]]; then
log_error "Invalid URL. Must be a YouTube URL."
return 1
fi
# Check if contains 'list=' (playlist indicator)
if [[ ! "$url" =~ list= ]]; then
log_warning "The URL may not be a playlist (missing 'list=' in URL)."
log_warning "Script will continue, but may process only one video."
fi
return 0
}
create_directory() {
local dir="$1"
if [ -d "$dir" ]; then
log_info "Directory already exists: $dir"
else
log_info "Creating directory: $dir"
if mkdir -p "$dir"; then
log_success "Directory created successfully!"
else
log_error "Failed to create directory: $dir"
exit 1
fi
fi
# Check write permissions
if [ ! -w "$dir" ]; then
log_error "No write permission for directory: $dir"
exit 1
fi
}
cleanup() {
if [ -f "$TEMP_URLS_FILE" ]; then
log_info "Removing temporary file..."
rm -f "$TEMP_URLS_FILE"
fi
}
is_track_downloaded() {
local track_index="$1"
local log_file="$2"
# Check in log file
if [ -f "$log_file" ] && grep -q "^${track_index}|" "$log_file" 2>/dev/null; then
return 0 # Already downloaded
fi
return 1 # Not downloaded
}
mark_track_downloaded() {
local track_index="$1"
local title="$2"
local log_file="$3"
# Add to log: INDEX|TITLE|TIMESTAMP
echo "${track_index}|${title}|$(date '+%Y-%m-%d %H:%M:%S')" >> "$log_file"
}
mark_track_failed() {
local track_index="$1"
local url="$2"
local error_msg="$3"
local log_file="$4"
# Add to failed log: INDEX|URL|ERROR|TIMESTAMP
echo "${track_index}|${url}|${error_msg}|$(date '+%Y-%m-%d %H:%M:%S')" >> "$log_file"
}
check_file_exists() {
local dest_dir="$1"
local track_index="$2"
local format="$3"
# Search for file starting with track index
local found_file=$(find "$dest_dir" -name "${track_index} - *.$format" -type f 2>/dev/null | head -n 1)
if [ -n "$found_file" ]; then
echo "$found_file"
return 0
fi
return 1
}
load_checkpoint_info() {
local log_file="$1"
if [ -f "$log_file" ]; then
local completed_count=$(wc -l < "$log_file" | tr -d ' ')
echo "$completed_count"
else
echo "0"
fi
}
# Ensure cleanup on interruption
trap cleanup EXIT INT TERM
# ==================== ARGUMENT PROCESSING ====================
PLAYLIST_URL="$DEFAULT_PLAYLIST_URL"
DEST_DIR="$DEFAULT_DEST_DIR"
AUDIO_FORMAT="$DEFAULT_AUDIO_FORMAT"
AUDIO_QUALITY="$DEFAULT_AUDIO_QUALITY"
DELAY="$DEFAULT_DELAY"
USE_RANDOM_DELAY="$RANDOM_DELAY"
USE_SANITIZE="$SANITIZE_NAMES"
USE_RESUME="$ENABLE_RESUME"
FORCE_DOWNLOAD="$FORCE_REDOWNLOAD"
while getopts "u:d:f:q:w:rscFh" opt; do
case $opt in
u)
PLAYLIST_URL="$OPTARG"
;;
d)
DEST_DIR="$OPTARG"
;;
f)
AUDIO_FORMAT="$OPTARG"
;;
q)
AUDIO_QUALITY="$OPTARG"
;;
w)
DELAY="$OPTARG"
;;
r)
USE_RANDOM_DELAY="1"
;;
s)
USE_SANITIZE="0"
;;
c)
USE_RESUME="0"
;;
F)
FORCE_DOWNLOAD="1"
;;
h)
show_help
;;
\?)
log_error "Invalid option: -$OPTARG"
echo "Use -h for help"
exit 1
;;
:)
log_error "Option -$OPTARG requires an argument"
echo "Use -h for help"
exit 1
;;
esac
done
# ==================== MAIN SCRIPT ====================
echo ""
log_info "═══════════════════════════════════════════════════════════"
log_info " Starting YouTube Playlist Download"
log_info "═══════════════════════════════════════════════════════════"
echo ""
# Check dependencies
check_dependencies
# Validate URL
log_info "Validating playlist URL..."
if ! validate_url "$PLAYLIST_URL"; then
exit 1
fi
log_success "URL validated!"
echo ""
# Create/check destination directory
log_info "Setting up destination directory..."
create_directory "$DEST_DIR"
echo ""
# Configure checkpoint log file
CHECKPOINT_LOG="$DEST_DIR/$DOWNLOAD_LOG_FILE"
FAILED_LOG="$DEST_DIR/$FAILED_LOG_FILE"
# Check if previous checkpoint exists
PREVIOUSLY_DOWNLOADED=0
if [ "$USE_RESUME" -eq 1 ] && [ "$FORCE_DOWNLOAD" -eq 0 ]; then
PREVIOUSLY_DOWNLOADED=$(load_checkpoint_info "$CHECKPOINT_LOG")
if [ "$PREVIOUSLY_DOWNLOADED" -gt 0 ]; then
log_info "Checkpoint found: $PREVIOUSLY_DOWNLOADED track(s) already downloaded"
fi
fi
# If force re-download, clear checkpoint
if [ "$FORCE_DOWNLOAD" -eq 1 ]; then
if [ -f "$CHECKPOINT_LOG" ]; then
log_warning "FORCE mode enabled: removing previous checkpoint..."
rm -f "$CHECKPOINT_LOG"
fi
fi
# Display settings
log_info "Download settings:"
echo " Playlist URL: $PLAYLIST_URL"
echo " Directory: $DEST_DIR"
echo " Format: $AUDIO_FORMAT"
echo " Quality: $AUDIO_QUALITY (0=best, 9=worst)"
if [ "$USE_RANDOM_DELAY" -eq 1 ]; then
echo " Delay between downloads: RANDOM (${RANDOM_DELAY_MIN}-${RANDOM_DELAY_MAX}s)"
else
echo " Delay between downloads: ${DELAY}s"
fi
if [ "$USE_SANITIZE" -eq 1 ]; then
echo " Name sanitization: ENABLED"
else
echo " Name sanitization: DISABLED"
fi
if [ "$USE_RESUME" -eq 1 ] && [ "$FORCE_DOWNLOAD" -eq 0 ]; then
echo " Resume system: ENABLED"
else
echo " Resume system: DISABLED"
fi
if [ "$FORCE_DOWNLOAD" -eq 1 ]; then
echo " Force mode: ENABLED (re-download everything)"
fi
echo ""
# Extract playlist URLs list
log_info "Extracting playlist URLs..."
if ! yt-dlp --flat-playlist -J "$PLAYLIST_URL" 2>/dev/null | jq -r '.entries[] | .url' > "$TEMP_URLS_FILE"; then
log_error "Failed to extract playlist URLs. Check the URL."
exit 1
fi
# Check if there are URLs
if [ ! -s "$TEMP_URLS_FILE" ]; then
log_error "No URLs found in playlist."
exit 1
fi
TOTAL_TRACKS=$(wc -l < "$TEMP_URLS_FILE" | tr -d ' ')
log_success "Found $TOTAL_TRACKS tracks in playlist!"
echo ""
# Process each track
CURRENT_TRACK=0
SUCCESSFUL_DOWNLOADS=0
FAILED_DOWNLOADS=0
SKIPPED_DOWNLOADS=0
while IFS= read -r URL; do
CURRENT_TRACK=$((CURRENT_TRACK + 1))
echo ""
log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
log_info "Processing track $CURRENT_TRACK of $TOTAL_TRACKS"
log_info "URL: $URL"
log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Format index with leading zeros (001, 002, etc.)
TRACK_INDEX=$(printf "%03d" $CURRENT_TRACK)
# Check if track was already downloaded (checkpoint)
SKIP_DOWNLOAD=0
if [ "$USE_RESUME" -eq 1 ] && [ "$FORCE_DOWNLOAD" -eq 0 ]; then
# Check in checkpoint log
if is_track_downloaded "$TRACK_INDEX" "$CHECKPOINT_LOG"; then
log_warning "Track already downloaded (checkpoint). Skipping..."
SKIPPED_DOWNLOADS=$((SKIPPED_DOWNLOADS + 1))
SKIP_DOWNLOAD=1
else
# Check if file exists in directory
EXISTING_FILE=$(check_file_exists "$DEST_DIR" "$TRACK_INDEX" "$AUDIO_FORMAT")
if [ -n "$EXISTING_FILE" ]; then
log_warning "File already exists: $(basename "$EXISTING_FILE")"
log_info "Updating checkpoint..."
# Add to checkpoint
EXISTING_TITLE=$(basename "$EXISTING_FILE" | sed "s/${TRACK_INDEX} - //;s/\.$AUDIO_FORMAT$//")
mark_track_downloaded "$TRACK_INDEX" "$EXISTING_TITLE" "$CHECKPOINT_LOG"
SKIPPED_DOWNLOADS=$((SKIPPED_DOWNLOADS + 1))
SKIP_DOWNLOAD=1
fi
fi
fi
# If should skip, continue to next track
if [ "$SKIP_DOWNLOAD" -eq 1 ]; then
continue
fi
# Execute download
DOWNLOAD_SUCCESS=0
TRACK_TITLE=""
ERROR_LOG_FILE="$DEST_DIR/.error_${TRACK_INDEX}.log"
if [ "$USE_SANITIZE" -eq 1 ]; then
# Download with temporary template and rename later
TEMP_TEMPLATE="$DEST_DIR/temp_${CURRENT_TRACK}_%(title)s.%(ext)s"
if yt-dlp -x --audio-format "$AUDIO_FORMAT" --audio-quality "$AUDIO_QUALITY" \
-o "$TEMP_TEMPLATE" "$URL" 2>"$ERROR_LOG_FILE"; then
# Find downloaded file
DOWNLOADED_FILE=$(find "$DEST_DIR" -name "temp_${CURRENT_TRACK}_*.$AUDIO_FORMAT" -type f | head -n 1)
if [ -n "$DOWNLOADED_FILE" ]; then
# Extract title from filename
FILENAME=$(basename "$DOWNLOADED_FILE")
TITLE=$(echo "$FILENAME" | sed "s/temp_${CURRENT_TRACK}_//;s/\.$AUDIO_FORMAT$//")
# Sanitize title
CLEAN_TITLE=$(sanitize_filename "$TITLE")
# New filename
NEW_FILENAME="${TRACK_INDEX} - ${CLEAN_TITLE}.${AUDIO_FORMAT}"
NEW_FILEPATH="$DEST_DIR/$NEW_FILENAME"
# Rename file
mv "$DOWNLOADED_FILE" "$NEW_FILEPATH"
log_success "Download completed: $NEW_FILENAME"
SUCCESSFUL_DOWNLOADS=$((SUCCESSFUL_DOWNLOADS + 1))
DOWNLOAD_SUCCESS=1
TRACK_TITLE="$CLEAN_TITLE"
# Remove error log if successful
rm -f "$ERROR_LOG_FILE"
else
log_error "Downloaded file not found."
FAILED_DOWNLOADS=$((FAILED_DOWNLOADS + 1))
# Log failure
if [ -f "$ERROR_LOG_FILE" ]; then
ERROR_MSG=$(tail -n 1 "$ERROR_LOG_FILE" | tr -d '\n')
log_warning "Error: $ERROR_MSG"
mark_track_failed "$TRACK_INDEX" "$URL" "$ERROR_MSG" "$FAILED_LOG"
fi
fi
else
FAILED_DOWNLOADS=$((FAILED_DOWNLOADS + 1))
log_error "Failed to download this track."
# Read and display error
if [ -f "$ERROR_LOG_FILE" ]; then
ERROR_MSG=$(grep -i "error\|ERROR" "$ERROR_LOG_FILE" | tail -n 1 | tr -d '\n')
if [ -z "$ERROR_MSG" ]; then
ERROR_MSG=$(tail -n 1 "$ERROR_LOG_FILE" | tr -d '\n')
fi
log_warning "Reason: ${ERROR_MSG:0:200}"
mark_track_failed "$TRACK_INDEX" "$URL" "$ERROR_MSG" "$FAILED_LOG"
else
mark_track_failed "$TRACK_INDEX" "$URL" "Unknown error" "$FAILED_LOG"
fi
fi
# Clean up error log
rm -f "$ERROR_LOG_FILE"
else
# Download without sanitization
OUTPUT_TEMPLATE="$DEST_DIR/${TRACK_INDEX} - %(title)s.%(ext)s"
if yt-dlp -x --audio-format "$AUDIO_FORMAT" --audio-quality "$AUDIO_QUALITY" \
-o "$OUTPUT_TEMPLATE" "$URL" 2>"$ERROR_LOG_FILE"; then
SUCCESSFUL_DOWNLOADS=$((SUCCESSFUL_DOWNLOADS + 1))
log_success "Download completed successfully!"
DOWNLOAD_SUCCESS=1
# Try to extract title from created file
CREATED_FILE=$(find "$DEST_DIR" -name "${TRACK_INDEX} - *.$AUDIO_FORMAT" -type f | head -n 1)
if [ -n "$CREATED_FILE" ]; then
TRACK_TITLE=$(basename "$CREATED_FILE" | sed "s/${TRACK_INDEX} - //;s/\.$AUDIO_FORMAT$//")
fi
# Remove error log if successful
rm -f "$ERROR_LOG_FILE"
else
FAILED_DOWNLOADS=$((FAILED_DOWNLOADS + 1))
log_error "Failed to download this track."
# Read and display error
if [ -f "$ERROR_LOG_FILE" ]; then
ERROR_MSG=$(grep -i "error\|ERROR" "$ERROR_LOG_FILE" | tail -n 1 | tr -d '\n')
if [ -z "$ERROR_MSG" ]; then
ERROR_MSG=$(tail -n 1 "$ERROR_LOG_FILE" | tr -d '\n')
fi
log_warning "Reason: ${ERROR_MSG:0:200}"
mark_track_failed "$TRACK_INDEX" "$URL" "$ERROR_MSG" "$FAILED_LOG"
else
mark_track_failed "$TRACK_INDEX" "$URL" "Unknown error" "$FAILED_LOG"
fi
fi
# Clean up error log
rm -f "$ERROR_LOG_FILE"
fi
# Register in checkpoint if download successful
if [ "$DOWNLOAD_SUCCESS" -eq 1 ] && [ "$USE_RESUME" -eq 1 ]; then
if [ -z "$TRACK_TITLE" ]; then
TRACK_TITLE="Unknown Track"
fi
mark_track_downloaded "$TRACK_INDEX" "$TRACK_TITLE" "$CHECKPOINT_LOG"
log_info "Checkpoint updated."
fi
# Delay between downloads (except for the last one)
if [ $CURRENT_TRACK -lt $TOTAL_TRACKS ]; then
if [ "$USE_RANDOM_DELAY" -eq 1 ]; then
# Generate random number between RANDOM_DELAY_MIN and RANDOM_DELAY_MAX
CURRENT_DELAY=$((RANDOM % (RANDOM_DELAY_MAX - RANDOM_DELAY_MIN + 1) + RANDOM_DELAY_MIN))
log_info "Waiting ${CURRENT_DELAY}s (random) before next track..."
sleep "$CURRENT_DELAY"
elif [ "$DELAY" -gt 0 ]; then
log_info "Waiting ${DELAY}s before next track..."
sleep "$DELAY"
fi
fi
done < "$TEMP_URLS_FILE"
# Final summary
echo ""
echo ""
log_info "═══════════════════════════════════════════════════════════"
log_info " DOWNLOAD SUMMARY"
log_info "═══════════════════════════════════════════════════════════"
log_success "Successful downloads: $SUCCESSFUL_DOWNLOADS"
if [ $SKIPPED_DOWNLOADS -gt 0 ]; then
log_warning "Skipped tracks (already exist): $SKIPPED_DOWNLOADS"
fi
if [ $FAILED_DOWNLOADS -gt 0 ]; then
log_error "Failed downloads: $FAILED_DOWNLOADS"
fi
log_info "Total tracks: $TOTAL_TRACKS"
log_info "Destination directory: $(cd "$DEST_DIR" && pwd)"
if [ "$USE_RESUME" -eq 1 ]; then
log_info "Checkpoint saved at: $CHECKPOINT_LOG"
fi
if [ $FAILED_DOWNLOADS -gt 0 ] && [ -f "$FAILED_LOG" ]; then
log_info "Failed tracks log: $FAILED_LOG"
fi
log_info "═══════════════════════════════════════════════════════════"
echo ""
TOTAL_COMPLETED=$((SUCCESSFUL_DOWNLOADS + SKIPPED_DOWNLOADS))
if [ $TOTAL_COMPLETED -eq $TOTAL_TRACKS ]; then
log_success "All tracks have been processed! 🎵"
elif [ $SUCCESSFUL_DOWNLOADS -gt 0 ]; then
log_success "Download completed with $SUCCESSFUL_DOWNLOADS new track(s)! 🎵"
if [ $FAILED_DOWNLOADS -gt 0 ]; then
log_warning "Run again to retry failed tracks."
fi
else
if [ $SKIPPED_DOWNLOADS -eq $TOTAL_TRACKS ]; then
log_success "All tracks were already downloaded previously! 🎵"
else
log_error "No new downloads completed successfully."
if [ $FAILED_DOWNLOADS -gt 0 ]; then
log_warning "Run again to retry failed tracks."
fi
exit 1
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment