gui navidrome support

pull/28/head
Broque Thomas 5 months ago
parent 41df3d84b7
commit ba37225e1b

@ -20,11 +20,17 @@
"api_key": "JELLYFIN_API_KEY",
"auto_detect": true
},
"navidrome": {
"base_url": "http://localhost:4533",
"username": "NAVIDROME_USERNAME",
"password": "NAVIDROME_PASSWORD",
"auto_detect": true
},
"soulseek": {
"slskd_url": "http://localhost:5030",
"slskd_url": "http://host.docker.internal:5030",
"api_key": "SoulseekAPIKey",
"download_path": "./Downloads",
"transfer_path": "./Transfer"
"download_path": "/app/downloads",
"transfer_path": "/app/Transfer"
},
"logging": {
"path": "logs/app.log",

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

@ -1943,6 +1943,7 @@ class DashboardDataProvider(QObject):
'spotify': ServiceStatus('Spotify', False, datetime.now()),
'plex': ServiceStatus('Plex', False, datetime.now()),
'jellyfin': ServiceStatus('Jellyfin', False, datetime.now()),
'navidrome': ServiceStatus('Navidrome', False, datetime.now()),
'soulseek': ServiceStatus('Soulseek', False, datetime.now())
}
self.download_stats = DownloadStats()
@ -2100,6 +2101,7 @@ class DashboardDataProvider(QObject):
'spotify': 'spotify_client',
'plex': 'plex_client',
'jellyfin': None, # Jellyfin doesn't need a client, tests via config
'navidrome': 'navidrome_client',
'soulseek': 'soulseek_client'
}
@ -3053,7 +3055,12 @@ class DashboardPage(QWidget):
# Create service status cards with dynamic media server
from config.settings import config_manager
active_server = config_manager.get_active_media_server()
server_name = "Plex" if active_server == "plex" else "Jellyfin"
server_name_map = {
'plex': 'Plex',
'jellyfin': 'Jellyfin',
'navidrome': 'Navidrome'
}
server_name = server_name_map.get(active_server, 'Jellyfin')
services = ['Spotify', server_name, 'Soulseek']
for service in services:
card = ServiceStatusCard(service)

@ -904,6 +904,125 @@ class JellyfinDetectionThread(QThread):
return False
class NavidromeDetectionThread(QThread):
progress_updated = pyqtSignal(int, str) # progress value, current url
detection_completed = pyqtSignal(str) # found_url (empty if not found)
def __init__(self):
super().__init__()
self.cancelled = False
def cancel(self):
self.cancelled = True
def run(self):
import requests
import socket
import ipaddress
def get_network_info():
"""Get comprehensive network information with subnet detection"""
try:
# Get local IP using socket method
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
local_ip = s.getsockname()[0]
s.close()
# Parse network info
network = ipaddress.ip_network(f"{local_ip}/24", strict=False)
return {
'local_ip': local_ip,
'network': network,
'subnet': str(network.network_address) + "/24"
}
except Exception as e:
print(f"Error getting network info: {e}")
return {'local_ip': '127.0.0.1', 'network': None, 'subnet': '127.0.0.1/32'}
try:
# Test common Navidrome URLs first
common_urls = [
"http://localhost:4533",
"http://127.0.0.1:4533",
"http://navidrome:4533"
]
network_info = get_network_info()
local_ip = network_info['local_ip']
# Add local IP with common ports
common_urls.extend([
f"http://{local_ip}:4533"
])
total_hosts = len(common_urls)
current_host = 0
# Test common URLs first
for url in common_urls:
if self.cancelled:
break
current_host += 1
self.progress_updated.emit(int((current_host / total_hosts) * 100), url)
if self.test_navidrome_url(url):
self.detection_completed.emit(url)
return
# If no common URLs worked, signal not found
self.detection_completed.emit("")
except Exception as e:
print(f"Navidrome detection error: {e}")
self.detection_completed.emit("")
def test_navidrome_url(self, url, timeout=5):
"""Test if URL hosts a Navidrome server by checking for ping endpoint"""
try:
# Test Navidrome ping endpoint
ping_url = f"{url.rstrip('/')}/rest/ping"
print(f"Testing Navidrome at: {ping_url}")
response = requests.get(ping_url, params={
'u': 'test',
't': 'test',
's': 'test',
'v': '1.16.1',
'c': 'SoulSync',
'f': 'json'
}, timeout=timeout)
print(f"Response status: {response.status_code}")
# Navidrome should return status 401 or 403 for invalid credentials, not 404
if response.status_code in [200, 401, 403]:
try:
data = response.json()
print(f"Response data: {data}")
# Check if it's a valid Subsonic API response
if 'subsonic-response' in data:
print(f"✓ Found Navidrome server at {url}")
return True
except Exception as e:
print(f"JSON parse error: {e}")
# Also try a simple GET to the root to see if it's at least a web server
try:
root_response = requests.get(url, timeout=timeout)
if root_response.status_code == 200 and 'navidrome' in root_response.text.lower():
print(f"✓ Found Navidrome web interface at {url}")
return True
except:
pass
return False
except Exception as e:
print(f"Error testing {url}: {e}")
return False
class SettingsGroup(QGroupBox):
def __init__(self, title: str, parent=None):
super().__init__(title, parent)
@ -1091,19 +1210,29 @@ class SettingsPage(QWidget):
jellyfin_config = config_manager.get_jellyfin_config()
self.jellyfin_url_input.setText(jellyfin_config.get('base_url', ''))
self.jellyfin_api_key_input.setText(jellyfin_config.get('api_key', ''))
# Load Navidrome config
navidrome_config = config_manager.get_navidrome_config()
self.navidrome_url_input.setText(navidrome_config.get('base_url', ''))
self.navidrome_username_input.setText(navidrome_config.get('username', ''))
self.navidrome_password_input.setText(navidrome_config.get('password', ''))
# Initialize server selection
active_server = config_manager.get_active_media_server()
self.pending_server_change = None
self.update_server_toggle_styles(active_server)
# Show/hide appropriate containers based on active server
self.plex_container.hide()
self.jellyfin_container.hide()
self.navidrome_container.hide()
if active_server == 'plex':
self.plex_container.show()
self.jellyfin_container.hide()
else:
self.plex_container.hide()
elif active_server == 'jellyfin':
self.jellyfin_container.show()
elif active_server == 'navidrome':
self.navidrome_container.show()
# Load Soulseek config
soulseek_config = config_manager.get_soulseek_config()
@ -1178,7 +1307,12 @@ class SettingsPage(QWidget):
# Save Jellyfin settings
config_manager.set('jellyfin.base_url', self.jellyfin_url_input.text())
config_manager.set('jellyfin.api_key', self.jellyfin_api_key_input.text())
# Save Navidrome settings
config_manager.set('navidrome.base_url', self.navidrome_url_input.text())
config_manager.set('navidrome.username', self.navidrome_username_input.text())
config_manager.set('navidrome.password', self.navidrome_password_input.text())
# Save pending server change if any
if hasattr(self, 'pending_server_change') and self.pending_server_change:
config_manager.set_active_media_server(self.pending_server_change)
@ -2260,9 +2394,16 @@ class SettingsPage(QWidget):
self.jellyfin_toggle_button.setFixedHeight(40)
self.jellyfin_toggle_button.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.jellyfin_toggle_button.clicked.connect(lambda: self.select_media_server('jellyfin'))
# Navidrome toggle button
self.navidrome_toggle_button = QPushButton()
self.navidrome_toggle_button.setFixedHeight(40)
self.navidrome_toggle_button.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.navidrome_toggle_button.clicked.connect(lambda: self.select_media_server('navidrome'))
toggle_container.addWidget(self.plex_toggle_button)
toggle_container.addWidget(self.jellyfin_toggle_button)
toggle_container.addWidget(self.navidrome_toggle_button)
server_selection_layout.addLayout(toggle_container)
# Restart warning (initially hidden)
@ -2397,7 +2538,78 @@ class SettingsPage(QWidget):
# Add Jellyfin frame to its container
jellyfin_container_layout.addWidget(jellyfin_frame)
# Navidrome Settings Container
self.navidrome_container = QWidget()
self.navidrome_container.setStyleSheet("background: transparent;")
navidrome_container_layout = QVBoxLayout(self.navidrome_container)
navidrome_container_layout.setContentsMargins(0, 0, 0, 0)
navidrome_container_layout.setSpacing(0)
# Navidrome settings
navidrome_frame = QFrame()
navidrome_frame.setStyleSheet("""
QFrame {
background: #333333;
border: 1px solid #444444;
border-radius: 8px;
padding: 8px;
}
""")
navidrome_layout = QVBoxLayout(navidrome_frame)
navidrome_layout.setSpacing(8)
navidrome_title = QLabel("Navidrome")
navidrome_title.setFont(QFont("Arial", 12, QFont.Weight.Bold))
navidrome_title.setStyleSheet("color: #ff6b6b;") # Navidrome red color
navidrome_layout.addWidget(navidrome_title)
# Server URL
navidrome_url_label = QLabel("Server URL:")
navidrome_url_label.setStyleSheet(self.get_label_style(11))
navidrome_layout.addWidget(navidrome_url_label)
navidrome_url_input_layout = QHBoxLayout()
self.navidrome_url_input = QLineEdit()
self.navidrome_url_input.setStyleSheet(self.get_input_style())
self.navidrome_url_input.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.form_inputs['navidrome.base_url'] = self.navidrome_url_input
navidrome_detect_btn = QPushButton("Auto-detect")
navidrome_detect_btn.setFixedSize(80, 30)
navidrome_detect_btn.clicked.connect(self.auto_detect_navidrome)
navidrome_detect_btn.setStyleSheet(self.get_test_button_style())
navidrome_url_input_layout.addWidget(self.navidrome_url_input)
navidrome_url_input_layout.addWidget(navidrome_detect_btn)
navidrome_layout.addLayout(navidrome_url_input_layout)
# Username
navidrome_username_label = QLabel("Username:")
navidrome_username_label.setStyleSheet(self.get_label_style(11))
navidrome_layout.addWidget(navidrome_username_label)
self.navidrome_username_input = QLineEdit()
self.navidrome_username_input.setStyleSheet(self.get_input_style())
self.navidrome_username_input.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.form_inputs['navidrome.username'] = self.navidrome_username_input
navidrome_layout.addWidget(self.navidrome_username_input)
# Password
navidrome_password_label = QLabel("Password:")
navidrome_password_label.setStyleSheet(self.get_label_style(11))
navidrome_layout.addWidget(navidrome_password_label)
self.navidrome_password_input = QLineEdit()
self.navidrome_password_input.setEchoMode(QLineEdit.EchoMode.Password)
self.navidrome_password_input.setStyleSheet(self.get_input_style())
self.navidrome_password_input.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self.form_inputs['navidrome.password'] = self.navidrome_password_input
navidrome_layout.addWidget(self.navidrome_password_input)
# Add Navidrome frame to its container
navidrome_container_layout.addWidget(navidrome_frame)
# Soulseek settings
soulseek_frame = QFrame()
soulseek_frame.setStyleSheet("""
@ -2454,6 +2666,7 @@ class SettingsPage(QWidget):
api_layout.addWidget(server_selection_container)
api_layout.addWidget(self.plex_container)
api_layout.addWidget(self.jellyfin_container)
api_layout.addWidget(self.navidrome_container)
api_layout.addWidget(soulseek_frame)
# Test connections
@ -2795,12 +3008,16 @@ class SettingsPage(QWidget):
self.update_server_toggle_styles(server_type)
# Show/hide appropriate containers
self.plex_container.hide()
self.jellyfin_container.hide()
self.navidrome_container.hide()
if server_type == 'plex':
self.plex_container.show()
self.jellyfin_container.hide()
else:
self.plex_container.hide()
elif server_type == 'jellyfin':
self.jellyfin_container.show()
elif server_type == 'navidrome':
self.navidrome_container.show()
except Exception as e:
logger.error(f"Error selecting media server: {e}")
@ -2870,9 +3087,21 @@ class SettingsPage(QWidget):
"jellyfin_icon.png",
32
)
if not hasattr(self, '_cached_navidrome_icon'):
self._cached_navidrome_icon = download_and_cache_logo(
"https://raw.githubusercontent.com/navidrome/navidrome/master/resources/logo-192x192.png",
"navidrome_icon.png",
32
)
# Fallback to a simple text-based icon if download fails
if self._cached_navidrome_icon.isNull():
logger.warning("Navidrome icon download failed, creating fallback icon")
self._cached_navidrome_icon = self._create_fallback_icon("N", "#ff6b6b")
plex_icon = self._cached_plex_icon
jellyfin_icon = self._cached_jellyfin_icon
navidrome_icon = self._cached_navidrome_icon
# Active button styles with appropriate colors
active_plex_style = """
@ -2904,6 +3133,21 @@ class SettingsPage(QWidget):
stop:1 rgba(150, 82, 175, 1.0));
}
"""
active_navidrome_style = """
QPushButton {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(255, 107, 107, 0.8),
stop:1 rgba(235, 87, 87, 0.9));
border: 2px solid rgba(255, 107, 107, 1);
border-radius: 8px;
}
QPushButton:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(255, 107, 107, 0.9),
stop:1 rgba(235, 87, 87, 1.0));
}
"""
# Inactive button style
inactive_style = """
@ -2918,19 +3162,64 @@ class SettingsPage(QWidget):
}
"""
# Set icons and styles
# Set icons, text, and styles
self.plex_toggle_button.setIcon(plex_icon)
self.plex_toggle_button.setIconSize(QSize(28, 28))
self.plex_toggle_button.setText("Plex")
self.jellyfin_toggle_button.setIcon(jellyfin_icon)
self.jellyfin_toggle_button.setIconSize(QSize(28, 28))
self.jellyfin_toggle_button.setText("Jellyfin")
self.navidrome_toggle_button.setIcon(navidrome_icon)
self.navidrome_toggle_button.setIconSize(QSize(28, 28))
self.navidrome_toggle_button.setText("Navidrome")
# Debug: Check if icons are properly loaded
if navidrome_icon.isNull():
logger.warning("Navidrome icon failed to load!")
else:
logger.info("Navidrome icon loaded successfully")
# Reset all buttons to inactive first
self.plex_toggle_button.setStyleSheet(inactive_style)
self.jellyfin_toggle_button.setStyleSheet(inactive_style)
self.navidrome_toggle_button.setStyleSheet(inactive_style)
# Set the active server button style
if active_server == 'plex':
self.plex_toggle_button.setStyleSheet(active_plex_style)
self.jellyfin_toggle_button.setStyleSheet(inactive_style)
else:
self.plex_toggle_button.setStyleSheet(inactive_style)
elif active_server == 'jellyfin':
self.jellyfin_toggle_button.setStyleSheet(active_jellyfin_style)
elif active_server == 'navidrome':
self.navidrome_toggle_button.setStyleSheet(active_navidrome_style)
def _create_fallback_icon(self, text, color):
"""Create a simple text-based fallback icon"""
from PyQt6.QtGui import QPixmap, QPainter, QFont, QColor
from PyQt6.QtCore import Qt
# Create a 32x32 pixmap
pixmap = QPixmap(32, 32)
pixmap.fill(Qt.GlobalColor.transparent)
painter = QPainter(pixmap)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
# Draw background circle
painter.setBrush(QColor(color))
painter.setPen(Qt.PenStyle.NoPen)
painter.drawEllipse(2, 2, 28, 28)
# Draw text
painter.setPen(QColor("white"))
font = QFont("Arial", 14, QFont.Weight.Bold)
painter.setFont(font)
painter.drawText(0, 0, 32, 32, Qt.AlignmentFlag.AlignCenter, text)
painter.end()
return QIcon(pixmap)
def auto_detect_jellyfin(self):
"""Auto-detect Jellyfin server URL using background thread"""
# Don't start new detection if one is already running
@ -3034,7 +3323,102 @@ class SettingsPage(QWidget):
self.detection_thread.start()
self.detection_dialog.show()
def auto_detect_navidrome(self):
"""Auto-detect Navidrome server URL using background thread"""
# Don't start new detection if one is already running
if self.detection_thread and self.detection_thread.isRunning():
return
# Create animated loading dialog
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton
from PyQt6.QtCore import QTimer, QPropertyAnimation, QRect
from PyQt6.QtGui import QPainter, QColor
self.detection_dialog = QDialog(self)
self.detection_dialog.setWindowTitle("Auto-detecting Navidrome Server")
self.detection_dialog.setModal(True)
self.detection_dialog.setFixedSize(400, 180)
self.detection_dialog.setWindowFlags(self.detection_dialog.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint)
# Apply dark theme styling
self.detection_dialog.setStyleSheet("""
QDialog {
background-color: #282828;
color: #ffffff;
border: 1px solid #404040;
border-radius: 8px;
}
QLabel {
color: #ffffff;
font-size: 14px;
}
QPushButton {
background-color: #404040;
border: 1px solid #606060;
border-radius: 4px;
color: #ffffff;
padding: 8px 16px;
font-size: 11px;
}
QPushButton:hover {
background-color: #505050;
}
""")
layout = QVBoxLayout(self.detection_dialog)
layout.setSpacing(20)
# Status text
status_label = QLabel("Scanning network for Navidrome servers...")
status_label.setWordWrap(True)
layout.addWidget(status_label)
# Cancel button
button_layout = QHBoxLayout()
cancel_btn = QPushButton("Cancel")
cancel_btn.clicked.connect(self.cancel_detection)
button_layout.addStretch()
button_layout.addWidget(cancel_btn)
layout.addLayout(button_layout)
# Start Navidrome detection thread
self.detection_thread = NavidromeDetectionThread()
self.detection_thread.progress_updated.connect(self.on_detection_progress, Qt.ConnectionType.QueuedConnection)
self.detection_thread.detection_completed.connect(self.on_navidrome_detection_completed, Qt.ConnectionType.QueuedConnection)
self.detection_thread.start()
self.detection_dialog.show()
def on_navidrome_detection_completed(self, found_url):
"""Handle Navidrome detection completion"""
# Stop animation and close dialog
if hasattr(self, 'loading_animation'):
self.loading_animation.stop()
if hasattr(self, 'detection_dialog') and self.detection_dialog:
self.detection_dialog.close()
self.detection_dialog = None
# Properly cleanup thread
if self.detection_thread:
if self.detection_thread.isRunning():
self.detection_thread.quit()
self.detection_thread.wait(1000) # Wait up to 1 second
self.detection_thread = None
if found_url:
self.navidrome_url_input.setText(found_url)
# Show success toast
from ui.components.toast_manager import ToastManager
toast_manager = ToastManager(self)
toast_manager.show_toast(f"✓ Navidrome server detected: {found_url}", "success", 4000)
else:
# Show error toast
from ui.components.toast_manager import ToastManager
toast_manager = ToastManager(self)
toast_manager.show_toast("❌ No Navidrome servers found on the network", "error", 4000)
def on_jellyfin_detection_completed(self, found_url):
"""Handle Jellyfin detection completion"""
# Stop animation and close dialog

@ -1709,7 +1709,8 @@ class PlaylistDetailsModal(QDialog):
self.parent_page.spotify_client,
self.parent_page.plex_client,
self.parent_page.soulseek_client,
getattr(self.parent_page, 'jellyfin_client', None)
getattr(self.parent_page, 'jellyfin_client', None),
getattr(self.parent_page, 'navidrome_client', None)
)
# Start sync
@ -1839,7 +1840,15 @@ class PlaylistDetailsModal(QDialog):
def on_analysis_started(self, total_tracks):
"""Handle analysis started signal"""
print(f"Started analyzing {total_tracks} tracks against Plex library")
# Get server name for log message
try:
from config.settings import config_manager
active_server = config_manager.get_active_media_server()
server_name = active_server.title() if active_server else "Plex"
except:
server_name = "Plex"
print(f"Started analyzing {total_tracks} tracks against {server_name} library")
def on_track_analyzed(self, track_index, result):
"""Handle individual track analysis completion"""
@ -1858,12 +1867,20 @@ class PlaylistDetailsModal(QDialog):
if not missing_tracks:
QMessageBox.information(self, "Analysis Complete",
"All tracks already exist in Plex library!\nNo downloads needed.")
f"All tracks already exist in {server_name} library!\nNo downloads needed.")
return
# Show results to user
message = f"Analysis complete!\n\n"
message += f"Tracks already in Plex: {len(existing_tracks)}\n"
# Get server name for display
try:
from config.settings import config_manager
active_server = config_manager.get_active_media_server()
server_name = active_server.title() if active_server else "Plex"
except:
server_name = "Plex"
message += f"Tracks already in {server_name}: {len(existing_tracks)}\n"
message += f"Tracks to download: {len(missing_tracks)}\n\n"
message += "Ready to start downloading missing tracks?"
@ -3086,11 +3103,12 @@ class SyncPage(QWidget):
sync_activity = pyqtSignal(str, str, str, str) # icon, title, subtitle, time
database_updated_externally = pyqtSignal()
def __init__(self, spotify_client=None, plex_client=None, soulseek_client=None, downloads_page=None, jellyfin_client=None, tidal_client=None, parent=None):
def __init__(self, spotify_client=None, plex_client=None, soulseek_client=None, downloads_page=None, jellyfin_client=None, navidrome_client=None, tidal_client=None, parent=None):
super().__init__(parent)
self.spotify_client = spotify_client
self.plex_client = plex_client
self.jellyfin_client = jellyfin_client
self.navidrome_client = navidrome_client
self.soulseek_client = soulseek_client
self.tidal_client = tidal_client or TidalClient()
self.downloads_page = downloads_page
@ -3293,7 +3311,7 @@ class SyncPage(QWidget):
if playlist.id in self.active_sync_workers:
# Already syncing
return False
# Create sync service if not available
if not hasattr(self, 'sync_service'):
from services.sync_service import PlaylistSyncService
@ -3301,7 +3319,8 @@ class SyncPage(QWidget):
self.spotify_client,
self.plex_client,
self.soulseek_client,
getattr(self, 'jellyfin_client', None)
getattr(self, 'jellyfin_client', None),
getattr(self, 'navidrome_client', None)
)
# Create sync worker
@ -3355,7 +3374,8 @@ class SyncPage(QWidget):
self.spotify_client,
self.plex_client,
self.soulseek_client,
getattr(self, 'jellyfin_client', None)
getattr(self, 'jellyfin_client', None),
getattr(self, 'navidrome_client', None)
)
# Create sync worker for sequential sync
@ -4059,7 +4079,15 @@ class SyncPage(QWidget):
title_label.setStyleSheet("color: #ffffff;")
# Subtitle
subtitle_label = QLabel("Synchronize your Spotify playlists with Plex")
# Get active server name for subtitle
try:
from config.settings import config_manager
active_server = config_manager.get_active_media_server()
server_name = active_server.title() if active_server else "Plex"
except:
server_name = "Plex"
subtitle_label = QLabel(f"Synchronize your Spotify playlists with {server_name}")
subtitle_label.setFont(QFont("Arial", 14))
subtitle_label.setStyleSheet("color: #b3b3b3;")
@ -8100,7 +8128,15 @@ class DownloadMissingTracksModal(QDialog):
# The modal now stays open.
# The process_finished signal is still emitted to unlock the main UI.
self.process_finished.emit()
QMessageBox.information(self, "Analysis Complete", "All tracks already exist in Plex! No downloads needed.")
# Get server name for message
try:
from config.settings import config_manager
active_server = config_manager.get_active_media_server()
server_name = active_server.title() if active_server else "Plex"
except:
server_name = "Plex"
QMessageBox.information(self, "Analysis Complete", f"All tracks already exist in {server_name}! No downloads needed.")
def on_analysis_failed(self, error_message):
print(f"❌ Analysis failed: {error_message}")

@ -1308,7 +1308,12 @@ class ModernSidebar(QWidget):
# Dynamic media server status - determine which server is active
from config.settings import config_manager
active_server = config_manager.get_active_media_server()
server_name = "Plex" if active_server == "plex" else "Jellyfin"
server_name_map = {
'plex': 'Plex',
'jellyfin': 'Jellyfin',
'navidrome': 'Navidrome'
}
server_name = server_name_map.get(active_server, 'Jellyfin')
self.media_server_status = StatusIndicator(server_name)
self.soulseek_status = StatusIndicator("Soulseek")
@ -1333,6 +1338,7 @@ class ModernSidebar(QWidget):
"spotify": self.spotify_status,
"plex": self.media_server_status,
"jellyfin": self.media_server_status,
"navidrome": self.media_server_status,
"soulseek": self.soulseek_status
}
@ -1341,7 +1347,12 @@ class ModernSidebar(QWidget):
def update_media_server_name(self, server_type: str):
"""Update the media server status indicator name"""
server_name = "Plex" if server_type == "plex" else "Jellyfin"
server_name_map = {
'plex': 'Plex',
'jellyfin': 'Jellyfin',
'navidrome': 'Navidrome'
}
server_name = server_name_map.get(server_type, 'Jellyfin')
if hasattr(self, 'media_server_status'):
self.media_server_status.update_name(server_name)

@ -6967,8 +6967,8 @@ async function loadDashboardData() {
metadataButton.addEventListener('click', handleMetadataUpdateButtonClick);
}
// Check active media server and hide metadata updater if Jellyfin
await checkAndHideMetadataUpdaterForJellyfin();
// Check active media server and hide metadata updater if not Plex
await checkAndHideMetadataUpdaterForNonPlex();
// Check for ongoing metadata update and restore state
await checkAndRestoreMetadataUpdateState();
@ -12968,24 +12968,24 @@ function updateMetadataProgressUI(status) {
}
/**
* Check active media server and hide metadata updater if Jellyfin
* Check active media server and hide metadata updater if not Plex
*/
async function checkAndHideMetadataUpdaterForJellyfin() {
async function checkAndHideMetadataUpdaterForNonPlex() {
try {
const response = await fetch('/api/active-media-server');
const data = await response.json();
if (data.success) {
const metadataCard = document.getElementById('metadata-updater-card');
if (metadataCard) {
if (data.active_server === 'jellyfin') {
// Hide metadata updater for Jellyfin (same as dashboard.py behavior)
metadataCard.style.display = 'none';
console.log('Metadata updater hidden: Jellyfin is active server');
} else {
// Show metadata updater for Plex
if (data.active_server === 'plex') {
// Show metadata updater only for Plex
metadataCard.style.display = 'block';
console.log('Metadata updater shown: Plex is active server');
} else {
// Hide metadata updater for Jellyfin and Navidrome (same as dashboard.py behavior)
metadataCard.style.display = 'none';
console.log(`Metadata updater hidden: ${data.active_server} is active server`);
}
}
}

Loading…
Cancel
Save