Generado: 2026-03-07 | Auditor: Antigravity (modo forense)
Syncify hoy es una aplicación de escritorio Tauri v2 que puede:
| Capacidad | Estado Real | Evidencia |
|---|---|---|
| Importar bibliotecas de Spotify, Qobuz, Tidal, Deezer, SoundCloud, Apple Music | ✅ Funcional vía API + Python bridges | src-tauri/src/commands/service.rs (67 KB, ~1700 líneas) |
| Almacenar tracks/albums/artists en SQLite | ✅ Funcional | src-tauri/src/db.rs:34 — 19 migraciones ejecutadas automáticamente |
| Autenticación OAuth para Spotify | ✅ Funcional (flujo nativo Rust) | src-tauri/src/commands/auth.rs |
| Autenticación para otros servicios | scripts/auth_bridge.py (13 KB) — depende de Python instalado en el sistema |
|
| Sistema de cola de descargas con worker | ✅ Worker background funcional | src-tauri/src/worker.rs (14 KB), main.rs:53-74 |
| Descargar tracks (Qobuz, Tidal, Amazon) | src-tauri/src/download/qobuz.rs, tidal.rs, amazon.rs |
|
| Enriquecimiento de metadatos (MusicBrainz, Spotify Audio Features, Last.fm genres) | ✅ Worker background automático | main.rs:78-346 — loop infinito con 5 min de espera |
| UI completa con 13 vistas | Ver Archivo 2 | |
| Persistir configuración desde UI | ❌ Parcial — múltiples secciones son cosméticas | Ver Archivo 2 |
Resumen brutal: El backend Rust tiene una profundidad real (186 comandos Tauri registrados, 12 servicios de streaming, 9 módulos de descarga). La UI renderiza datos reales en Library, Queue, y Accounts. Sin embargo, grandes secciones de Settings, Dashboard, y StatusBar muestran datos falsos pintados directamente en el template.
| Dependencia | Versión | Propósito |
|---|---|---|
tauri |
2.x | Framework desktop |
tauri-plugin-shell |
2.x | Ejecutar Python subprocesses |
tauri-plugin-dialog |
2.x | Diálogos nativos |
sqlx |
0.8 | SQLite async con migraciones |
tokio |
1.x | Runtime async |
reqwest |
0.12 | HTTP client (APIs streaming) |
aes-gcm |
0.10 | Cifrado de credenciales |
lofty |
0.18 | Lectura/escritura de tags de audio |
serde / serde_json |
1.x | Serialización |
uuid |
1.x (v4) | IDs únicos |
sha2 |
0.10 | Hashing para derivar clave de cifrado |
dotenvy |
0.15 | Variables de entorno |
futures / futures-util |
0.3 | Utilidades async |
Fuente: src-tauri/Cargo.toml
| Dependencia | Versión | Propósito |
|---|---|---|
vue |
^3.4.0 | Framework UI |
vue-router |
^4.2.0 | Routing |
@tauri-apps/api |
^2.0.0 | IPC con Rust |
tailwindcss |
^4.1.18 | CSS framework |
vite |
^5.4.0 | Build tool |
vitest |
^4.0.16 | Testing |
typescript |
^5.3.0 | Tipado |
Fuente: ui/package.json
| Herramienta | Requerido | Propósito |
|---|---|---|
| Python 3.x | Sí (para auth, lyrics, metadata, fingerprinting, download bridges) | 13+ scripts en scripts/ |
| FFmpeg | Condicional | Conversión de audio (scripts/conversion_bridge.py) |
| fpcalc (Chromaprint) | Condicional | Audio fingerprinting (scripts/fingerprint_bridge.py) |
Syncify/
├── src-tauri/ # BACKEND RUST (Tauri v2)
│ ├── src/
│ │ ├── main.rs # Entry point + enrichment worker INLINE (541 líneas)
│ │ ├── db.rs # SQLite pool init + migraciones (46 líneas)
│ │ ├── crypto.rs # AES-256-GCM credential encryption (123 líneas)
│ │ ├── models.rs # Structs SQLx (16 KB)
│ │ ├── worker.rs # Download worker background (14 KB)
│ │ ├── downloader.rs # Download orchestration (11 KB)
│ │ ├── import_cache.rs # Dedup imports (6 KB)
│ │ ├── commands/ # 16 archivos Tauri commands (~300 KB total)
│ │ │ ├── service.rs # 67 KB — el archivo más grande del proyecto
│ │ │ ├── settings.rs # 26 KB — 7 sprints de settings
│ │ │ ├── migration.rs # 34 KB — migración entre servicios
│ │ │ ├── library.rs # 25 KB — CRUD biblioteca
│ │ │ ├── lyrics.rs # 23 KB — fetch, save, embed lyrics
│ │ │ ├── enrichment.rs # 17 KB — MusicBrainz, Spotify, Last.fm
│ │ │ ├── dashboard.rs # 16 KB — stats, health checks
│ │ │ ├── accounts.rs # 15 KB — CRUD cuentas
│ │ │ ├── metadata.rs # 14 KB — metadata editing
│ │ │ ├── tools.rs # 28 KB — scanner, organizer, converter, fingerprinter
│ │ │ └── queue.rs # 10 KB — cola de descargas
│ │ ├── services/ # 12 archivos — conectores de streaming
│ │ │ ├── spotify.rs # 41 KB — el conector más completo
│ │ │ ├── deezer.rs # 21 KB
│ │ │ ├── qobuz.rs # 18 KB
│ │ │ ├── tidal.rs # 16 KB
│ │ │ ├── soundcloud.rs # 13 KB
│ │ │ ├── apple_music.rs # 13 KB
│ │ │ ├── musicbrainz.rs # 10 KB
│ │ │ ├── rate_limiter.rs # 7 KB
│ │ │ └── lastfm.rs # 5 KB
│ │ └── download/ # 9 archivos — descargadores por servicio
│ │ ├── tidal.rs # 17 KB
│ │ ├── qobuz.rs # 16 KB
│ │ ├── lyrics.rs # 11 KB (lyrics downloader)
│ │ ├── amazon.rs # 8 KB
│ │ └── orchestrator.rs # 6 KB
│ └── tauri.conf.json # Configuración Tauri
│
├── ui/ # FRONTEND VUE 3
│ └── src/
│ ├── App.vue # 16 KB — layout, nav, routing
│ ├── views/ # 13 vistas (~600 KB total)
│ │ ├── MetadataView.vue # 92 KB — la vista más grande
│ │ ├── SettingsView.vue # 90 KB
│ │ ├── LibraryView.vue # 78 KB
│ │ ├── MigrationView.vue # 73 KB
│ │ ├── AccountsView.vue # 61 KB
│ │ ├── LyricsView.vue # 60 KB
│ │ ├── DownloadsView.vue # 47 KB
│ │ ├── PlaylistView.vue # 37 KB
│ │ ├── DashboardView.vue # 27 KB
│ │ ├── QueueView.vue # 11 KB
│ │ ├── ArtistDetailView # 10 KB
│ │ ├── AlbumDetailView # 8 KB
│ │ └── LogsView.vue # 4 KB
│ ├── components/ # 26 componentes
│ ├── api/ # 11 módulos API — solo 4 exportados en index.ts
│ ├── composables/ # 14 composables para estado reactivo
│ └── __tests__/ # 6 archivos de tests
│
├── scripts/ # 26 Python bridge scripts
├── migrations/ # 19 SQL migrations (0001-0019)
├── conductor/ # Documentación del producto y estilos
└── Cargo.toml # Crate raíz "syncify-core" (sin ejecutable real)
Archivo: src-tauri/tauri.conf.json:23
"security": {
"csp": null
}Impacto: Cualquier XSS en el webview puede ejecutar código arbitrario. La CSP nula desactiva toda protección contra inyección de scripts.
Archivo: src-tauri/src/crypto.rs:23-51
La clave AES-256 se deriva de SHA256("syncify-music-library-v1" + USERNAME + HOME_DIR + HOSTNAME). Todos estos valores son públicos o fácilmente adivinables. Un atacante con acceso al sistema puede derivar la misma clave y descifrar todas las credenciales de streaming almacenadas en la base de datos.
No se usa Keychain/Credential Store del sistema operativo.
Archivo: src-tauri/src/commands/service.rs:1704-1705
std::env::var("QOBUZ_APP_ID").unwrap(),
std::env::var("QOBUZ_APP_SECRET").unwrap(),Impacto: Si las variables de entorno no están definidas, la aplicación crashea en panic sin ningún mensaje de error manejado.
Archivos afectados con unwrap() fuera de tests:
commands/service.rs— 5 instancias (líneas 80, 162, 322, 1378, 1551)commands/accounts.rs— 4 instancias (líneas 338, 351, 390, 498)commands/settings.rs— 3 instancias (líneas 799, 807, 814)commands/dashboard.rs— 3 instancias (líneas 424, 443, 448)services/spotify.rs— 1 instancia (línea 827)services/musicbrainz.rs— 2 instancias (líneas 75, 83)download/tidal.rs— 2 instancias (líneas 142, 182)download/progress.rs— 7 instancias (líneas 172-200)download/http_client.rs— 5 instancias (líneas 80-139)download/lyrics.rs— 2 instancias (líneas 77, 89)
Impacto: Cada unwrap() es un potencial panic que mata la aplicación sin posibilidad de recuperación.
Archivo: src-tauri/src/main.rs:78-346 (268 líneas)
Un loop infinito de enriquecimiento (MusicBrainz + Spotify Audio Features + Last.fm Genres) está embebido directamente en el closure de .setup() de Tauri. No es un servicio separado ni un módulo testeable. Si cualquier panic ocurre dentro de este loop, no hay recovery.
Este archivo contiene toda la lógica de importación de 6 servicios de streaming. No hay separación por servicio. Difícil de mantener, testear, o razonar sobre.
Archivo: src-tauri/src/db.rs:43-44
let project_root = std::env::current_dir().unwrap_or_default();
project_root.join("data").join("syncify.db")La ruta de la DB depende del directorio de trabajo actual. Si la app se ejecuta desde un shortcut con un CWD diferente, creará/usará una base de datos diferente. Debería usar dirs::data_dir() para producción.
Archivo: src-tauri/src/commands/mod.rs:12-25
Todos los archivos de comandos se importan via include!() en vez de mod. Esto significa que comparten el mismo scope de imports, no son módulos independientes, y el IDE no puede proveer aislamiento de nombres. Es un antipatrón en Rust.
Archivo: Cargo.toml (raíz) y src/main.rs (3 líneas: fn main() { println!("placeholder") })
El crate raíz fue creado durante bootstrap pero nunca se integró con src-tauri. No comparte código con el backend Tauri. Es peso muerto.
La app depende de python en PATH para autenticación de 5 servicios, lyrics, fingerprinting, conversión, y organización. No hay verificación al inicio de que Python esté instalado ni que los paquetes (spotipy, etc.) estén disponibles. Si Python no existe, los comandos fallan silenciosamente retornando errores genéricos.