mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-05 04:53:01 +00:00
Reorganize repository structure for better organization
- Move backend code to swingmusic/ folder - Move client applications to root level (swingmusic-android, swingmusic-desktop, swingmusic-webclient) - Remove intermediate backend/ and clients/ folders - Update README with new folder structure and setup instructions - Clean and organized repository layout
This commit is contained in:
@@ -1,283 +0,0 @@
|
||||
"""
|
||||
Library integration service for Spotify downloads
|
||||
Handles automatic addition of downloaded tracks to SwingMusic library
|
||||
"""
|
||||
|
||||
import os
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
from swingmusic.db.libdata import TrackTable
|
||||
from swingmusic.db.engine import DbEngine
|
||||
from swingmusic.config import UserConfig
|
||||
from swingmusic.utils import create_valid_filename
|
||||
from swingmusic import logger
|
||||
|
||||
|
||||
class LibraryIntegrator:
|
||||
"""Handles integration of downloaded tracks into SwingMusic library"""
|
||||
|
||||
def __init__(self):
|
||||
self.config = UserConfig()
|
||||
self.music_dirs = self.config.rootDirs
|
||||
|
||||
def add_downloaded_track(self, download_item: Dict[str, Any]) -> bool:
|
||||
"""
|
||||
Add a downloaded track to the SwingMusic library
|
||||
|
||||
Args:
|
||||
download_item: Dictionary containing download information
|
||||
|
||||
Returns:
|
||||
bool: True if successfully added, False otherwise
|
||||
"""
|
||||
try:
|
||||
if not download_item.get('file_path') or not os.path.exists(download_item['file_path']):
|
||||
logger.error(f"Downloaded file not found: {download_item.get('file_path')}")
|
||||
return False
|
||||
|
||||
# Check if track already exists in library
|
||||
if self._track_exists(download_item['file_path']):
|
||||
logger.info(f"Track already exists in library: {download_item['file_path']}")
|
||||
return True
|
||||
|
||||
# Create track record
|
||||
track_data = self._create_track_data(download_item)
|
||||
|
||||
# Insert into database
|
||||
self._insert_track(track_data)
|
||||
|
||||
logger.info(f"Added track to library: {track_data['title']} by {track_data['artists']}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding track to library: {e}")
|
||||
return False
|
||||
|
||||
def add_downloaded_album(self, download_item: Dict[str, Any], track_files: list[str]) -> int:
|
||||
"""
|
||||
Add all tracks from a downloaded album to the library
|
||||
|
||||
Args:
|
||||
download_item: Album download information
|
||||
track_files: List of downloaded track file paths
|
||||
|
||||
Returns:
|
||||
int: Number of tracks successfully added
|
||||
"""
|
||||
added_count = 0
|
||||
|
||||
try:
|
||||
for track_file in track_files:
|
||||
if not os.path.exists(track_file):
|
||||
logger.warning(f"Track file not found: {track_file}")
|
||||
continue
|
||||
|
||||
# Check if track already exists
|
||||
if self._track_exists(track_file):
|
||||
logger.info(f"Track already exists in library: {track_file}")
|
||||
added_count += 1
|
||||
continue
|
||||
|
||||
# Create track data for album track
|
||||
track_data = self._create_album_track_data(download_item, track_file)
|
||||
|
||||
# Insert into database
|
||||
self._insert_track(track_data)
|
||||
added_count += 1
|
||||
|
||||
logger.info(f"Added {added_count} tracks from album to library")
|
||||
return added_count
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding album to library: {e}")
|
||||
return added_count
|
||||
|
||||
def _track_exists(self, filepath: str) -> bool:
|
||||
"""Check if track already exists in library"""
|
||||
try:
|
||||
with DbEngine.manager() as conn:
|
||||
result = conn.execute(
|
||||
TrackTable.select().where(TrackTable.filepath == filepath)
|
||||
)
|
||||
return result.scalar() is not None
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking if track exists: {e}")
|
||||
return False
|
||||
|
||||
def _create_track_data(self, download_item: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Create track data dictionary from download item"""
|
||||
filepath = download_item['file_path']
|
||||
file_stat = os.stat(filepath)
|
||||
|
||||
# Extract metadata from download item
|
||||
title = download_item.get('title', 'Unknown Title')
|
||||
artist = download_item.get('artist', 'Unknown Artist')
|
||||
album = download_item.get('album', 'Unknown Album')
|
||||
|
||||
# Generate hashes
|
||||
trackhash = self._generate_track_hash(filepath, title, artist)
|
||||
albumhash = self._generate_album_hash(album, artist)
|
||||
|
||||
# Extract file information
|
||||
folder = os.path.basename(os.path.dirname(filepath))
|
||||
|
||||
return {
|
||||
'title': title,
|
||||
'artists': artist,
|
||||
'albumartists': artist,
|
||||
'album': album,
|
||||
'albumhash': albumhash,
|
||||
'trackhash': trackhash,
|
||||
'filepath': filepath,
|
||||
'folder': folder,
|
||||
'duration': download_item.get('duration_ms', 0) // 1000, # Convert to seconds
|
||||
'bitrate': self._get_bitrate_from_quality(download_item.get('quality', 'flac')),
|
||||
'date': self._parse_date(download_item.get('release_date')),
|
||||
'track': download_item.get('track_number', 1),
|
||||
'disc': 1,
|
||||
'last_mod': int(file_stat.st_mtime),
|
||||
'extra': {
|
||||
'spotify_id': download_item.get('spotify_id'),
|
||||
'source': download_item.get('source', 'spotify'),
|
||||
'download_date': datetime.now().isoformat()
|
||||
}
|
||||
}
|
||||
|
||||
def _create_album_track_data(self, download_item: Dict[str, Any], track_file: str) -> Dict[str, Any]:
|
||||
"""Create track data for album track"""
|
||||
file_stat = os.stat(track_file)
|
||||
|
||||
# Extract filename for title (if metadata not available)
|
||||
filename = os.path.splitext(os.path.basename(track_file))[0]
|
||||
|
||||
# Use download item metadata as base
|
||||
title = download_item.get('title', filename)
|
||||
artist = download_item.get('artist', 'Unknown Artist')
|
||||
album = download_item.get('album', 'Unknown Album')
|
||||
|
||||
# Generate hashes
|
||||
trackhash = self._generate_track_hash(track_file, title, artist)
|
||||
albumhash = self._generate_album_hash(album, artist)
|
||||
|
||||
# Extract file information
|
||||
folder = os.path.basename(os.path.dirname(track_file))
|
||||
|
||||
return {
|
||||
'title': title,
|
||||
'artists': artist,
|
||||
'albumartists': artist,
|
||||
'album': album,
|
||||
'albumhash': albumhash,
|
||||
'trackhash': trackhash,
|
||||
'filepath': track_file,
|
||||
'folder': folder,
|
||||
'duration': download_item.get('duration_ms', 0) // 1000,
|
||||
'bitrate': self._get_bitrate_from_quality(download_item.get('quality', 'flac')),
|
||||
'date': self._parse_date(download_item.get('release_date')),
|
||||
'track': download_item.get('track_number', 1),
|
||||
'disc': 1,
|
||||
'last_mod': int(file_stat.st_mtime),
|
||||
'extra': {
|
||||
'spotify_id': download_item.get('spotify_id'),
|
||||
'source': download_item.get('source', 'spotify'),
|
||||
'download_date': datetime.now().isoformat(),
|
||||
'album_download': True
|
||||
}
|
||||
}
|
||||
|
||||
def _insert_track(self, track_data: Dict[str, Any]):
|
||||
"""Insert track into database"""
|
||||
try:
|
||||
with DbEngine.manager(commit=True) as conn:
|
||||
conn.execute(TrackTable.insert().values(track_data))
|
||||
except Exception as e:
|
||||
logger.error(f"Error inserting track: {e}")
|
||||
raise
|
||||
|
||||
def _generate_track_hash(self, filepath: str, title: str, artist: str) -> str:
|
||||
"""Generate unique track hash"""
|
||||
content = f"{filepath}:{title}:{artist}"
|
||||
return hashlib.md5(content.encode()).hexdigest()
|
||||
|
||||
def _generate_album_hash(self, album: str, artist: str) -> str:
|
||||
"""Generate album hash"""
|
||||
content = f"{album}:{artist}"
|
||||
return hashlib.md5(content.encode()).hexdigest()
|
||||
|
||||
def _get_bitrate_from_quality(self, quality: str) -> int:
|
||||
"""Get approximate bitrate based on quality"""
|
||||
quality_bitrates = {
|
||||
'flac': 1411, # Approximate FLAC bitrate
|
||||
'mp3_320': 320,
|
||||
'mp3_128': 128
|
||||
}
|
||||
return quality_bitrates.get(quality, 320)
|
||||
|
||||
def _parse_date(self, date_str: Optional[str]) -> Optional[int]:
|
||||
"""Parse date string to timestamp"""
|
||||
if not date_str:
|
||||
return None
|
||||
|
||||
try:
|
||||
# Try various date formats
|
||||
formats = ['%Y-%m-%d', '%Y', '%Y-%m']
|
||||
for fmt in formats:
|
||||
try:
|
||||
dt = datetime.strptime(date_str, fmt)
|
||||
return int(dt.timestamp())
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def remove_downloaded_track(self, filepath: str) -> bool:
|
||||
"""
|
||||
Remove a downloaded track from the library
|
||||
|
||||
Args:
|
||||
filepath: Path to the track file
|
||||
|
||||
Returns:
|
||||
bool: True if successfully removed
|
||||
"""
|
||||
try:
|
||||
with DbEngine.manager(commit=True) as conn:
|
||||
result = conn.execute(
|
||||
TrackTable.delete().where(TrackTable.filepath == filepath)
|
||||
)
|
||||
return result.rowcount > 0
|
||||
except Exception as e:
|
||||
logger.error(f"Error removing track from library: {e}")
|
||||
return False
|
||||
|
||||
def update_track_metadata(self, filepath: str, metadata: Dict[str, Any]) -> bool:
|
||||
"""
|
||||
Update metadata for a track in the library
|
||||
|
||||
Args:
|
||||
filepath: Path to the track file
|
||||
metadata: New metadata to apply
|
||||
|
||||
Returns:
|
||||
bool: True if successfully updated
|
||||
"""
|
||||
try:
|
||||
with DbEngine.manager(commit=True) as conn:
|
||||
result = conn.execute(
|
||||
TrackTable.update()
|
||||
.where(TrackTable.filepath == filepath)
|
||||
.values(metadata)
|
||||
)
|
||||
return result.rowcount > 0
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating track metadata: {e}")
|
||||
return False
|
||||
|
||||
|
||||
# Global instance
|
||||
library_integrator = LibraryIntegrator()
|
||||
Reference in New Issue
Block a user