Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
<<<<<<< Updated upstream
SPOTIFY_CLIENT_ID=your_client_id_here
SPOTIFY_CLIENT_SECRET=your_client_secret_here
SPOTIFY_REDIRECT_URI=http://localhost:8888/callback
=======
# Spotify API Credentials
# IMPORTANTE: NO uses comillas ni espacios alrededor de los valores.
# Ejemplo: SPOTIFY_CLIENT_ID=abc123def456...

SPOTIFY_CLIENT_ID=tu_client_id_de_32_caracteres
SPOTIFY_CLIENT_SECRET=tu_client_secret_de_32_caracteres
SPOTIFY_REDIRECT_URI=http://127.0.0.1:8888/callback
>>>>>>> Stashed changes
84 changes: 84 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,47 @@
# Changelog

<<<<<<< Updated upstream
=======
## [1.2.0] - 2026-04-27

### Added
- **🛠️ New Tools Category**: Added a dedicated "Utilities" section in the GUI to better organize the toolkit.
- **🔗 Playlist Merger**: New tool to combine multiple playlists into a new one, with duplicate detection.
- **🎭 Mood Mixer**: Filter any playlist by audio features (Energy, Chill, Danceable, Happy) using Spotify's AI analysis.
- **💾 Library Backup**: One-click full backup of all your playlists and Liked Songs to JSON files.
- **🐛 CLI/GUI Fix**: Resolved a critical bug where sub-scripts would fail to parse arguments when launched from the GUI.

## [1.1.1] - 2026-04-27

### Added
- **📤 Enhanced Metadata Export**: Added CLI arguments support to the export tool. You can now specify playlist IDs and formats via command line.
- **📄 Robust Data Flattening**: Improved CSV export with better flattening of nested Spotify data (Artists, Albums, ISRC, etc.) for better Excel compatibility.
- **🖥️ CLI Menu Expansion**: Integrated Metadata Export and Trend Reports into the CLI menu (`cli_menu.py`).

## [1.1.0] - 2026-04-26

### Added
- **🎵 Full "Liked Songs" Support**: Unified track fetching logic across the entire toolkit. Now you can use your favorite songs in all tools, including Smart Shuffle and Artist Extractor.
- **🛠️ Build System Overhaul**: Updated GitHub Actions and PyInstaller configuration to ensure all dependencies (like `tqdm`) are correctly bundled.
- **🛡️ Credential Robustness**: Improved `.env` loading to handle accidental spaces and quotes.

### Fixed
- **🧹 Bug Fixes**: Resolved merge conflicts in `reorder_tracks.py` and improved progress reporting accuracy.

---

## [1.0.9] - 2026-04-26

### Added
- **🚀 CLI Progress Bars**: Integrated `tqdm` for real-time visual feedback in the terminal for all long-running operations (fetching tracks, processing artists, shuffling, etc.).

### Fixed
- **🧹 Code Cleanup**: Resolved merge conflicts and cleaned up logic in `reorder_tracks.py`.
- **🛠️ Robustness**: Improved progress tracking accuracy across all modules.

---

>>>>>>> Stashed changes
## [1.0.8] - 2026-04-16

### Added
Expand Down Expand Up @@ -61,6 +103,48 @@

# Historial de Cambios (Changelog)

<<<<<<< Updated upstream
=======
## [1.2.0] - 2026-04-27

### Añadido
- **🛠️ Nueva Categoría de Herramientas**: Sección dedicada de "Herramientas" en la interfaz para una mejor organización.
- **🔗 Fusionador de Playlists**: Nueva herramienta para combinar varias listas en una nueva, con detección de duplicados.
- **🎭 Mood Mixer**: Filtra cualquier playlist por características de audio (Energética, Relajada, Bailable, Feliz).
- **💾 Respaldo de Biblioteca**: Copia de seguridad completa de todas tus playlists y canciones favoritas a archivos JSON en un solo clic.
- **🐛 Corrección CLI/GUI**: Solucionado un error crítico donde los sub-scripts fallaban al procesar argumentos al lanzarse desde la interfaz.

## [1.1.1] - 2026-04-27

### Añadido
- **📤 Exportación de Metadatos Mejorada**: Soporte para argumentos de línea de comandos en la herramienta de exportación. Ahora puedes especificar IDs de playlist y formatos vía CLI.
- **📄 Aplanamiento de Datos Robusto**: Mejora en la exportación CSV con un mejor aplanamiento de los datos anidados de Spotify (Artistas, Álbumes, ISRC, etc.) para una mejor compatibilidad con Excel.
- **🖥️ Expansión del Menú CLI**: Integración de la Exportación de Metadatos e Informe de Tendencias en el menú de consola (`cli_menu.py`).

## [1.1.0] - 2026-04-26

### Añadido
- **🎵 Soporte Completo de "Liked Songs"**: Unificada la lógica de obtención de canciones en todo el toolkit. Ahora puedes usar tus canciones favoritas en todas las herramientas, incluyendo Smart Shuffle y Artist Extractor.
- **🛠️ Mejora del Sistema de Build**: Actualizada la GitHub Action y la configuración de PyInstaller para asegurar que todas las librerías (como `tqdm`) se incluyan correctamente.
- **🛡️ Robustez de Credenciales**: Mejora en la carga del archivo `.env` para gestionar espacios y comillas accidentales.

### Corregido
- **🧹 Corrección de Errores**: Resolución de conflictos en `reorder_tracks.py` y mejora en la precisión de las barras de progreso.

---

## [1.0.9] - 2026-04-26

### Añadido
- **🚀 Barras de Progreso en CLI**: Integración de `tqdm` para ofrecer retroalimentación visual en tiempo real en la terminal durante operaciones largas (descarga de canciones, análisis de artistas, mezclas, etc.).

### Corregido
- **🧹 Limpieza de Código**: Resolución de conflictos de fusión y limpieza de lógica en `reorder_tracks.py`.
- **🛠️ Robustez**: Mejora en la precisión del seguimiento de progreso en todos los módulos.

---

>>>>>>> Stashed changes
## [1.0.8] - 2026-04-16

### Añadido
Expand Down
13 changes: 11 additions & 2 deletions SpotifyToolkit.spec
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# -*- mode: python ; coding: utf-8 -*-


import customtkinter
import os

ctk_path = os.path.dirname(customtkinter.__file__)

a = Analysis(
['main.py'],
pathex=[],
Expand All @@ -16,13 +21,17 @@ a = Analysis(
('top_tracks_generator', 'top_tracks_generator'),
('mood_mixer', 'mood_mixer'),
('metadata_export', 'metadata_export'),
('utils', 'utils')
('utils', 'utils'),
(ctk_path, 'customtkinter'),
('playlist_merger', 'playlist_merger'),
('mood_mixer', 'mood_mixer'),
('library_backup', 'library_backup')
],
hiddenimports=['spotipy', 'customtkinter', 'difflib'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
excludes=['torch', 'numpy', 'scipy', 'matplotlib', 'pandas', 'tensorflow', 'onnxruntime', 'torchvision', 'lxml', 'fsspec', 'imageio', 'ffmpeg', 'cv2', 'IPython', 'PIL.ImageQt'],
noarchive=False,
optimize=0,
)
Expand Down
6 changes: 5 additions & 1 deletion cli_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ def main_menu():
"6": ("Smart Shuffle", "smart_shuffle/smart_shuffle.py"),
"7": ("Playlist Duration (Time)", "playlist_time/playlist_time.py"),
"8": ("Reorder Tracks", "reorder_tracks/reorder_tracks.py"),
"9": ("Mood Mixer", "mood_mixer/mood_mixer.py")
"9": ("Trend Reports", "trend_reports/trend_reports.py"),
"10": ("Metadata Export", "metadata_export/metadata_export.py"),
"11": ("Playlist Merger", "playlist_merger/playlist_merger.py"),
"12": ("Mood Mixer", "mood_mixer/mood_mixer.py"),
"13": ("Library Backup", "library_backup/library_backup.py")
}

while True:
Expand Down
94 changes: 94 additions & 0 deletions library_backup/library_backup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import os
import sys
import spotipy
import json
from datetime import datetime
from spotipy.oauth2 import SpotifyOAuth
from tqdm import tqdm

# --- CONFIGURACIÓN Y AUTENTICACIÓN ---
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
sys.path.append(project_root)

try:
from utils.auth import get_spotify_client
from utils.helpers import get_user_playlists, get_all_tracks, get_export_dir
sp = get_spotify_client()
except ImportError:
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(
client_id=os.getenv("SPOTIFY_CLIENT_ID"),
client_secret=os.getenv("SPOTIFY_CLIENT_SECRET"),
redirect_uri=os.getenv("SPOTIFY_REDIRECT_URI"),
scope='user-library-read playlist-read-private'
))

def main():
print("\n=== LIBRARY BACKUP (Copia de Seguridad Total) ===")
print("Esta herramienta exportará todas tus playlists y canciones favoritas.")

confirm = input("\n¿Deseas iniciar el respaldo completo? (s/n): ").strip().lower()
if confirm != 's': return

date_str = datetime.now().strftime("%Y-%m-%d_%H-%M")
export_dir = get_export_dir()
backup_dir = os.path.join(export_dir, f"Spotify_Backup_{date_str}")
os.makedirs(backup_dir, exist_ok=True)

print(f"\n📂 Los archivos se guardarán en la carpeta: {backup_dir}")

# 1. Liked Songs
print("\n📦 Respaldando 'Canciones Favoritas'...")
liked_tracks = get_all_tracks(sp, "liked_songs")

liked_data = []
for item in liked_tracks:
track = item.get('track')
if track:
liked_data.append({
"name": track.get('name', 'Unknown'),
"artists": ", ".join([a['name'] for a in track.get('artists', [])]),
"album": track.get('album', {}).get('name', 'Unknown'),
"uri": track.get('uri', '')
})

with open(os.path.join(backup_dir, "Liked_Songs.json"), 'w', encoding='utf-8') as f:
json.dump(liked_data, f, indent=4, ensure_ascii=False)

# 2. All Playlists
playlists = get_user_playlists(sp)
print(f"\n📦 Se han encontrado {len(playlists)} playlists para respaldar.")

total_playlists = len(playlists)
for idx, pl in enumerate(playlists):
name = pl.get('name', f"Playlist_{idx}").replace(" ", "_").replace("/", "-").replace("\\", "-")
print(f"\n[{idx+1}/{total_playlists}] Procesando: {name}")

tracks = get_all_tracks(sp, "playlist", pl.get('id'))

pl_data = []
for item in tracks:
track = item.get('track')
if track:
pl_data.append({
"name": track.get('name', 'Unknown'),
"artists": ", ".join([a['name'] for a in track.get('artists', [])]),
"album": track.get('album', {}).get('name', 'Unknown'),
"uri": track.get('uri', '')
})

filename = f"{name}.json"
try:
with open(os.path.join(backup_dir, filename), 'w', encoding='utf-8') as f:
json.dump(pl_data, f, indent=4, ensure_ascii=False)
except Exception as e:
print(f"⚠️ Error al guardar {filename}: {e}")

# Progreso para la GUI
percent = int(((idx + 1) / total_playlists) * 100)
print(f"PROG:{min(percent, 100)}")
sys.stdout.flush()

print(f"\n✅ ¡Respaldo completado! Revisa la carpeta '{backup_dir}'.")

if __name__ == "__main__":
main()
Loading
Loading