remove metadata updater tool for jellyfin since most of the support is lost.
This commit is contained in:
parent
fe84f1dbb8
commit
ceeb4c22dc
2 changed files with 295 additions and 57 deletions
|
|
@ -37,6 +37,13 @@ class JellyfinArtist:
|
||||||
self.title = jellyfin_data.get('Name', 'Unknown Artist')
|
self.title = jellyfin_data.get('Name', 'Unknown Artist')
|
||||||
self.addedAt = self._parse_date(jellyfin_data.get('DateCreated'))
|
self.addedAt = self._parse_date(jellyfin_data.get('DateCreated'))
|
||||||
|
|
||||||
|
# Create genres property from Jellyfin data (empty list for now since data structure needs investigation)
|
||||||
|
self.genres = []
|
||||||
|
# TODO: Map Jellyfin genre data to match Plex format
|
||||||
|
|
||||||
|
# Create summary property from Jellyfin data (used for timestamp storage)
|
||||||
|
self.summary = jellyfin_data.get('Overview', '') or ''
|
||||||
|
|
||||||
def _parse_date(self, date_str: Optional[str]) -> Optional[datetime]:
|
def _parse_date(self, date_str: Optional[str]) -> Optional[datetime]:
|
||||||
"""Parse Jellyfin date string to datetime"""
|
"""Parse Jellyfin date string to datetime"""
|
||||||
if not date_str:
|
if not date_str:
|
||||||
|
|
@ -128,6 +135,9 @@ class JellyfinClient:
|
||||||
self._album_cache = {}
|
self._album_cache = {}
|
||||||
self._track_cache = {}
|
self._track_cache = {}
|
||||||
self._artist_cache = {}
|
self._artist_cache = {}
|
||||||
|
|
||||||
|
# Metadata-only mode flag for performance optimization
|
||||||
|
self._metadata_only_mode = False
|
||||||
self._all_albums_cache = None
|
self._all_albums_cache = None
|
||||||
self._all_tracks_cache = None
|
self._all_tracks_cache = None
|
||||||
self._cache_populated = False
|
self._cache_populated = False
|
||||||
|
|
@ -245,6 +255,12 @@ class JellyfinClient:
|
||||||
"""Aggressively pre-populate ALL caches to eliminate individual API calls"""
|
"""Aggressively pre-populate ALL caches to eliminate individual API calls"""
|
||||||
if self._cache_populated:
|
if self._cache_populated:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Check if we're in metadata-only mode and skip expensive operations
|
||||||
|
if self._metadata_only_mode:
|
||||||
|
logger.info("🎯 Skipping cache population for metadata-only operation")
|
||||||
|
self._cache_populated = True
|
||||||
|
return
|
||||||
|
|
||||||
logger.info("🚀 Starting aggressive Jellyfin cache population to eliminate slow individual API calls...")
|
logger.info("🚀 Starting aggressive Jellyfin cache population to eliminate slow individual API calls...")
|
||||||
if self._progress_callback:
|
if self._progress_callback:
|
||||||
|
|
@ -1155,4 +1171,183 @@ class JellyfinClient:
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Error checking if Jellyfin library is scanning: {e}")
|
logger.debug(f"Error checking if Jellyfin library is scanning: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Metadata update methods for compatibility with metadata updater
|
||||||
|
def update_artist_genres(self, artist, genres: List[str]):
|
||||||
|
"""Update artist genres - currently not implemented for Jellyfin"""
|
||||||
|
try:
|
||||||
|
# TODO: Implement Jellyfin genre update API
|
||||||
|
# For now, just log and return success to continue processing
|
||||||
|
logger.debug(f"Genre update not yet implemented for Jellyfin artist: {artist.title}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error updating genres for {artist.title}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_artist_poster(self, artist, image_data: bytes):
|
||||||
|
"""Update artist poster image using Jellyfin API"""
|
||||||
|
try:
|
||||||
|
artist_id = artist.ratingKey
|
||||||
|
if not artist_id:
|
||||||
|
return False
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
url = f"{self.base_url}/Items/{artist_id}/Images/Primary"
|
||||||
|
headers = {
|
||||||
|
'X-Emby-Token': self.api_key
|
||||||
|
}
|
||||||
|
|
||||||
|
# Try multiple approaches to find what works with Jellyfin
|
||||||
|
|
||||||
|
# Method 1: Try with different field names that Jellyfin might expect
|
||||||
|
method1_files = {'data': ('poster.jpg', image_data, 'image/jpeg')}
|
||||||
|
try:
|
||||||
|
response = requests.post(url, files=method1_files, headers=headers, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
logger.info(f"Updated poster for {artist.title} (method 1)")
|
||||||
|
return True
|
||||||
|
except Exception as e1:
|
||||||
|
logger.debug(f"Method 1 failed for {artist.title}: {e1}")
|
||||||
|
|
||||||
|
# Method 2: Try with raw data and proper content-type
|
||||||
|
try:
|
||||||
|
headers_raw = {
|
||||||
|
'X-Emby-Token': self.api_key,
|
||||||
|
'Content-Type': 'image/jpeg'
|
||||||
|
}
|
||||||
|
response = requests.post(url, data=image_data, headers=headers_raw, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
logger.info(f"Updated poster for {artist.title} (method 2)")
|
||||||
|
return True
|
||||||
|
except Exception as e2:
|
||||||
|
logger.debug(f"Method 2 failed for {artist.title}: {e2}")
|
||||||
|
|
||||||
|
# Method 3: Try with different endpoint structure
|
||||||
|
try:
|
||||||
|
alt_url = f"{self.base_url}/Items/{artist_id}/Images/Primary/0"
|
||||||
|
response = requests.post(alt_url, data=image_data, headers=headers_raw, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
logger.info(f"Updated poster for {artist.title} (method 3)")
|
||||||
|
return True
|
||||||
|
except Exception as e3:
|
||||||
|
logger.debug(f"Method 3 failed for {artist.title}: {e3}")
|
||||||
|
|
||||||
|
# All methods failed
|
||||||
|
logger.error(f"All image upload methods failed for {artist.title}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error updating poster for {artist.title}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_album_poster(self, album, image_data: bytes):
|
||||||
|
"""Update album poster image using Jellyfin API"""
|
||||||
|
try:
|
||||||
|
album_id = album.ratingKey
|
||||||
|
if not album_id:
|
||||||
|
return False
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
url = f"{self.base_url}/Items/{album_id}/Images/Primary"
|
||||||
|
headers = {
|
||||||
|
'X-Emby-Token': self.api_key
|
||||||
|
}
|
||||||
|
|
||||||
|
# Try multiple approaches to find what works with Jellyfin
|
||||||
|
|
||||||
|
# Method 1: Try with different field names that Jellyfin might expect
|
||||||
|
method1_files = {'data': ('poster.jpg', image_data, 'image/jpeg')}
|
||||||
|
try:
|
||||||
|
response = requests.post(url, files=method1_files, headers=headers, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
logger.info(f"Updated poster for album '{album.title}' (method 1)")
|
||||||
|
return True
|
||||||
|
except Exception as e1:
|
||||||
|
logger.debug(f"Method 1 failed for album '{album.title}': {e1}")
|
||||||
|
|
||||||
|
# Method 2: Try with raw data and proper content-type
|
||||||
|
try:
|
||||||
|
headers_raw = {
|
||||||
|
'X-Emby-Token': self.api_key,
|
||||||
|
'Content-Type': 'image/jpeg'
|
||||||
|
}
|
||||||
|
response = requests.post(url, data=image_data, headers=headers_raw, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
logger.info(f"Updated poster for album '{album.title}' (method 2)")
|
||||||
|
return True
|
||||||
|
except Exception as e2:
|
||||||
|
logger.debug(f"Method 2 failed for album '{album.title}': {e2}")
|
||||||
|
|
||||||
|
# Method 3: Try with different endpoint structure
|
||||||
|
try:
|
||||||
|
alt_url = f"{self.base_url}/Items/{album_id}/Images/Primary/0"
|
||||||
|
response = requests.post(alt_url, data=image_data, headers=headers_raw, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
logger.info(f"Updated poster for album '{album.title}' (method 3)")
|
||||||
|
return True
|
||||||
|
except Exception as e3:
|
||||||
|
logger.debug(f"Method 3 failed for album '{album.title}': {e3}")
|
||||||
|
|
||||||
|
# All methods failed
|
||||||
|
logger.error(f"All image upload methods failed for album '{album.title}'")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error updating poster for album '{album.title}': {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_artist_biography(self, artist) -> bool:
|
||||||
|
"""Update artist overview/biography - currently not implemented for Jellyfin"""
|
||||||
|
try:
|
||||||
|
# TODO: Implement Jellyfin biography update API
|
||||||
|
# For now, just log and return success to continue processing
|
||||||
|
logger.debug(f"Biography update not yet implemented for Jellyfin artist: {artist.title}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error updating biography for {artist.title}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def needs_update_by_age(self, artist, refresh_interval_days: int) -> bool:
|
||||||
|
"""Check if artist needs updating based on age threshold - simplified for Jellyfin"""
|
||||||
|
try:
|
||||||
|
# For now, just return True for all artists since we don't have timestamp tracking yet
|
||||||
|
# TODO: Implement timestamp tracking in Jellyfin artist metadata
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Error checking update age for {artist.title}: {e}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_artist_ignored(self, artist) -> bool:
|
||||||
|
"""Check if artist is manually marked to be ignored - simplified for Jellyfin"""
|
||||||
|
try:
|
||||||
|
# For now, no artists are ignored
|
||||||
|
# TODO: Implement ignore flag tracking in Jellyfin artist metadata
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Error checking ignore status for {artist.title}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def parse_update_timestamp(self, artist) -> Optional[datetime]:
|
||||||
|
"""Parse the last update timestamp from artist summary - not implemented for Jellyfin"""
|
||||||
|
try:
|
||||||
|
# TODO: Implement timestamp parsing from Jellyfin artist overview
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Error parsing timestamp for {artist.title}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_metadata_only_mode(self, enabled: bool = True):
|
||||||
|
"""Enable metadata-only mode to skip expensive track caching"""
|
||||||
|
try:
|
||||||
|
self._metadata_only_mode = enabled
|
||||||
|
if enabled:
|
||||||
|
logger.info("🎯 Metadata-only mode enabled - will skip expensive track caching")
|
||||||
|
else:
|
||||||
|
logger.info("🎯 Metadata-only mode disabled")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error setting metadata-only mode: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
@ -1404,18 +1404,19 @@ class SimpleWishlistDownloadWorker(QRunnable):
|
||||||
|
|
||||||
|
|
||||||
class MetadataUpdateWorker(QThread):
|
class MetadataUpdateWorker(QThread):
|
||||||
"""Worker thread for updating Plex artist metadata using Spotify data"""
|
"""Worker thread for updating artist metadata using Spotify data (supports both Plex and Jellyfin)"""
|
||||||
progress_updated = pyqtSignal(str, int, int, float) # current_artist, processed, total, percentage
|
progress_updated = pyqtSignal(str, int, int, float) # current_artist, processed, total, percentage
|
||||||
artist_updated = pyqtSignal(str, bool, str) # artist_name, success, details
|
artist_updated = pyqtSignal(str, bool, str) # artist_name, success, details
|
||||||
finished = pyqtSignal(int, int, int) # total_processed, successful, failed
|
finished = pyqtSignal(int, int, int) # total_processed, successful, failed
|
||||||
error = pyqtSignal(str) # error_message
|
error = pyqtSignal(str) # error_message
|
||||||
artists_loaded = pyqtSignal(int, int) # total_artists, artists_to_process
|
artists_loaded = pyqtSignal(int, int) # total_artists, artists_to_process
|
||||||
|
|
||||||
def __init__(self, artists, plex_client, spotify_client, refresh_interval_days=30):
|
def __init__(self, artists, media_client, spotify_client, server_type, refresh_interval_days=30):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.artists = artists
|
self.artists = artists
|
||||||
self.plex_client = plex_client
|
self.media_client = media_client # Can be plex_client or jellyfin_client
|
||||||
self.spotify_client = spotify_client
|
self.spotify_client = spotify_client
|
||||||
|
self.server_type = server_type # "plex" or "jellyfin"
|
||||||
self.matching_engine = MusicMatchingEngine()
|
self.matching_engine = MusicMatchingEngine()
|
||||||
self.refresh_interval_days = refresh_interval_days
|
self.refresh_interval_days = refresh_interval_days
|
||||||
self.should_stop = False
|
self.should_stop = False
|
||||||
|
|
@ -1428,14 +1429,23 @@ class MetadataUpdateWorker(QThread):
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.should_stop = True
|
self.should_stop = True
|
||||||
|
|
||||||
|
def get_artist_name(self, artist):
|
||||||
|
"""Get artist name consistently across Plex and Jellyfin"""
|
||||||
|
# Both Plex and Jellyfin wrapper objects have .title attribute
|
||||||
|
return getattr(artist, 'title', 'Unknown Artist')
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Process all artists one by one"""
|
"""Process all artists one by one"""
|
||||||
try:
|
try:
|
||||||
# Load artists in background if not provided
|
# Load artists in background if not provided
|
||||||
if self.artists is None:
|
if self.artists is None:
|
||||||
all_artists = self.plex_client.get_all_artists()
|
# Enable lightweight mode for Jellyfin to skip track caching
|
||||||
|
if self.server_type == "jellyfin":
|
||||||
|
self.media_client.set_metadata_only_mode(True)
|
||||||
|
|
||||||
|
all_artists = self.media_client.get_all_artists()
|
||||||
if not all_artists:
|
if not all_artists:
|
||||||
self.error.emit("No artists found in Plex library")
|
self.error.emit(f"No artists found in {self.server_type.title()} library")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Filter artists that need processing
|
# Filter artists that need processing
|
||||||
|
|
@ -1460,7 +1470,7 @@ class MetadataUpdateWorker(QThread):
|
||||||
artist_name = getattr(artist, 'title', 'Unknown Artist')
|
artist_name = getattr(artist, 'title', 'Unknown Artist')
|
||||||
|
|
||||||
# Double-check ignore flag right before processing (in case it was added after loading)
|
# Double-check ignore flag right before processing (in case it was added after loading)
|
||||||
if self.plex_client.is_artist_ignored(artist):
|
if self.media_client.is_artist_ignored(artist):
|
||||||
return (artist_name, True, "Skipped (ignored)")
|
return (artist_name, True, "Skipped (ignored)")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -1505,9 +1515,12 @@ class MetadataUpdateWorker(QThread):
|
||||||
def artist_needs_processing(self, artist):
|
def artist_needs_processing(self, artist):
|
||||||
"""Check if an artist needs metadata processing using age-based detection"""
|
"""Check if an artist needs metadata processing using age-based detection"""
|
||||||
try:
|
try:
|
||||||
# Use PlexClient's age-based checking with configured interval
|
# Check if artist is manually ignored
|
||||||
# This also handles the ignore flag check internally
|
if self.media_client.is_artist_ignored(artist):
|
||||||
return self.plex_client.needs_update_by_age(artist, self.refresh_interval_days)
|
return False
|
||||||
|
|
||||||
|
# Use media client's age-based checking with configured interval
|
||||||
|
return self.media_client.needs_update_by_age(artist, self.refresh_interval_days)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error checking artist {getattr(artist, 'title', 'Unknown')}: {e}")
|
print(f"Error checking artist {getattr(artist, 'title', 'Unknown')}: {e}")
|
||||||
|
|
@ -1557,14 +1570,18 @@ class MetadataUpdateWorker(QThread):
|
||||||
if genres_updated:
|
if genres_updated:
|
||||||
changes_made.append("genres")
|
changes_made.append("genres")
|
||||||
|
|
||||||
# Update album artwork
|
# Update album artwork (only for Plex, skip for Jellyfin due to API issues)
|
||||||
albums_updated = self.update_album_artwork(artist, spotify_artist)
|
if self.server_type == "plex":
|
||||||
if albums_updated > 0:
|
albums_updated = self.update_album_artwork(artist, spotify_artist)
|
||||||
changes_made.append(f"{albums_updated} album art")
|
if albums_updated > 0:
|
||||||
|
changes_made.append(f"{albums_updated} album art")
|
||||||
|
else:
|
||||||
|
# Skip album artwork for Jellyfin until API issues are resolved
|
||||||
|
logger.debug(f"Skipping album artwork updates for Jellyfin artist: {artist.title}")
|
||||||
|
|
||||||
if changes_made:
|
if changes_made:
|
||||||
# Update artist biography with timestamp to track last update
|
# Update artist biography with timestamp to track last update
|
||||||
biography_updated = self.plex_client.update_artist_biography(artist)
|
biography_updated = self.media_client.update_artist_biography(artist)
|
||||||
if biography_updated:
|
if biography_updated:
|
||||||
changes_made.append("timestamp")
|
changes_made.append("timestamp")
|
||||||
|
|
||||||
|
|
@ -1572,7 +1589,7 @@ class MetadataUpdateWorker(QThread):
|
||||||
return True, details
|
return True, details
|
||||||
else:
|
else:
|
||||||
# Even if no metadata changes, update biography to record we checked this artist
|
# Even if no metadata changes, update biography to record we checked this artist
|
||||||
self.plex_client.update_artist_biography(artist)
|
self.media_client.update_artist_biography(artist)
|
||||||
return True, "Already up to date"
|
return True, "Already up to date"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -1642,8 +1659,8 @@ class MetadataUpdateWorker(QThread):
|
||||||
|
|
||||||
print(f"[DEBUG] Updating genres for '{artist.title}' to: {genre_list}")
|
print(f"[DEBUG] Updating genres for '{artist.title}' to: {genre_list}")
|
||||||
|
|
||||||
# Use Plex API to update genres
|
# Use media client API to update genres
|
||||||
success = self.plex_client.update_artist_genres(artist, genre_list)
|
success = self.media_client.update_artist_genres(artist, genre_list)
|
||||||
if success:
|
if success:
|
||||||
print(f"[DEBUG] Successfully updated genres for '{artist.title}'")
|
print(f"[DEBUG] Successfully updated genres for '{artist.title}'")
|
||||||
return True
|
return True
|
||||||
|
|
@ -1801,8 +1818,8 @@ class MetadataUpdateWorker(QThread):
|
||||||
print(f"Invalid image data for album '{album_title}'")
|
print(f"Invalid image data for album '{album_title}'")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Upload to Plex using our new method
|
# Upload using media client
|
||||||
success = self.plex_client.update_album_poster(album, image_data)
|
success = self.media_client.update_album_poster(album, image_data)
|
||||||
if success:
|
if success:
|
||||||
print(f"✅ Updated artwork for album '{album_title}'")
|
print(f"✅ Updated artwork for album '{album_title}'")
|
||||||
else:
|
else:
|
||||||
|
|
@ -1852,26 +1869,31 @@ class MetadataUpdateWorker(QThread):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def upload_artist_poster(self, artist, image_data):
|
def upload_artist_poster(self, artist, image_data):
|
||||||
"""Upload poster to Plex"""
|
"""Upload poster using media client"""
|
||||||
try:
|
try:
|
||||||
# Use Plex client's update method if available
|
# Use media client's update method if available
|
||||||
if hasattr(self.plex_client, 'update_artist_poster'):
|
if hasattr(self.media_client, 'update_artist_poster'):
|
||||||
return self.plex_client.update_artist_poster(artist, image_data)
|
return self.media_client.update_artist_poster(artist, image_data)
|
||||||
|
|
||||||
# Fallback: direct Plex API call
|
# Fallback for Plex: direct API call
|
||||||
server = self.plex_client.server
|
if self.server_type == "plex":
|
||||||
upload_url = f"{server._baseurl}/library/metadata/{artist.ratingKey}/posters"
|
import requests
|
||||||
headers = {
|
server = self.media_client.server
|
||||||
'X-Plex-Token': server._token,
|
upload_url = f"{server._baseurl}/library/metadata/{artist.ratingKey}/posters"
|
||||||
'Content-Type': 'image/jpeg'
|
headers = {
|
||||||
}
|
'X-Plex-Token': server._token,
|
||||||
|
'Content-Type': 'image/jpeg'
|
||||||
response = requests.post(upload_url, data=image_data, headers=headers)
|
}
|
||||||
response.raise_for_status()
|
|
||||||
|
response = requests.post(upload_url, data=image_data, headers=headers)
|
||||||
# Refresh artist to see changes
|
response.raise_for_status()
|
||||||
artist.refresh()
|
|
||||||
return True
|
# Refresh artist to see changes
|
||||||
|
artist.refresh()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
# For other server types, return False since we only have fallback for Plex
|
||||||
|
return False
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error uploading poster: {e}")
|
print(f"Error uploading poster: {e}")
|
||||||
|
|
@ -3092,13 +3114,20 @@ class DashboardPage(QWidget):
|
||||||
self.database_widget = DatabaseUpdaterWidget()
|
self.database_widget = DatabaseUpdaterWidget()
|
||||||
self.database_widget.start_button.clicked.connect(self.toggle_database_update)
|
self.database_widget.start_button.clicked.connect(self.toggle_database_update)
|
||||||
|
|
||||||
# Metadata updater widget (SECOND)
|
# Metadata updater widget (SECOND) - only show for Plex
|
||||||
self.metadata_widget = MetadataUpdaterWidget()
|
from config.settings import config_manager
|
||||||
self.metadata_widget.start_button.clicked.connect(self.toggle_metadata_update)
|
active_server = config_manager.get_active_media_server()
|
||||||
|
|
||||||
|
if active_server == "plex":
|
||||||
|
self.metadata_widget = MetadataUpdaterWidget()
|
||||||
|
self.metadata_widget.start_button.clicked.connect(self.toggle_metadata_update)
|
||||||
|
else:
|
||||||
|
self.metadata_widget = None # Hide for Jellyfin
|
||||||
|
|
||||||
layout.addWidget(header_label)
|
layout.addWidget(header_label)
|
||||||
layout.addWidget(self.database_widget)
|
layout.addWidget(self.database_widget)
|
||||||
layout.addWidget(self.metadata_widget)
|
if self.metadata_widget: # Only add if it exists
|
||||||
|
layout.addWidget(self.metadata_widget)
|
||||||
|
|
||||||
return section
|
return section
|
||||||
|
|
||||||
|
|
@ -3399,6 +3428,9 @@ class DashboardPage(QWidget):
|
||||||
|
|
||||||
def toggle_metadata_update(self):
|
def toggle_metadata_update(self):
|
||||||
"""Toggle metadata update process"""
|
"""Toggle metadata update process"""
|
||||||
|
if not self.metadata_widget:
|
||||||
|
return # Metadata widget not available (Jellyfin server)
|
||||||
|
|
||||||
current_text = self.metadata_widget.start_button.text()
|
current_text = self.metadata_widget.start_button.text()
|
||||||
if "Begin" in current_text:
|
if "Begin" in current_text:
|
||||||
# Start metadata update
|
# Start metadata update
|
||||||
|
|
@ -3418,13 +3450,17 @@ class DashboardPage(QWidget):
|
||||||
active_server = config_manager.get_active_media_server()
|
active_server = config_manager.get_active_media_server()
|
||||||
|
|
||||||
# Currently metadata updater only supports Plex
|
# Currently metadata updater only supports Plex
|
||||||
if active_server != "plex":
|
# Check if we have the active media server client
|
||||||
self.add_activity_item("❌", "Metadata Update", f"Metadata updater only supports Plex (active: {active_server.title()})", "Now")
|
if active_server == "jellyfin":
|
||||||
return
|
media_client = self.data_provider.service_clients.get('jellyfin_client')
|
||||||
|
if not media_client:
|
||||||
if not hasattr(self, 'data_provider') or not self.data_provider.service_clients.get('plex_client'):
|
self.add_activity_item("❌", "Metadata Update", "Jellyfin client not available", "Now")
|
||||||
self.add_activity_item("❌", "Metadata Update", "Plex client not available", "Now")
|
return
|
||||||
return
|
else:
|
||||||
|
media_client = self.data_provider.service_clients.get('plex_client')
|
||||||
|
if not media_client:
|
||||||
|
self.add_activity_item("❌", "Metadata Update", "Plex client not available", "Now")
|
||||||
|
return
|
||||||
|
|
||||||
if not self.data_provider.service_clients.get('spotify_client'):
|
if not self.data_provider.service_clients.get('spotify_client'):
|
||||||
self.add_activity_item("❌", "Metadata Update", "Spotify client not available", "Now")
|
self.add_activity_item("❌", "Metadata Update", "Spotify client not available", "Now")
|
||||||
|
|
@ -3432,13 +3468,14 @@ class DashboardPage(QWidget):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get refresh interval from dropdown
|
# Get refresh interval from dropdown
|
||||||
refresh_interval_days = self.metadata_widget.get_refresh_interval_days()
|
refresh_interval_days = self.metadata_widget.get_refresh_interval_days() if self.metadata_widget else 30
|
||||||
|
|
||||||
# Start the metadata update worker (it will handle artist retrieval in background)
|
# Start the metadata update worker (it will handle artist retrieval in background)
|
||||||
self.metadata_worker = MetadataUpdateWorker(
|
self.metadata_worker = MetadataUpdateWorker(
|
||||||
None, # Artists will be loaded in the worker thread
|
None, # Artists will be loaded in the worker thread
|
||||||
self.data_provider.service_clients['plex_client'],
|
media_client,
|
||||||
self.data_provider.service_clients['spotify_client'],
|
self.data_provider.service_clients['spotify_client'],
|
||||||
|
active_server,
|
||||||
refresh_interval_days
|
refresh_interval_days
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -3450,8 +3487,9 @@ class DashboardPage(QWidget):
|
||||||
self.metadata_worker.artists_loaded.connect(self.on_artists_loaded)
|
self.metadata_worker.artists_loaded.connect(self.on_artists_loaded)
|
||||||
|
|
||||||
# Update UI and start
|
# Update UI and start
|
||||||
self.metadata_widget.update_progress(True, "Loading artists...", 0, 0, 0.0)
|
if self.metadata_widget:
|
||||||
self.add_activity_item("🎵", "Metadata Update", "Loading artists from Plex library...", "Now")
|
self.metadata_widget.update_progress(True, "Loading artists...", 0, 0, 0.0)
|
||||||
|
self.add_activity_item("🎵", "Metadata Update", "Loading artists from library...", "Now")
|
||||||
|
|
||||||
self.metadata_worker.start()
|
self.metadata_worker.start()
|
||||||
|
|
||||||
|
|
@ -3473,7 +3511,8 @@ class DashboardPage(QWidget):
|
||||||
if self.metadata_worker.isRunning():
|
if self.metadata_worker.isRunning():
|
||||||
self.metadata_worker.terminate()
|
self.metadata_worker.terminate()
|
||||||
|
|
||||||
self.metadata_widget.update_progress(False, "", 0, 0, 0.0)
|
if self.metadata_widget:
|
||||||
|
self.metadata_widget.update_progress(False, "", 0, 0, 0.0)
|
||||||
self.add_activity_item("⏹️", "Metadata Update", "Stopped metadata update process", "Now")
|
self.add_activity_item("⏹️", "Metadata Update", "Stopped metadata update process", "Now")
|
||||||
|
|
||||||
def artist_needs_processing(self, artist):
|
def artist_needs_processing(self, artist):
|
||||||
|
|
@ -3512,7 +3551,8 @@ class DashboardPage(QWidget):
|
||||||
|
|
||||||
def on_metadata_progress(self, current_artist, processed, total, percentage):
|
def on_metadata_progress(self, current_artist, processed, total, percentage):
|
||||||
"""Handle metadata update progress"""
|
"""Handle metadata update progress"""
|
||||||
self.metadata_widget.update_progress(True, current_artist, processed, total, percentage)
|
if self.metadata_widget:
|
||||||
|
self.metadata_widget.update_progress(True, current_artist, processed, total, percentage)
|
||||||
|
|
||||||
def on_artist_updated(self, artist_name, success, details):
|
def on_artist_updated(self, artist_name, success, details):
|
||||||
"""Handle individual artist update completion"""
|
"""Handle individual artist update completion"""
|
||||||
|
|
@ -3523,13 +3563,15 @@ class DashboardPage(QWidget):
|
||||||
|
|
||||||
def on_metadata_finished(self, total_processed, successful, failed):
|
def on_metadata_finished(self, total_processed, successful, failed):
|
||||||
"""Handle metadata update completion"""
|
"""Handle metadata update completion"""
|
||||||
self.metadata_widget.update_progress(False, "", 0, 0, 0.0)
|
if self.metadata_widget:
|
||||||
|
self.metadata_widget.update_progress(False, "", 0, 0, 0.0)
|
||||||
summary = f"Processed {total_processed} artists: {successful} updated, {failed} failed"
|
summary = f"Processed {total_processed} artists: {successful} updated, {failed} failed"
|
||||||
self.add_activity_item("🎵", "Metadata Complete", summary, "Now")
|
self.add_activity_item("🎵", "Metadata Complete", summary, "Now")
|
||||||
|
|
||||||
def on_metadata_error(self, error_message):
|
def on_metadata_error(self, error_message):
|
||||||
"""Handle metadata update error"""
|
"""Handle metadata update error"""
|
||||||
self.metadata_widget.update_progress(False, "", 0, 0, 0.0)
|
if self.metadata_widget:
|
||||||
|
self.metadata_widget.update_progress(False, "", 0, 0, 0.0)
|
||||||
self.add_activity_item("❌", "Metadata Error", error_message, "Now")
|
self.add_activity_item("❌", "Metadata Error", error_message, "Now")
|
||||||
|
|
||||||
def on_service_status_updated(self, service: str, connected: bool, response_time: float, error: str):
|
def on_service_status_updated(self, service: str, connected: bool, response_time: float, error: str):
|
||||||
|
|
@ -3569,7 +3611,8 @@ class DashboardPage(QWidget):
|
||||||
|
|
||||||
def on_metadata_progress_updated(self, is_running: bool, current_artist: str, processed: int, total: int, percentage: float):
|
def on_metadata_progress_updated(self, is_running: bool, current_artist: str, processed: int, total: int, percentage: float):
|
||||||
"""Handle metadata update progress"""
|
"""Handle metadata update progress"""
|
||||||
self.metadata_widget.update_progress(is_running, current_artist, processed, total, percentage)
|
if self.metadata_widget:
|
||||||
|
self.metadata_widget.update_progress(is_running, current_artist, processed, total, percentage)
|
||||||
|
|
||||||
def on_sync_progress_updated(self, current_playlist: str, active_syncs: int):
|
def on_sync_progress_updated(self, current_playlist: str, active_syncs: int):
|
||||||
"""Handle sync progress updates"""
|
"""Handle sync progress updates"""
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue