You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SoulSync/webui/index.html

6787 lines
499 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes">
<title>SoulSync - Music Sync & Manager</title>
<link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.png') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='mobile.css') }}">
</head>
<body>
<!-- Launch PIN Lock Screen -->
<div id="launch-pin-overlay" class="launch-pin-overlay" style="display: none;">
<div class="launch-pin-container" id="launch-pin-container">
<!-- PIN Entry (default view) -->
<div id="launch-pin-entry">
<div class="launch-pin-icon">🔒</div>
<h2 class="launch-pin-title">SoulSync is Locked</h2>
<p class="launch-pin-subtitle">Enter your PIN to continue</p>
<input type="password" id="launch-pin-input" class="launch-pin-input" maxlength="20" placeholder="PIN" autocomplete="off">
<button id="launch-pin-submit" class="launch-pin-submit">Unlock</button>
<p id="launch-pin-error" class="launch-pin-error" style="display: none;"></p>
<button class="launch-pin-forgot" onclick="showForgotPinView()">Forgot PIN?</button>
</div>
<!-- Forgot PIN (recovery view) -->
<div id="launch-pin-recovery" style="display: none;">
<div class="launch-pin-icon">🔑</div>
<h2 class="launch-pin-title">Verify Your Identity</h2>
<p class="launch-pin-subtitle">Enter any API key, token, or secret you configured in SoulSync settings</p>
<input type="password" id="launch-recovery-input" class="launch-pin-input" maxlength="200" placeholder="Paste any API credential..." autocomplete="off" style="letter-spacing: 1px;">
<button id="launch-recovery-submit" class="launch-pin-submit" onclick="submitRecoveryCredential()">Verify & Reset PIN</button>
<p id="launch-recovery-error" class="launch-pin-error" style="display: none;"></p>
<button class="launch-pin-forgot" onclick="showPinEntryView()">← Back to PIN</button>
</div>
</div>
</div>
<!-- Profile Picker Overlay -->
<div id="profile-picker-overlay" class="profile-picker-overlay" style="display: none;">
<div class="profile-picker-container">
<h2 class="profile-picker-title">Who's listening?</h2>
<div id="profile-picker-grid" class="profile-picker-grid"></div>
<div id="profile-picker-actions" class="profile-picker-actions" style="display: none;">
<button id="manage-profiles-btn" class="profile-manage-btn">Manage Profiles</button>
</div>
</div>
<!-- PIN Dialog -->
<div id="profile-pin-dialog" class="profile-pin-dialog" style="display: none;">
<div class="profile-pin-content">
<div id="profile-pin-avatar" class="profile-pin-avatar"></div>
<p id="profile-pin-name" class="profile-pin-name"></p>
<input type="password" id="profile-pin-input" class="profile-pin-input" maxlength="6" placeholder="Enter PIN" autocomplete="off">
<div class="profile-pin-buttons">
<button id="profile-pin-cancel" class="profile-pin-cancel">Cancel</button>
<button id="profile-pin-submit" class="profile-pin-submit">Submit</button>
</div>
<p id="profile-pin-error" class="profile-pin-error" style="display: none;"></p>
</div>
</div>
<!-- Profile Management Panel -->
<div id="profile-manage-panel" class="profile-manage-panel" style="display: none;">
<div class="profile-manage-content">
<div class="profile-manage-header">
<h3>Manage Profiles</h3>
<button id="profile-manage-close" class="profile-manage-close-btn">&times;</button>
</div>
<div id="profile-manage-list" class="profile-manage-list"></div>
<div class="profile-manage-add">
<h4>Add Profile</h4>
<input type="text" id="new-profile-name" class="profile-input" placeholder="Profile name" maxlength="20">
<input type="url" id="new-profile-avatar-url" class="profile-input" placeholder="Avatar image URL (optional)">
<div class="profile-color-picker">
<span class="profile-color-swatch" data-color="#6366f1" style="background:#6366f1" title="Indigo"></span>
<span class="profile-color-swatch" data-color="#ec4899" style="background:#ec4899" title="Pink"></span>
<span class="profile-color-swatch" data-color="#10b981" style="background:#10b981" title="Green"></span>
<span class="profile-color-swatch" data-color="#f59e0b" style="background:#f59e0b" title="Amber"></span>
<span class="profile-color-swatch" data-color="#3b82f6" style="background:#3b82f6" title="Blue"></span>
<span class="profile-color-swatch" data-color="#ef4444" style="background:#ef4444" title="Red"></span>
<span class="profile-color-swatch" data-color="#8b5cf6" style="background:#8b5cf6" title="Purple"></span>
<span class="profile-color-swatch" data-color="#14b8a6" style="background:#14b8a6" title="Teal"></span>
</div>
<input type="password" id="new-profile-pin" class="profile-input" placeholder="PIN (optional)" maxlength="6">
<div class="profile-settings-section">
<label class="profile-settings-label">Home Page</label>
<select id="new-profile-home-page" class="profile-input">
<option value="">Default (Discover)</option>
<option value="dashboard">Dashboard</option>
<option value="sync">Sync</option>
<option value="downloads">Search</option>
<option value="discover">Discover</option>
<option value="artists">Artists</option>
<option value="automations">Automations</option>
<option value="library">Library</option>
<option value="import">Import</option>
</select>
<label class="profile-settings-label">Page Access</label>
<div id="new-profile-allowed-pages" class="profile-page-checkboxes">
<label><input type="checkbox" value="dashboard" checked> Dashboard</label>
<label><input type="checkbox" value="sync" checked> Sync</label>
<label><input type="checkbox" value="downloads" checked> Search</label>
<label><input type="checkbox" value="discover" checked> Discover</label>
<label><input type="checkbox" value="artists" checked> Artists</label>
<label><input type="checkbox" value="automations" checked> Automations</label>
<label><input type="checkbox" value="library" checked> Library</label>
<label><input type="checkbox" value="import" checked> Import</label>
<label><input type="checkbox" value="help" checked disabled> Help & Docs</label>
</div>
<label class="profile-checkbox-label">
<input type="checkbox" id="new-profile-can-download" checked> Can download music
</label>
</div>
<button id="create-profile-btn" class="profile-create-btn">Create Profile</button>
</div>
<div id="admin-pin-section" class="admin-pin-section" style="display: none;">
<h4>Admin PIN</h4>
<p class="admin-pin-note">Required when multiple profiles exist</p>
<input type="password" id="admin-pin-input" class="profile-input" placeholder="Set admin PIN" maxlength="6">
<button id="set-admin-pin-btn" class="profile-create-btn">Set Admin PIN</button>
</div>
</div>
</div>
</div>
<!-- Personal Settings Modal -->
<div id="personal-settings-overlay" class="personal-settings-overlay" style="display: none;" onclick="if(event.target===this)closePersonalSettings()">
<div class="personal-settings-container">
<div class="personal-settings-header">
<h3>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
My Settings
</h3>
<button class="personal-settings-close" onclick="closePersonalSettings()">&times;</button>
</div>
<div class="personal-settings-body" id="personal-settings-body">
<!-- Populated dynamically by JS -->
</div>
</div>
</div>
<div class="main-container">
<!-- Mobile Navigation -->
<button class="hamburger-btn" id="hamburger-btn" aria-label="Toggle navigation">
<span class="hamburger-line"></span>
<span class="hamburger-line"></span>
<span class="hamburger-line"></span>
</button>
<div class="mobile-overlay" id="mobile-overlay"></div>
<!-- Sidebar - Always Visible -->
<div class="sidebar">
<!-- Header Section -->
<div class="sidebar-header">
<div class="sidebar-brand">
<img src="/static/trans2.png" alt="SoulSync" class="sidebar-logo">
<div class="sidebar-brand-text">
<h1 class="app-name">SoulSync</h1>
<p class="app-subtitle">Music Sync & Manager</p>
</div>
</div>
<div id="profile-indicator" class="profile-indicator" style="display: none;" title="Switch profile">
<div id="profile-indicator-avatar" class="profile-indicator-avatar"></div>
<span id="profile-indicator-name" class="profile-indicator-name"></span>
<button id="personal-settings-btn" class="personal-settings-trigger" onclick="event.stopPropagation(); openPersonalSettings()" title="My Settings">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
</button>
</div>
</div>
<!-- Navigation Section -->
<nav class="sidebar-nav">
<button class="nav-button active" data-page="dashboard">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="4" rx="1"/><rect x="3" y="14" width="7" height="4" rx="1"/><rect x="14" y="11" width="7" height="7" rx="1"/><line x1="5" y1="20" x2="5" y2="22"/><line x1="8" y1="19" x2="8" y2="22"/></svg></span>
<span class="nav-text">Dashboard</span>
</button>
<button class="nav-button" data-page="sync">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/><line x1="4" y1="4" x2="9" y2="9"/></svg></span>
<span class="nav-text">Sync</span>
</button>
<button class="nav-button" data-page="downloads">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="11" y1="8" x2="11" y2="14"/><polyline points="8 11 11 14 14 11"/></svg></span>
<span class="nav-text">Search</span>
</button>
<button class="nav-button" data-page="discover">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76" fill="currentColor" opacity="0.2" stroke="currentColor"/></svg></span>
<span class="nav-text">Discover</span>
</button>
<button class="nav-button" data-page="playlist-explorer">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="5" r="3"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="12" x2="5" y2="18"/><line x1="12" y1="12" x2="19" y2="18"/><circle cx="5" cy="19" r="2"/><circle cx="19" cy="19" r="2"/><line x1="12" y1="12" x2="12" y2="18"/><circle cx="12" cy="19" r="2"/></svg></span>
<span class="nav-text">Explorer</span>
</button>
<button class="nav-button" data-page="artists">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg></span>
<span class="nav-text">Artists</span>
</button>
<button class="nav-button" data-page="automations">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg></span>
<span class="nav-text">Automations</span>
</button>
<button class="nav-button" data-page="library">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/><line x1="9" y1="7" x2="16" y2="7"/><line x1="9" y1="11" x2="14" y2="11"/></svg></span>
<span class="nav-text">Library</span>
</button>
<button class="nav-button" data-page="stats">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/></svg></span>
<span class="nav-text">Stats</span>
</button>
<button class="nav-button" data-page="import">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg></span>
<span class="nav-text">Import</span>
</button>
<button class="nav-button" data-page="settings">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span>
<span class="nav-text">Settings</span>
</button>
<button class="nav-button" data-page="issues">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></span>
<span class="nav-text">Issues</span>
<span class="issues-nav-badge hidden" id="issues-nav-badge">0</span>
</button>
<button class="nav-button" data-page="help">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></span>
<span class="nav-text">Help & Docs</span>
</button>
<button class="nav-button" data-page="hydrabase" id="hydrabase-nav" style="display: none;">
<span class="nav-icon"><svg class="nav-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg></span>
<span class="nav-text">Hydrabase</span>
</button>
</nav>
<!-- Spacer -->
<div class="sidebar-spacer"></div>
<!-- Media Player Section -->
<div class="media-player" id="media-player">
<!-- Progress bar at top of player -->
<div class="player-top-progress">
<div class="progress-bar-container">
<div class="progress-track">
<div class="progress-fill" id="progress-fill"></div>
</div>
<input type="range" class="progress-bar" id="progress-bar" min="0" max="100" value="0" step="0.1">
</div>
<div class="time-display">
<span class="current-time" id="current-time">0:00</span>
<span class="total-time" id="total-time">0:00</span>
</div>
</div>
<!-- Loading Animation -->
<div class="loading-animation hidden" id="loading-animation">
<div class="loading-bar">
<div class="loading-progress"></div>
</div>
<div class="loading-text">0%</div>
</div>
<!-- Track info row -->
<div class="media-header">
<img class="sidebar-album-art" id="sidebar-album-art" src="" alt="">
<div class="media-info">
<div class="track-title" id="track-title">No track</div>
<div class="artist-name" id="artist-name">Unknown Artist</div>
<div class="album-name header-album" id="album-name">Unknown Album</div>
</div>
</div>
<!-- Controls row (always visible) -->
<div class="media-controls-row">
<button class="play-button" id="play-button" disabled>
<svg class="play-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
<svg class="pause-icon" viewBox="0 0 24 24" fill="currentColor" style="display:none"><path d="M6 4h4v16H6zm8 0h4v16h-4z"/></svg>
</button>
<div class="volume-control">
<span class="volume-icon">
<svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.16c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.16c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg>
</span>
<input type="range" class="volume-slider" id="volume-slider" min="0" max="100" value="70">
</div>
<button class="stop-button" id="stop-button" disabled>
<svg viewBox="0 0 24 24" fill="currentColor" width="12" height="12"><rect x="6" y="6" width="12" height="12" rx="1.5"/></svg>
</button>
<button class="expand-hint" aria-label="Expand player">
<svg viewBox="0 0 16 16" fill="currentColor" width="12" height="12"><path d="M3 10l5 4 5-4" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
</div>
<!-- Expanded Content (kept for backward compat, hidden) -->
<div class="media-expanded hidden" id="media-expanded"></div>
<!-- No Track Message -->
<div class="no-track-message" id="no-track-message">
<svg class="no-track-icon" viewBox="0 0 48 48" fill="none" width="28" height="28">
<circle cx="24" cy="24" r="22" stroke="currentColor" stroke-width="1.5" opacity="0.25"/>
<path d="M20 16l12 8-12 8z" fill="currentColor" opacity="0.2"/>
</svg>
<span>Play a track to get started</span>
</div>
</div>
<!-- Support Button -->
<div class="support-section">
<button class="support-button" onclick="showSupportModal()">Support SoulSync</button>
</div>
<!-- Version Section -->
<div class="version-section">
<button class="version-button" onclick="showVersionInfo()">v2.1</button>
</div>
<!-- Status Section -->
<div class="status-section">
<h4 class="status-title">Service Status</h4>
<div class="status-indicator" id="spotify-indicator">
<span class="status-dot disconnected"></span>
<span class="status-name" id="music-source-name">Spotify</span>
</div>
<div class="status-indicator" id="media-server-indicator">
<span class="status-dot disconnected"></span>
<span class="status-name" id="media-server-name">Plex</span>
</div>
<div class="status-indicator" id="soulseek-indicator">
<span class="status-dot disconnected"></span>
<span class="status-name" id="download-source-name">Soulseek</span>
</div>
</div>
</div>
<!-- Sidebar Audio Visualizer (right edge, outside sidebar to avoid overflow clip) -->
<div class="sidebar-visualizer" id="sidebar-visualizer"></div>
<!-- Main Content Area -->
<div class="main-content">
<!-- Global particle canvas for page background animations -->
<canvas id="page-particles-canvas"></canvas>
<!-- Dashboard Page -->
<div class="page active" id="dashboard-page">
<div class="dashboard-container">
<div class="dashboard-header">
<div class="header-text">
<h2 class="header-title"><img src="/static/dashboard.png" class="page-header-icon" alt=""><span>System Dashboard</span></h2>
<p class="header-subtitle">Monitor your music system health and manage operations</p>
</div>
<div class="header-spacer"></div>
<div class="header-actions">
<!-- MusicBrainz Enrichment Status Icon -->
<div class="mb-button-container">
<button class="musicbrainz-button" id="musicbrainz-button"
title="MusicBrainz Library Enrichment">
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/MusicBrainz_Logo_%282016%29.svg/500px-MusicBrainz_Logo_%282016%29.svg.png"
alt="MusicBrainz" class="mb-logo">
<div class="mb-spinner"></div>
</button>
<!-- MusicBrainz Hover Tooltip -->
<div class="musicbrainz-tooltip" id="musicbrainz-tooltip">
<div class="tooltip-content">
<div class="tooltip-header">🎵 MusicBrainz Enrichment</div>
<div class="tooltip-body" id="mb-tooltip-body">
<div class="tooltip-status">Status: <span id="mb-tooltip-status">Idle</span>
</div>
<div class="tooltip-current" id="mb-tooltip-current">No active matches</div>
<div class="tooltip-progress" id="mb-tooltip-progress">Progress: 0 / 0</div>
</div>
</div>
</div>
</div>
<!-- AudioDB Enrichment Status Icon -->
<div class="audiodb-button-container">
<button class="audiodb-button" id="audiodb-button" title="AudioDB Artist Enrichment">
<img src='data:image/png;base64,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'
alt="AudioDB" class="audiodb-logo">
<div class="audiodb-spinner"></div>
</button>
<!-- AudioDB Hover Tooltip -->
<div class="audiodb-tooltip" id="audiodb-tooltip">
<div class="audiodb-tooltip-content">
<div class="audiodb-tooltip-header">🎶 AudioDB Enrichment</div>
<div class="audiodb-tooltip-body" id="audiodb-tooltip-body">
<div class="tooltip-status">Status: <span
id="audiodb-tooltip-status">Idle</span>
</div>
<div class="tooltip-current" id="audiodb-tooltip-current">No active matches
</div>
<div class="tooltip-progress" id="audiodb-tooltip-progress">Progress: 0 / 0
</div>
</div>
</div>
</div>
</div>
<!-- Deezer Enrichment Status Icon -->
<div class="deezer-button-container">
<button class="deezer-button" id="deezer-button" title="Deezer Library Enrichment">
<img src="https://cdn.brandfetch.io/idEUKgCNtu/theme/dark/symbol.svg?c=1bxid64Mup7aczewSAYMX&t=1758260798610"
alt="Deezer" class="deezer-logo">
<div class="deezer-spinner"></div>
</button>
<!-- Deezer Hover Tooltip -->
<div class="deezer-tooltip" id="deezer-tooltip">
<div class="deezer-tooltip-content">
<div class="deezer-tooltip-header">🎧 Deezer Enrichment</div>
<div class="deezer-tooltip-body" id="deezer-tooltip-body">
<div class="tooltip-status">Status: <span
id="deezer-tooltip-status">Idle</span>
</div>
<div class="tooltip-current" id="deezer-tooltip-current">No active matches
</div>
<div class="tooltip-progress" id="deezer-tooltip-progress">Progress: 0 / 0
</div>
</div>
</div>
</div>
</div>
<!-- Spotify Enrichment Status Icon -->
<div class="spotify-enrich-button-container">
<button class="spotify-enrich-button" id="spotify-enrich-button" title="Spotify Library Enrichment">
<img src="https://storage.googleapis.com/pr-newsroom-wp/1/2023/05/Spotify_Primary_Logo_RGB_Green.png"
alt="Spotify" class="spotify-enrich-logo">
<div class="spotify-enrich-spinner"></div>
</button>
<!-- Spotify Hover Tooltip -->
<div class="spotify-enrich-tooltip" id="spotify-enrich-tooltip">
<div class="spotify-enrich-tooltip-content">
<div class="spotify-enrich-tooltip-header">Spotify Enrichment</div>
<div class="spotify-enrich-tooltip-body" id="spotify-enrich-tooltip-body">
<div class="tooltip-status">Status: <span
id="spotify-enrich-tooltip-status">Idle</span>
</div>
<div class="tooltip-current" id="spotify-enrich-tooltip-current">No active matches
</div>
<div class="tooltip-progress" id="spotify-enrich-tooltip-progress">Progress: 0 / 0
</div>
</div>
</div>
</div>
</div>
<!-- iTunes Enrichment Status Icon -->
<div class="itunes-enrich-button-container">
<button class="itunes-enrich-button" id="itunes-enrich-button" title="iTunes Library Enrichment">
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/ITunes_logo.svg/960px-ITunes_logo.svg.png"
alt="iTunes" class="itunes-enrich-logo">
<div class="itunes-enrich-spinner"></div>
</button>
<!-- iTunes Hover Tooltip -->
<div class="itunes-enrich-tooltip" id="itunes-enrich-tooltip">
<div class="itunes-enrich-tooltip-content">
<div class="itunes-enrich-tooltip-header">iTunes Enrichment</div>
<div class="itunes-enrich-tooltip-body" id="itunes-enrich-tooltip-body">
<div class="tooltip-status">Status: <span
id="itunes-enrich-tooltip-status">Idle</span>
</div>
<div class="tooltip-current" id="itunes-enrich-tooltip-current">No active matches
</div>
<div class="tooltip-progress" id="itunes-enrich-tooltip-progress">Progress: 0 / 0
</div>
</div>
</div>
</div>
</div>
<!-- Last.fm Enrichment Status Icon -->
<div class="lastfm-enrich-button-container">
<button class="lastfm-enrich-button" id="lastfm-enrich-button" title="Last.fm Library Enrichment">
<img src="https://www.last.fm/static/images/lastfm_avatar_twitter.52a5d69a85ac.png"
alt="Last.fm" class="lastfm-enrich-logo">
<div class="lastfm-enrich-spinner"></div>
</button>
<div class="lastfm-enrich-tooltip" id="lastfm-enrich-tooltip">
<div class="lastfm-enrich-tooltip-content">
<div class="lastfm-enrich-tooltip-header">Last.fm Enrichment</div>
<div class="lastfm-enrich-tooltip-body" id="lastfm-enrich-tooltip-body">
<div class="tooltip-status">Status: <span
id="lastfm-enrich-tooltip-status">Idle</span>
</div>
<div class="tooltip-current" id="lastfm-enrich-tooltip-current">No active matches
</div>
<div class="tooltip-progress" id="lastfm-enrich-tooltip-progress">Progress: 0 / 0
</div>
</div>
</div>
</div>
</div>
<!-- Genius Enrichment Status Icon -->
<div class="genius-enrich-button-container">
<button class="genius-enrich-button" id="genius-enrich-button" title="Genius Library Enrichment">
<img src="https://images.genius.com/8ed669cadd956443e29c70361ec4f372.1000x1000x1.png"
alt="Genius" class="genius-enrich-logo">
<div class="genius-enrich-spinner"></div>
</button>
<div class="genius-enrich-tooltip" id="genius-enrich-tooltip">
<div class="genius-enrich-tooltip-content">
<div class="genius-enrich-tooltip-header">Genius Enrichment</div>
<div class="genius-enrich-tooltip-body" id="genius-enrich-tooltip-body">
<div class="tooltip-status">Status: <span
id="genius-enrich-tooltip-status">Idle</span>
</div>
<div class="tooltip-current" id="genius-enrich-tooltip-current">No active matches
</div>
<div class="tooltip-progress" id="genius-enrich-tooltip-progress">Progress: 0 / 0
</div>
</div>
</div>
</div>
</div>
<!-- Tidal Enrichment Status Icon -->
<div class="tidal-enrich-button-container">
<button class="tidal-enrich-button" id="tidal-enrich-button" title="Tidal Library Enrichment">
<img src="https://www.svgrepo.com/show/519734/tidal.svg"
alt="Tidal" class="tidal-enrich-logo">
<div class="tidal-enrich-spinner"></div>
</button>
<div class="tidal-enrich-tooltip" id="tidal-enrich-tooltip">
<div class="tidal-enrich-tooltip-content">
<div class="tidal-enrich-tooltip-header">Tidal Enrichment</div>
<div class="tidal-enrich-tooltip-body" id="tidal-enrich-tooltip-body">
<div class="tooltip-status">Status: <span
id="tidal-enrich-tooltip-status">Idle</span>
</div>
<div class="tooltip-current" id="tidal-enrich-tooltip-current">No active matches
</div>
<div class="tooltip-progress" id="tidal-enrich-tooltip-progress">Progress: 0 / 0
</div>
</div>
</div>
</div>
</div>
<!-- Qobuz Enrichment Status Icon -->
<div class="qobuz-enrich-button-container">
<button class="qobuz-enrich-button" id="qobuz-enrich-button" title="Qobuz Library Enrichment">
<img src="https://www.svgrepo.com/show/504778/qobuz.svg"
alt="Qobuz" class="qobuz-enrich-logo">
<div class="qobuz-enrich-spinner"></div>
</button>
<div class="qobuz-enrich-tooltip" id="qobuz-enrich-tooltip">
<div class="qobuz-enrich-tooltip-content">
<div class="qobuz-enrich-tooltip-header">Qobuz Enrichment</div>
<div class="qobuz-enrich-tooltip-body" id="qobuz-enrich-tooltip-body">
<div class="tooltip-status">Status: <span
id="qobuz-enrich-tooltip-status">Idle</span>
</div>
<div class="tooltip-current" id="qobuz-enrich-tooltip-current">No active matches
</div>
<div class="tooltip-progress" id="qobuz-enrich-tooltip-progress">Progress: 0 / 0
</div>
</div>
</div>
</div>
</div>
<!-- Hydrabase P2P Mirror Status Icon -->
<div class="hydrabase-button-container" id="hydrabase-button-container" style="display: none;">
<button class="hydrabase-button" id="hydrabase-button" title="Hydrabase P2P Mirror">
<img src="/static/hydrabase.png" alt="Hydrabase" class="hydrabase-worker-logo">
<div class="hydrabase-spinner"></div>
</button>
<!-- Hydrabase Worker Tooltip -->
<div class="hydrabase-tooltip" id="hydrabase-tooltip">
<div class="hydrabase-tooltip-content">
<div class="hydrabase-tooltip-header">Hydrabase P2P Mirror</div>
<div class="hydrabase-tooltip-body" id="hydrabase-tooltip-body">
<div class="tooltip-status">Status: <span id="hydrabase-tooltip-status">Active</span></div>
</div>
</div>
</div>
</div>
<!-- Library Repair Worker Status Icon -->
<div class="repair-button-container">
<button class="repair-button" id="repair-button" title="Library Maintenance">
<img src="/static/whisoul.png" alt="Repair" class="repair-logo">
<div class="repair-spinner"></div>
<span class="repair-badge" id="repair-findings-badge" style="display:none">0</span>
</button>
<!-- Repair Worker Tooltip -->
<div class="repair-tooltip" id="repair-tooltip">
<div class="repair-tooltip-content">
<div class="repair-tooltip-header">🔧 Library Repair</div>
<div class="repair-tooltip-body" id="repair-tooltip-body">
<div class="tooltip-status">Status: <span
id="repair-tooltip-status">Idle</span>
</div>
<div class="tooltip-current" id="repair-tooltip-current">No active repairs
</div>
<div class="tooltip-progress" id="repair-tooltip-progress">Progress: 0 / 0
</div>
</div>
</div>
</div>
</div>
<!-- SoulID Worker Status Icon -->
<div class="soulid-button-container">
<button class="soulid-button" id="soulid-button" title="SoulID Generator">
<img src="/static/trans2.png" alt="SoulID" class="soulid-logo">
<div class="soulid-spinner"></div>
</button>
<div class="soulid-tooltip" id="soulid-tooltip">
<div class="soulid-tooltip-content">
<div class="soulid-tooltip-header">SoulID Generator</div>
<div class="soulid-tooltip-body" id="soulid-tooltip-body">
<div class="tooltip-status">Status: <span
id="soulid-tooltip-status">Idle</span>
</div>
<div class="tooltip-current" id="soulid-tooltip-current">No items processing
</div>
<div class="tooltip-progress" id="soulid-tooltip-progress">Progress: 0 pending
</div>
</div>
</div>
</div>
</div>
<button class="import-button" id="import-button" onclick="navigateToPage('import')"
title="Import Music from Staging">
<img src="https://cdn-icons-png.flaticon.com/512/8765/8765164.png" alt="Import"
class="import-logo">
</button>
<button class="header-button watchlist-button" id="watchlist-button">
<span class="hero-btn-icon">👁️</span>
<span class="hero-btn-label">Watchlist</span>
<span class="hero-btn-badge" id="watchlist-badge">0</span>
<span class="hero-btn-shimmer"></span>
</button>
<button class="header-button wishlist-button" id="wishlist-button">
<span class="hero-btn-icon">🎵</span>
<span class="hero-btn-label">Wishlist</span>
<span class="hero-btn-badge" id="wishlist-badge">0</span>
<span class="hero-btn-shimmer"></span>
</button>
</div>
</div>
<div class="dashboard-section">
<h3 class="section-title">Service Status</h3>
<div class="service-status-grid">
<div class="service-card" id="spotify-service-card">
<div class="service-card-header">
<span class="service-card-title" id="music-source-title">Spotify</span>
<span class="service-card-indicator disconnected"
id="spotify-status-indicator"></span>
</div>
<p class="service-card-status-text" id="spotify-status-text">Disconnected</p>
<p class="service-card-response-time" id="spotify-response-time">Response: --</p>
<div class="service-card-footer">
<button class="service-card-button"
onclick="testDashboardConnection('spotify')">Test Connection</button>
</div>
</div>
<div class="service-card" id="media-server-service-card">
<div class="service-card-header">
<span class="service-card-title" id="media-server-service-name">Server</span>
<span class="service-card-indicator disconnected"
id="media-server-status-indicator"></span>
</div>
<p class="service-card-status-text" id="media-server-status-text">Disconnected</p>
<p class="service-card-response-time" id="media-server-response-time">Response: --</p>
<div class="service-card-footer">
<button class="service-card-button" onclick="testDashboardConnection('server')">Test
Connection</button>
</div>
</div>
<div class="service-card" id="soulseek-service-card">
<div class="service-card-header">
<span class="service-card-title" id="download-source-title">Soulseek</span>
<span class="service-card-indicator disconnected"
id="soulseek-status-indicator"></span>
</div>
<p class="service-card-status-text" id="soulseek-status-text">Disconnected</p>
<p class="service-card-response-time" id="soulseek-response-time">Response: --</p>
<div class="service-card-footer">
<button class="service-card-button"
onclick="testDashboardConnection('soulseek')">Test Connection</button>
</div>
</div>
</div>
<div class="enrichment-section">
<div class="enrichment-section-header">
<span class="enrichment-section-label">Enrichment Services</span>
</div>
<div class="enrichment-status-grid" id="enrichment-status-grid">
<!-- Dynamically populated by JS -->
</div>
</div>
</div>
<!-- Recent Syncs Section -->
<div class="dashboard-section">
<h3 class="section-title">Recent Syncs</h3>
<div class="sync-history-cards" id="sync-history-cards">
<!-- Dynamically populated by JS -->
</div>
</div>
<div class="dashboard-section">
<h3 class="section-title">System Statistics</h3>
<div class="stats-grid-dashboard">
<div class="stat-card-dashboard" id="active-downloads-card">
<p class="stat-card-title">Active Downloads</p>
<p class="stat-card-value">0</p>
<p class="stat-card-subtitle">Currently downloading</p>
</div>
<div class="stat-card-dashboard" id="finished-downloads-card">
<p class="stat-card-title">Finished Downloads</p>
<p class="stat-card-value">0</p>
<p class="stat-card-subtitle">Completed this session</p>
</div>
<div class="stat-card-dashboard" id="download-speed-card">
<p class="stat-card-title">Download Speed</p>
<p class="stat-card-value">0 KB/s</p>
<p class="stat-card-subtitle">Combined speed</p>
</div>
<div class="stat-card-dashboard" id="active-syncs-card">
<p class="stat-card-title">Active Syncs</p>
<p class="stat-card-value">0</p>
<p class="stat-card-subtitle">Playlists syncing</p>
</div>
<div class="stat-card-dashboard" id="uptime-card">
<p class="stat-card-title">System Uptime</p>
<p class="stat-card-value">0m</p>
<p class="stat-card-subtitle">Application runtime</p>
</div>
<div class="stat-card-dashboard" id="memory-card">
<p class="stat-card-title">Memory Usage</p>
<p class="stat-card-value">--</p>
<p class="stat-card-subtitle">Current usage</p>
</div>
</div>
</div>
<div class="dashboard-section" id="dashboard-active-downloads-section" style="display: none;">
<h3 class="section-title">Active Downloads</h3>
<div id="dashboard-downloads-container"></div>
</div>
<div class="dashboard-section">
<h3 class="section-title">Tools & Operations</h3>
<div class="tools-grid">
<div class="tool-card" id="db-updater-card">
<div class="tool-card-header">
<h4 class="tool-card-title">Database Updater</h4>
<button class="tool-help-button" data-tool="db-updater"
title="Learn more about this tool">?</button>
</div>
<p class="tool-card-info">Last Full Refresh: <span id="db-last-refresh">Never</span></p>
<div class="tool-card-stats">
<div class="stat-item">
<span class="stat-item-label">Artists:</span>
<span class="stat-item-value" id="db-stat-artists">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Albums:</span>
<span class="stat-item-value" id="db-stat-albums">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Tracks:</span>
<span class="stat-item-value" id="db-stat-tracks">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Size:</span>
<span class="stat-item-value" id="db-stat-size">0.0 MB</span>
</div>
</div>
<div class="tool-card-controls">
<select id="db-refresh-type">
<option value="incremental">Incremental Update</option>
<option value="full">Full Refresh</option>
</select>
<button id="db-update-button">Update Database</button>
</div>
<div class="tool-card-progress-section">
<p class="progress-phase-label" id="db-phase-label">Idle</p>
<div class="progress-bar-container">
<div class="progress-bar-fill" id="db-progress-bar" style="width: 0%;"></div>
</div>
<p class="progress-details-label" id="db-progress-label">0 / 0 artists (0.0%)</p>
</div>
</div>
<div class="tool-card" id="metadata-updater-card">
<div class="tool-card-header">
<h4 class="tool-card-title">Metadata Updater</h4>
<button class="tool-help-button" data-tool="metadata-updater"
title="Learn more about this tool">?</button>
</div>
<p class="metadata-updater-description tool-card-info">Updates artist photos, genres,
and album art from Spotify.</p>
<div class="tool-card-controls">
<select id="metadata-refresh-interval">
<option value="180">6 months</option>
<option value="90">3 months</option>
<option value="30" selected>1 month</option>
<option value="14">2 weeks</option>
<option value="7">1 week</option>
<option value="0">Full refresh</option>
</select>
<button id="metadata-update-button">Begin Update</button>
</div>
<div class="tool-card-progress-section">
<p class="progress-phase-label" id="metadata-phase-label">Current Artist: Not
running</p>
<div class="progress-bar-container">
<div class="progress-bar-fill" id="metadata-progress-bar" style="width: 0%;">
</div>
</div>
<p class="progress-details-label" id="metadata-progress-label">0 / 0 artists (0.0%)
</p>
</div>
</div>
<div class="tool-card" id="quality-scanner-card">
<div class="tool-card-header">
<h4 class="tool-card-title">Quality Scanner</h4>
<button class="tool-help-button" data-tool="quality-scanner"
title="Learn more about this tool">?</button>
</div>
<p class="tool-card-info">Scan library for tracks below quality preferences</p>
<div class="tool-card-stats">
<div class="stat-item">
<span class="stat-item-label">Processed:</span>
<span class="stat-item-value" id="quality-stat-processed">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Quality Met:</span>
<span class="stat-item-value" id="quality-stat-met">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Low Quality:</span>
<span class="stat-item-value" id="quality-stat-low">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Matched:</span>
<span class="stat-item-value" id="quality-stat-matched">0</span>
</div>
</div>
<div class="tool-card-controls">
<select id="quality-scan-scope">
<option value="watchlist">Watchlist Artists Only</option>
<option value="all">All Library Tracks</option>
</select>
<button id="quality-scan-button">Scan Library</button>
</div>
<div class="tool-card-progress-section">
<p class="progress-phase-label" id="quality-phase-label">Ready to scan</p>
<div class="progress-bar-container">
<div class="progress-bar-fill" id="quality-progress-bar" style="width: 0%;">
</div>
</div>
<p class="progress-details-label" id="quality-progress-label">0 / 0 tracks scanned
(0.0%)</p>
</div>
</div>
<div class="tool-card" id="duplicate-cleaner-card">
<div class="tool-card-header">
<h4 class="tool-card-title">Duplicate Cleaner</h4>
<button class="tool-help-button" data-tool="duplicate-cleaner"
title="Learn more about this tool">?</button>
</div>
<p class="tool-card-info">Detect and remove duplicate tracks in Transfer folder</p>
<div class="tool-card-stats">
<div class="stat-item">
<span class="stat-item-label">Files Scanned:</span>
<span class="stat-item-value" id="duplicate-stat-scanned">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Duplicates Found:</span>
<span class="stat-item-value" id="duplicate-stat-found">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Deleted:</span>
<span class="stat-item-value" id="duplicate-stat-deleted">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Space Freed:</span>
<span class="stat-item-value" id="duplicate-stat-space">0 MB</span>
</div>
</div>
<div class="tool-card-controls">
<button id="duplicate-clean-button">Clean Duplicates</button>
</div>
<div class="tool-card-progress-section">
<p class="progress-phase-label" id="duplicate-phase-label">Ready to scan</p>
<div class="progress-bar-container">
<div class="progress-bar-fill" id="duplicate-progress-bar" style="width: 0%;">
</div>
</div>
<p class="progress-details-label" id="duplicate-progress-label">0 files scanned
(0.0%)</p>
</div>
</div>
<div class="tool-card" id="discovery-pool-card">
<div class="tool-card-header">
<h4 class="tool-card-title">Discovery Pool</h4>
</div>
<p class="tool-card-info">View and fix matched/failed discovery results across all mirrored playlists</p>
<div class="tool-card-stats" id="discovery-pool-stats">
<div class="stat-item">
<span class="stat-item-label">Matched:</span>
<span class="stat-item-value" id="discovery-pool-matched-count">&mdash;</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Failed:</span>
<span class="stat-item-value" id="discovery-pool-failed-count" style="background-color: rgba(239, 68, 68, 0.15); color: #ef4444;">&mdash;</span>
</div>
</div>
<div class="tool-card-controls">
<button onclick="openDiscoveryPoolModal()">Open Discovery Pool</button>
</div>
</div>
<div class="tool-card" id="retag-tool-card">
<div class="tool-card-header">
<h4 class="tool-card-title">Retag Tool</h4>
<button class="tool-help-button" data-tool="retag-tool"
title="Learn more about this tool">?</button>
</div>
<p class="tool-card-info">Fix metadata on previously downloaded albums &amp; singles</p>
<div class="tool-card-stats">
<div class="stat-item">
<span class="stat-item-label">Groups:</span>
<span class="stat-item-value" id="retag-stat-groups">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Tracks:</span>
<span class="stat-item-value" id="retag-stat-tracks">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Artists:</span>
<span class="stat-item-value" id="retag-stat-artists">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Status:</span>
<span class="stat-item-value" id="retag-stat-status">Idle</span>
</div>
</div>
<div class="tool-card-controls">
<button id="retag-open-button">Open Retag Tool</button>
</div>
<div class="tool-card-progress-section">
<p class="progress-phase-label" id="retag-phase-label">Ready</p>
<div class="progress-bar-container">
<div class="progress-bar-fill" id="retag-progress-bar" style="width: 0%;">
</div>
</div>
<p class="progress-details-label" id="retag-progress-label">0 / 0 tracks (0.0%)</p>
</div>
</div>
<div class="tool-card" id="media-scan-card" style="display: none;">
<div class="tool-card-header">
<h4 class="tool-card-title">Media Server Scan</h4>
<button class="tool-help-button" data-tool="media-scan"
title="Learn more about this tool">?</button>
</div>
<p class="tool-card-info">Manually trigger Plex media library scan for music</p>
<div class="tool-card-stats">
<div class="stat-item">
<span class="stat-item-label">Last Scan:</span>
<span class="stat-item-value" id="media-scan-last-time">Never</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Status:</span>
<span class="stat-item-value" id="media-scan-status">Idle</span>
</div>
</div>
<div class="tool-card-controls">
<button id="media-scan-button" class="media-scan-btn">
<span class="scan-icon">📡</span>
<span class="scan-text">Scan Library</span>
</button>
</div>
<div class="tool-card-progress-section">
<p class="progress-phase-label" id="media-scan-phase-label">Ready to scan</p>
<div class="progress-bar-container">
<div class="progress-bar-fill" id="media-scan-progress-bar" style="width: 0%;">
</div>
</div>
<p class="progress-details-label" id="media-scan-progress-label">Waiting for scan
request</p>
</div>
</div>
<div class="tool-card" id="backup-manager-card">
<div class="tool-card-header">
<h4 class="tool-card-title">Backup Manager</h4>
<button class="tool-help-button" data-tool="backup-manager"
title="Learn more about this tool">?</button>
</div>
<p class="tool-card-info">Create, download, restore and manage database backups</p>
<div class="tool-card-stats">
<div class="stat-item">
<span class="stat-item-label">Last Backup:</span>
<span class="stat-item-value" id="backup-stat-last">Never</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Backups:</span>
<span class="stat-item-value" id="backup-stat-count">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Latest Size:</span>
<span class="stat-item-value" id="backup-stat-latest-size"></span>
</div>
<div class="stat-item">
<span class="stat-item-label">DB Size:</span>
<span class="stat-item-value" id="backup-stat-db-size"></span>
</div>
</div>
<div class="tool-card-controls">
<button id="backup-now-button">Backup Now</button>
</div>
<div id="backup-list-container" class="backup-list-container"></div>
</div>
<!-- Metadata Cache Tool Card -->
<div class="tool-card" id="metadata-cache-card">
<div class="tool-card-header">
<h4 class="tool-card-title">Metadata Cache</h4>
<button class="tool-help-button" data-tool="metadata-cache"
title="Learn more about this tool">?</button>
</div>
<p class="tool-card-info">Cached API responses from Spotify &amp; iTunes</p>
<div class="tool-card-stats">
<div class="stat-item">
<span class="stat-item-label">Artists:</span>
<span class="stat-item-value" id="mcache-stat-artists">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Albums:</span>
<span class="stat-item-value" id="mcache-stat-albums">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Tracks:</span>
<span class="stat-item-value" id="mcache-stat-tracks">0</span>
</div>
<div class="stat-item">
<span class="stat-item-label">Hits:</span>
<span class="stat-item-value" id="mcache-stat-hits">0</span>
</div>
</div>
<div class="tool-card-controls">
<button id="mcache-browse-button" onclick="openMetadataCacheModal()">Browse Cache</button>
<button onclick="openCacheHealthModal()" class="tool-card-btn-secondary">Cache Health</button>
</div>
</div>
</div>
</div>
<div class="dashboard-section">
<div class="section-title-row">
<h3 class="section-title">Recent Activity</h3>
<button class="library-history-btn" onclick="openLibraryHistoryModal()" title="View full library history">History</button>
</div>
<div class="activity-feed-container" id="dashboard-activity-feed">
<div class="activity-item">
<span class="activity-icon">📊</span>
<div class="activity-text-content">
<p class="activity-title">System Started</p>
<p class="activity-subtitle">Dashboard initialized successfully</p>
</div>
<p class="activity-time">Now</p>
</div>
</div>
</div>
</div>
</div>
<!-- Main container for the Sync page -->
<div class="page" id="sync-page">
<!-- Header -->
<div class="sync-header">
<div class="sync-header-row">
<div>
<h2 class="sync-title"><img src="/static/sync.png" class="page-header-icon" alt=""><span>Playlist Sync</span></h2>
<p class="sync-subtitle">Synchronize your Spotify, Tidal, and YouTube playlists with your media
server</p>
</div>
<button class="sync-history-btn" onclick="openSyncHistoryModal()" title="View sync history">Sync History</button>
</div>
</div>
<!-- Main two-column content area -->
<div class="sync-content-area">
<!-- Left Panel: Tabbed Playlist Section -->
<div class="sync-main-panel">
<div class="sync-tabs">
<button class="sync-tab-button sync-tab-server active" data-tab="server">
<span class="tab-icon server-icon"></span> Server Playlists
</button>
<div class="sync-tab-divider"></div>
<button class="sync-tab-button" data-tab="spotify">
<span class="tab-icon spotify-icon"></span> Spotify
</button>
<button class="sync-tab-button" data-tab="spotify-public">
<span class="tab-icon spotify-icon"></span> Spotify Link
</button>
<button class="sync-tab-button" data-tab="tidal">
<span class="tab-icon tidal-icon"></span> Tidal
</button>
<button class="sync-tab-button" data-tab="deezer">
<span class="tab-icon deezer-icon"></span> Deezer
</button>
<button class="sync-tab-button" data-tab="youtube">
<span class="tab-icon youtube-icon"></span> YouTube
</button>
<button class="sync-tab-button" data-tab="beatport">
<span class="tab-icon beatport-icon"></span> Beatport
</button>
<button class="sync-tab-button" data-tab="import-file">
<span class="tab-icon import-file-icon"></span> Import
</button>
<button class="sync-tab-button" data-tab="mirrored">
<span class="tab-icon mirrored-icon"></span> Mirrored
</button>
</div>
<!-- Spotify Tab Content -->
<div class="sync-tab-content" id="spotify-tab-content">
<div class="playlist-header">
<h3>Your Spotify Playlists</h3>
<button class="refresh-button" id="spotify-refresh-btn">🔄 Refresh</button>
</div>
<div class="playlist-scroll-container" id="spotify-playlist-container">
<div class="playlist-placeholder">Click 'Refresh' to load your Spotify playlists.</div>
</div>
</div>
<!-- Tidal Tab Content -->
<div class="sync-tab-content" id="tidal-tab-content">
<div class="playlist-header">
<h3>Your Tidal Playlists</h3>
<button class="refresh-button tidal" id="tidal-refresh-btn">🔄 Refresh</button>
</div>
<div class="playlist-scroll-container" id="tidal-playlist-container">
<div class="playlist-placeholder">Click 'Refresh' to load your Tidal playlists.</div>
</div>
</div>
<!-- Deezer Tab Content -->
<div class="sync-tab-content" id="deezer-tab-content">
<div class="youtube-input-section">
<input type="text" id="deezer-url-input"
placeholder="Paste Deezer Playlist URL...">
<button id="deezer-parse-btn">Load Playlist</button>
</div>
<div class="url-history-bar" id="deezer-url-history" style="display:none"></div>
<div class="playlist-scroll-container" id="deezer-playlist-container">
<div class="playlist-placeholder">Paste a Deezer playlist URL above to get started.</div>
</div>
</div>
<!-- YouTube Tab Content -->
<div class="sync-tab-content" id="youtube-tab-content">
<div class="youtube-input-section">
<input type="text" id="youtube-url-input"
placeholder="Paste YouTube Music Playlist URL...">
<button id="youtube-parse-btn">Parse Playlist</button>
</div>
<div class="url-history-bar" id="youtube-url-history" style="display:none"></div>
<div class="playlist-scroll-container" id="youtube-playlist-container">
<div class="playlist-placeholder">Parsed YouTube playlists will appear here.</div>
</div>
</div>
<!-- Spotify Public Link Tab Content -->
<div class="sync-tab-content" id="spotify-public-tab-content">
<div class="youtube-input-section">
<input type="text" id="spotify-public-url-input"
placeholder="Paste Spotify Playlist or Album URL...">
<button id="spotify-public-parse-btn">Load</button>
</div>
<div class="url-history-bar" id="spotify-public-url-history" style="display:none"></div>
<div class="playlist-scroll-container" id="spotify-public-playlist-container">
<div class="playlist-placeholder">Paste a Spotify playlist or album URL above to load tracks without needing Spotify API credentials.</div>
</div>
</div>
<!-- Beatport Tab Content -->
<div class="sync-tab-content" id="beatport-tab-content">
<!-- Beatport Nested Tabs (hidden — Browse is the only view) -->
<div class="beatport-tabs" style="display: none;">
<button class="beatport-tab-button active" data-beatport-tab="rebuild">
<span class="tab-icon rebuild-icon"></span> Browse
</button>
<button class="beatport-tab-button" data-beatport-tab="browse">
<span class="tab-icon browse-icon"></span> Browse Charts
</button>
<button class="beatport-tab-button" data-beatport-tab="playlists">
<span class="tab-icon playlist-icon"></span> My Playlists
</button>
</div>
<!-- Browse Charts Tab Content -->
<div class="beatport-tab-content" id="beatport-browse-content">
<div class="beatport-navigation">
<!-- New Homepage Main View -->
<div class="beatport-main-view active" id="beatport-main-view">
<div class="beatport-hero">
<div class="beatport-hero-bg"></div>
<div class="beatport-hero-content">
<h2>Browse Beatport Charts</h2>
<p>Explore top electronic music charts and discover new tracks</p>
<div class="beatport-stats">
<span class="stat-item">39 Genres</span>
<span class="stat-divider"></span>
<span class="stat-item">Top 100</span>
<span class="stat-divider"></span>
<span class="stat-item">Daily Updates</span>
</div>
</div>
</div>
<!-- Genre Explorer Section -->
<div class="homepage-genre-section">
<h3 class="section-title">🎵 Genre Explorer</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-action="show-genres">
<div class="chart-type-icon">🎵</div>
<div class="chart-type-info">
<h3>Browse All Genres</h3>
<p>House, Techno, Trance, and 36 more genres</p>
<span class="track-count">39 Genres</span>
</div>
</div>
</div>
</div>
<!-- Main Charts Section -->
<div class="homepage-main-charts-section">
<h3 class="section-title">📊 Main Charts</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-chart-type="top-10"
data-chart-endpoint="/api/beatport/top-100">
<div class="chart-type-icon">🔥</div>
<div class="chart-type-info">
<h3>Beatport Top 10</h3>
<p>Current hottest tracks</p>
<span class="track-count">10 tracks</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="top-100"
data-chart-endpoint="/api/beatport/top-100">
<div class="chart-type-icon">💯</div>
<div class="chart-type-info">
<h3>Beatport Top 100</h3>
<p>Complete chart rankings</p>
<span class="track-count">100 tracks</span>
</div>
</div>
</div>
</div>
<!-- Releases Section -->
<div class="homepage-releases-section">
<h3 class="section-title">🎵 Releases</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-chart-type="releases-top-10"
data-chart-endpoint="/api/beatport/homepage/top-10-releases">
<div class="chart-type-icon">🆕</div>
<div class="chart-type-info">
<h3>Top 10 Releases</h3>
<p>Newest releases trending</p>
<span class="track-count">10 releases</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="releases-top-100"
data-chart-endpoint="/api/beatport/top-100-releases">
<div class="chart-type-icon">📊</div>
<div class="chart-type-info">
<h3>Top 100 Releases</h3>
<p>All trending releases</p>
<span class="track-count">100 releases</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="latest-releases"
data-chart-endpoint="/api/beatport/homepage/new-releases">
<div class="chart-type-icon">🕒</div>
<div class="chart-type-info">
<h3>Latest Releases</h3>
<p>Recently published</p>
<span class="track-count">50 releases</span>
</div>
</div>
</div>
</div>
<!-- Hype Section -->
<div class="homepage-hype-section">
<h3 class="section-title">🔥 Hype</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-chart-type="hype-top-10"
data-chart-endpoint="/api/beatport/hype-top-100">
<div class="chart-type-icon">🚀</div>
<div class="chart-type-info">
<h3>Hype Top 10</h3>
<p>Hottest trending tracks</p>
<span class="track-count">10 tracks</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="hype-top-100"
data-chart-endpoint="/api/beatport/hype-top-100">
<div class="chart-type-icon">🔥</div>
<div class="chart-type-info">
<h3>Hype Top 100</h3>
<p>Complete hype chart rankings</p>
<span class="track-count">100 tracks</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="hype-picks"
data-chart-endpoint="/api/beatport/homepage/hype-picks">
<div class="chart-type-icon"></div>
<div class="chart-type-info">
<h3>Hype Picks</h3>
<p>Editor selected hype tracks</p>
<span class="track-count">50 tracks</span>
</div>
</div>
</div>
</div>
<!-- DJ Charts Section -->
<div class="homepage-dj-charts-section">
<h3 class="section-title">🎧 DJ Charts Collection</h3>
<p class="section-description">DJ curated chart collections</p>
<div class="charts-loading-inline" id="dj-charts-loading-inline">
<div class="loading-spinner-small"></div>
<p>Loading DJ chart collections...</p>
</div>
<div class="dj-charts-grid" id="dj-charts-grid">
<!-- Charts will be populated here -->
</div>
</div>
<!-- Featured Charts Section -->
<div class="homepage-featured-charts-section">
<h3 class="section-title">⭐ Featured Charts Collection</h3>
<p class="section-description">Editor curated chart collections</p>
<div class="charts-loading-inline" id="featured-charts-loading-inline">
<div class="loading-spinner-small"></div>
<p>Loading featured chart collections...</p>
</div>
<div class="featured-charts-grid" id="featured-charts-grid">
<!-- Charts will be populated here -->
</div>
</div>
</div>
<!-- Genre Explorer Sub-View -->
<div class="beatport-sub-view" id="beatport-genres-view">
<div class="beatport-breadcrumb">
<button class="breadcrumb-back">← Back to Categories</button>
<span class="breadcrumb-path">Browse Charts > Genre Explorer</span>
</div>
<div class="beatport-genre-grid">
<div class="beatport-genre-item" data-genre-slug="house" data-genre-id="5">
<div class="genre-icon">🏠</div>
<h3>House</h3>
<span class="genre-track-count">Top 100</span>
</div>
<div class="beatport-genre-item" data-genre-slug="tech-house"
data-genre-id="11">
<div class="genre-icon">🔧</div>
<h3>Tech House</h3>
<span class="genre-track-count">Top 100</span>
</div>
<div class="beatport-genre-item" data-genre-slug="techno" data-genre-id="6">
<div class="genre-icon"></div>
<h3>Techno</h3>
<span class="genre-track-count">Top 100</span>
</div>
<div class="beatport-genre-item" data-genre-slug="deep-house"
data-genre-id="12">
<div class="genre-icon">🌊</div>
<h3>Deep House</h3>
<span class="genre-track-count">Top 100</span>
</div>
<div class="beatport-genre-item" data-genre-slug="trance" data-genre-id="7">
<div class="genre-icon">🌀</div>
<h3>Trance</h3>
<span class="genre-track-count">Top 100</span>
</div>
<div class="beatport-genre-item" data-genre-slug="drum-and-bass"
data-genre-id="1">
<div class="genre-icon">🥁</div>
<h3>Drum & Bass</h3>
<span class="genre-track-count">Top 100</span>
</div>
<div class="beatport-genre-item" data-genre-slug="dubstep"
data-genre-id="18">
<div class="genre-icon">🎵</div>
<h3>Dubstep</h3>
<span class="genre-track-count">Top 100</span>
</div>
<div class="beatport-genre-item" data-genre-slug="progressive-house"
data-genre-id="15">
<div class="genre-icon">📈</div>
<h3>Progressive House</h3>
<span class="genre-track-count">Top 100</span>
</div>
<div class="beatport-genre-item" data-genre-slug="melodic-house-and-techno"
data-genre-id="90">
<div class="genre-icon">🎼</div>
<h3>Melodic House & Techno</h3>
<span class="genre-track-count">Top 100</span>
</div>
<div class="beatport-genre-item" data-genre-slug="afro-house"
data-genre-id="89">
<div class="genre-icon">🌍</div>
<h3>Afro House</h3>
<span class="genre-track-count">Top 100</span>
</div>
<div class="beatport-genre-item" data-genre-slug="minimal"
data-genre-id="14">
<div class="genre-icon"></div>
<h3>Minimal</h3>
<span class="genre-track-count">Top 100</span>
</div>
<div class="beatport-genre-item" data-genre-slug="nu-disco"
data-genre-id="50">
<div class="genre-icon"></div>
<h3>Nu Disco</h3>
<span class="genre-track-count">Top 100</span>
</div>
</div>
</div>
<!-- Genre Detail Sub-View -->
<div class="beatport-sub-view" id="beatport-genre-detail-view">
<div class="beatport-breadcrumb">
<button class="breadcrumb-back" id="genre-detail-back">← Back to Genre
Explorer</button>
<span class="breadcrumb-path" id="genre-detail-breadcrumb">Browse Charts >
Genre Explorer > Loading...</span>
</div>
<div class="genre-detail-header">
<div class="genre-detail-info">
<h2 id="genre-detail-title">Loading Genre...</h2>
<p id="genre-detail-description">Explore all chart types for this genre
</p>
</div>
</div>
<!-- Main Chart Types Section -->
<div class="genre-main-charts-section">
<h3 class="section-title">📊 Main Charts</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-chart-type="top-10">
<div class="chart-type-icon">🔥</div>
<div class="chart-type-info">
<h3 id="genre-top-10-title">Top 10</h3>
<p>Current hottest tracks</p>
<span class="track-count">10 tracks</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="top-100">
<div class="chart-type-icon">💯</div>
<div class="chart-type-info">
<h3 id="genre-top-100-title">Top 100</h3>
<p>Complete chart rankings</p>
<span class="track-count">100 tracks</span>
</div>
</div>
</div>
</div>
<!-- Releases Section -->
<div class="genre-releases-section">
<h3 class="section-title">🎵 Releases</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-chart-type="releases-top-10">
<div class="chart-type-icon">🆕</div>
<div class="chart-type-info">
<h3 id="genre-releases-top-10-title">Top 10 Releases</h3>
<p>Newest releases trending</p>
<span class="track-count">10 releases</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="releases-top-100">
<div class="chart-type-icon">📊</div>
<div class="chart-type-info">
<h3 id="genre-releases-top-100-title">Top 100 Releases</h3>
<p>All trending releases</p>
<span class="track-count">100 releases</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="latest-releases">
<div class="chart-type-icon">🕒</div>
<div class="chart-type-info">
<h3 id="genre-latest-releases-title">Latest Releases</h3>
<p>Recently published</p>
<span class="track-count">50 releases</span>
</div>
</div>
</div>
</div>
<!-- Editorial Section -->
<div class="genre-editorial-section">
<h3 class="section-title">⭐ Editorial</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-chart-type="staff-picks">
<div class="chart-type-icon"></div>
<div class="chart-type-info">
<h3 id="genre-staff-picks-title">Staff Picks</h3>
<p>Editor curated selection</p>
<span class="track-count">50 tracks</span>
</div>
</div>
</div>
</div>
<!-- Hype Section -->
<div class="genre-hype-section">
<h3 class="section-title">🔥 Hype</h3>
<div class="genre-chart-types-grid">
<div class="genre-chart-type-card" data-chart-type="hype-top-10">
<div class="chart-type-icon">🚀</div>
<div class="chart-type-info">
<h3 id="genre-hype-top-10-title">Hype Top 10</h3>
<p>Hottest trending tracks</p>
<span class="track-count">10 tracks</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="hype-top-100">
<div class="chart-type-icon">🔥</div>
<div class="chart-type-info">
<h3 id="genre-hype-top-100-title">Hype Top 100</h3>
<p>Complete hype chart rankings</p>
<span class="track-count">100 tracks</span>
</div>
</div>
<div class="genre-chart-type-card" data-chart-type="hype-picks">
<div class="chart-type-icon"></div>
<div class="chart-type-info">
<h3 id="genre-hype-picks-title">Hype Picks</h3>
<p>Editor selected hype tracks</p>
<span class="track-count">50 tracks</span>
</div>
</div>
</div>
</div>
<!-- New Charts Section (Always Visible) -->
<div class="genre-new-charts-section">
<h3 class="section-title">📈 New Charts Collection</h3>
<p class="section-description">Artist and DJ curated chart collections</p>
<!-- Always Visible Charts List -->
<div class="new-charts-content" id="new-charts-content">
<div class="charts-loading-inline" id="charts-loading-inline">
<div class="loading-spinner-small"></div>
<p>Loading chart collections...</p>
</div>
<div class="new-charts-grid" id="new-charts-grid">
<!-- Charts will be populated here -->
</div>
</div>
</div>
</div>
<!-- Genre Charts List Sub-View -->
<div class="beatport-sub-view" id="beatport-genre-charts-list-view">
<div class="beatport-breadcrumb">
<button class="breadcrumb-back" id="genre-charts-list-back">← Back to Genre
Charts</button>
<span class="breadcrumb-path" id="genre-charts-list-breadcrumb">Browse
Charts > Genre Explorer > Genre Charts > New Charts</span>
</div>
<div class="genre-charts-list-header">
<div class="genre-charts-list-info">
<h2 id="genre-charts-list-title">Loading Charts...</h2>
<p id="genre-charts-list-description">Browse all available chart
collections for this genre</p>
</div>
</div>
<div class="genre-charts-list-container">
<div class="charts-loading-placeholder" id="charts-loading-placeholder">
<div class="loading-spinner"></div>
<p>🔍 Loading chart collections...</p>
</div>
<div class="genre-charts-grid" id="genre-charts-grid">
<!-- Charts will be populated dynamically -->
</div>
</div>
</div>
</div>
</div>
<!-- My Playlists Tab Content -->
<div class="beatport-tab-content" id="beatport-playlists-content">
<div class="playlist-header">
<h3>My Beatport Playlists</h3>
<button class="refresh-button beatport" id="beatport-clear-btn">🗑️ Clear</button>
</div>
<div class="playlist-scroll-container" id="beatport-playlist-container">
<div class="playlist-placeholder">Your created Beatport playlists will appear here.
</div>
</div>
</div>
<!-- Rebuild Tab Content -->
<div class="beatport-tab-content active" id="beatport-rebuild-content">
<div class="beatport-rebuild-slider-container">
<div class="beatport-rebuild-slider" id="beatport-rebuild-slider">
<div class="beatport-rebuild-slider-track" id="beatport-rebuild-slider-track">
<!-- Loading placeholder -->
<div class="beatport-rebuild-loading">
<div class="beatport-rebuild-loading-content">
<h2>🎯 Loading Fresh Beatport Tracks...</h2>
<p>Fetching the latest music from Beatport</p>
</div>
</div>
</div>
<!-- Slider Navigation -->
<div class="beatport-rebuild-slider-nav">
<button class="beatport-rebuild-nav-btn beatport-rebuild-prev-btn"
id="beatport-rebuild-prev-btn"></button>
<button class="beatport-rebuild-nav-btn beatport-rebuild-next-btn"
id="beatport-rebuild-next-btn"></button>
</div>
<!-- Slider Indicators -->
<div class="beatport-rebuild-slider-indicators">
<!-- Indicators will be dynamically generated -->
</div>
</div>
</div>
<!-- Navigation Buttons Section -->
<div class="beatport-nav-buttons-section">
<div class="beatport-nav-buttons-container">
<button class="beatport-nav-button" id="browse-by-genre-btn">
<span class="beatport-nav-icon genre-icon"></span>
<span class="beatport-nav-text">Browse by Genre</span>
</button>
<button class="beatport-nav-button" id="beatport-top100-btn">
<span class="beatport-nav-icon top100-icon"></span>
<span class="beatport-nav-text">Beatport Top 100</span>
</button>
<button class="beatport-nav-button" id="hype-top100-btn">
<span class="beatport-nav-icon hype-icon"></span>
<span class="beatport-nav-text">Hype Top 100</span>
</button>
</div>
</div>
<!-- Beatport Download Bubbles -->
<div id="beatport-downloads-section" class="artist-downloads-section" style="display: none;"></div>
<!-- Top 10 Lists Section -->
<div class="beatport-top10-section">
<div class="beatport-top10-header">
<h2 class="beatport-top10-title">🏆 Top 10 Lists</h2>
<p class="beatport-top10-subtitle">Current trending tracks from Beatport charts
</p>
</div>
<div class="beatport-top10-container">
<!-- Beatport Top 10 List -->
<div class="beatport-top10-list" id="beatport-top10-list">
<div class="beatport-top10-list-header">
<h3 class="beatport-top10-list-title">🎵 Beatport Top 10</h3>
<p class="beatport-top10-list-subtitle">Most popular tracks on Beatport
</p>
</div>
<div class="beatport-top10-tracks" id="beatport-top10-tracks">
<!-- Loading placeholder -->
<div class="beatport-top10-loading">
<div class="beatport-top10-loading-content">
<h4>🎵 Loading Beatport Top 10...</h4>
<p>Fetching trending tracks</p>
</div>
</div>
</div>
</div>
<!-- Hype Top 10 List -->
<div class="beatport-hype10-list" id="beatport-hype10-list">
<div class="beatport-hype10-list-header">
<h3 class="beatport-hype10-list-title">🔥 Hype Top 10</h3>
<p class="beatport-hype10-list-subtitle">Editor's hottest trending picks
</p>
</div>
<div class="beatport-hype10-tracks" id="beatport-hype10-tracks">
<!-- Loading placeholder -->
<div class="beatport-hype10-loading">
<div class="beatport-hype10-loading-content">
<h4>🔥 Loading Hype Top 10...</h4>
<p>Fetching editor's picks</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Top 10 Releases Section -->
<div class="beatport-releases-top10-section">
<div class="beatport-releases-top10-header">
<h2 class="beatport-releases-top10-title">💿 Top 10 Releases</h2>
<p class="beatport-releases-top10-subtitle">Most popular albums and EPs on
Beatport</p>
</div>
<div class="beatport-releases-top10-container">
<div class="beatport-releases-top10-list" id="beatport-releases-top10-list">
<!-- Loading placeholder -->
<div class="beatport-releases-top10-loading">
<div class="beatport-releases-top10-loading-content">
<h4>💿 Loading Top 10 Releases...</h4>
<p>Fetching trending albums and EPs</p>
</div>
</div>
</div>
</div>
</div>
<!-- New Releases Grid Slideshow Section -->
<div class="beatport-releases-section">
<div class="beatport-releases-header">
<h2 class="beatport-releases-title">🆕 New Releases</h2>
<p class="beatport-releases-subtitle">Latest albums and EPs from Beatport</p>
</div>
<div class="beatport-releases-slider-container">
<div class="beatport-releases-slider" id="beatport-releases-slider">
<div class="beatport-releases-slider-track"
id="beatport-releases-slider-track">
<!-- Loading placeholder -->
<div class="beatport-releases-loading">
<div class="beatport-releases-loading-content">
<h3>📀 Loading New Releases...</h3>
<p>Fetching the latest albums and EPs</p>
</div>
</div>
</div>
<!-- Slider Navigation -->
<div class="beatport-releases-slider-nav">
<button class="beatport-releases-nav-btn beatport-releases-prev-btn"
id="beatport-releases-prev-btn"></button>
<button class="beatport-releases-nav-btn beatport-releases-next-btn"
id="beatport-releases-next-btn"></button>
</div>
<!-- Slider Indicators -->
<div class="beatport-releases-slider-indicators"
id="beatport-releases-slider-indicators">
<!-- Indicators will be dynamically generated -->
</div>
</div>
</div>
</div>
<!-- Hype Picks Grid Slideshow Section -->
<div class="beatport-hype-picks-section">
<div class="beatport-hype-picks-header">
<h2 class="beatport-hype-picks-title">🔥 Hype Picks</h2>
<p class="beatport-hype-picks-subtitle">Editor selected trending tracks from
Beatport</p>
</div>
<div class="beatport-hype-picks-slider-container">
<div class="beatport-hype-picks-slider" id="beatport-hype-picks-slider">
<div class="beatport-hype-picks-slider-track"
id="beatport-hype-picks-slider-track">
<!-- Loading placeholder -->
<div class="beatport-hype-picks-loading">
<div class="beatport-hype-picks-loading-content">
<h3>🔥 Loading Hype Picks...</h3>
<p>Fetching the hottest trending tracks</p>
</div>
</div>
</div>
<!-- Slider Navigation -->
<div class="beatport-hype-picks-slider-nav">
<button class="beatport-hype-picks-nav-btn beatport-hype-picks-prev-btn"
id="beatport-hype-picks-prev-btn"></button>
<button class="beatport-hype-picks-nav-btn beatport-hype-picks-next-btn"
id="beatport-hype-picks-next-btn"></button>
</div>
<!-- Slider Indicators -->
<div class="beatport-hype-picks-slider-indicators"
id="beatport-hype-picks-slider-indicators">
<!-- Indicators will be dynamically generated -->
</div>
</div>
</div>
</div>
<!-- Featured Charts Grid Slideshow Section -->
<div class="beatport-charts-section">
<div class="beatport-charts-header">
<h2 class="beatport-charts-title">🔥 Featured Charts</h2>
<p class="beatport-charts-subtitle">Top chart collections from Beatport creators
</p>
</div>
<div class="beatport-charts-slider-container">
<div class="beatport-charts-slider" id="beatport-charts-slider">
<div class="beatport-charts-slider-track" id="beatport-charts-slider-track">
<!-- Loading placeholder -->
<div class="beatport-charts-loading">
<div class="beatport-charts-loading-content">
<h3>📊 Loading Featured Charts...</h3>
<p>Fetching top chart collections</p>
</div>
</div>
</div>
<!-- Slider Navigation -->
<div class="beatport-charts-slider-nav">
<button class="beatport-charts-nav-btn beatport-charts-prev-btn"
id="beatport-charts-prev-btn"></button>
<button class="beatport-charts-nav-btn beatport-charts-next-btn"
id="beatport-charts-next-btn"></button>
</div>
<!-- Slider Indicators -->
<div class="beatport-charts-slider-indicators"
id="beatport-charts-slider-indicators">
<!-- Indicators will be dynamically generated -->
</div>
</div>
</div>
</div>
<!-- DJ Charts Carousel Section -->
<div class="beatport-dj-section">
<div class="beatport-dj-header">
<h2 class="beatport-dj-title">🎧 DJ Charts</h2>
<p class="beatport-dj-subtitle">Curated charts from top DJs and artists</p>
</div>
<div class="beatport-dj-slider-container">
<div class="beatport-dj-slider" id="beatport-dj-slider">
<div class="beatport-dj-slider-track" id="beatport-dj-slider-track">
<!-- Loading placeholder -->
<div class="beatport-dj-loading">
<div class="beatport-dj-loading-content">
<h3>🎧 Loading DJ Charts...</h3>
<p>Fetching curated DJ selections</p>
</div>
</div>
</div>
<!-- Slider Navigation -->
<div class="beatport-dj-slider-nav">
<button class="beatport-dj-nav-btn beatport-dj-prev-btn"
id="beatport-dj-prev-btn"></button>
<button class="beatport-dj-nav-btn beatport-dj-next-btn"
id="beatport-dj-next-btn"></button>
</div>
<!-- Slider Indicators -->
<div class="beatport-dj-slider-indicators"
id="beatport-dj-slider-indicators">
<!-- Indicators will be dynamically generated -->
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Import File Tab Content -->
<div class="sync-tab-content" id="import-file-tab-content">
<div class="playlist-header">
<h3>Import Playlist from File</h3>
</div>
<!-- Step 1: Upload Zone -->
<div id="import-file-upload-zone" class="import-file-zone">
<div class="import-file-zone-inner" id="import-file-dropzone">
<div class="import-file-zone-icon">📄</div>
<div class="import-file-zone-title">Drop your file here</div>
<div class="import-file-zone-subtitle">or click to browse</div>
<div class="import-file-zone-formats">Supported: CSV, TSV, TXT</div>
<input type="file" id="import-file-input" accept=".csv,.tsv,.txt" style="display:none">
</div>
<div class="import-file-format-hints">
<div class="import-file-hint">
<span class="import-file-hint-label">CSV / TSV</span>
<span class="import-file-hint-text">First row as headers (e.g. Title, Artist, Album). Columns are auto-detected or can be mapped manually.</span>
</div>
<div class="import-file-hint">
<span class="import-file-hint-label">TXT</span>
<span class="import-file-hint-text">One track per line (e.g. Artist - Title). Format and separator can be adjusted after upload.</span>
</div>
</div>
</div>
<!-- Step 2: Column Mapping & Preview (hidden until file is parsed) -->
<div id="import-file-preview-section" style="display:none">
<!-- File info bar -->
<div class="import-file-info-bar">
<span id="import-file-name-label" class="import-file-info-filename"></span>
<span id="import-file-track-count" class="import-file-info-count"></span>
<button class="import-file-clear-btn" onclick="importFileClear()" title="Clear and start over"></button>
</div>
<!-- Format selector (for plain text files) -->
<div id="import-file-text-format" class="import-file-format-bar" style="display:none">
<span class="import-file-format-label">Line format:</span>
<select id="import-file-text-order" class="import-file-select" onchange="importFileReparse()">
<option value="artist-title">Artist - Title</option>
<option value="title-artist">Title - Artist</option>
</select>
<span class="import-file-format-label" style="margin-left:12px">Separator:</span>
<select id="import-file-text-separator" class="import-file-select" onchange="importFileReparse()">
<option value=" - "> - </option>
<option value=" — "></option>
<option value="|">|</option>
<option value="/"> / </option>
</select>
</div>
<!-- Column mapping (for CSV/TSV files) -->
<div id="import-file-column-mapping" class="import-file-mapping-bar" style="display:none">
<span class="import-file-format-label">Column mapping:</span>
<div id="import-file-mapping-selects" class="import-file-mapping-selects">
<!-- Dynamically populated with dropdowns per CSV column -->
</div>
</div>
<!-- Preview table -->
<div class="import-file-preview-table-wrap">
<table class="import-file-preview-table" id="import-file-preview-table">
<thead>
<tr>
<th>#</th>
<th>Track</th>
<th>Artist</th>
<th>Album</th>
</tr>
</thead>
<tbody id="import-file-preview-tbody">
</tbody>
</table>
</div>
<!-- Playlist name + import button -->
<div class="import-file-action-bar">
<input type="text" id="import-file-playlist-name"
class="import-file-name-input"
placeholder="Enter playlist name..." maxlength="200">
<button class="import-file-import-btn" id="import-file-import-btn"
onclick="importFileSubmit()" disabled>
Import as Mirrored Playlist
</button>
</div>
</div>
</div>
<!-- Mirrored Playlists Tab Content -->
<div class="sync-tab-content" id="mirrored-tab-content">
<div class="playlist-header">
<h3>Mirrored Playlists</h3>
<button class="pool-trigger-btn" onclick="openDiscoveryPoolModal()" title="View matched and failed discovery tracks">Discovery Pool</button>
<button class="refresh-button mirrored" id="mirrored-refresh-btn">Refresh</button>
</div>
<div class="playlist-scroll-container" id="mirrored-playlist-container">
<div class="playlist-placeholder">Playlists you parse from any service will appear here as persistent backups.</div>
</div>
</div>
<!-- Server Playlist Manager Tab -->
<div class="sync-tab-content active" id="server-tab-content">
<div class="playlist-header">
<h3 id="server-tab-title">Server Playlists</h3>
<button class="refresh-button" id="server-refresh-btn" onclick="loadServerPlaylists()">🔄 Refresh</button>
</div>
<div id="server-playlist-view">
<!-- Playlist list (default view) -->
<div class="playlist-scroll-container" id="server-playlist-container">
<div class="playlist-placeholder">Playlists from your media server will load automatically.</div>
</div>
<!-- Disambiguation modal -->
<div id="server-disambig-overlay" class="server-disambig-overlay hidden">
<div class="server-disambig-modal">
<div class="server-disambig-header">
<div>
<h3 class="server-disambig-title">Multiple Sources Found</h3>
<p class="server-disambig-subtitle" id="server-disambig-subtitle"></p>
</div>
<button class="server-disambig-close" onclick="closeServerDisambig()">&times;</button>
</div>
<div class="server-disambig-list" id="server-disambig-list"></div>
</div>
</div>
<!-- Comparison editor -->
<div id="server-editor" style="display: none;">
<div class="server-editor-header">
<button class="server-editor-back" onclick="serverEditorBack()">← Back</button>
<div class="server-editor-info">
<h4 class="server-editor-name" id="server-editor-name"></h4>
<span class="server-editor-meta" id="server-editor-meta"></span>
</div>
<div class="server-editor-stats" id="server-editor-stats"></div>
<button class="server-editor-refresh" onclick="_serverEditorRefresh()" title="Re-fetch from server">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2v6h-6M3 12a9 9 0 0115.356-6.356L21 8M3 22v-6h6M21 12a9 9 0 01-15.356 6.356L3 16"/></svg>
</button>
</div>
<div id="server-no-source-banner" class="server-no-source-banner" style="display:none">
No mirrored playlist found matching this name. Showing server tracks only.
</div>
<div class="server-editor-filters">
<button class="discog-filter active" data-filter="all" onclick="_serverEditorFilter(this, 'all')">All</button>
<button class="discog-filter" data-filter="matched" onclick="_serverEditorFilter(this, 'matched')">Matched</button>
<button class="discog-filter" data-filter="missing" onclick="_serverEditorFilter(this, 'missing')">Missing</button>
<button class="discog-filter" data-filter="extra" onclick="_serverEditorFilter(this, 'extra')">Extra</button>
</div>
<div class="server-compare-columns">
<div class="server-compare-col source" id="server-col-source">
<div class="server-col-header">
<span class="server-col-icon" id="server-col-source-icon"></span>
<span class="server-col-label" id="server-col-source-label">Source</span>
<span class="server-col-count" id="server-col-source-count"></span>
</div>
<div class="server-col-scroll" id="server-col-source-scroll"></div>
</div>
<div class="server-compare-col server" id="server-col-server">
<div class="server-col-header">
<span class="server-col-icon" id="server-col-server-icon"></span>
<span class="server-col-label" id="server-col-server-label">Server</span>
<span class="server-col-count" id="server-col-server-count"></span>
</div>
<div class="server-col-scroll" id="server-col-server-scroll"></div>
</div>
</div>
<div class="server-editor-footer" id="server-editor-footer"></div>
</div>
</div>
</div>
</div>
<!-- Right Panel: Sidebar with Options & Logging -->
<div class="sync-sidebar">
<div class="sidebar-section">
<h4>Sync Actions</h4>
<div id="selection-info">Select playlists to sync</div>
<button id="start-sync-btn" class="neo-button" disabled>Start Sync</button>
</div>
<div class="sidebar-section progress-section">
<h4>Sync Progress</h4>
<div class="progress-bar-container">
<div class="progress-bar-fill" id="sync-progress-bar" style="width: 0%;"></div>
</div>
<div id="sync-progress-text">Ready to sync...</div>
<textarea id="sync-log-area" readonly>Waiting for sync to start...</textarea>
</div>
</div>
</div>
</div>
<!-- Downloads Page -->
<div class="page" id="downloads-page">
<!--
This top-level container replicates the QSplitter from downloads.py,
creating the two-panel layout for the page.
-->
<div class="downloads-content manager-hidden">
<!-- ======================================================= -->
<!-- == LEFT PANEL: Search, Filters, and Results == -->
<!-- ======================================================= -->
<div class="downloads-main-panel">
<!-- Header: Replicates create_elegant_header() -->
<div class="downloads-header">
<div class="downloads-header-content">
<div class="downloads-header-text">
<h2 class="downloads-title"><img src="/static/search.png" class="page-header-icon" alt=""><span>Music Downloads</span></h2>
<p class="downloads-subtitle">Search, discover, and download high-quality music</p>
</div>
<button id="toggle-download-manager-btn" class="toggle-manager-btn"
title="Toggle Download Manager">
<span class="toggle-icon"></span>
</button>
</div>
</div>
<!-- Search Mode Toggle -->
<div class="search-mode-toggle-container">
<div class="search-mode-toggle" data-active="enhanced">
<button class="search-mode-btn active" data-mode="enhanced">
<span class="mode-icon"></span>
<span class="mode-label">Enhanced Search</span>
</button>
<button class="search-mode-btn" data-mode="basic">
<span class="mode-icon">🔍</span>
<span class="mode-label">Basic Search</span>
</button>
<div class="toggle-slider"></div>
</div>
</div>
<!-- Basic Search Section (Current) -->
<div id="basic-search-section" class="search-section">
<!-- Search Bar: Replicates create_elegant_search_bar() -->
<div class="search-bar-container">
<input type="text" id="downloads-search-input"
placeholder="Search for music... (e.g., 'Virtual Mage', 'Queen Bohemian Rhapsody')">
<button id="downloads-cancel-btn" class="hidden">✕ Cancel</button>
<button id="downloads-search-btn">🔍 Search</button>
</div>
<div id="filters-container" class="filters-container hidden">
<div class="filter-toggle-header">
<button id="filter-toggle-btn" class="filter-toggle-btn">⏷ Filters</button>
</div>
<div id="filter-content" class="filter-content hidden">
<!-- Filter by Type -->
<div class="filter-group">
<label class="filter-label">Type:</label>
<button class="filter-btn active" data-filter-type="type"
data-value="all">All</button>
<button class="filter-btn" data-filter-type="type"
data-value="album">Albums</button>
<button class="filter-btn" data-filter-type="type"
data-value="track">Singles</button>
</div>
<!-- Filter by Format -->
<div class="filter-group">
<label class="filter-label">Format:</label>
<button class="filter-btn active" data-filter-type="format"
data-value="all">All</button>
<button class="filter-btn" data-filter-type="format"
data-value="flac">FLAC</button>
<button class="filter-btn" data-filter-type="format"
data-value="mp3">MP3</button>
<!-- Added missing format buttons -->
<button class="filter-btn" data-filter-type="format"
data-value="ogg">OGG</button>
<button class="filter-btn" data-filter-type="format"
data-value="aac">AAC</button>
<button class="filter-btn" data-filter-type="format"
data-value="wma">WMA</button>
</div>
<!-- Sort Controls -->
<div class="filter-group">
<label class="filter-label">Sort by:</label>
<button id="sort-order-btn" class="filter-btn sort-order-btn"
data-order="desc"></button>
<!-- Added all sort options from the GUI -->
<button class="filter-btn active" data-filter-type="sort"
data-value="relevance">Relevance</button>
<button class="filter-btn" data-filter-type="sort"
data-value="quality_score">Quality</button>
<button class="filter-btn" data-filter-type="sort"
data-value="size">Size</button>
<button class="filter-btn" data-filter-type="sort"
data-value="title">Name</button>
<button class="filter-btn" data-filter-type="sort"
data-value="username">Uploader</button>
<button class="filter-btn" data-filter-type="sort"
data-value="bitrate">Bitrate</button>
<button class="filter-btn" data-filter-type="sort"
data-value="duration">Duration</button>
<button class="filter-btn" data-filter-type="sort"
data-value="availability">Available</button>
<button class="filter-btn" data-filter-type="sort"
data-value="upload_speed">Speed</button>
</div>
</div>
</div>
<!-- Search Status Bar -->
<div class="search-status-container">
<div class="spinner-animation hidden"></div>
<p id="search-status-text">Ready to search • Enter artist, song, or album name</p>
<div class="dots-animation hidden"></div>
</div>
<!-- Search Results Area: Replicates the QScrollArea -->
<div class="search-results-container">
<div class="search-results-header">
<h3>Search Results</h3>
</div>
<div class="search-results-scroll-area" id="search-results-area">
<!--
The placeholder search results have been removed.
This area will now be populated by JavaScript based on API responses.
-->
<div class="search-results-placeholder">
<p>Your search results will appear here.</p>
</div>
</div>
</div>
</div>
<!-- End Basic Search Section -->
<!-- Enhanced Search Section (New) -->
<div id="enhanced-search-section" class="search-section active">
<!-- Enhanced Search Bar with Dropdown -->
<div class="enhanced-search-input-wrapper">
<div class="enhanced-search-bar-container">
<div class="enhanced-search-wrapper">
<div class="enhanced-search-icon"></div>
<input type="text" id="enhanced-search-input"
placeholder="Search for artists, albums, or tracks...">
<button id="enhanced-cancel-btn" class="enhanced-cancel-btn hidden"></button>
</div>
<button id="enhanced-search-btn" class="enhanced-search-btn">
<span class="btn-icon">👁️</span>
<span class="btn-text">Show Results</span>
</button>
</div>
<!-- Enhanced Search Dropdown (Overlay Panel) -->
<div id="enhanced-dropdown" class="enhanced-dropdown hidden">
<div class="enhanced-dropdown-content">
<!-- Mobile close bar -->
<button id="enhanced-dropdown-close" class="enhanced-dropdown-close">
<span></span> Close Results
</button>
<!-- Loading State -->
<div id="enhanced-loading" class="enhanced-loading hidden">
<div class="spinner"></div>
<p id="enhanced-loading-text">Searching across Spotify and your library...
</p>
</div>
<!-- Empty State -->
<div id="enhanced-empty" class="enhanced-empty hidden">
<div class="empty-icon">🔍</div>
<p>No results found</p>
</div>
<!-- Results Container -->
<div id="enhanced-results-container" class="enhanced-results-container hidden">
<!-- Source Tabs -->
<div id="enh-source-tabs" class="enh-source-tabs hidden"></div>
<!-- Artists Container (Side by Side) -->
<div class="enh-artists-wrapper">
<!-- DB Artists -->
<div id="enh-db-artists-section"
class="enh-dropdown-section enh-artist-section hidden">
<div class="enh-section-header">
<span class="enh-section-icon">📚</span>
<h4 class="enh-section-title">In Your Library</h4>
<span class="enh-section-count"
id="enh-db-artists-count">0</span>
</div>
<div class="enh-compact-list" id="enh-db-artists-list"></div>
</div>
<!-- Spotify Artists -->
<div id="enh-spotify-artists-section"
class="enh-dropdown-section enh-artist-section hidden">
<div class="enh-section-header">
<span class="enh-section-icon">🎤</span>
<h4 class="enh-section-title">Artists</h4>
<span class="enh-section-count"
id="enh-spotify-artists-count">0</span>
</div>
<div class="enh-compact-list" id="enh-spotify-artists-list"></div>
</div>
</div>
<!-- Albums -->
<div id="enh-albums-section" class="enh-dropdown-section hidden">
<div class="enh-section-header">
<span class="enh-section-icon">💿</span>
<h4 class="enh-section-title">Albums</h4>
<span class="enh-section-count" id="enh-albums-count">0</span>
</div>
<div class="enh-compact-list" id="enh-albums-list"></div>
</div>
<!-- Singles & EPs -->
<div id="enh-singles-section" class="enh-dropdown-section hidden">
<div class="enh-section-header">
<span class="enh-section-icon">🎶</span>
<h4 class="enh-section-title">Singles & EPs</h4>
<span class="enh-section-count" id="enh-singles-count">0</span>
</div>
<div class="enh-compact-list" id="enh-singles-list"></div>
</div>
<!-- Tracks -->
<div id="enh-tracks-section" class="enh-dropdown-section hidden">
<div class="enh-section-header">
<span class="enh-section-icon">🎵</span>
<h4 class="enh-section-title">Tracks</h4>
<span class="enh-section-count" id="enh-tracks-count">0</span>
</div>
<div class="enh-compact-list" id="enh-tracks-list"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Main Search Results Area (for slskd results) -->
<div class="search-results-container">
<div class="search-results-header">
<h3>Search Results</h3>
</div>
<div class="search-results-scroll-area" id="enhanced-main-results-area">
<div class="search-results-placeholder">
<p>Search results will appear here when you select an album or track.</p>
</div>
</div>
</div>
</div>
<!-- End Enhanced Search Section -->
</div>
<!-- ======================================================= -->
<!-- == RIGHT PANEL: Controls and Download Queue == -->
<!-- ======================================================= -->
<div class="downloads-side-panel">
<!-- Controls Panel: Replicates create_collapsible_controls_panel() -->
<div class="controls-panel">
<h3 class="controls-panel__header">Download Manager</h3>
<div class="controls-panel__stats">
<p id="active-downloads-label">• Active Downloads: 0</p>
<p id="finished-downloads-label">• Finished Downloads: 0</p>
</div>
<div class="controls-panel__actions">
<button class="controls-panel__clear-btn">🗑️ Clear Completed</button>
<button class="controls-panel__cancel-all-btn">⛔ Clear Current</button>
</div>
</div>
<!-- Download Queue: Replicates TabbedDownloadManager -->
<div class="download-manager">
<div class="download-manager__tabs">
<button class="tab-btn active" data-tab="active-queue">Download Queue (0)</button>
<button class="tab-btn" data-tab="finished-queue">Finished (0)</button>
</div>
<div class="download-manager__content">
<!-- Active Queue -->
<div class="download-queue active" id="active-queue">
<div class="download-queue__empty-message">No active downloads.</div>
</div>
<!-- Finished Queue -->
<div class="download-queue" id="finished-queue">
<div class="download-queue__empty-message">No finished downloads.</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Artists Page -->
<div class="page" id="artists-page">
<!-- Initial Search State -->
<div class="artists-search-state" id="artists-search-state">
<div class="artists-search-container">
<div class="artists-welcome-section">
<h2 class="artists-welcome-title"><img src="/static/discover.png" class="page-header-icon" alt=""><span>Discover Artists</span></h2>
<p class="artists-welcome-subtitle">Search for your favorite artists and explore their
complete discography</p>
</div>
<div class="artists-search-input-container">
<input type="text" id="artists-search-input" class="artists-search-input"
placeholder="Search for an artist...">
<div class="artists-search-icon">🔍</div>
</div>
<div class="artists-search-status" id="artists-search-status">
Start typing to search for artists
</div>
</div>
</div>
<!-- Search Results State -->
<div class="artists-results-state hidden" id="artists-results-state">
<div class="artists-results-header">
<button class="artists-back-button" id="artists-back-button">
<span class="back-icon"></span>
<span>Back to Search</span>
</button>
<div class="artists-search-header">
<input type="text" id="artists-header-search-input" class="artists-header-search-input"
placeholder="Search for an artist...">
</div>
</div>
<div class="artists-results-content">
<div class="artists-results-title">Search Results</div>
<div class="artists-cards-container" id="artists-cards-container">
<!-- Artist cards will be dynamically populated here -->
</div>
</div>
</div>
<!-- Artist Detail State -->
<div class="artist-detail-state hidden" id="artist-detail-state">
<!-- Hero Section -->
<div class="artists-hero-section" id="artists-hero-section">
<div class="artists-hero-bg" id="artists-hero-bg"></div>
<div class="artists-hero-overlay"></div>
<div class="artists-hero-content">
<button class="artists-hero-back" id="artist-detail-back-button">
<span></span> Back
</button>
<div class="artists-hero-main">
<div class="artists-hero-image" id="artists-hero-image"></div>
<div class="artists-hero-info">
<h1 class="artists-hero-name" id="artists-hero-name">Artist Name</h1>
<div class="artists-hero-badges" id="artists-hero-badges"></div>
<div class="artists-hero-genres" id="artists-hero-genres"></div>
<div class="artists-hero-bio" id="artists-hero-bio"></div>
<div class="artists-hero-stats" id="artists-hero-stats"></div>
<button class="discog-download-btn discog-btn-compact" id="discog-download-btn-artists" onclick="openDiscographyModal()" style="display:none;">
<span class="discog-btn-icon"></span>
<span class="discog-btn-text">Download Discography</span>
<span class="discog-btn-shimmer"></span>
</button>
</div>
</div>
<div class="artists-hero-actions">
<button class="artist-detail-watchlist-btn" id="artist-detail-watchlist-btn">
<span class="watchlist-icon">👁️</span>
<span class="watchlist-text">Add to Watchlist</span>
</button>
<button class="artist-detail-watchlist-settings-btn hidden" id="artist-detail-watchlist-settings-btn" title="Watchlist Settings">
&#9881;
</button>
</div>
</div>
</div>
<!-- Keep old hidden elements for backward compatibility with JS refs -->
<div id="search-artist-detail-image" style="display:none"></div>
<div id="search-artist-detail-name" style="display:none"></div>
<div id="search-artist-detail-genres" style="display:none"></div>
<div class="artist-detail-content">
<div class="artist-detail-tabs">
<button class="artist-tab active" data-tab="albums" id="albums-tab">
<span class="tab-icon">💿</span>
<span>Albums</span>
</button>
<button class="artist-tab" data-tab="singles" id="singles-tab">
<span class="tab-icon">🎵</span>
<span>Singles & EPs</span>
</button>
</div>
<div class="artist-detail-discography">
<div class="tab-content active" id="albums-content">
<div class="album-cards-container" id="album-cards-container">
<!-- Album cards will be populated here -->
</div>
</div>
<div class="tab-content" id="singles-content">
<div class="singles-cards-container" id="singles-cards-container">
<!-- Singles cards will be populated here -->
</div>
</div>
</div>
<!-- Similar Artists Section -->
<div class="similar-artists-section" id="similar-artists-section">
<div class="similar-artists-header">
<h3 class="similar-artists-title">Similar Artists</h3>
<p class="similar-artists-subtitle">Discover artists with a similar sound</p>
</div>
<!-- Loading State -->
<div class="similar-artists-loading hidden" id="similar-artists-loading">
<div class="loading-spinner-small"></div>
<span>Finding similar artists...</span>
</div>
<!-- Error State -->
<div class="similar-artists-error hidden" id="similar-artists-error">
<span class="error-icon">⚠️</span>
<span class="error-text">Unable to load similar artists</span>
</div>
<!-- Similar Artists Bubbles Container -->
<div class="similar-artists-bubbles-container" id="similar-artists-bubbles-container">
<!-- Artist bubble cards will be populated here -->
</div>
</div>
</div>
</div>
</div>
<!-- Automations Page -->
<div class="page" id="automations-page">
<!-- List View -->
<div class="automations-list-view" id="automations-list-view">
<div class="automations-container">
<div class="dashboard-header">
<div class="header-text">
<h2 class="header-title"><img src="/static/automation.png" class="page-header-icon" alt=""><span>Automations</span></h2>
<p class="header-subtitle">Configure scheduled tasks and automated workflows</p>
</div>
<div class="header-spacer"></div>
<div class="header-actions">
<button class="auto-new-btn" onclick="showAutomationBuilder()">+ New Automation</button>
</div>
</div>
<div class="automations-stats" id="automations-stats"></div>
<div class="auto-filter-bar" id="auto-filter-bar" style="display:none;">
<input type="text" class="auto-filter-search" id="auto-filter-search" placeholder="Search automations...">
<select class="auto-filter-select" id="auto-filter-trigger"><option value="">All Triggers</option></select>
<select class="auto-filter-select" id="auto-filter-action"><option value="">All Actions</option></select>
<span class="auto-filter-count" id="auto-filter-count"></span>
</div>
<div class="automations-list" id="automations-list"></div>
<div class="automations-empty" id="automations-empty" style="display:none;">
<div class="automations-empty-icon">&#9889;</div>
<div class="automations-empty-title">No automations yet</div>
<div class="automations-empty-text">Create your first automation to schedule tasks and trigger actions automatically.</div>
<button class="auto-new-btn" onclick="showAutomationBuilder()">+ New Automation</button>
</div>
</div>
</div>
<!-- Builder View -->
<div class="automations-builder-view" id="automations-builder-view" style="display:none;">
<div class="builder-header">
<button class="builder-back-btn" onclick="hideAutomationBuilder()" title="Back to list">&#8592;</button>
<input type="text" id="builder-name" class="builder-name-input" placeholder="Automation Name">
<input type="text" id="builder-group-name" class="builder-group-input" placeholder="Group (optional)" list="builder-group-list">
<datalist id="builder-group-list"></datalist>
<div class="builder-header-actions">
<button class="btn-cancel" onclick="hideAutomationBuilder()">Cancel</button>
<button class="btn-save" onclick="saveAutomation()">Save</button>
</div>
</div>
<div class="builder-content">
<div class="builder-sidebar" id="builder-sidebar"></div>
<div class="builder-canvas" id="builder-canvas"></div>
</div>
</div>
</div>
<!-- Library Page -->
<div class="page" id="library-page">
<div class="library-container">
<!-- Header -->
<div class="library-header">
<div class="library-header-content">
<h2 class="library-title"><img src="/static/library.png" class="page-header-icon" alt=""><span>Music Library</span></h2>
<p class="library-subtitle">Browse your complete music collection</p>
</div>
<div class="library-stats" id="library-stats">
<span class="library-stat">
<span class="stat-number" id="library-artist-count">0</span>
<span class="stat-label">Artists</span>
</span>
</div>
</div>
<!-- Search and Filters -->
<div class="library-controls">
<div class="library-search-container">
<input type="text" id="library-search-input" class="library-search-input"
placeholder="Search artists...">
<div class="library-search-icon">🔍</div>
</div>
<!-- Watchlist Filter -->
<div class="watchlist-filter" id="watchlist-filter">
<button class="watchlist-filter-btn active" data-filter="all">All</button>
<button class="watchlist-filter-btn" data-filter="watched">Watched</button>
<button class="watchlist-filter-btn" data-filter="unwatched">Unwatched</button>
<button class="library-watchlist-all-btn hidden" id="library-watchlist-all-btn" onclick="openWatchAllUnwatchedModal()">
<span class="watchlist-all-icon">👁️</span>
<span class="watchlist-all-text">Watch All Unwatched</span>
</button>
</div>
<!-- Alphabet Selector -->
<div class="alphabet-selector" id="alphabet-selector">
<div class="alphabet-selector-inner">
<button class="alphabet-btn active" data-letter="all">All</button>
<button class="alphabet-btn" data-letter="a">A</button>
<button class="alphabet-btn" data-letter="b">B</button>
<button class="alphabet-btn" data-letter="c">C</button>
<button class="alphabet-btn" data-letter="d">D</button>
<button class="alphabet-btn" data-letter="e">E</button>
<button class="alphabet-btn" data-letter="f">F</button>
<button class="alphabet-btn" data-letter="g">G</button>
<button class="alphabet-btn" data-letter="h">H</button>
<button class="alphabet-btn" data-letter="i">I</button>
<button class="alphabet-btn" data-letter="j">J</button>
<button class="alphabet-btn" data-letter="k">K</button>
<button class="alphabet-btn" data-letter="l">L</button>
<button class="alphabet-btn" data-letter="m">M</button>
<button class="alphabet-btn" data-letter="n">N</button>
<button class="alphabet-btn" data-letter="o">O</button>
<button class="alphabet-btn" data-letter="p">P</button>
<button class="alphabet-btn" data-letter="q">Q</button>
<button class="alphabet-btn" data-letter="r">R</button>
<button class="alphabet-btn" data-letter="s">S</button>
<button class="alphabet-btn" data-letter="t">T</button>
<button class="alphabet-btn" data-letter="u">U</button>
<button class="alphabet-btn" data-letter="v">V</button>
<button class="alphabet-btn" data-letter="w">W</button>
<button class="alphabet-btn" data-letter="x">X</button>
<button class="alphabet-btn" data-letter="y">Y</button>
<button class="alphabet-btn" data-letter="z">Z</button>
<button class="alphabet-btn" data-letter="#">#</button>
</div>
</div>
</div>
<!-- Content Area -->
<div class="library-content">
<!-- Loading State -->
<div class="library-loading hidden" id="library-loading">
<div class="loading-spinner"></div>
<div class="loading-text">Loading artists...</div>
</div>
<!-- Artist Grid -->
<div class="library-artists-grid" id="library-artists-grid">
<!-- Artist cards will be populated here -->
</div>
<!-- Empty State -->
<div class="library-empty hidden" id="library-empty">
<div class="empty-icon">🎵</div>
<div class="empty-title">No artists found</div>
<div class="empty-subtitle">Try adjusting your search or filters</div>
</div>
<!-- Pagination -->
<div class="library-pagination hidden" id="library-pagination">
<button class="pagination-btn" id="prev-page-btn" disabled>
<span>← Previous</span>
</button>
<div class="pagination-info">
<span id="page-info">Page 1 of 1</span>
</div>
<button class="pagination-btn" id="next-page-btn" disabled>
<span>Next →</span>
</button>
</div>
</div>
</div>
</div>
<!-- Artist Detail Page -->
<div class="page" id="artist-detail-page">
<div class="page-header">
<button class="back-btn" id="artist-detail-back-btn">
<span>← Back to Library</span>
</button>
<button class="library-artist-watchlist-btn" id="library-artist-watchlist-btn">
<span class="watchlist-icon">👁️</span>
<span class="watchlist-text">Add to Watchlist</span>
</button>
<button class="library-artist-enhance-btn hidden" id="library-artist-enhance-btn"
onclick="openEnhanceQualityModal()">
<span class="enhance-icon"></span>
<span class="enhance-text">Enhance Quality</span>
</button>
<button class="library-artist-radio-btn" id="library-artist-radio-btn"
onclick="playArtistRadio()">
<span class="radio-icon">📻</span>
<span class="radio-text">Artist Radio</span>
</button>
</div>
<!-- Artist Hero Section -->
<div class="artist-hero-section" id="artist-hero-section">
<div class="artist-hero-content">
<!-- Left: Image -->
<div class="artist-image-container">
<img class="artist-image" id="artist-detail-image" src="" alt="Artist Image" />
<div class="artist-image-fallback" id="artist-detail-image-fallback">🎵</div>
</div>
<!-- Center: Identity + Meta -->
<div class="artist-info">
<div class="artist-hero-identity">
<h1 class="artist-name" id="artist-detail-name">Artist Name</h1>
<div class="artist-hero-badges" id="artist-hero-badges"></div>
</div>
<div class="artist-genres-container" id="artist-genres"></div>
<div class="artist-hero-bio" id="artist-hero-bio" style="display:none;"></div>
<div class="artist-hero-numbers">
<div class="artist-hero-stat" id="artist-hero-listeners" style="display:none;">
<span class="hero-stat-value">0</span>
<span class="hero-stat-label">listeners</span>
</div>
<div class="artist-hero-stat" id="artist-hero-playcount" style="display:none;">
<span class="hero-stat-value">0</span>
<span class="hero-stat-label">plays</span>
</div>
</div>
<div class="discog-download-wrap" id="discog-download-wrap" style="display:none;">
<button class="discog-download-btn discog-btn-compact" id="discog-download-btn" onclick="openDiscographyModal()">
<span class="discog-btn-icon"></span>
<span class="discog-btn-text">Download Discography</span>
<span class="discog-btn-shimmer"></span>
</button>
</div>
<div class="collection-overview">
<div class="collection-category">
<span class="category-label">Albums</span>
<div class="completion-bar"><div class="completion-fill" id="albums-completion-fill" style="width: 0%"></div></div>
<span class="category-stats" id="albums-stats">0/0</span>
</div>
<div class="collection-category">
<span class="category-label">EPs</span>
<div class="completion-bar"><div class="completion-fill" id="eps-completion-fill" style="width: 0%"></div></div>
<span class="category-stats" id="eps-stats">0/0</span>
</div>
<div class="collection-category">
<span class="category-label">Singles</span>
<div class="completion-bar"><div class="completion-fill" id="singles-completion-fill" style="width: 0%"></div></div>
<span class="category-stats" id="singles-stats">0/0</span>
</div>
</div>
<!-- Per-artist enrichment coverage -->
<div class="artist-enrichment-coverage" id="artist-enrichment-coverage" style="display:none;"></div>
</div>
<!-- Right: Top Tracks only -->
<div class="artist-hero-right" id="artist-hero-sidebar" style="display:none;">
<div class="hero-sidebar-title">Popular on Last.fm</div>
<div class="hero-top-tracks" id="hero-top-tracks"></div>
</div>
</div>
</div>
<div class="artist-detail-content">
<!-- Loading State -->
<div class="artist-detail-loading hidden" id="artist-detail-loading">
<div class="loading-spinner"></div>
<p>Loading artist discography...</p>
</div>
<!-- Error State -->
<div class="artist-detail-error hidden" id="artist-detail-error">
<div class="error-icon">⚠️</div>
<h3>Failed to load artist details</h3>
<p id="artist-detail-error-message">An error occurred while loading the artist's discography.
</p>
<button class="retry-btn" id="artist-detail-retry-btn">Retry</button>
</div>
<!-- Main Content -->
<div class="artist-detail-main" id="artist-detail-main">
<!-- Discography Filters -->
<div class="discography-filters" id="discography-filters">
<div class="filter-group">
<span class="filter-label">Show</span>
<button class="discography-filter-btn active" data-filter="category" data-value="albums">Albums</button>
<button class="discography-filter-btn active" data-filter="category" data-value="eps">EPs</button>
<button class="discography-filter-btn active" data-filter="category" data-value="singles">Singles</button>
</div>
<div class="filter-divider"></div>
<div class="filter-group">
<span class="filter-label">Include</span>
<button class="discography-filter-btn active" data-filter="content" data-value="live">Live</button>
<button class="discography-filter-btn active" data-filter="content" data-value="compilations">Compilations</button>
<button class="discography-filter-btn active" data-filter="content" data-value="featured">Featured</button>
</div>
<div class="filter-divider"></div>
<div class="filter-group">
<span class="filter-label">Status</span>
<button class="discography-filter-btn active" data-filter="ownership" data-value="all">All</button>
<button class="discography-filter-btn" data-filter="ownership" data-value="owned">Owned</button>
<button class="discography-filter-btn" data-filter="ownership" data-value="missing">Missing</button>
</div>
<div class="filter-divider"></div>
<div class="filter-group">
<span class="filter-label">View</span>
<button class="enhanced-view-toggle-btn active" data-view="standard" onclick="toggleEnhancedView(false)">Standard</button>
<button class="enhanced-view-toggle-btn" data-view="enhanced" onclick="toggleEnhancedView(true)">Enhanced</button>
</div>
</div>
<!-- Discography Sections -->
<div class="discography-sections">
<!-- Albums Section -->
<div class="discography-section" id="albums-section">
<div class="section-header">
<h3>Albums</h3>
<div class="section-stats">
<span id="albums-owned-count">0 owned</span>
<span id="albums-missing-count">0 missing</span>
</div>
</div>
<div class="releases-grid" id="albums-grid">
<!-- Album cards will be populated here -->
</div>
</div>
<!-- EPs Section -->
<div class="discography-section" id="eps-section">
<div class="section-header">
<h3>EPs</h3>
<div class="section-stats">
<span id="eps-owned-count">0 owned</span>
<span id="eps-missing-count">0 missing</span>
</div>
</div>
<div class="releases-grid" id="eps-grid">
<!-- EP cards will be populated here -->
</div>
</div>
<!-- Singles Section -->
<div class="discography-section" id="singles-section">
<div class="section-header">
<h3>Singles</h3>
<div class="section-stats">
<span id="singles-owned-count">0 owned</span>
<span id="singles-missing-count">0 missing</span>
</div>
</div>
<div class="releases-grid" id="singles-grid">
<!-- Single cards will be populated here -->
</div>
</div>
</div>
<!-- Enhanced Library Management View -->
<div class="enhanced-view-container hidden" id="enhanced-view-container">
<!-- Populated dynamically by renderEnhancedView() -->
</div>
</div>
</div>
</div>
<!-- Enhanced Bulk Edit Modal -->
<div class="modal-overlay hidden" id="enhanced-bulk-edit-overlay">
<div class="enhanced-bulk-modal">
<div class="enhanced-bulk-modal-header">
<h3 id="enhanced-bulk-modal-title">Batch Edit Tracks</h3>
<button class="enhanced-bulk-modal-close" onclick="closeBulkEditModal()">&times;</button>
</div>
<div class="enhanced-bulk-modal-body" id="enhanced-bulk-modal-body">
<!-- Populated dynamically -->
</div>
<div class="enhanced-bulk-modal-footer">
<button class="enhanced-bulk-btn secondary" onclick="closeBulkEditModal()">Cancel</button>
<button class="enhanced-bulk-btn primary" onclick="executeBulkEdit()">Apply Changes</button>
</div>
</div>
</div>
<!-- Enhanced Bulk Actions Bar -->
<div class="enhanced-bulk-bar" id="enhanced-bulk-bar">
<div class="enhanced-bulk-bar-info">
<span class="enhanced-bulk-bar-count" id="enhanced-bulk-count">0</span>
<span class="enhanced-bulk-bar-label">tracks selected</span>
</div>
<div class="enhanced-bulk-bar-actions">
<button class="enhanced-bulk-btn secondary" onclick="showBulkEditModal()">Edit Selected</button>
<button class="enhanced-bulk-btn tag-write" onclick="batchWriteTagsSelected()">Write Tags</button>
<button class="enhanced-bulk-btn clear" onclick="clearTrackSelection()">Clear Selection</button>
</div>
</div>
<!-- Tag Preview Modal -->
<div class="modal-overlay hidden" id="tag-preview-overlay">
<div class="enhanced-bulk-modal tag-preview-modal">
<div class="enhanced-bulk-modal-header">
<h3 id="tag-preview-title">Write Tags to File</h3>
<button class="enhanced-bulk-modal-close" onclick="closeTagPreviewModal()">&times;</button>
</div>
<div class="enhanced-bulk-modal-body" id="tag-preview-body">
<!-- Populated dynamically -->
</div>
<div class="enhanced-bulk-modal-footer">
<label class="tag-preview-cover-label">
<input type="checkbox" id="tag-preview-embed-cover" checked>
Embed cover art
</label>
<label class="tag-preview-cover-label hidden" id="tag-preview-sync-label">
<input type="checkbox" id="tag-preview-sync-server" checked>
<span id="tag-preview-sync-text">Sync to server</span>
</label>
<div class="tag-preview-footer-actions">
<button class="enhanced-bulk-btn secondary" onclick="closeTagPreviewModal()">Cancel</button>
<button class="enhanced-bulk-btn primary" id="tag-preview-write-btn" onclick="executeWriteTags()">Write Tags</button>
</div>
</div>
</div>
</div>
<!-- Batch Tag Preview Modal -->
<div class="modal-overlay hidden" id="batch-tag-preview-overlay">
<div class="enhanced-bulk-modal batch-tag-preview-modal">
<div class="enhanced-bulk-modal-header">
<h3 id="batch-tag-preview-title">Write Tags</h3>
<button class="enhanced-bulk-modal-close" onclick="closeBatchTagPreviewModal()">&times;</button>
</div>
<div id="batch-tag-preview-summary"></div>
<div class="enhanced-bulk-modal-body batch-tag-preview-body" id="batch-tag-preview-body">
<!-- Populated dynamically -->
</div>
<div class="enhanced-bulk-modal-footer">
<label class="tag-preview-cover-label">
<input type="checkbox" id="batch-tag-preview-embed-cover" checked>
Embed cover art
</label>
<label class="tag-preview-cover-label hidden" id="batch-tag-preview-sync-label">
<input type="checkbox" id="batch-tag-preview-sync-server" checked>
<span id="batch-tag-preview-sync-text">Sync to server</span>
</label>
<div class="tag-preview-footer-actions">
<button class="enhanced-bulk-btn secondary" onclick="closeBatchTagPreviewModal()">Cancel</button>
<button class="enhanced-bulk-btn primary" id="batch-tag-preview-write-btn" onclick="executeBatchWriteTags()">Write Tags</button>
</div>
</div>
</div>
</div>
<!-- Reorganize Album Modal -->
<div class="modal-overlay hidden" id="reorganize-overlay">
<div class="enhanced-bulk-modal reorganize-modal">
<div class="enhanced-bulk-modal-header">
<h3 id="reorganize-modal-title">Reorganize Album</h3>
<button class="enhanced-bulk-modal-close" onclick="closeReorganizeModal()">&times;</button>
</div>
<div class="enhanced-bulk-modal-body" id="reorganize-modal-body">
<!-- Populated dynamically -->
</div>
<div class="enhanced-bulk-modal-footer" id="reorganize-modal-footer">
<button class="enhanced-bulk-btn secondary" onclick="closeReorganizeModal()">Cancel</button>
<button class="enhanced-bulk-btn primary" id="reorganize-apply-btn" onclick="executeReorganize()" disabled>Apply</button>
</div>
</div>
</div>
<!-- Discover Page -->
<div class="page" id="discover-page">
<div class="discover-container">
<!-- Hero Section -->
<div class="discover-hero">
<div class="discover-hero-background" id="discover-hero-bg"></div>
<div class="discover-hero-overlay"></div>
<!-- Navigation Arrows -->
<button class="discover-hero-nav discover-hero-nav-prev" onclick="navigateDiscoverHero(-1)"
aria-label="Previous artist">
<span></span>
</button>
<button class="discover-hero-nav discover-hero-nav-next" onclick="navigateDiscoverHero(1)"
aria-label="Next artist">
<span></span>
</button>
<!-- Discover Page Help Button -->
<button class="tool-help-button discover-page-help-button" data-tool="discover-page"
title="Learn about the Discover page">?</button>
<div class="discover-hero-content">
<div class="discover-hero-info">
<div class="discover-hero-label">FEATURED ARTIST</div>
<h1 class="discover-hero-title" id="discover-hero-title">Loading...</h1>
<p class="discover-hero-subtitle" id="discover-hero-subtitle">Discover new music
tailored to your taste</p>
<div class="discover-hero-meta" id="discover-hero-meta">
<!-- Popularity and genres will be populated here -->
</div>
<div class="discover-hero-actions">
<button class="discover-hero-button secondary" id="discover-hero-discography"
onclick="viewDiscoverHeroDiscography()">
<span class="button-icon">📀</span>
<span class="button-text">View Discography</span>
</button>
<button class="discover-hero-button primary watchlist-toggle-btn"
id="discover-hero-add" onclick="toggleDiscoverHeroWatchlist(event)">
<span class="watchlist-icon">👁️</span>
<span class="watchlist-text">Add to Watchlist</span>
</button>
</div>
</div>
<div class="discover-hero-image" id="discover-hero-image">
<div class="hero-image-placeholder">🎧</div>
</div>
</div>
<!-- Slideshow Indicators -->
<div class="discover-hero-indicators" id="discover-hero-indicators"></div>
<!-- Hero Bottom Buttons -->
<div class="discover-hero-bottom-actions">
<button class="discover-hero-watch-all" id="discover-hero-watch-all" onclick="watchAllHeroArtists(this)">
<span class="watch-all-icon">👁️</span>
<span class="watch-all-text">Watch All</span>
</button>
<button class="discover-hero-view-all" id="discover-hero-view-all" onclick="openRecommendedArtistsModal()">
View Recommended
</button>
</div>
</div>
<!-- Spotify Library Section -->
<div class="discover-section" id="spotify-library-section" style="display: none;">
<div class="discover-section-header">
<div>
<h2 class="discover-section-title">Your Spotify Library</h2>
<p class="discover-section-subtitle" id="spotify-library-subtitle">Your saved albums on Spotify</p>
</div>
<div class="spotify-library-header-actions">
<button class="spotify-library-action-btn spotify-library-refresh-btn" onclick="refreshSpotifyLibraryCache()" title="Refresh from Spotify">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M23 4v6h-6M1 20v-6h6"/><path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"/></svg>
Refresh
</button>
<button class="spotify-library-action-btn spotify-library-download-btn" id="spotify-library-download-missing-btn" onclick="downloadMissingSpotifyLibraryAlbums()" style="display: none;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
Download Missing
</button>
</div>
</div>
<div class="spotify-library-filters" id="spotify-library-filters" style="display: none;">
<input type="text" class="spotify-library-search" id="spotify-library-search"
placeholder="Search by artist or album..." oninput="debouncedSpotifyLibrarySearch()">
<select id="spotify-library-status-filter" class="spotify-library-select" onchange="loadSpotifyLibraryAlbums()">
<option value="all">All Albums</option>
<option value="missing">Missing</option>
<option value="owned">Owned</option>
</select>
<select id="spotify-library-sort" class="spotify-library-select" onchange="loadSpotifyLibraryAlbums()">
<option value="date_saved">Date Saved</option>
<option value="artist_name">Artist</option>
<option value="album_name">Album</option>
<option value="release_date">Release Date</option>
</select>
</div>
<div class="spotify-library-grid" id="spotify-library-grid">
<div class="discover-loading">
<div class="loading-spinner"></div>
<p>Loading your Spotify library...</p>
</div>
</div>
<div class="spotify-library-pagination" id="spotify-library-pagination" style="display: none;"></div>
</div>
<!-- Recent Releases Section -->
<div class="discover-section">
<div class="discover-section-header">
<h2 class="discover-section-title">Recent Releases</h2>
<p class="discover-section-subtitle">New music from artists you follow</p>
</div>
<div class="discover-carousel" id="recent-releases-carousel">
<!-- Content will be populated dynamically -->
<div class="discover-loading">
<div class="loading-spinner"></div>
<p>Loading recent releases...</p>
</div>
</div>
</div>
<!-- Seasonal Albums Section (Auto-shows based on current season) -->
<div class="discover-section" id="seasonal-albums-section" style="display: none;">
<div class="discover-section-header">
<h2 class="discover-section-title" id="seasonal-albums-title">Seasonal</h2>
<p class="discover-section-subtitle" id="seasonal-albums-subtitle">Seasonal music</p>
</div>
<div class="discover-carousel" id="seasonal-albums-carousel">
<!-- Content will be populated dynamically -->
</div>
</div>
<!-- Seasonal Playlist Section (Auto-shows based on current season) -->
<div class="discover-section" id="seasonal-playlist-section" style="display: none;">
<div class="discover-section-header">
<div>
<h2 class="discover-section-title" id="seasonal-playlist-title">Seasonal Mix</h2>
<p class="discover-section-subtitle" id="seasonal-playlist-subtitle">Curated seasonal
playlist</p>
</div>
<div class="discover-section-actions">
<button class="action-button secondary"
onclick="openDownloadModalForDiscoverPlaylist('seasonal_playlist', 'Seasonal Mix')"
title="Download missing tracks">
<span class="button-icon"></span>
<span class="button-text">Download</span>
</button>
<button class="action-button primary" id="seasonal-playlist-sync-btn"
onclick="startDiscoverPlaylistSync('seasonal_playlist', 'Seasonal Mix')"
title="Sync to media server">
<span class="button-icon"></span>
<span class="button-text">Sync</span>
</button>
</div>
</div>
<!-- Sync Status Display -->
<div class="discover-sync-status" id="seasonal-playlist-sync-status" style="display: none;">
<div class="sync-status-content">
<div class="sync-status-label">
<span class="sync-icon"></span>
<span>Syncing to media server...</span>
</div>
<div class="sync-status-stats">
<span class="sync-stat"><span
id="seasonal-playlist-sync-completed">0</span></span>
<span class="sync-stat"><span id="seasonal-playlist-sync-pending">0</span></span>
<span class="sync-stat"><span id="seasonal-playlist-sync-failed">0</span></span>
<span class="sync-stat">(<span
id="seasonal-playlist-sync-percentage">0</span>%)</span>
</div>
</div>
</div>
<div class="discover-playlist-container compact" id="seasonal-playlist">
<!-- Content will be populated dynamically -->
</div>
</div>
<!-- Recently Added Section -->
<div class="discover-section" style="display: none;">
<div class="discover-section-header">
<h2 class="discover-section-title">🆕 Recently Added</h2>
<p class="discover-section-subtitle">Latest additions to your library</p>
</div>
<div class="discover-playlist-container compact" id="personalized-recently-added">
<!-- Content will be populated dynamically -->
</div>
</div>
<!-- Daily Mixes Section -->
<div class="discover-section" style="display: none;">
<div class="discover-section-header">
<h2 class="discover-section-title">🎵 Daily Mixes</h2>
<p class="discover-section-subtitle">Personalized mixes based on your taste</p>
</div>
<div class="discover-more-playlists-grid" id="daily-mixes-grid">
<!-- Content will be populated dynamically -->
</div>
</div>
<!-- Fresh Tape Section -->
<div class="discover-section">
<div class="discover-section-header">
<div>
<h2 class="discover-section-title">Fresh Tape</h2>
<p class="discover-section-subtitle">New drops from recent releases</p>
</div>
<div class="discover-section-actions">
<button class="action-button secondary"
onclick="openDownloadModalForDiscoverPlaylist('release_radar', 'Fresh Tape')"
title="Download missing tracks">
<span class="button-icon"></span>
<span class="button-text">Download</span>
</button>
<button class="action-button primary" id="release-radar-sync-btn"
onclick="startDiscoverPlaylistSync('release_radar', 'Fresh Tape')"
title="Sync to media server">
<span class="button-icon"></span>
<span class="button-text">Sync</span>
</button>
</div>
</div>
<!-- Sync Status Display -->
<div class="discover-sync-status" id="release-radar-sync-status" style="display: none;">
<div class="sync-status-content">
<div class="sync-status-label">
<span class="sync-icon"></span>
<span>Syncing to media server...</span>
</div>
<div class="sync-status-stats">
<span class="sync-stat"><span id="release-radar-sync-total">0</span></span>
<span class="sync-separator">/</span>
<span class="sync-stat"><span id="release-radar-sync-matched">0</span></span>
<span class="sync-separator">/</span>
<span class="sync-stat"><span id="release-radar-sync-failed">0</span></span>
<span class="sync-stat">(<span id="release-radar-sync-percentage">0</span>%)</span>
</div>
</div>
</div>
<div class="discover-playlist-container compact" id="release-radar-playlist">
<!-- Content will be populated dynamically -->
<div class="discover-loading">
<div class="loading-spinner"></div>
<p>Loading fresh tape...</p>
</div>
</div>
</div>
<!-- The Archives Section -->
<div class="discover-section">
<div class="discover-section-header">
<div>
<h2 class="discover-section-title">The Archives</h2>
<p class="discover-section-subtitle">Curated from your collection</p>
</div>
<div class="discover-section-actions">
<button class="action-button secondary"
onclick="openDownloadModalForDiscoverPlaylist('discovery_weekly', 'The Archives')"
title="Download missing tracks">
<span class="button-icon"></span>
<span class="button-text">Download</span>
</button>
<button class="action-button primary" id="discovery-weekly-sync-btn"
onclick="startDiscoverPlaylistSync('discovery_weekly', 'The Archives')"
title="Sync to media server">
<span class="button-icon"></span>
<span class="button-text">Sync</span>
</button>
</div>
</div>
<!-- Sync Status Display -->
<div class="discover-sync-status" id="discovery-weekly-sync-status" style="display: none;">
<div class="sync-status-content">
<div class="sync-status-label">
<span class="sync-icon"></span>
<span>Syncing to media server...</span>
</div>
<div class="sync-status-stats">
<span class="sync-stat"><span id="discovery-weekly-sync-total">0</span></span>
<span class="sync-separator">/</span>
<span class="sync-stat"><span id="discovery-weekly-sync-matched">0</span></span>
<span class="sync-separator">/</span>
<span class="sync-stat"><span id="discovery-weekly-sync-failed">0</span></span>
<span class="sync-stat">(<span
id="discovery-weekly-sync-percentage">0</span>%)</span>
</div>
</div>
</div>
<div class="discover-playlist-container compact" id="discovery-weekly-playlist">
<!-- Content will be populated dynamically -->
<div class="discover-loading">
<div class="loading-spinner"></div>
<p>Loading the archives...</p>
</div>
</div>
</div>
<!-- Popular Picks Section -->
<div class="discover-section" style="display: none;">
<div class="discover-section-header">
<div>
<h2 class="discover-section-title">🔥 Popular Picks</h2>
<p class="discover-section-subtitle">Trending tracks from new discoveries</p>
</div>
<div class="discover-section-actions">
<button class="action-button secondary"
onclick="openDownloadModalForDiscoverPlaylist('popular_picks', 'Popular Picks')"
title="Download missing tracks">
<span class="button-icon"></span>
<span class="button-text">Download</span>
</button>
<button class="action-button primary" id="popular-picks-sync-btn"
onclick="startDiscoverPlaylistSync('popular_picks', 'Popular Picks')"
title="Sync to media server">
<span class="button-icon"></span>
<span class="button-text">Sync</span>
</button>
</div>
</div>
<!-- Sync Status Display -->
<div class="discover-sync-status" id="popular-picks-sync-status" style="display: none;">
<div class="sync-status-content">
<div class="sync-status-label">
<span class="sync-icon"></span>
<span>Syncing to media server...</span>
</div>
<div class="sync-status-stats">
<span class="sync-stat"><span id="popular-picks-sync-completed">0</span></span>
<span class="sync-stat"><span id="popular-picks-sync-pending">0</span></span>
<span class="sync-stat"><span id="popular-picks-sync-failed">0</span></span>
<span class="sync-stat">(<span id="popular-picks-sync-percentage">0</span>%)</span>
</div>
</div>
</div>
<div class="discover-playlist-container compact" id="personalized-popular-picks">
<!-- Content will be populated dynamically -->
</div>
</div>
<!-- Hidden Gems Section -->
<div class="discover-section" style="display: none;">
<div class="discover-section-header">
<div>
<h2 class="discover-section-title">🌟 Hidden Gems</h2>
<p class="discover-section-subtitle">Underground discoveries waiting for you</p>
</div>
<div class="discover-section-actions">
<button class="action-button secondary"
onclick="openDownloadModalForDiscoverPlaylist('hidden_gems', 'Hidden Gems')"
title="Download missing tracks">
<span class="button-icon"></span>
<span class="button-text">Download</span>
</button>
<button class="action-button primary" id="hidden-gems-sync-btn"
onclick="startDiscoverPlaylistSync('hidden_gems', 'Hidden Gems')"
title="Sync to media server">
<span class="button-icon"></span>
<span class="button-text">Sync</span>
</button>
</div>
</div>
<!-- Sync Status Display -->
<div class="discover-sync-status" id="hidden-gems-sync-status" style="display: none;">
<div class="sync-status-content">
<div class="sync-status-label">
<span class="sync-icon"></span>
<span>Syncing to media server...</span>
</div>
<div class="sync-status-stats">
<span class="sync-stat"><span id="hidden-gems-sync-completed">0</span></span>
<span class="sync-stat"><span id="hidden-gems-sync-pending">0</span></span>
<span class="sync-stat"><span id="hidden-gems-sync-failed">0</span></span>
<span class="sync-stat">(<span id="hidden-gems-sync-percentage">0</span>%)</span>
</div>
</div>
</div>
<div class="discover-playlist-container compact" id="personalized-hidden-gems">
<!-- Content will be populated dynamically -->
</div>
</div>
<!-- Your Top 50 Section -->
<div class="discover-section" style="display: none;">
<div class="discover-section-header">
<h2 class="discover-section-title">🏆 Your Top 50</h2>
<p class="discover-section-subtitle">All-time favorites from your library</p>
</div>
<div class="discover-playlist-container compact" id="personalized-top-tracks">
<!-- Content will be populated dynamically -->
</div>
</div>
<!-- Forgotten Favorites Section -->
<div class="discover-section" style="display: none;">
<div class="discover-section-header">
<h2 class="discover-section-title">💎 Forgotten Favorites</h2>
<p class="discover-section-subtitle">Rediscover tracks you used to love</p>
</div>
<div class="discover-playlist-container compact" id="personalized-forgotten-favorites">
<!-- Content will be populated dynamically -->
</div>
</div>
<!-- Discovery Shuffle Section -->
<div class="discover-section" style="display: none;">
<div class="discover-section-header">
<div>
<h2 class="discover-section-title">🔀 Discovery Shuffle</h2>
<p class="discover-section-subtitle">Random tracks from your discovery pool - different
every time</p>
</div>
<div class="discover-section-actions">
<button class="action-button secondary"
onclick="openDownloadModalForDiscoverPlaylist('discovery_shuffle', 'Discovery Shuffle')"
title="Download missing tracks">
<span class="button-icon"></span>
<span class="button-text">Download</span>
</button>
<button class="action-button primary" id="discovery-shuffle-sync-btn"
onclick="startDiscoverPlaylistSync('discovery_shuffle', 'Discovery Shuffle')"
title="Sync to media server">
<span class="button-icon"></span>
<span class="button-text">Sync</span>
</button>
</div>
</div>
<!-- Sync Status Display -->
<div class="discover-sync-status" id="discovery-shuffle-sync-status" style="display: none;">
<div class="sync-status-content">
<div class="sync-status-label">
<span class="sync-icon"></span>
<span>Syncing to media server...</span>
</div>
<div class="sync-status-stats">
<span class="sync-stat"><span
id="discovery-shuffle-sync-completed">0</span></span>
<span class="sync-stat"><span id="discovery-shuffle-sync-pending">0</span></span>
<span class="sync-stat"><span id="discovery-shuffle-sync-failed">0</span></span>
<span class="sync-stat">(<span
id="discovery-shuffle-sync-percentage">0</span>%)</span>
</div>
</div>
</div>
<div class="discover-playlist-container compact" id="personalized-discovery-shuffle">
<!-- Content will be populated dynamically -->
</div>
</div>
<!-- Familiar Favorites Section -->
<div class="discover-section" style="display: none;">
<div class="discover-section-header">
<div>
<h2 class="discover-section-title">❤️ Familiar Favorites</h2>
<p class="discover-section-subtitle">Your reliable go-to tracks</p>
</div>
<div class="discover-section-actions">
<button class="action-button secondary"
onclick="openDownloadModalForDiscoverPlaylist('familiar_favorites', 'Familiar Favorites')"
title="Download missing tracks">
<span class="button-icon"></span>
<span class="button-text">Download</span>
</button>
<button class="action-button primary" id="familiar-favorites-sync-btn"
onclick="startDiscoverPlaylistSync('familiar_favorites', 'Familiar Favorites')"
title="Sync to media server">
<span class="button-icon"></span>
<span class="button-text">Sync</span>
</button>
</div>
</div>
<!-- Sync Status Display -->
<div class="discover-sync-status" id="familiar-favorites-sync-status" style="display: none;">
<div class="sync-status-content">
<div class="sync-status-label">
<span class="sync-icon"></span>
<span>Syncing to media server...</span>
</div>
<div class="sync-status-stats">
<span class="sync-stat"><span
id="familiar-favorites-sync-completed">0</span></span>
<span class="sync-stat"><span id="familiar-favorites-sync-pending">0</span></span>
<span class="sync-stat"><span id="familiar-favorites-sync-failed">0</span></span>
<span class="sync-stat">(<span
id="familiar-favorites-sync-percentage">0</span>%)</span>
</div>
</div>
</div>
<div class="discover-playlist-container compact" id="personalized-familiar-favorites">
<!-- Content will be populated dynamically -->
</div>
</div>
<!-- Build a Playlist Section -->
<div class="discover-section">
<div class="discover-section-header">
<h2 class="discover-section-title">
Build a Playlist
<span class="bp-info-toggle" onclick="document.getElementById('bp-info-panel').classList.toggle('visible')" title="How it works">?</span>
</h2>
<p class="discover-section-subtitle">Create a custom playlist from your favorite artists</p>
</div>
<div class="bp-info-panel" id="bp-info-panel">
<div class="bp-info-content">
<p><strong>How it works:</strong></p>
<ol>
<li>Search and select 1-5 seed artists you like</li>
<li>Hit Generate &mdash; the app finds similar artists, pulls their albums, and picks tracks at random</li>
<li>You get a fresh 50-track playlist mixing your picks with new discoveries</li>
</ol>
<p class="bp-info-note">Tip: The more seed artists you add, the more varied the playlist will be.</p>
</div>
</div>
<div class="build-playlist-container">
<!-- Artist Search -->
<div class="build-playlist-search-section">
<div class="bp-search-input-wrapper">
<svg class="bp-search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
<input type="text" id="build-playlist-search"
placeholder="Search for an artist..."
oninput="searchBuildPlaylistArtists()"
autocomplete="off" />
<div class="bp-search-spinner" id="bp-search-spinner" style="display: none;">
<div class="loading-spinner" style="width: 18px; height: 18px; border-width: 2px;"></div>
</div>
</div>
<div id="build-playlist-search-results" class="build-playlist-search-results"></div>
</div>
<!-- Selected Artists -->
<div class="build-playlist-selected-section">
<div class="bp-selected-header">
<h3>Seed Artists</h3>
<span class="bp-selected-counter" id="bp-selected-counter">0 / 5</span>
</div>
<div id="build-playlist-selected-artists" class="build-playlist-selected-artists">
<div class="build-playlist-no-selection">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width: 32px; height: 32px; opacity: 0.4; margin-bottom: 8px;"><path d="M12 4.5v15m7.5-7.5h-15"/></svg>
<span>Search above to add seed artists</span>
</div>
</div>
</div>
<!-- Generate Button -->
<div class="build-playlist-actions">
<button id="build-playlist-generate-btn" class="build-playlist-generate-btn"
onclick="generateBuildPlaylist()" disabled>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 18px; height: 18px;"><polygon points="5 3 19 12 5 21 5 3"/></svg>
Generate Playlist
</button>
<div id="build-playlist-loading" class="build-playlist-loading" style="display: none;">
<div class="loading-spinner"></div>
<span>Finding similar artists and building your playlist...</span>
</div>
</div>
<!-- Generated Playlist Results -->
<div id="build-playlist-results-wrapper" style="display: none;">
<!-- Playlist Header with Actions -->
<div class="discover-section-header" style="margin-top: 20px;">
<div>
<h3 id="build-playlist-results-title"
style="margin: 0; color: #fff; font-size: 18px;">Generated Playlist</h3>
<p id="build-playlist-results-subtitle"
style="margin: 4px 0 0 0; color: #999; font-size: 13px;"></p>
</div>
<div class="discover-section-actions">
<button class="action-button secondary"
onclick="openDownloadModalForBuildPlaylist()"
title="Download missing tracks">
<span class="button-icon"></span>
<span class="button-text">Download</span>
</button>
<button class="action-button primary" id="build-playlist-sync-btn"
onclick="startDiscoverPlaylistSync('build_playlist', 'Custom Playlist')"
title="Sync to media server">
<span class="button-icon"></span>
<span class="button-text">Sync</span>
</button>
</div>
</div>
<!-- Sync Status Display -->
<div class="discover-sync-status" id="build-playlist-sync-status"
style="display: none;">
<div class="sync-status-content">
<div class="sync-status-label">
<span class="sync-icon"></span>
<span>Syncing to media server...</span>
</div>
<div class="sync-status-stats">
<span class="sync-stat"><span
id="build-playlist-sync-completed">0</span></span>
<span class="sync-stat"><span
id="build-playlist-sync-pending">0</span></span>
<span class="sync-stat"><span
id="build-playlist-sync-failed">0</span></span>
<span class="sync-stat">(<span
id="build-playlist-sync-percentage">0</span>%)</span>
</div>
</div>
</div>
<!-- Metadata Display -->
<div id="build-playlist-metadata-display"></div>
<!-- Track List -->
<div id="build-playlist-results" class="discover-playlist-container compact">
<!-- Generated playlist will appear here -->
</div>
</div>
</div>
</div>
<!-- ListenBrainz Playlists (Tabbed) -->
<div class="discover-section">
<div class="discover-section-header">
<div>
<h2 class="discover-section-title">🧠 ListenBrainz Playlists</h2>
<p class="discover-section-subtitle" id="listenbrainz-section-subtitle">Playlists from ListenBrainz</p>
</div>
<div class="discover-section-actions">
<button class="action-button primary" id="listenbrainz-refresh-btn"
onclick="refreshListenBrainzPlaylists()"
title="Refresh playlists from ListenBrainz">
<span class="button-icon">🔄</span>
<span class="button-text">Refresh</span>
</button>
</div>
</div>
<!-- ListenBrainz Tabs -->
<div class="listenbrainz-tabs" id="listenbrainz-tabs">
<div class="discover-loading">
<div class="loading-spinner"></div>
<p>Loading playlists...</p>
</div>
</div>
<!-- ListenBrainz Tab Content -->
<div class="listenbrainz-tab-content" id="listenbrainz-tab-content">
<!-- Content will be populated dynamically -->
</div>
</div>
<!-- Time Machine (Tabbed by Decade) -->
<div class="discover-section">
<div class="discover-section-header">
<h2 class="discover-section-title">⏰ Time Machine</h2>
<p class="discover-section-subtitle">Explore music from different decades</p>
</div>
<!-- Decade Tabs (will be populated dynamically) -->
<div class="decade-tabs" id="decade-tabs">
<div class="discover-loading">
<div class="loading-spinner"></div>
<p>Loading decades...</p>
</div>
</div>
<!-- Decade Tab Contents (will be populated dynamically) -->
<div id="decade-tab-contents"></div>
</div>
<!-- Browse by Genre (Tabbed by Genre) -->
<div class="discover-section">
<div class="discover-section-header">
<h2 class="discover-section-title">🎵 Browse by Genre</h2>
<p class="discover-section-subtitle">Discover music by your favorite genres</p>
</div>
<!-- Genre Tabs (will be populated dynamically) -->
<div class="genre-tabs" id="genre-tabs">
<div class="discover-loading">
<div class="loading-spinner"></div>
<p>Loading genres...</p>
</div>
</div>
<!-- Genre Tab Contents (will be populated dynamically) -->
<div id="genre-tab-contents"></div>
</div>
</div>
</div>
<!-- Playlist Explorer Page -->
<div class="page" id="playlist-explorer-page">
<div class="explorer-container">
<!-- Header (compact) -->
<div class="dashboard-header" style="margin-bottom: 12px;">
<div class="header-text">
<h2 class="header-title"><img src="/static/explorer.png" class="page-header-icon" alt=""><span>Playlist Explorer</span></h2>
</div>
</div>
<!-- Playlist picker + controls inline -->
<div class="explorer-playlist-picker" id="explorer-playlist-picker">
<div class="explorer-picker-top">
<div class="explorer-picker-tabs" id="explorer-picker-tabs"></div>
<div class="explorer-controls">
<div class="explorer-mode-toggle">
<button class="explorer-mode-btn active" data-mode="albums"
onclick="explorerSetMode('albums')">Albums</button>
<button class="explorer-mode-btn" data-mode="discographies"
onclick="explorerSetMode('discographies')">Discographies</button>
</div>
<button class="explorer-build-btn" id="explorer-build-btn"
onclick="explorerBuildTree()">Explore</button>
</div>
</div>
<div class="explorer-picker-scroll" id="explorer-picker-scroll">
<!-- Populated by JS -->
</div>
</div>
<!-- Action bar (sticky, appears after tree built) -->
<div class="explorer-action-bar" id="explorer-action-bar" style="display: none;">
<span class="explorer-selection-count" id="explorer-selection-count">0 albums selected</span>
<div class="explorer-action-buttons">
<button class="explorer-action-btn" onclick="explorerSelectAll()">Select All</button>
<button class="explorer-action-btn" onclick="explorerDeselectAll()">Deselect</button>
<button class="explorer-action-btn primary" onclick="explorerAddToWishlist()">
Add to Wishlist</button>
</div>
<span class="explorer-nav-hint">Scroll to zoom &middot; Right-drag to pan &middot; Double-click album for tracks</span>
</div>
<!-- Tree viewport -->
<div class="explorer-viewport" id="explorer-viewport">
<div class="explorer-zoom-controls">
<button class="explorer-zoom-btn" onclick="explorerZoom(0.15)" title="Zoom in">+</button>
<button class="explorer-zoom-btn" onclick="explorerZoom(-0.15)" title="Zoom out">&minus;</button>
<button class="explorer-zoom-btn" onclick="explorerFitToView()" title="Fit to view">&#11036;</button>
<button class="explorer-zoom-btn" onclick="_explorer._zoom=1; explorerZoom(0)" title="Reset zoom">1:1</button>
</div>
<div class="explorer-tree" id="explorer-tree">
<svg class="explorer-svg" id="explorer-svg"></svg>
<!-- Empty state -->
<div class="explorer-empty" id="explorer-empty">
<div class="explorer-empty-icon">
<svg viewBox="0 0 80 80" fill="none" stroke="currentColor" stroke-width="1.5">
<circle cx="40" cy="12" r="8"/>
<line x1="40" y1="20" x2="40" y2="35"/>
<line x1="40" y1="35" x2="18" y2="55"/>
<line x1="40" y1="35" x2="62" y2="55"/>
<circle cx="18" cy="60" r="6"/>
<circle cx="40" cy="60" r="6"/>
<circle cx="62" cy="60" r="6"/>
<line x1="40" y1="35" x2="40" y2="54"/>
</svg>
</div>
<p class="explorer-empty-title">Select a playlist to explore</p>
<p class="explorer-empty-desc">Choose a mirrored playlist and mode above, then click Explore to build the discovery tree</p>
</div>
</div>
</div>
<!-- Building progress -->
<div class="explorer-progress" id="explorer-progress" style="display: none;">
<div class="explorer-progress-bar">
<div class="explorer-progress-fill" id="explorer-progress-fill"></div>
</div>
<span class="explorer-progress-text" id="explorer-progress-text">Building tree...</span>
</div>
</div>
</div>
<!-- Settings Page -->
<div class="page" id="settings-page">
<div class="dashboard-header">
<div class="header-text">
<h2 class="header-title"><img src="/static/settings.png" class="page-header-icon" alt=""><span>Settings</span></h2>
<p class="header-subtitle">Configure services, downloads, and preferences</p>
</div>
<div class="header-spacer"></div>
<div class="header-actions">
<button class="save-button" onclick="document.getElementById('save-settings').click()">Save Settings</button>
</div>
</div>
<div class="settings-content">
<!-- Category Tab Bar -->
<div class="stg-tabbar">
<button class="stg-tab active" data-tab="connections" onclick="switchSettingsTab('connections')">Connections</button>
<button class="stg-tab" data-tab="downloads" onclick="switchSettingsTab('downloads')">Downloads</button>
<button class="stg-tab" data-tab="library" onclick="switchSettingsTab('library')">Library</button>
<button class="stg-tab" data-tab="appearance" onclick="switchSettingsTab('appearance')">Appearance</button>
<button class="stg-tab" data-tab="advanced" onclick="switchSettingsTab('advanced')">Advanced</button>
</div>
<!-- Settings Panels -->
<div class="settings-columns">
<!-- Left Column - API Configuration -->
<div class="settings-left-column">
<!-- API Configuration -->
<div class="settings-group" data-stg="connections">
<h3>API Configuration</h3>
<!-- Spotify Settings -->
<div class="api-service-frame">
<h4 class="service-title spotify-title">Spotify</h4>
<div class="form-group">
<label>Client ID:</label>
<input type="text" id="spotify-client-id" placeholder="Spotify Client ID">
</div>
<div class="form-group">
<label>Client Secret:</label>
<input type="password" id="spotify-client-secret"
placeholder="Spotify Client Secret">
</div>
<div class="form-group">
<label>Redirect URI:</label>
<input type="text" id="spotify-redirect-uri"
placeholder="http://127.0.0.1:8888/callback">
</div>
<div class="callback-info">
<div class="callback-label">Current Redirect URI:</div>
<div class="callback-url" id="spotify-callback-display">
http://127.0.0.1:8888/callback</div>
<div class="callback-help">Add this URL to your Spotify app's 'Redirect URIs' in
the Spotify Developer Dashboard</div>
</div>
<div class="form-actions">
<button class="auth-button" onclick="authenticateSpotify()">🔐
Authenticate</button>
<button class="auth-button disconnect-button" id="spotify-disconnect-btn"
onclick="disconnectSpotify()" style="display: none;">🔌
Disconnect</button>
<button class="auth-button disconnect-button"
onclick="clearSpotifyCacheAndFallback()"
title="Clear Spotify token cache and switch to your configured fallback metadata source">🗑️
Clear Cache &amp; Use Fallback</button>
</div>
</div>
<!-- Tidal Playlist/Metadata Auth -->
<div class="api-service-frame">
<h4 class="service-title tidal-title">Tidal (Playlists & Metadata)</h4>
<div class="form-group">
<label>Client ID:</label>
<input type="text" id="tidal-client-id" placeholder="Tidal Client ID">
</div>
<div class="form-group">
<label>Client Secret:</label>
<input type="password" id="tidal-client-secret"
placeholder="Tidal Client Secret">
</div>
<div class="form-group">
<label>Redirect URI:</label>
<input type="text" id="tidal-redirect-uri"
placeholder="http://127.0.0.1:8889/tidal/callback">
</div>
<div class="callback-info">
<div class="callback-label">Current Redirect URI:</div>
<div class="callback-url" id="tidal-callback-display">
http://127.0.0.1:8889/tidal/callback</div>
<div class="callback-help">Add this URL to your Tidal app configuration</div>
</div>
<div class="form-actions">
<button class="auth-button" onclick="authenticateTidal()">🔐
Authenticate</button>
</div>
</div>
<!-- Qobuz Metadata/Enrichment Auth -->
<div class="api-service-frame">
<h4 class="service-title qobuz-title">Qobuz (Metadata & Enrichment)</h4>
<div id="qobuz-connection-logged-in" style="display: none; margin-bottom: 8px;">
<span id="qobuz-connection-user-info" class="setting-help-text" style="color: #4caf50;"></span>
<button class="auth-button" onclick="logoutQobuz()" style="margin-left: 8px;">
Disconnect
</button>
</div>
<div id="qobuz-connection-form">
<div class="form-group">
<label>Email:</label>
<input type="email" id="qobuz-connection-email" class="form-input"
placeholder="Qobuz email" autocomplete="email">
</div>
<div class="form-group">
<label>Password:</label>
<input type="password" id="qobuz-connection-password" class="form-input"
placeholder="Qobuz password" autocomplete="current-password">
</div>
<div class="form-actions">
<button class="auth-button" id="qobuz-connection-login-btn" onclick="loginQobuzFromConnections()">
Connect Qobuz
</button>
<span id="qobuz-connection-status" class="setting-help-text" style="margin-left: 8px;"></span>
</div>
</div>
<div class="setting-help-text" style="margin-top: 6px;">
Connects Qobuz for metadata enrichment (ISRC, labels, copyright). Also used for downloads if Qobuz is your download source.
</div>
</div>
<!-- ListenBrainz Settings -->
<div class="api-service-frame">
<h4 class="service-title listenbrainz-title">ListenBrainz</h4>
<div class="form-group">
<label>API Base URL:</label>
<input type="text" id="listenbrainz-base-url"
placeholder="Leave empty for official (api.listenbrainz.org)">
</div>
<div class="form-group">
<label>User Token:</label>
<input type="password" id="listenbrainz-token"
placeholder="ListenBrainz User Token">
</div>
<div class="callback-info">
<div class="callback-help">Get your token from <a
href="https://listenbrainz.org/profile/" target="_blank"
style="color: #eb743b;">ListenBrainz Settings</a></div>
<div class="callback-help">Self-hosted? Enter your server URL (e.g. http://localhost:8093)</div>
</div>
<div class="form-group" style="margin-top: 12px;">
<label class="checkbox-label">
<input type="checkbox" id="listenbrainz-scrobble-enabled">
Scrobble plays to ListenBrainz
</label>
</div>
</div>
<!-- AcoustID Settings -->
<div class="api-service-frame">
<h4 class="service-title acoustid-title">AcoustID Verification</h4>
<div class="form-group" style="margin-bottom: 12px;">
<label class="checkbox-label"
style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
<input type="checkbox" id="acoustid-enabled"
style="width: 16px; height: 16px;">
<span>Enable Download Verification</span>
</label>
<div style="color: #888; font-size: 0.8em; margin-top: 4px; margin-left: 24px;">
Verifies downloaded audio matches expected track using fingerprints
</div>
</div>
<div class="form-group">
<label>API Key:</label>
<input type="password" id="acoustid-api-key" placeholder="AcoustID API Key">
</div>
<div class="callback-info">
<div class="callback-help">Get your free API key from <a
href="https://acoustid.org/new-application" target="_blank"
style="color: #ba55d3;">AcoustID Applications</a></div>
<div class="callback-help"
style="opacity: 0.7; font-size: 0.85em; margin-top: 4px;">
The fpcalc fingerprint tool is automatically downloaded if needed.
Failed verifications move files to Quarantine folder.
</div>
</div>
<button class="test-button" onclick="clearQuarantine()" style="margin-top: 10px; background: rgba(255,82,82,0.15); border-color: rgba(255,82,82,0.3); color: #ff5252;">Clear Quarantine</button>
</div>
<!-- Last.fm Settings -->
<div class="api-service-frame">
<h4 class="service-title lastfm-title">Last.fm</h4>
<div class="form-group">
<label>API Key:</label>
<input type="password" id="lastfm-api-key"
placeholder="Last.fm API Key">
</div>
<div class="form-group">
<label>API Secret:</label>
<input type="password" id="lastfm-api-secret"
placeholder="Last.fm API Secret (required for scrobbling)">
</div>
<div class="callback-info">
<div class="callback-help">Get your API key and secret from <a
href="https://www.last.fm/api/account/create" target="_blank"
style="color: #d51007;">Last.fm API Account</a></div>
<div class="callback-help">API key: used for metadata enrichment. API secret: required for scrobbling.</div>
</div>
<div class="form-group" style="margin-top: 12px;">
<label class="checkbox-label">
<input type="checkbox" id="lastfm-scrobble-enabled">
Scrobble plays to Last.fm
</label>
</div>
<div class="form-actions" id="lastfm-scrobble-actions">
<button class="test-button" onclick="authorizeLastfmScrobbling()">Authorize Scrobbling</button>
<span class="setting-help-text" id="lastfm-scrobble-status"></span>
</div>
</div>
<!-- Genius Settings -->
<div class="api-service-frame">
<h4 class="service-title genius-title">Genius</h4>
<div class="form-group">
<label>Client Access Token:</label>
<input type="password" id="genius-access-token"
placeholder="Genius Client Access Token">
</div>
<div class="callback-info">
<div class="callback-help">Get your token from <a
href="https://genius.com/api-clients" target="_blank"
style="color: #ffff64;">Genius API Clients</a></div>
<div class="callback-help">Generate a "Client Access Token" — no OAuth flow needed.</div>
</div>
</div>
<!-- iTunes Settings -->
<div class="api-service-frame">
<h4 class="service-title itunes-title">iTunes / Apple Music</h4>
<div class="form-group">
<label>Storefront Country:</label>
<select id="itunes-country">
<option value="US">United States (US)</option>
<option value="GB">United Kingdom (GB)</option>
<option value="CA">Canada (CA)</option>
<option value="AU">Australia (AU)</option>
<option value="DE">Germany (DE)</option>
<option value="FR">France (FR)</option>
<option value="JP">Japan (JP)</option>
<option value="KR">South Korea (KR)</option>
<option value="BR">Brazil (BR)</option>
<option value="SE">Sweden (SE)</option>
<option value="NL">Netherlands (NL)</option>
<option value="IT">Italy (IT)</option>
<option value="ES">Spain (ES)</option>
<option value="MX">Mexico (MX)</option>
<option value="IN">India (IN)</option>
<option value="RU">Russia (RU)</option>
</select>
</div>
<div class="callback-info">
<div class="callback-help">Sets the primary Apple Music storefront. Region-specific albums are auto-searched across other storefronts as fallback.</div>
</div>
</div>
<!-- Metadata Source Selection -->
<div class="api-service-frame">
<h4 class="service-title" style="color: #e8e8e8;">Metadata Source</h4>
<div class="form-group">
<label>Fallback Source:</label>
<select id="metadata-fallback-source">
<option value="itunes">iTunes / Apple Music</option>
<option value="deezer">Deezer</option>
</select>
</div>
<div class="callback-info">
<div class="callback-help">When Spotify is not connected, this source provides artist, album, and track metadata. Hydrabase requires a connection configured above.</div>
</div>
</div>
<!-- Hydrabase P2P Metadata -->
<div class="api-service-frame" data-stg="connections">
<h4 class="service-title" style="color: #00b4d8;">Hydrabase</h4>
<input type="hidden" id="hydrabase-enabled" value="false">
<div class="form-group">
<label>WebSocket URL:</label>
<input type="text" id="hydrabase-url" placeholder="ws://localhost:4545">
</div>
<div class="form-group">
<label>API Key:</label>
<input type="password" id="hydrabase-api-key" placeholder="Hydrabase API Key">
</div>
<div class="form-group" style="margin-bottom: 8px;">
<label class="checkbox-label" style="display: flex; align-items: center; gap: 8px; cursor: pointer; padding: 0;">
<input type="checkbox" id="hydrabase-auto-connect" style="width: 16px; height: 16px;">
<span>Auto-connect on startup</span>
</label>
</div>
<div class="callback-info">
<div class="callback-help">P2P metadata network. When enabled and connected, replaces Spotify/iTunes as the primary metadata source for searches.</div>
</div>
<div class="form-actions">
<button class="test-button" id="hydrabase-connect-btn" onclick="toggleHydrabaseFromSettings()">Connect</button>
<span id="hydrabase-settings-status" style="font-size: 0.82em; color: rgba(255,255,255,0.4); margin-left: 8px;"></span>
</div>
</div>
<!-- Test Connection Buttons -->
<div class="api-test-buttons">
<button class="test-button" onclick="testConnection('spotify')">Test
Spotify</button>
<button class="test-button" onclick="testConnection('tidal')">Test Tidal</button>
<button class="test-button" onclick="testConnection('listenbrainz')">Test
ListenBrainz</button>
<button class="test-button" onclick="testConnection('acoustid')">Test
AcoustID</button>
<button class="test-button" onclick="testConnection('lastfm')">Test
Last.fm</button>
<button class="test-button" onclick="testConnection('genius')">Test
Genius</button>
</div>
</div>
<!-- Server Connections -->
<div class="settings-group" data-stg="connections">
<h3>Server Connections</h3>
<!-- Server Toggle Buttons -->
<div class="server-toggle-container">
<button class="server-toggle-btn active" id="plex-toggle"
onclick="toggleServer('plex')">
<img src="https://www.plex.tv/wp-content/themes/plex/assets/img/plex-logo.svg"
alt="Plex" class="server-logo">
</button>
<button class="server-toggle-btn" id="jellyfin-toggle"
onclick="toggleServer('jellyfin')">
<img src="https://jellyfin.org/images/logo.svg" alt="Jellyfin"
class="server-logo">
</button>
<button class="server-toggle-btn" id="navidrome-toggle"
onclick="toggleServer('navidrome')">
<img src="https://tweakers.net/ext/i/2007323764.png" alt="Navidrome"
class="server-logo">
Navidrome
</button>
</div>
<!-- Plex Settings -->
<div class="server-config-container" id="plex-container">
<div class="form-group">
<label>Plex Server URL:</label>
<input type="url" id="plex-url" placeholder="http://localhost:32400">
</div>
<div class="form-group">
<label>Plex Token:</label>
<input type="password" id="plex-token" placeholder="X-Plex-Token">
</div>
<div class="form-group" id="plex-library-selector-container" style="display: none;">
<label>Music Library:</label>
<select id="plex-music-library" onchange="selectPlexLibrary()">
<option value="">Loading...</option>
</select>
<small style="color: #999; font-size: 0.9em; display: block; margin-top: 5px;">
Select which music library to use (doesn't affect config file)
</small>
</div>
<div class="form-actions">
<button class="detect-button" onclick="autoDetectPlex()">Auto-detect</button>
<button class="test-button" onclick="testConnection('plex')">Test</button>
</div>
</div>
<!-- Jellyfin Settings -->
<div class="server-config-container hidden" id="jellyfin-container">
<div class="form-group">
<label>Jellyfin Server URL:</label>
<input type="url" id="jellyfin-url" placeholder="http://localhost:8096">
</div>
<div class="form-group">
<label>API Key:</label>
<input type="password" id="jellyfin-api-key" placeholder="Jellyfin API Key">
</div>
<div class="form-actions">
<button class="detect-button"
onclick="autoDetectJellyfin()">Auto-detect</button>
<button class="test-button" onclick="testConnection('jellyfin')">Test</button>
</div>
<div class="form-group">
<label>API Timeout (seconds):</label>
<input type="number" id="jellyfin-timeout" placeholder="120" min="15"
max="300" value="120">
<small class="settings-hint">Timeout for
bulk API requests during database sync (15-300s). Increase if your
Jellyfin server is slow to respond.</small>
</div>
<div class="form-group" id="jellyfin-user-selector-container"
style="display: none;">
<label>Jellyfin User</label>
<select id="jellyfin-user" onchange="selectJellyfinUser()">
<option value="">Select User</option>
</select>
<small>Select which user's music library to scan</small>
</div>
<div class="form-group" id="jellyfin-library-selector-container"
style="display: none;">
<label>Music Library</label>
<select id="jellyfin-music-library" onchange="selectJellyfinLibrary()">
<option value="">Select Library</option>
</select>
<small>Select which music library to use (doesn't affect config file)</small>
</div>
</div>
<!-- Navidrome Settings -->
<div class="server-config-container hidden" id="navidrome-container">
<div class="form-group">
<label>Navidrome Server URL:</label>
<input type="url" id="navidrome-url" placeholder="http://localhost:4533">
</div>
<div class="form-group">
<label>Username:</label>
<input type="text" id="navidrome-username" placeholder="Username">
</div>
<div class="form-group">
<label>Password:</label>
<input type="password" id="navidrome-password" placeholder="Password">
</div>
<div class="form-group" id="navidrome-folder-selector-container"
style="display: none;">
<label>Music Library</label>
<select id="navidrome-music-folder" onchange="selectNavidromeMusicFolder()">
<option value="">All Libraries</option>
</select>
<small>Select which music library to import from</small>
</div>
<div class="form-actions">
<button class="detect-button"
onclick="autoDetectNavidrome()">Auto-detect</button>
<button class="test-button" onclick="testConnection('navidrome')">Test</button>
</div>
</div>
<!-- Server Test Button -->
<div class="server-test-section">
<button class="test-button server-test-btn" onclick="testConnection('server')">Test
Server</button>
</div>
</div>
</div>
<!-- Right Column - Download Settings, Database, Metadata, Logging -->
<div class="settings-right-column">
<!-- Download Settings -->
<div class="settings-group" data-stg="downloads">
<h3>Download Settings</h3>
<div class="setting-help-text">
These are container-internal paths. Only modify them if you know what you're doing — incorrect values will break downloads.
</div>
<div class="form-group">
<label>Slskd Download Dir:</label>
<div class="path-input-group">
<input type="text" id="download-path" placeholder="./downloads" readonly>
<button class="browse-button locked" onclick="togglePathLock('download', this)">Unlock</button>
</div>
</div>
<div class="form-group">
<label>Matched Transfer Dir (Plex Music Dir?):</label>
<div class="path-input-group">
<input type="text" id="transfer-path" placeholder="./Transfer" readonly>
<button class="browse-button locked" onclick="togglePathLock('transfer', this)">Unlock</button>
</div>
</div>
<div class="form-group">
<label>Import Staging Dir:</label>
<div class="path-input-group">
<input type="text" id="staging-path" placeholder="./Staging" readonly>
<button class="browse-button locked" onclick="togglePathLock('staging', this)">Unlock</button>
</div>
</div>
<div class="form-group">
<label>Download Source:</label>
<select id="download-source-mode" class="form-select"
onchange="updateDownloadSourceUI()">
<option value="soulseek">Soulseek Only</option>
<option value="youtube">YouTube Only</option>
<option value="tidal">Tidal Only</option>
<option value="qobuz">Qobuz Only</option>
<option value="hifi">HiFi Only (Free Lossless)</option>
<option value="deezer_dl">Deezer Only</option>
<option value="hybrid">Hybrid (Primary + Fallback)</option>
</select>
<div class="setting-help-text">
Choose where to download music from. Hybrid mode tries primary source first,
then falls back to secondary if it fails.
</div>
</div>
<div class="form-group">
<label>Stream / Preview Source:</label>
<select id="stream-source" class="form-select">
<option value="youtube">YouTube (Instant)</option>
<option value="active">Active Download Source</option>
</select>
<div class="setting-help-text">
Where to stream audio when previewing tracks. YouTube is instant and requires no setup.
"Active Download Source" uses your configured source (Tidal, Qobuz, etc.) — if that's Soulseek, YouTube is used automatically.
</div>
</div>
<!-- Hybrid Mode Settings (shown only when hybrid is selected) -->
<div id="hybrid-settings-container" style="display: none;">
<div class="setting-help-text">
Drag to reorder source priority. Enable sources you want to use. Downloads try each enabled source in order.
</div>
<div class="hybrid-source-list" id="hybrid-source-list">
<!-- Populated by JS -->
</div>
<!-- Hidden selects for backward compatibility with saveSettings -->
<select id="hybrid-primary-source" style="display:none;"><option value="soulseek">Soulseek</option><option value="youtube">YouTube</option><option value="tidal">Tidal</option><option value="qobuz">Qobuz</option><option value="hifi">HiFi</option><option value="deezer_dl">Deezer</option></select>
<select id="hybrid-secondary-source" style="display:none;"><option value="soulseek">Soulseek</option><option value="youtube">YouTube</option><option value="tidal">Tidal</option><option value="qobuz">Qobuz</option><option value="hifi">HiFi</option><option value="deezer_dl">Deezer</option></select>
</div>
<!-- Soulseek Settings (shown when soulseek is active source) -->
<div id="soulseek-settings-container" style="display: none;">
<div class="form-group">
<label>slskd URL:</label>
<div class="path-input-group">
<input type="url" id="soulseek-url" placeholder="http://localhost:5030">
<button class="detect-button"
onclick="autoDetectSlskd()">Auto-detect</button>
</div>
</div>
<div class="form-group">
<label>API Key:</label>
<input type="password" id="soulseek-api-key" placeholder="Slskd API Key">
</div>
<div class="form-group">
<label>Search Timeout (seconds):</label>
<input type="number" id="soulseek-search-timeout" placeholder="60" min="15"
max="300" value="60">
<small class="settings-hint">How long to search
for tracks (15-300 seconds)</small>
</div>
<div class="form-group">
<label>Search Timeout Buffer (seconds):</label>
<input type="number" id="soulseek-search-timeout-buffer" placeholder="15"
min="5" max="60" value="15">
<small class="settings-hint">Extra time to wait
for late results (5-60 seconds)</small>
</div>
<div class="form-group">
<label>Minimum Peer Upload Speed:</label>
<select id="soulseek-min-peer-speed">
<option value="0">Any speed</option>
<option value="1">1 Mbps</option>
<option value="2">2 Mbps</option>
<option value="3">3 Mbps</option>
<option value="4">4 Mbps</option>
<option value="5">5 Mbps</option>
<option value="10">10 Mbps</option>
</select>
<small class="settings-hint">Ignore search results from peers with upload speed below this threshold</small>
</div>
<div class="form-group">
<label>Max Peer Queue Length:</label>
<select id="soulseek-max-peer-queue">
<option value="0">No limit</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
<small class="settings-hint">Skip peers with a queue longer than this (prefers peers with shorter queues regardless)</small>
</div>
<div class="form-group">
<label>Download Timeout (minutes):</label>
<input type="number" id="soulseek-download-timeout" placeholder="10" min="2"
max="60" value="10">
<small class="settings-hint">Abandon stuck downloads after this many minutes</small>
</div>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="soulseek-auto-clear-searches" checked>
Auto-clear slskd search history
</label>
<small class="settings-hint">Automatically cleans old searches from slskd when over 200 entries.
Disable if you want to keep manual searches in slskd's queue.</small>
</div>
<div class="form-actions" style="margin-top: 8px;">
<button class="test-button" onclick="testConnection('soulseek')">Test Soulseek Connection</button>
</div>
</div>
<!-- Tidal Download Settings (shown only when tidal mode is selected) -->
<div id="tidal-download-settings-container" style="display: none;">
<div class="form-group">
<label>Tidal Download Quality:</label>
<select id="tidal-download-quality" class="form-select">
<option value="low">Low (AAC 96kbps)</option>
<option value="high">High (AAC 320kbps)</option>
<option value="lossless">Lossless (FLAC 16-bit/44.1kHz)</option>
<option value="hires">HiRes (FLAC 24-bit/96kHz)</option>
</select>
<div class="setting-help-text">
Audio quality for Tidal downloads. HiRes requires a Tidal HiFi Plus subscription.
</div>
<label class="checkbox-inline" style="margin-top: 8px;">
<input type="checkbox" id="tidal-allow-fallback" checked>
Allow quality fallback
</label>
<div class="setting-help-text">
When disabled, only downloads at the exact quality selected. If unavailable, skips to the next source.
</div>
</div>
<div class="form-group">
<label>Tidal Download Auth:</label>
<div class="form-actions" style="margin-top: 4px;">
<button class="auth-button" id="tidal-download-auth-btn" onclick="startTidalDownloadAuth()">
Link Tidal Account
</button>
<span id="tidal-download-auth-status" class="setting-help-text" style="margin-left: 8px;"></span>
</div>
<div id="tidal-download-auth-code" style="display: none; margin-top: 8px;" class="setting-help-text">
</div>
</div>
</div>
<!-- Qobuz Settings (shown only when qobuz mode is selected) -->
<div id="qobuz-settings-container" style="display: none;">
<div class="form-group">
<label>Qobuz Download Quality:</label>
<select id="qobuz-quality" class="form-select">
<option value="mp3">MP3 320kbps</option>
<option value="lossless">Lossless (FLAC 16-bit/44.1kHz)</option>
<option value="hires">Hi-Res (FLAC 24-bit/96kHz)</option>
<option value="hires_max">Hi-Res Max (FLAC 24-bit/192kHz)</option>
</select>
<div class="setting-help-text">
Audio quality for Qobuz downloads. Hi-Res requires a Qobuz Studio or Sublime subscription.
</div>
<label class="checkbox-inline" style="margin-top: 8px;">
<input type="checkbox" id="qobuz-allow-fallback" checked>
Allow quality fallback
</label>
<div class="setting-help-text">
When disabled, only downloads at the exact quality selected. If unavailable, skips to the next source.
</div>
</div>
<div class="form-group">
<label>Qobuz Account:</label>
<div id="qobuz-auth-logged-in" style="display: none; margin-top: 4px;">
<span id="qobuz-auth-user-info" class="setting-help-text" style="color: #4caf50;"></span>
<button class="auth-button" onclick="logoutQobuz()" style="margin-left: 8px;">
Disconnect
</button>
</div>
<div id="qobuz-auth-form" style="margin-top: 4px;">
<input type="email" id="qobuz-email" class="form-input"
placeholder="Qobuz email" autocomplete="email"
style="margin-bottom: 8px;">
<input type="password" id="qobuz-password" class="form-input"
placeholder="Qobuz password" autocomplete="current-password"
style="margin-bottom: 8px;">
<div class="form-actions">
<button class="auth-button" id="qobuz-login-btn" onclick="loginQobuz()">
Connect Qobuz
</button>
<span id="qobuz-auth-status" class="setting-help-text" style="margin-left: 8px;"></span>
</div>
</div>
<div class="setting-help-text" style="margin-top: 6px;">
Requires a paid Qobuz subscription. Streams are DRM-free.
</div>
</div>
</div>
<!-- HiFi Download Settings (shown only when hifi mode is selected) -->
<div id="hifi-download-settings-container" style="display: none;">
<div class="form-group">
<label>HiFi Download Quality:</label>
<select id="hifi-download-quality" class="form-select">
<option value="low">Low (AAC 96kbps)</option>
<option value="high">High (AAC 320kbps)</option>
<option value="lossless">Lossless (FLAC 16-bit/44.1kHz)</option>
<option value="hires">HiRes (FLAC 24-bit/96kHz)</option>
</select>
<div class="setting-help-text">
Audio quality for HiFi downloads. Uses public API instances — no account required.
</div>
<label class="checkbox-inline" style="margin-top: 8px;">
<input type="checkbox" id="hifi-allow-fallback" checked>
Allow quality fallback
</label>
<div class="setting-help-text">
When disabled, only downloads at the exact quality selected. If unavailable, skips to the next source.
</div>
</div>
<div class="form-group">
<label>HiFi Status:</label>
<div class="form-actions" style="margin-top: 4px;">
<button class="test-button" id="hifi-test-btn" onclick="testHiFiConnection()">
Test Connection
</button>
<span id="hifi-connection-status" class="setting-help-text" style="margin-left: 8px;"></span>
</div>
<div class="setting-help-text" style="margin-top: 6px;">
Free lossless downloads via community-run hifi-api instances. No authentication or subscription needed.
</div>
</div>
</div>
<!-- Deezer Download Settings (shown only when deezer_dl mode is selected) -->
<div id="deezer-download-settings-container" style="display: none;">
<div class="form-group">
<label>Deezer Download Quality:</label>
<select id="deezer-download-quality" class="form-select">
<option value="mp3_128">MP3 128kbps (Free)</option>
<option value="mp3_320">MP3 320kbps (Premium)</option>
<option value="flac">FLAC Lossless (HiFi)</option>
</select>
<div class="setting-help-text">
Audio quality for Deezer downloads. FLAC requires a Deezer HiFi subscription.
MP3 320 requires Premium or higher.
</div>
<label class="checkbox-inline" style="margin-top: 8px;">
<input type="checkbox" id="deezer-allow-fallback" checked>
Allow quality fallback
</label>
<div class="setting-help-text">
When disabled, only downloads at the exact quality selected. If unavailable, skips to the next source.
</div>
</div>
<div class="form-group">
<label>Deezer ARL Token:</label>
<input type="password" id="deezer-download-arl" class="form-input"
placeholder="Paste your ARL cookie token here">
<div class="setting-help-text">
Your ARL token authenticates with Deezer. To get it:
log into <a href="https://www.deezer.com" target="_blank" style="color: rgb(var(--accent-rgb));">deezer.com</a>
→ open browser DevTools (F12) → Application tab → Cookies → copy the <code>arl</code> value.
</div>
</div>
<div class="form-group">
<label>Deezer Status:</label>
<div class="form-actions" style="margin-top: 4px;">
<button class="test-button" id="deezer-download-test-btn" onclick="testDeezerDownloadConnection()">
Test Connection
</button>
<span id="deezer-download-status" class="setting-help-text" style="margin-left: 8px;"></span>
</div>
</div>
</div>
<!-- YouTube Settings (shown when youtube or hybrid mode) -->
<div id="youtube-settings-container" style="display: none;">
<div class="form-group">
<label>YouTube Browser Cookies:</label>
<select id="youtube-cookies-browser" class="form-select">
<option value="">None (No cookies)</option>
<option value="chrome">Chrome</option>
<option value="firefox">Firefox</option>
<option value="edge">Edge</option>
<option value="brave">Brave</option>
<option value="opera">Opera</option>
<option value="safari">Safari</option>
</select>
<div class="setting-help-text">
If YouTube shows "Sign in to confirm you're not a bot", select your browser here.
SoulSync will use your browser's YouTube cookies to authenticate.
</div>
</div>
<div class="form-group">
<label>YouTube Download Delay:</label>
<input type="number" id="youtube-download-delay" min="0" max="30" step="1"
value="3" placeholder="3">
<div class="setting-help-text">
Seconds between YouTube downloads to avoid bot detection. Default: 3
</div>
</div>
</div>
<div class="form-group">
<label>Log Level:</label>
<select id="log-level-select" class="form-select" onchange="changeLogLevel()">
<option value="DEBUG">DEBUG (Detailed)</option>
<option value="INFO" selected>INFO (Normal)</option>
<option value="WARNING">WARNING (Minimal)</option>
<option value="ERROR">ERROR (Critical Only)</option>
</select>
<div class="setting-help-text">
Controls the level of detail in application logs. DEBUG shows all details, INFO
shows general operations, WARNING and ERROR show only issues.
</div>
</div>
</div>
<!-- Quality Profile Settings (Soulseek only) -->
<div class="settings-group" id="quality-profile-section" data-stg="downloads">
<h3>🎵 Quality Profile</h3>
<!-- Presets -->
<div class="quality-presets">
<label>Quick Presets:</label>
<div class="preset-buttons">
<button class="preset-button" onclick="applyQualityPreset('audiophile')"
title="FLAC only, strict size constraints">
🎧 Audiophile
</button>
<button class="preset-button active" onclick="applyQualityPreset('balanced')"
title="FLAC preferred, MP3 fallback">
⚖️ Balanced
</button>
<button class="preset-button" onclick="applyQualityPreset('space_saver')"
title="MP3 preferred, smaller sizes">
💾 Space Saver
</button>
</div>
</div>
<!-- FLAC Quality -->
<div class="quality-tier">
<div class="quality-tier-header">
<label class="checkbox-label">
<input type="checkbox" id="quality-flac-enabled" checked
onchange="toggleQuality('flac')">
<span class="quality-tier-name">FLAC (Lossless)</span>
</label>
<span class="quality-tier-priority" id="priority-flac">Priority: 1</span>
</div>
<div class="quality-tier-sliders" id="sliders-flac">
<div class="slider-group">
<label>Bitrate Range:</label>
<div class="dual-slider-container">
<input type="range" class="range-slider range-slider-min" id="flac-min"
min="0" max="10000" value="500" step="100"
oninput="updateQualityRange('flac')">
<input type="range" class="range-slider range-slider-max" id="flac-max"
min="0" max="10000" value="10000" step="100"
oninput="updateQualityRange('flac')">
<div class="range-slider-track"></div>
</div>
<div class="slider-values">
<span id="flac-min-value">500 kbps</span>
<span>-</span>
<span id="flac-max-value">10000 kbps</span>
</div>
</div>
</div>
<div class="flac-bit-depth-selector" id="flac-bit-depth-selector">
<label>Bit Depth:</label>
<div class="bit-depth-buttons">
<button class="bit-depth-btn active" data-value="any" onclick="setFlacBitDepth('any')">Any</button>
<button class="bit-depth-btn" data-value="16" onclick="setFlacBitDepth('16')">16-bit</button>
<button class="bit-depth-btn" data-value="24" onclick="setFlacBitDepth('24')">24-bit</button>
</div>
<div class="flac-fallback-toggle" id="flac-fallback-toggle" style="display: none; margin-top: 6px;">
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer; font-size: 0.8em; color: #ccc;">
<input type="checkbox" id="flac-bit-depth-fallback" checked onchange="setFlacBitDepthFallback(this.checked)">
Accept other bit depths as fallback
</label>
<div style="color: #888; font-size: 0.75em; margin-top: 2px;">
When enabled, accepts any FLAC rather than rejecting on bit depth mismatch
</div>
</div>
</div>
</div>
<!-- MP3 320 Quality -->
<div class="quality-tier">
<div class="quality-tier-header">
<label class="checkbox-label">
<input type="checkbox" id="quality-mp3_320-enabled" checked
onchange="toggleQuality('mp3_320')">
<span class="quality-tier-name">MP3 320 kbps</span>
</label>
<span class="quality-tier-priority" id="priority-mp3_320">Priority: 2</span>
</div>
<div class="quality-tier-sliders" id="sliders-mp3_320">
<div class="slider-group">
<label>Bitrate Range:</label>
<div class="dual-slider-container">
<input type="range" class="range-slider range-slider-min"
id="mp3_320-min" min="0" max="500" value="280" step="10"
oninput="updateQualityRange('mp3_320')">
<input type="range" class="range-slider range-slider-max"
id="mp3_320-max" min="0" max="500" value="500" step="10"
oninput="updateQualityRange('mp3_320')">
<div class="range-slider-track"></div>
</div>
<div class="slider-values">
<span id="mp3_320-min-value">280 kbps</span>
<span>-</span>
<span id="mp3_320-max-value">500 kbps</span>
</div>
</div>
</div>
</div>
<!-- MP3 256 Quality -->
<div class="quality-tier">
<div class="quality-tier-header">
<label class="checkbox-label">
<input type="checkbox" id="quality-mp3_256-enabled" checked
onchange="toggleQuality('mp3_256')">
<span class="quality-tier-name">MP3 256 kbps</span>
</label>
<span class="quality-tier-priority" id="priority-mp3_256">Priority: 3</span>
</div>
<div class="quality-tier-sliders" id="sliders-mp3_256">
<div class="slider-group">
<label>Bitrate Range:</label>
<div class="dual-slider-container">
<input type="range" class="range-slider range-slider-min"
id="mp3_256-min" min="0" max="400" value="200" step="10"
oninput="updateQualityRange('mp3_256')">
<input type="range" class="range-slider range-slider-max"
id="mp3_256-max" min="0" max="400" value="400" step="10"
oninput="updateQualityRange('mp3_256')">
<div class="range-slider-track"></div>
</div>
<div class="slider-values">
<span id="mp3_256-min-value">200 kbps</span>
<span>-</span>
<span id="mp3_256-max-value">400 kbps</span>
</div>
</div>
</div>
</div>
<!-- MP3 192 Quality -->
<div class="quality-tier">
<div class="quality-tier-header">
<label class="checkbox-label">
<input type="checkbox" id="quality-mp3_192-enabled"
onchange="toggleQuality('mp3_192')">
<span class="quality-tier-name">MP3 192 kbps</span>
</label>
<span class="quality-tier-priority" id="priority-mp3_192">Priority: 4</span>
</div>
<div class="quality-tier-sliders disabled" id="sliders-mp3_192">
<div class="slider-group">
<label>Bitrate Range:</label>
<div class="dual-slider-container">
<input type="range" class="range-slider range-slider-min"
id="mp3_192-min" min="0" max="300" value="150" step="10"
oninput="updateQualityRange('mp3_192')">
<input type="range" class="range-slider range-slider-max"
id="mp3_192-max" min="0" max="300" value="300" step="10"
oninput="updateQualityRange('mp3_192')">
<div class="range-slider-track"></div>
</div>
<div class="slider-values">
<span id="mp3_192-min-value">150 kbps</span>
<span>-</span>
<span id="mp3_192-max-value">300 kbps</span>
</div>
</div>
</div>
</div>
<!-- Fallback Option -->
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="quality-fallback-enabled" checked>
Allow fallback to any quality if preferred qualities unavailable
</label>
</div>
<div class="help-text">
<strong>How it works:</strong> Downloads try each enabled quality in priority order
(1 = highest).
MIN bitrate catches fake/transcoded files (e.g., FLAC below 500 kbps is likely a
re-encoded MP3). MAX bitrate limits hi-res files if you want to save space.
When track duration is unavailable, a generous file-size safety net is used instead.
</div>
</div>
<!-- File Organization Settings -->
<div class="settings-group" data-stg="library">
<h3>📁 File Organization</h3>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="file-organization-enabled" checked>
Enable custom file organization templates
</label>
</div>
<div class="form-group">
<label>Album Path Template:</label>
<input type="text" id="template-album-path"
placeholder="$albumartist/$albumartist - $album/$track - $title">
<small class="settings-hint">Variables: $albumartist, $artist, $artistletter, $album, $albumtype,
$title, $track, $disc, $year, $quality (filename only)</small>
</div>
<div class="form-group">
<label>Single Path Template:</label>
<input type="text" id="template-single-path"
placeholder="$artist/$artist - $title/$title">
<small class="settings-hint">Variables: $albumartist, $artist, $artistletter, $title, $album, $albumtype, $year, $quality (filename only)</small>
</div>
<div class="form-group">
<label>Playlist Path Template:</label>
<input type="text" id="template-playlist-path"
placeholder="$playlist/$artist - $title">
<small class="settings-hint">Variables: $playlist, $albumartist, $artist, $artistletter, $title, $year, $quality (filename only)</small>
</div>
<div class="form-group">
<label>Multi-Disc Folder Label:</label>
<select id="disc-label">
<option value="Disc">Disc (e.g., Disc 1/)</option>
<option value="CD">CD (e.g., CD 1/)</option>
</select>
<small class="settings-hint">Label used for auto-created disc subfolders on multi-disc albums.</small>
</div>
<div class="form-group">
<label>Collaborative Album Artist:</label>
<select id="collab-artist-mode" class="form-select">
<option value="first">First Listed Artist (recommended)</option>
<option value="all">All Artists Combined</option>
</select>
<small class="settings-hint">How $albumartist resolves for albums with multiple artists.
"First Listed" uses only the primary artist for folder names (e.g., "Larry June" instead of "Larry June, Curren$y & The Alchemist").
Full artist list is always preserved in file metadata tags.</small>
</div>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="allow-duplicate-tracks" checked>
Allow duplicate tracks across albums
</label>
<small class="settings-hint">When enabled, the same song can be added to the wishlist from
different albums (e.g., completing a discography where albums share tracks).
When disabled, tracks with the same name and artist are skipped.</small>
</div>
<div class="form-group">
<button class="test-button" onclick="resetFileOrganizationTemplates()"
style="background: #666;">
🔄 Reset to Defaults
</button>
<small class="settings-hint">
Restores original path structure. Your downloads will be organized like before.
</small>
</div>
</div>
<!-- Metadata Enhancement Settings -->
<div class="settings-group" data-stg="library">
<h3>🎵 Post-Processing</h3>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="metadata-enabled" checked
onchange="document.getElementById('post-processing-options').style.display = this.checked ? 'block' : 'none'">
Enable post-processing on downloaded files
</label>
<small class="settings-hint">Master toggle — disabling this skips all metadata, artwork, and lyrics processing</small>
</div>
<div id="post-processing-options">
<!-- Core Features -->
<div class="post-processing-section">
<div class="post-processing-section-title">Core Features</div>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="embed-album-art" checked>
Embed album art into file
</label>
</div>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="cover-art-download" checked>
Download cover.jpg to album folder
</label>
</div>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="prefer-caa-art">
Use MusicBrainz Cover Art Archive for album art
</label>
<small>Higher resolution but quality may vary. When off, uses Spotify/iTunes/Deezer art (consistent 640x640).</small>
</div>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="lrclib-enabled" checked>
Generate .lrc lyrics files (LRClib)
</label>
</div>
</div>
<!-- Tag Embedding — per-tag toggles grouped by source -->
<div class="post-processing-section">
<div class="post-processing-section-title">Tag Embedding</div>
<small class="settings-hint" style="margin-bottom: 8px; display: block;">Toggle a service to enable/disable all its tags, or expand to control individual tags.</small>
<!-- Spotify -->
<div class="tag-service-group">
<div class="tag-service-header" onclick="toggleTagGroup(this)">
<span class="tag-group-arrow">&#9654;</span>
<label class="checkbox-label" onclick="event.stopPropagation()">
<input type="checkbox" id="embed-spotify" checked onchange="toggleServiceTags(this, 'spotify')"> Spotify
</label>
<span class="tag-service-count">3 tags</span>
</div>
<div class="tag-service-body" style="display:none;">
<label class="checkbox-label"><input type="checkbox" data-config="spotify.tags.track_id" checked> Track ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="spotify.tags.artist_id" checked> Artist ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="spotify.tags.album_id" checked> Album ID</label>
</div>
</div>
<!-- iTunes -->
<div class="tag-service-group">
<div class="tag-service-header" onclick="toggleTagGroup(this)">
<span class="tag-group-arrow">&#9654;</span>
<label class="checkbox-label" onclick="event.stopPropagation()">
<input type="checkbox" id="embed-itunes" checked onchange="toggleServiceTags(this, 'itunes')"> iTunes
</label>
<span class="tag-service-count">3 tags</span>
</div>
<div class="tag-service-body" style="display:none;">
<label class="checkbox-label"><input type="checkbox" data-config="itunes.tags.track_id" checked> Track ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="itunes.tags.artist_id" checked> Artist ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="itunes.tags.album_id" checked> Album ID</label>
</div>
</div>
<!-- MusicBrainz -->
<div class="tag-service-group">
<div class="tag-service-header" onclick="toggleTagGroup(this)">
<span class="tag-group-arrow">&#9654;</span>
<label class="checkbox-label" onclick="event.stopPropagation()">
<input type="checkbox" id="embed-musicbrainz" checked onchange="toggleServiceTags(this, 'musicbrainz')"> MusicBrainz
</label>
<span class="tag-service-count">18 tags</span>
</div>
<div class="tag-service-body" style="display:none;">
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.recording_id" checked> Recording ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.artist_id" checked> Artist ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.release_id" checked> Release ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.release_group_id" checked> Release Group ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.album_artist_id" checked> Album Artist ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.release_track_id" checked> Release Track ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.release_type" checked> Release Type</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.original_date" checked> Original Date</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.release_status" checked> Release Status</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.release_country" checked> Release Country</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.barcode" checked> Barcode</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.media" checked> Media</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.total_discs" checked> Total Discs</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.catalog_number" checked> Catalog Number</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.script" checked> Script</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.asin" checked> ASIN</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.isrc" checked> ISRC</label>
<label class="checkbox-label"><input type="checkbox" data-config="musicbrainz.tags.genres" checked> Genres</label>
</div>
</div>
<!-- Deezer -->
<div class="tag-service-group">
<div class="tag-service-header" onclick="toggleTagGroup(this)">
<span class="tag-group-arrow">&#9654;</span>
<label class="checkbox-label" onclick="event.stopPropagation()">
<input type="checkbox" id="embed-deezer" checked onchange="toggleServiceTags(this, 'deezer')"> Deezer
</label>
<span class="tag-service-count">4 tags</span>
</div>
<div class="tag-service-body" style="display:none;">
<label class="checkbox-label"><input type="checkbox" data-config="deezer.tags.track_id" checked> Track ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="deezer.tags.artist_id" checked> Artist ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="deezer.tags.bpm" checked> BPM</label>
<label class="checkbox-label"><input type="checkbox" data-config="deezer.tags.isrc" checked> ISRC</label>
</div>
</div>
<!-- AudioDB -->
<div class="tag-service-group">
<div class="tag-service-header" onclick="toggleTagGroup(this)">
<span class="tag-group-arrow">&#9654;</span>
<label class="checkbox-label" onclick="event.stopPropagation()">
<input type="checkbox" id="embed-audiodb" checked onchange="toggleServiceTags(this, 'audiodb')"> AudioDB
</label>
<span class="tag-service-count">4 tags</span>
</div>
<div class="tag-service-body" style="display:none;">
<label class="checkbox-label"><input type="checkbox" data-config="audiodb.tags.track_id" checked> Track ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="audiodb.tags.mood" checked> Mood</label>
<label class="checkbox-label"><input type="checkbox" data-config="audiodb.tags.style" checked> Style</label>
<label class="checkbox-label"><input type="checkbox" data-config="audiodb.tags.genre" checked> Genre</label>
</div>
</div>
<!-- Tidal -->
<div class="tag-service-group">
<div class="tag-service-header" onclick="toggleTagGroup(this)">
<span class="tag-group-arrow">&#9654;</span>
<label class="checkbox-label" onclick="event.stopPropagation()">
<input type="checkbox" id="embed-tidal" checked onchange="toggleServiceTags(this, 'tidal')"> Tidal
</label>
<span class="tag-service-count">4 tags</span>
</div>
<div class="tag-service-body" style="display:none;">
<label class="checkbox-label"><input type="checkbox" data-config="tidal.tags.track_id" checked> Track ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="tidal.tags.artist_id" checked> Artist ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="tidal.tags.isrc" checked> ISRC</label>
<label class="checkbox-label"><input type="checkbox" data-config="tidal.tags.copyright" checked> Copyright</label>
</div>
</div>
<!-- Qobuz -->
<div class="tag-service-group">
<div class="tag-service-header" onclick="toggleTagGroup(this)">
<span class="tag-group-arrow">&#9654;</span>
<label class="checkbox-label" onclick="event.stopPropagation()">
<input type="checkbox" id="embed-qobuz" checked onchange="toggleServiceTags(this, 'qobuz')"> Qobuz
</label>
<span class="tag-service-count">5 tags</span>
</div>
<div class="tag-service-body" style="display:none;">
<label class="checkbox-label"><input type="checkbox" data-config="qobuz.tags.track_id" checked> Track ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="qobuz.tags.artist_id" checked> Artist ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="qobuz.tags.isrc" checked> ISRC</label>
<label class="checkbox-label"><input type="checkbox" data-config="qobuz.tags.copyright" checked> Copyright</label>
<label class="checkbox-label"><input type="checkbox" data-config="qobuz.tags.label" checked> Label</label>
</div>
</div>
<!-- Last.fm -->
<div class="tag-service-group">
<div class="tag-service-header" onclick="toggleTagGroup(this)">
<span class="tag-group-arrow">&#9654;</span>
<label class="checkbox-label" onclick="event.stopPropagation()">
<input type="checkbox" id="embed-lastfm" checked onchange="toggleServiceTags(this, 'lastfm')"> Last.fm
</label>
<span class="tag-service-count">2 tags</span>
</div>
<div class="tag-service-body" style="display:none;">
<label class="checkbox-label"><input type="checkbox" data-config="lastfm.tags.genres" checked> Genres</label>
<label class="checkbox-label"><input type="checkbox" data-config="lastfm.tags.url" checked> URL</label>
</div>
</div>
<!-- Genius -->
<div class="tag-service-group">
<div class="tag-service-header" onclick="toggleTagGroup(this)">
<span class="tag-group-arrow">&#9654;</span>
<label class="checkbox-label" onclick="event.stopPropagation()">
<input type="checkbox" id="embed-genius" checked onchange="toggleServiceTags(this, 'genius')"> Genius
</label>
<span class="tag-service-count">2 tags</span>
</div>
<div class="tag-service-body" style="display:none;">
<label class="checkbox-label"><input type="checkbox" data-config="genius.tags.track_id" checked> Track ID</label>
<label class="checkbox-label"><input type="checkbox" data-config="genius.tags.url" checked> URL</label>
</div>
</div>
<!-- General -->
<div class="tag-service-group">
<div class="tag-service-header" onclick="toggleTagGroup(this)">
<span class="tag-group-arrow">&#9654;</span>
<label class="checkbox-label" style="pointer-events:none;">General</label>
<span class="tag-service-count">2 tags</span>
</div>
<div class="tag-service-body" style="display:none;">
<label class="checkbox-label"><input type="checkbox" data-config="metadata_enhancement.tags.quality_tag" checked> Quality
<small class="tag-embed-desc">Audio quality (e.g. FLAC, MP3-320)</small>
</label>
<label class="checkbox-label"><input type="checkbox" data-config="metadata_enhancement.tags.genre_merge" checked> Genre Merging
<small class="tag-embed-desc">Merge genres from Spotify + enabled sources</small>
</label>
</div>
</div>
</div>
<div class="form-group">
<label>Supported Formats:</label>
<div class="supported-formats">MP3, FLAC, MP4/M4A, OGG</div>
</div>
</div>
</div>
<!-- Post-Download Conversion Settings -->
<div class="settings-group" data-stg="library">
<h3>📀 Post-Download Conversion</h3>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="downsample-hires">
Downsample Hi-Res FLAC to CD quality (16-bit / 44.1kHz)
</label>
</div>
<div class="help-text">
When a 24-bit or high sample rate FLAC is downloaded, it will be
automatically converted to 16-bit/44.1kHz. The hi-res original is
replaced — not kept alongside. Requires ffmpeg.
</div>
<div class="form-group" style="margin-top: 16px;">
<label class="checkbox-label">
<input type="checkbox" id="lossy-copy-enabled"
onchange="document.getElementById('lossy-copy-options').style.display = this.checked ? 'block' : 'none'">
Create lossy copy of downloaded FLAC files
</label>
</div>
<div id="lossy-copy-options" style="display: none;">
<div class="form-group">
<label>Codec:</label>
<select id="lossy-copy-codec" onchange="updateLossyBitrateOptions()">
<option value="mp3">MP3</option>
<option value="opus">Opus</option>
<option value="aac">AAC (M4A)</option>
</select>
</div>
<div class="form-group">
<label>Bitrate:</label>
<select id="lossy-copy-bitrate">
<option value="320">320 kbps</option>
<option value="256">256 kbps</option>
<option value="192">192 kbps</option>
<option value="128">128 kbps</option>
</select>
</div>
<div class="help-text">
After downloading a FLAC, a lossy copy at the selected codec and bitrate
will be created in the same folder. Requires ffmpeg.
</div>
<div class="form-group" style="margin-top: 12px;">
<label class="checkbox-label">
<input type="checkbox" id="lossy-copy-delete-original">
Blasphemy Mode — Delete original FLAC after conversion
</label>
</div>
<div class="help-text" style="color: rgba(255, 100, 100, 0.8);">
Warning: The original high-quality file will be permanently deleted.
Only the lossy copy will remain.
</div>
</div>
</div>
<!-- Listening Stats Settings -->
<div class="settings-group" data-stg="library">
<h3>📊 Listening Stats</h3>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="listening-stats-enabled">
Enable listening stats collection from media server
</label>
</div>
<div class="form-group">
<label>Poll Interval (minutes):</label>
<input type="number" id="listening-stats-interval" min="5" max="1440" value="30">
</div>
<div class="help-text">
Polls your active media server (Plex, Jellyfin, or Navidrome) for play history
and displays stats on the Stats page.
</div>
</div>
<!-- Security Settings -->
<div class="settings-group" data-stg="advanced">
<h3>🔒 Security</h3>
<div class="form-group" id="security-pin-setup" style="display: none;">
<label>Set Admin PIN:</label>
<div class="setting-help-text" style="margin-bottom: 8px;">
You need to set a PIN before enabling the lock screen.
</div>
<input type="password" id="security-new-pin" placeholder="Enter PIN" maxlength="20" autocomplete="off" style="margin-bottom: 6px;">
<input type="password" id="security-confirm-pin" placeholder="Confirm PIN" maxlength="20" autocomplete="off" style="margin-bottom: 6px;">
<button class="auth-button" id="security-save-pin-btn" onclick="saveSecurityPin()">Save PIN</button>
<p id="security-pin-msg" class="setting-help-text" style="margin-top: 6px; display: none;"></p>
</div>
<div class="form-group">
<label class="toggle-label">
<input type="checkbox" id="security-require-pin" onchange="handleSecurityPinToggle(this)">
<span>Require PIN to access SoulSync</span>
</label>
<div class="setting-help-text">
When enabled, a lock screen appears on every page load. PIN is verified against the admin account. Closing the browser tab requires re-entry.
</div>
</div>
<div class="form-group" id="security-change-pin-section" style="display: none;">
<button class="auth-button" onclick="showChangeSecurityPin()">Change PIN</button>
</div>
</div>
<!-- Discovery Settings -->
<div class="settings-group" data-stg="advanced">
<h3>🔍 Discovery Pool Settings</h3>
<div class="form-group">
<label>Lookback Period:</label>
<select id="discovery-lookback-period" class="form-select">
<option value="7">Last 7 days</option>
<option value="30" selected>Last 30 days (Default)</option>
<option value="90">Last 90 days</option>
<option value="180">Last 6 months</option>
<option value="all">Entire discography</option>
</select>
<div class="setting-help-text">
Controls how far back to scan when adding new artists or refreshing discovery
pool.
Once scanned, artists are kept updated with new releases only.
</div>
</div>
<div class="form-group">
<label>Hemisphere:</label>
<select id="discovery-hemisphere" class="form-select">
<option value="northern">Northern Hemisphere</option>
<option value="southern">Southern Hemisphere</option>
</select>
<div class="setting-help-text">
Adjusts seasonal playlists on the Discover page to match your region.
Southern hemisphere flips seasons (e.g. December = Summer, June = Winter).
Holidays like Christmas and Halloween stay calendar-fixed.
</div>
</div>
</div>
</div>
<!-- Third Column - Database, Metadata, Playlist Sync, Logging -->
<div class="settings-third-column">
<!-- UI Appearance -->
<div class="settings-group" data-stg="appearance">
<h3>UI Appearance</h3>
<div class="form-group">
<label>Accent Color:</label>
<div class="accent-color-selector">
<select id="accent-preset" class="form-select">
<option value="#1db954">Spotify Green</option>
<option value="#1d8ab9">Ocean Blue</option>
<option value="#a78bfa">Purple</option>
<option value="#8b5cf6">Boulder Purple</option>
<option value="#f59e0b">Sunset Orange</option>
<option value="#f43f5e">Rose</option>
<option value="#14b8a6">Teal</option>
<option value="custom">Custom</option>
</select>
<div class="accent-preview-swatch" id="accent-preview-swatch"></div>
</div>
<small class="settings-hint">Changes the accent color across the entire UI</small>
</div>
<div class="form-group" id="custom-color-group" style="display:none;">
<label>Custom Color:</label>
<input type="color" id="accent-custom-color" value="#1db954">
</div>
<div class="form-group">
<label>Sidebar Visualizer:</label>
<select id="sidebar-visualizer-type" class="form-select">
<option value="bars">Bars</option>
<option value="wave">Wave</option>
<option value="spectrum">Spectrum</option>
<option value="mirror">Mirror</option>
<option value="equalizer">Equalizer</option>
<option value="none">None</option>
</select>
<small class="settings-hint">Audio-reactive visualizer on the sidebar edge when music is playing</small>
</div>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="particles-enabled" checked>
Background Particles
</label>
<small class="settings-hint">Animated particle effects behind each page. Disable to reduce GPU usage.</small>
</div>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="worker-orbs-enabled" checked>
Worker Orbs
</label>
<small class="settings-hint">Dashboard header buttons animate as floating orbs. Hover the header to expand. Desktop only.</small>
</div>
</div>
<!-- Database Settings -->
<div class="settings-group" data-stg="advanced">
<h3>Database Settings</h3>
<div class="form-group">
<label>Concurrent Workers:</label>
<select id="max-workers">
<option value="3">3</option>
<option value="4">4</option>
<option value="5" selected>5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
</div>
<div class="help-text">Number of parallel threads for database updates. Higher values =
faster updates but more server load.</div>
</div>
<!-- Content Filter Settings -->
<div class="settings-group" data-stg="library">
<h3>🔞 Content Filter</h3>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="allow-explicit" checked>
Allow explicit content in downloads
</label>
</div>
<div class="help-text">
When disabled, tracks marked as explicit on Spotify will be skipped
during matched downloads. Does not affect manual Soulseek searches.
</div>
</div>
<!-- M3U Export Settings -->
<div class="settings-group" data-stg="library">
<h3>📋 M3U Playlist Export</h3>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="m3u-export-enabled">
Auto-save M3U file when downloading playlists and albums
</label>
</div>
<div class="help-text">
Saves an M3U playlist file to the same folder as the downloaded tracks.
</div>
</div>
<!-- Playlist Sync Settings -->
<div class="settings-group" data-stg="library">
<h3>Playlist Sync Settings</h3>
<div class="form-group">
<label class="checkbox-label">
<input type="checkbox" id="create-backup" checked>
Create playlist backups before sync
</label>
</div>
</div>
<!-- Logging Information (Read-only) -->
<div class="settings-group" data-stg="advanced">
<h3>Logging Information</h3>
<div class="form-group">
<label>Log Level:</label>
<div class="readonly-field" id="log-level-display">DEBUG</div>
</div>
<div class="form-group">
<label>Log Path:</label>
<div class="readonly-field" id="log-path-display">logs/app.log</div>
</div>
</div>
<!-- SoulSync API Keys -->
<div class="settings-group" data-stg="advanced">
<h3>SoulSync API</h3>
<div class="api-service-frame">
<h4 class="service-title" style="color: #64ffda;">REST API Keys</h4>
<div class="setting-help-text" style="margin-bottom: 12px; color: #888; font-size: 12px;">
Generate API keys to access SoulSync remotely via the <code>/api/v1/</code> endpoints.
Keys are shown once at creation — store them securely.
</div>
<!-- Existing API Keys List -->
<div id="api-keys-list" style="margin-bottom: 12px;">
<div style="color: #666; font-size: 13px; padding: 8px 0;">Loading...</div>
</div>
<!-- Generate New Key -->
<div class="form-group" style="margin-bottom: 8px;">
<label>New Key Label:</label>
<input type="text" id="api-key-label" placeholder="e.g. Discord Bot, Home Assistant">
</div>
<div class="form-actions">
<button class="auth-button" onclick="generateApiKey()">🔑 Generate API Key</button>
</div>
<!-- Newly Generated Key Display (hidden by default) -->
<div id="api-key-generated" style="display: none; margin-top: 12px; padding: 12px; background: rgba(100, 255, 218, 0.08); border: 1px solid rgba(100, 255, 218, 0.2); border-radius: 8px;">
<div style="font-size: 11px; color: #64ffda; margin-bottom: 6px; font-weight: 600;">
Copy this key now — it won't be shown again!
</div>
<div style="display: flex; align-items: center; gap: 8px;">
<code id="api-key-value" style="flex: 1; font-size: 12px; word-break: break-all; color: #fff; background: rgba(0,0,0,0.3); padding: 8px; border-radius: 4px;"></code>
<button onclick="copyApiKey()" style="padding: 6px 12px; background: rgba(100,255,218,0.15); border: 1px solid rgba(100,255,218,0.3); color: #64ffda; border-radius: 4px; cursor: pointer; white-space: nowrap;">Copy</button>
</div>
</div>
</div>
</div>
<!-- Developer Mode -->
<div class="settings-group" data-stg="advanced">
<h3>🔧 Developer Mode</h3>
<div class="form-group">
<label>Password:</label>
<div style="display: flex; gap: 8px;">
<input type="password" id="dev-mode-password" placeholder="Enter dev password">
<button class="test-button" onclick="activateDevMode()">Activate</button>
</div>
<div id="dev-mode-status" style="margin-top: 8px; color: #888;">Inactive</div>
</div>
</div>
</div>
</div>
<!-- Save Button -->
<div class="settings-actions">
<button class="save-button" id="save-settings">Save Settings</button>
</div>
</div>
</div>
<!-- Hydrabase Page (Dev Mode Only) -->
<div class="page" id="hydrabase-page">
<div class="page-header">
<h2><img src="/static/hydrabase.png?v=1" class="page-header-icon" alt=""><span>Hydrabase</span></h2>
<div style="display: flex; align-items: center; gap: 12px;">
<span id="hydra-connection-status" style="color: #888; font-size: 13px;">Disconnected</span>
<button class="test-button" id="hydra-connect-btn" onclick="hydrabaseToggleConnection()">Connect</button>
</div>
</div>
<!-- Connection Config -->
<div class="hydrabase-card" style="margin-bottom: 16px;">
<div style="display: flex; gap: 12px; align-items: end;">
<div style="flex: 2;">
<label style="font-size: 12px; color: #888; display: block; margin-bottom: 4px;">WebSocket URL</label>
<input type="text" id="hydra-ws-url" placeholder="ws://localhost:4545" value="ws://localhost:4545">
</div>
<div style="flex: 1;">
<label style="font-size: 12px; color: #888; display: block; margin-bottom: 4px;">API Key</label>
<input type="text" id="hydra-api-key" placeholder="Your Hydrabase API key">
</div>
</div>
</div>
<div class="hydrabase-container">
<!-- Left Panel — API Inputs -->
<div class="hydrabase-panel hydrabase-inputs">
<h3>API Calls</h3>
<div class="hydrabase-card">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<h4 style="margin: 0;">Tracks</h4>
<button class="test-button" onclick="hydrabaseSendRaw('hydra-payload-track')">Send</button>
</div>
<textarea class="hydra-payload" id="hydra-payload-track" rows="4">{ "request": { "type": "track", "query": "" }, "nonce": 0 }</textarea>
</div>
<div class="hydrabase-card">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<h4 style="margin: 0;">Albums</h4>
<button class="test-button" onclick="hydrabaseSendRaw('hydra-payload-album')">Send</button>
</div>
<textarea class="hydra-payload" id="hydra-payload-album" rows="4">{ "request": { "type": "album", "query": "" }, "nonce": 0 }</textarea>
</div>
<div class="hydrabase-card">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<h4 style="margin: 0;">Artists</h4>
<button class="test-button" onclick="hydrabaseSendRaw('hydra-payload-artist')">Send</button>
</div>
<textarea class="hydra-payload" id="hydra-payload-artist" rows="4">{ "request": { "type": "artists", "query": "" }, "nonce": 0 }</textarea>
</div>
<div class="hydrabase-card">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<h4 style="margin: 0;">Discography</h4>
<button class="test-button" onclick="hydrabaseSendRaw('hydra-payload-discog')">Send</button>
</div>
<textarea class="hydra-payload" id="hydra-payload-discog" rows="4">{ "request": { "type": "discography", "query": "" }, "nonce": 0 }</textarea>
</div>
<div class="hydrabase-card">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<h4 style="margin: 0;">Album Tracks</h4>
<button class="test-button" onclick="hydrabaseSendRaw('hydra-payload-albumtracks')">Send</button>
</div>
<textarea class="hydra-payload" id="hydra-payload-albumtracks" rows="4">{ "request": { "type": "album.tracks", "query": "" }, "nonce": 0 }</textarea>
</div>
</div>
<!-- Right Panel — API Responses -->
<div class="hydrabase-panel hydrabase-responses">
<h3>Response</h3>
<div class="hydrabase-response-area" id="hydra-response">API responses will appear here</div>
</div>
</div>
<!-- Network Stats -->
<div class="hydrabase-card" style="margin-top: 16px;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h3 style="margin: 0;">Network</h3>
<span id="hydra-peer-count" style="color: #888; font-size: 13px;">Peers: --</span>
</div>
</div>
<!-- Source Comparisons -->
<div class="hydrabase-card" style="margin-top: 16px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<h3 style="margin: 0;">Source Comparisons</h3>
<button class="test-button" onclick="loadHydrabaseComparisons()">Refresh</button>
</div>
<p style="color: #888; font-size: 12px; margin: 0 0 8px 0;">When Hydrabase is the active metadata source, searches are compared against Spotify and iTunes in the background.</p>
<div id="hydra-comparisons-container">
<p style="color: #666; font-size: 13px;">No comparisons yet. Search with Hydrabase active to generate comparisons.</p>
</div>
</div>
</div>
<!-- Stats Page -->
<div class="page" id="stats-page">
<div class="stats-container">
<div class="stats-header">
<div class="stats-header-title">
<img src="/static/trans2.png" alt="Stats" class="page-header-icon">
<h1 class="header-title">Listening Stats</h1>
</div>
<div class="stats-header-controls">
<div class="stats-time-range" id="stats-time-range">
<button class="stats-range-btn active" data-range="7d">7 Days</button>
<button class="stats-range-btn" data-range="30d">30 Days</button>
<button class="stats-range-btn" data-range="12m">12 Months</button>
<button class="stats-range-btn" data-range="all">All Time</button>
</div>
<div class="stats-sync-controls">
<span class="stats-last-synced" id="stats-last-synced"></span>
<button class="stats-sync-btn" id="stats-sync-btn" onclick="triggerStatsSync()" title="Sync now"><span class="stats-sync-icon"></span></button>
</div>
</div>
</div>
<!-- Overview Cards -->
<div class="stats-overview" id="stats-overview">
<div class="stats-card">
<div class="stats-card-value" id="stats-total-plays">0</div>
<div class="stats-card-label">Total Plays</div>
</div>
<div class="stats-card">
<div class="stats-card-value" id="stats-listening-time">0h</div>
<div class="stats-card-label">Listening Time</div>
</div>
<div class="stats-card">
<div class="stats-card-value" id="stats-unique-artists">0</div>
<div class="stats-card-label">Artists</div>
</div>
<div class="stats-card">
<div class="stats-card-value" id="stats-unique-albums">0</div>
<div class="stats-card-label">Albums</div>
</div>
<div class="stats-card">
<div class="stats-card-value" id="stats-unique-tracks">0</div>
<div class="stats-card-label">Tracks</div>
</div>
</div>
<!-- Main Grid: Charts + Rankings -->
<div class="stats-main-grid">
<div class="stats-left-col">
<div class="stats-section-card">
<div class="stats-section-title">Listening Activity</div>
<div style="position:relative;height:220px;">
<canvas id="stats-timeline-chart"></canvas>
</div>
</div>
<div class="stats-section-card">
<div class="stats-section-title">Genre Breakdown</div>
<div class="stats-genre-chart-container">
<canvas id="stats-genre-chart"></canvas>
<div class="stats-genre-legend" id="stats-genre-legend"></div>
</div>
</div>
<div class="stats-section-card">
<div class="stats-section-title">Recently Played</div>
<div class="stats-recent-list" id="stats-recent-plays"></div>
</div>
</div>
<div class="stats-right-col">
<div class="stats-section-card">
<div class="stats-section-title">Top Artists</div>
<div class="stats-top-artists-visual" id="stats-top-artists-visual"></div>
<div class="stats-ranked-list" id="stats-top-artists"></div>
</div>
<div class="stats-section-card">
<div class="stats-section-title">Top Albums</div>
<div class="stats-ranked-list" id="stats-top-albums"></div>
</div>
<div class="stats-section-card">
<div class="stats-section-title">Top Tracks</div>
<div class="stats-ranked-list" id="stats-top-tracks"></div>
</div>
</div>
</div>
<!-- Library Health -->
<div class="stats-section-card stats-full-width">
<div class="stats-section-title">Library Health</div>
<div class="stats-health-grid" id="stats-library-health">
<div class="stats-health-item">
<div class="stats-health-label">Format Breakdown</div>
<div class="stats-format-bar" id="stats-format-bar"></div>
</div>
<div class="stats-health-item">
<div class="stats-health-value" id="stats-unplayed">0</div>
<div class="stats-health-label">Unplayed Tracks</div>
</div>
<div class="stats-health-item">
<div class="stats-health-value" id="stats-total-duration">0h</div>
<div class="stats-health-label">Total Duration</div>
</div>
<div class="stats-health-item">
<div class="stats-health-value" id="stats-total-tracks-count">0</div>
<div class="stats-health-label">Total Tracks</div>
</div>
</div>
<div class="stats-enrichment" id="stats-enrichment-coverage"></div>
</div>
<!-- Database Storage -->
<div class="stats-section-card stats-full-width">
<div class="stats-section-title">Database Storage</div>
<div class="stats-db-storage-wrap">
<div class="stats-db-chart-container">
<canvas id="stats-db-storage-chart"></canvas>
<div class="stats-db-total" id="stats-db-total"></div>
</div>
<div class="stats-db-legend" id="stats-db-legend"></div>
</div>
</div>
<!-- Empty State -->
<div class="stats-empty hidden" id="stats-empty">
<div class="stats-empty-icon">📊</div>
<h3>No Listening Data Yet</h3>
<p>Enable "Listening Stats" in Settings to start tracking your listening activity from your media server.</p>
</div>
</div>
</div>
<!-- Import Page -->
<div class="page" id="import-page">
<div class="import-page-container">
<!-- Header with staging info -->
<div class="import-page-header">
<div class="import-page-title-row">
<h1 class="import-page-title"><img src="/static/import.png" class="page-header-icon" alt=""><span>Import Music</span></h1>
<button class="import-page-refresh-btn" onclick="importPageRefreshStaging()" title="Re-scan staging folder">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M13.65 2.35A8 8 0 1 0 16 8h-2a6 6 0 1 1-1.76-4.24L10 6h6V0l-2.35 2.35z"/></svg>
Refresh
</button>
</div>
<div class="import-page-staging-bar" id="import-staging-bar">
<span class="import-staging-path" id="import-page-staging-path">Staging folder: loading...</span>
<span class="import-staging-stats" id="import-page-staging-stats"></span>
</div>
</div>
<!-- Processing Queue -->
<div class="import-page-queue hidden" id="import-page-queue">
<div class="import-page-queue-header">
<span class="import-page-queue-title">Processing</span>
<button class="import-page-queue-clear" id="import-page-queue-clear" onclick="importPageClearFinishedJobs()">Clear finished</button>
</div>
<div class="import-page-queue-list" id="import-page-queue-list"></div>
</div>
<!-- Tab Bar -->
<div class="import-page-tab-bar">
<button class="import-page-tab active" id="import-page-tab-album" onclick="importPageSwitchTab('album')">Albums</button>
<button class="import-page-tab" id="import-page-tab-singles" onclick="importPageSwitchTab('singles')">Singles</button>
</div>
<!-- Album Tab -->
<div class="import-page-tab-content active" id="import-page-album-content">
<!-- Search state -->
<div id="import-page-album-search-section">
<div class="import-page-suggestions" id="import-page-suggestions">
<div class="import-page-section-label">Suggested from your staging folder</div>
<div class="import-page-album-grid" id="import-page-suggestions-grid"></div>
</div>
<div class="import-page-search-bar">
<input type="text" id="import-page-album-search-input" class="import-page-search-input"
placeholder="Search for an album..." onkeydown="if(event.key==='Enter')importPageSearchAlbum()">
<button class="import-page-search-btn" onclick="importPageSearchAlbum()">Search</button>
<button class="import-page-clear-btn hidden" id="import-page-album-clear-btn"
onclick="importPageResetAlbumSearch()" title="Clear search"></button>
</div>
<div class="import-page-album-grid" id="import-page-album-results"></div>
</div>
<!-- Match state (hidden initially) -->
<div id="import-page-album-match-section" class="hidden">
<div class="import-page-album-hero" id="import-page-album-hero"></div>
<div class="import-page-match-header">
<h3>Track Matching</h3>
<div class="import-page-match-actions">
<button class="import-page-secondary-btn" onclick="importPageAutoRematch()">Re-match Automatically</button>
<button class="import-page-back-btn" onclick="importPageResetAlbumSearch()">Back to Search</button>
</div>
</div>
<div class="import-page-match-list" id="import-page-match-list"></div>
<!-- Unmatched file pool for drag-drop -->
<div class="import-page-unmatched-pool" id="import-page-unmatched-pool">
<div class="import-page-pool-label">Unmatched Files (<span id="import-page-unmatched-count">0</span>)</div>
<div class="import-page-pool-chips" id="import-page-pool-chips"></div>
</div>
<div class="import-page-match-footer">
<div class="import-page-match-stats" id="import-page-match-stats"></div>
<button class="import-page-process-btn" id="import-page-album-process-btn"
onclick="importPageProcessAlbum()">Process Album</button>
</div>
</div>
</div>
<!-- Singles Tab -->
<div class="import-page-tab-content" id="import-page-singles-content">
<div class="import-page-singles-header">
<div class="import-page-singles-actions">
<button class="import-page-secondary-btn" onclick="importPageSelectAllSingles()">
<span id="import-page-select-all-text">Select All</span>
</button>
<button class="import-page-process-btn" id="import-page-singles-process-btn"
onclick="importPageProcessSingles()" disabled>Process Selected (0)</button>
</div>
</div>
<div class="import-page-singles-list" id="import-page-singles-list">
<div class="import-page-empty-state">Navigate to this page to scan your staging folder for audio files.</div>
</div>
</div>
</div>
</div>
<!-- Issues Page -->
<div class="page" id="issues-page">
<div class="issues-container">
<div class="issues-header">
<div class="issues-header-left">
<h2 class="issues-title">Issues</h2>
<p class="issues-subtitle" id="issues-subtitle">Track and resolve library problems</p>
</div>
<div class="issues-header-right">
<div class="issues-filters" id="issues-filters">
<select id="issues-filter-status" class="issues-filter-select" onchange="loadIssuesPage()">
<option value="">All Status</option>
<option value="open" selected>Open</option>
<option value="in_progress">In Progress</option>
<option value="resolved">Resolved</option>
<option value="dismissed">Dismissed</option>
</select>
<select id="issues-filter-category" class="issues-filter-select" onchange="loadIssuesPage()">
<option value="">All Categories</option>
<optgroup label="Track Issues">
<option value="wrong_track">Wrong Track</option>
<option value="wrong_artist">Wrong Artist</option>
<option value="wrong_album">Wrong Album</option>
<option value="audio_quality">Audio Quality</option>
</optgroup>
<optgroup label="Album Issues">
<option value="wrong_cover">Wrong Cover Art</option>
<option value="duplicate_tracks">Duplicate Tracks</option>
<option value="missing_tracks">Missing Tracks</option>
<option value="incomplete_album">Incomplete Album</option>
</optgroup>
<optgroup label="Both">
<option value="wrong_metadata">Wrong Metadata</option>
<option value="other">Other</option>
</optgroup>
</select>
</div>
</div>
</div>
<div class="issues-stats" id="issues-stats"></div>
<div class="issues-list" id="issues-list">
<div class="issues-empty">Loading issues...</div>
</div>
</div>
</div>
<!-- Report Issue Modal -->
<div class="modal-overlay hidden" id="report-issue-overlay">
<div class="enhanced-bulk-modal report-issue-modal">
<div class="enhanced-bulk-modal-header">
<h3 id="report-issue-title">Report an Issue</h3>
<button class="enhanced-bulk-modal-close" onclick="closeReportIssueModal()">&times;</button>
</div>
<div class="enhanced-bulk-modal-body" id="report-issue-body">
<!-- Populated dynamically -->
</div>
<div class="enhanced-bulk-modal-footer">
<button class="enhanced-bulk-btn secondary" onclick="closeReportIssueModal()">Cancel</button>
<button class="enhanced-bulk-btn primary" id="report-issue-submit-btn" onclick="submitIssue()">Submit Issue</button>
</div>
</div>
</div>
<!-- Issue Detail Modal (Admin) -->
<div class="modal-overlay hidden" id="issue-detail-overlay">
<div class="enhanced-bulk-modal issue-detail-modal">
<div class="enhanced-bulk-modal-header">
<h3 id="issue-detail-title">Issue Details</h3>
<button class="enhanced-bulk-modal-close" onclick="closeIssueDetailModal()">&times;</button>
</div>
<div class="enhanced-bulk-modal-body" id="issue-detail-body">
<!-- Populated dynamically -->
</div>
<div class="enhanced-bulk-modal-footer" id="issue-detail-footer">
<button class="enhanced-bulk-btn secondary" onclick="closeIssueDetailModal()">Close</button>
</div>
</div>
</div>
<!-- Help & Docs Page -->
<div class="page" id="help-page">
<div class="docs-layout">
<nav class="docs-sidebar" id="docs-sidebar">
<div class="docs-sidebar-header">
<h3>Documentation</h3>
<input type="text" class="docs-search" id="docs-search-input" placeholder="Search docs..." autocomplete="off">
</div>
<div class="docs-nav" id="docs-nav">
<!-- Populated by JS -->
</div>
</nav>
<main class="docs-content" id="docs-content">
<!-- Populated by JS -->
</main>
</div>
</div>
</div> <!-- /main-content -->
</div> <!-- /main-container -->
<!-- Loading Overlay -->
<div class="loading-overlay hidden" id="loading-overlay">
<div class="loading-spinner"></div>
<div class="loading-message">Processing...</div>
</div>
<!-- Toast Notifications -->
<div class="toast-container" id="toast-container"></div>
<!-- Hidden HTML5 Audio Player for Streaming -->
<audio id="audio-player" style="display: none;"></audio>
<!-- Expanded Now Playing Modal -->
<div class="np-modal-overlay hidden" id="np-modal-overlay">
<div class="np-modal">
<button class="np-close-btn" id="np-close-btn" title="Minimize">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
</button>
<div class="np-body">
<!-- Left: album art + track info -->
<div class="np-left">
<div class="np-album-art-container">
<img class="np-album-art hidden" id="np-album-art" src="" alt="Album Art">
<div class="np-album-art-placeholder" id="np-album-art-placeholder">
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/><line x1="12" y1="2" x2="12" y2="5"/><line x1="12" y1="19" x2="12" y2="22"/>
</svg>
</div>
</div>
<div class="np-track-info">
<div class="np-track-title" id="np-track-title">No track</div>
<div class="np-artist-name" id="np-artist-name">Unknown Artist</div>
<div class="np-album-name" id="np-album-name">Unknown Album</div>
<div class="np-format-badges" id="np-format-badges"></div>
</div>
<div class="np-action-buttons" id="np-action-buttons">
<button class="np-action-btn" id="np-goto-artist" title="Go to Artist">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
<span>View Artist</span>
</button>
</div>
</div>
<!-- Right: controls -->
<div class="np-right">
<div class="np-progress-section">
<div class="np-progress-bar-container">
<div class="np-progress-track">
<div class="np-progress-fill" id="np-progress-fill"></div>
</div>
<input type="range" class="np-progress-bar" id="np-progress-bar" min="0" max="100" value="0" step="0.1">
</div>
<div class="np-time-display">
<span class="np-current-time" id="np-current-time">0:00</span>
<span class="np-total-time" id="np-total-time">0:00</span>
</div>
</div>
<div class="np-controls-row">
<button class="np-btn np-btn-shuffle" id="np-shuffle-btn" title="Shuffle">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/><line x1="4" y1="4" x2="9" y2="9"/></svg>
</button>
<button class="np-btn np-btn-prev" id="np-prev-btn" title="Previous" disabled>
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg>
</button>
<button class="np-btn np-btn-play" id="np-play-btn" title="Play">
<svg class="np-icon-play" width="28" height="28" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
<svg class="np-icon-pause hidden" width="28" height="28" viewBox="0 0 24 24" fill="currentColor"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
<div class="np-buffering-ring hidden" id="np-buffering-ring"></div>
</button>
<button class="np-btn np-btn-next" id="np-next-btn" title="Next" disabled>
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg>
</button>
<button class="np-btn np-btn-repeat" id="np-repeat-btn" title="Repeat">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
<span class="np-repeat-one-badge hidden" id="np-repeat-one-badge">1</span>
</button>
</div>
<div class="np-volume-row">
<button class="np-mute-btn" id="np-mute-btn" title="Mute">
<svg class="np-icon-vol" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
<svg class="np-icon-muted hidden" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/></svg>
</button>
<div class="np-volume-slider-container">
<div class="np-volume-track">
<div class="np-volume-fill" id="np-volume-fill"></div>
</div>
<input type="range" class="np-volume-slider" id="np-volume-slider" min="0" max="100" value="70">
</div>
</div>
<div class="np-stop-row">
<button class="np-btn np-btn-stop" id="np-stop-btn" title="Stop playback">
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><rect x="4" y="4" width="16" height="16" rx="2"/></svg>
<span>Stop</span>
</button>
</div>
<div class="np-visualizer" id="np-visualizer">
<div class="np-viz-bar"></div>
<div class="np-viz-bar"></div>
<div class="np-viz-bar"></div>
<div class="np-viz-bar"></div>
<div class="np-viz-bar"></div>
<div class="np-viz-bar"></div>
<div class="np-viz-bar"></div>
</div>
</div>
</div>
<!-- Queue Panel -->
<div class="np-queue-panel" id="np-queue-panel">
<div class="np-queue-header">
<button class="np-queue-toggle" id="np-queue-toggle" title="Toggle queue">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
<span>Queue</span>
<span class="np-queue-count" id="np-queue-count"></span>
</button>
<div class="np-queue-header-actions">
<button class="np-radio-btn" id="np-radio-btn" title="Radio mode - auto-add similar tracks">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h.01"/><path d="M8.5 16.429a5 5 0 0 1 7 0"/><path d="M5 12.859a10 10 0 0 1 14 0"/><path d="M1.5 9.289a15 15 0 0 1 21 0"/></svg>
</button>
<button class="np-queue-clear-btn" id="np-queue-clear" title="Clear queue">Clear</button>
</div>
</div>
<div class="np-queue-body hidden" id="np-queue-body">
<div class="np-queue-empty" id="np-queue-empty">Queue is empty</div>
<div class="np-queue-list" id="np-queue-list"></div>
</div>
</div>
</div>
</div>
<!-- Matched Download Modal -->
<div class="modal-overlay hidden" id="matching-modal-overlay">
<div class="matching-modal" id="matching-modal">
<div class="matching-modal-header">
<h2 id="matching-modal-title">Match Download to Spotify</h2>
<button class="matching-modal-close" onclick="closeMatchingModal()"></button>
</div>
<div class="matching-modal-content">
<!-- Artist Selection Stage -->
<div id="artist-selection-stage" class="selection-stage">
<div class="stage-header">
<h3 id="artist-stage-title">Step 1: Select the correct Artist</h3>
<p class="stage-subtitle">Choose the artist that best matches your download</p>
</div>
<div class="suggestions-section">
<h4 class="suggestions-title">Top Suggestions</h4>
<div class="suggestions-container" id="artist-suggestions">
<!-- Artist suggestion cards will be populated here -->
</div>
</div>
<div class="manual-search-section">
<h4 class="suggestions-title">Or, Search Manually</h4>
<input type="text" id="artist-search-input" class="search-input"
placeholder="Search for an artist...">
<div class="suggestions-container" id="artist-manual-results">
<!-- Manual search results will be populated here -->
</div>
</div>
</div>
<!-- Album Selection Stage (for album downloads) -->
<div id="album-selection-stage" class="selection-stage hidden">
<div class="stage-header">
<h3 id="album-stage-title">Step 2: Select the correct Album</h3>
<p class="stage-subtitle">Choose the album that best matches your download for <span
id="selected-artist-name"></span></p>
</div>
<div class="suggestions-section">
<h4 class="suggestions-title">Top Suggestions</h4>
<div class="suggestions-container" id="album-suggestions">
<!-- Album suggestion cards will be populated here -->
</div>
</div>
<div class="manual-search-section">
<h4 class="suggestions-title">Or, Search Manually</h4>
<input type="text" id="album-search-input" class="search-input"
placeholder="Search for an album...">
<div class="suggestions-container" id="album-manual-results">
<!-- Manual search results will be populated here -->
</div>
</div>
</div>
</div>
<div class="matching-modal-actions">
<button id="skip-matching-btn" class="modal-button modal-button--secondary">Skip Matching</button>
<button id="cancel-match-btn" class="modal-button modal-button--cancel">Cancel</button>
<button id="confirm-match-btn" class="modal-button modal-button--primary" disabled>Confirm
Selection</button>
</div>
</div>
</div>
<!-- Support Modal -->
<div class="support-modal-overlay hidden" id="support-modal-overlay" onclick="closeSupportModal()">
<div class="support-modal" onclick="event.stopPropagation()">
<button class="support-modal-close-btn" onclick="closeSupportModal()">&times;</button>
<div class="support-modal-logo">
<img src="/static/favicon.png" alt="SoulSync" class="support-modal-logo-img">
<h2 class="support-modal-title">SoulSync</h2>
</div>
<p class="support-modal-message">
I built this app because I wanted it, not to make money. SoulSync is and always will be free.
If it's saved you time or you just want to show some love, any support is genuinely appreciated.
</p>
<div class="support-modal-links">
<a href="https://ko-fi.com/boulderbadgedad" target="_blank" rel="noopener" class="support-link support-link-kofi">
<svg class="support-link-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M23.881 8.948c-.773-4.085-4.859-4.593-4.859-4.593H.723c-.604 0-.679.798-.679.798s-.082 7.324-.022 11.822c.164 2.424 2.586 2.672 2.586 2.672s8.267-.023 11.966-.049c2.438-.426 2.683-2.566 2.658-3.734 4.352.24 7.422-2.831 6.649-6.916zm-11.062 3.511c-1.246 1.453-4.011 3.976-4.011 3.976s-.121.119-.31.023c-.076-.057-.108-.09-.108-.09-.443-.441-3.368-3.049-4.034-3.954-.709-.965-1.041-2.7-.091-3.71.951-1.01 3.005-1.086 4.363.407 0 0 1.565-1.782 3.468-.963 1.904.82 1.832 3.011.723 4.311zm6.173.478c-.928.116-1.682.028-1.682.028V7.284h1.77s1.971.551 1.971 2.638c0 1.913-.985 2.667-2.059 3.015z"/></svg>
<span>Ko-fi</span>
</a>
<button class="support-link support-link-btc" onclick="copyAddress('3JVWrRSkozAQSmw5DXYVxYKsM9bndPTqdS', 'Bitcoin')">
<svg class="support-link-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M23.638 14.904c-1.602 6.43-8.113 10.34-14.542 8.736C2.67 22.05-1.244 15.525.362 9.105 1.962 2.67 8.475-1.243 14.9.358c6.43 1.605 10.342 8.115 8.738 14.546zm-6.35-4.613c.24-1.59-.974-2.45-2.64-3.03l.54-2.153-1.315-.33-.525 2.107c-.345-.087-.7-.169-1.053-.25l.53-2.12-1.32-.33-.54 2.16c-.285-.065-.565-.13-.84-.2l-1.815-.45-.35 1.407s.975.225.955.238c.535.136.63.494.615.78l-.617 2.48c.037.01.085.025.138.047l-.14-.036-.865 3.47c-.067.165-.235.413-.615.32.015.02-.96-.24-.96-.24l-.655 1.515 1.71.426c.32.08.63.163.94.24l-.55 2.19 1.32.33.54-2.17c.36.1.705.19 1.05.273l-.535 2.16 1.32.33.55-2.19c2.24.427 3.93.257 4.64-1.774.57-1.637-.03-2.58-1.217-3.196.854-.193 1.5-.753 1.67-1.907zm-3 4.22c-.404 1.64-3.157.75-4.05.53l.72-2.9c.896.225 3.757.67 3.33 2.37zm.41-4.24c-.37 1.49-2.662.735-3.405.548l.654-2.64c.744.186 3.137.537 2.75 2.084z"/></svg>
<span>Bitcoin</span>
</button>
<button class="support-link support-link-eth" onclick="copyAddress('0xAe2343c88a657436941181D7dD07DE83d29D24eD', 'Ethereum')">
<svg class="support-link-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M11.944 17.97L4.58 13.62 11.943 24l7.37-10.38-7.372 4.35h.003zM12.056 0L4.69 12.223l7.365 4.354 7.365-4.35L12.056 0z"/></svg>
<span>Ethereum</span>
</button>
</div>
</div>
</div>
<!-- Version Info Modal -->
<div class="version-modal-overlay hidden" id="version-modal-overlay" onclick="closeVersionModal()">
<div class="version-modal" onclick="event.stopPropagation()">
<!-- Header -->
<div class="version-modal-header">
<h2 class="version-modal-title">What's New in SoulSync</h2>
<div class="version-modal-subtitle">Version 2.1 — Latest Changes</div>
</div>
<!-- Content Area with Scroll -->
<div class="version-modal-content">
<div class="version-content-container" id="version-content-container">
<!-- Content will be populated by JavaScript -->
</div>
</div>
<!-- Footer -->
<div class="version-modal-footer">
<button class="version-modal-close" onclick="closeVersionModal()">Close</button>
</div>
</div>
</div>
<!-- Add to Wishlist Modal -->
<div class="modal-overlay hidden" id="add-to-wishlist-modal-overlay">
<div class="add-to-wishlist-modal" id="add-to-wishlist-modal">
<div class="add-to-wishlist-modal-header">
<div class="add-to-wishlist-modal-hero" id="add-to-wishlist-modal-hero">
<!-- Hero content will be dynamically populated -->
</div>
<div class="add-to-wishlist-modal-header-actions">
<span class="add-to-wishlist-modal-close" onclick="closeAddToWishlistModal()">&times;</span>
</div>
</div>
<div class="add-to-wishlist-modal-content">
<div class="add-to-wishlist-modal-body">
<div class="wishlist-track-list-container">
<div class="wishlist-track-list-header">
<h3>Tracks to Add to Wishlist</h3>
<p class="wishlist-track-list-subtitle">All tracks from this release will be added to your
wishlist</p>
</div>
<div class="wishlist-track-list" id="wishlist-track-list">
<!-- Track list will be dynamically populated -->
</div>
</div>
</div>
<div class="add-to-wishlist-modal-footer">
<div class="wishlist-modal-actions">
<button class="wishlist-modal-btn wishlist-modal-btn-secondary"
onclick="closeAddToWishlistModal()">
Close
</button>
<button class="wishlist-modal-btn wishlist-modal-btn-download" id="wishlist-download-now-btn"
onclick="handleWishlistDownloadNow()">
Download Now
</button>
<button class="wishlist-modal-btn wishlist-modal-btn-primary" id="confirm-add-to-wishlist-btn">
Add to Wishlist
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Watchlist Artist Config Modal -->
<div class="modal-overlay hidden" id="watchlist-artist-config-modal-overlay">
<div class="watchlist-artist-config-modal" id="watchlist-artist-config-modal">
<div class="watchlist-artist-config-header">
<div class="watchlist-artist-config-hero" id="watchlist-artist-config-hero">
<!-- Hero content will be dynamically populated -->
</div>
<span class="watchlist-artist-config-close" onclick="closeWatchlistArtistConfigModal()">&times;</span>
</div>
<div class="watchlist-artist-config-content">
<div class="watchlist-artist-config-body">
<div class="config-section">
<h3 class="config-section-title">Download Preferences</h3>
<p class="config-section-subtitle">Select which types of releases to monitor for this artist</p>
<div class="config-options">
<label class="config-option">
<input type="checkbox" id="config-include-albums" checked>
<div class="config-option-content">
<div class="config-option-icon">💿</div>
<div class="config-option-text">
<span class="config-option-title">Albums</span>
<span class="config-option-description">Full-length studio albums</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="config-include-eps" checked>
<div class="config-option-content">
<div class="config-option-icon">🎵</div>
<div class="config-option-text">
<span class="config-option-title">EPs</span>
<span class="config-option-description">Extended plays (4-6 tracks)</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="config-include-singles" checked>
<div class="config-option-content">
<div class="config-option-icon">🎶</div>
<div class="config-option-text">
<span class="config-option-title">Singles</span>
<span class="config-option-description">Single tracks and 2-3 track
releases</span>
</div>
</div>
</label>
</div>
</div>
<div class="config-section">
<h3 class="config-section-title">Content Filters</h3>
<p class="config-section-subtitle">Check to INCLUDE, leave unchecked to EXCLUDE (default: all
excluded)</p>
<div class="config-options">
<label class="config-option">
<input type="checkbox" id="config-include-live">
<div class="config-option-content">
<div class="config-option-icon">🎤</div>
<div class="config-option-text">
<span class="config-option-title">Include Live Versions</span>
<span class="config-option-description">Check to include live performances and
concerts</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="config-include-remixes">
<div class="config-option-content">
<div class="config-option-icon">🎧</div>
<div class="config-option-text">
<span class="config-option-title">Include Remixes</span>
<span class="config-option-description">Check to include remix versions and
edits</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="config-include-acoustic">
<div class="config-option-content">
<div class="config-option-icon">🎸</div>
<div class="config-option-text">
<span class="config-option-title">Include Acoustic Versions</span>
<span class="config-option-description">Check to include acoustic and stripped
versions</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="config-include-compilations">
<div class="config-option-content">
<div class="config-option-icon">📀</div>
<div class="config-option-text">
<span class="config-option-title">Include Compilations</span>
<span class="config-option-description">Check to include greatest hits and
collections</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="config-include-instrumentals">
<div class="config-option-content">
<div class="config-option-icon">🎹</div>
<div class="config-option-text">
<span class="config-option-title">Include Instrumentals</span>
<span class="config-option-description">Check to include instrumental, karaoke,
and backing track versions</span>
</div>
</div>
</label>
</div>
</div>
<div class="config-section">
<h3 class="config-section-title">Scan Lookback</h3>
<p class="config-section-subtitle">How far back to look for releases on first scan of this artist</p>
<div class="form-group" style="margin: 12px 0 0;">
<select id="config-lookback-days" class="form-select">
<option value="">Use Global Setting</option>
<option value="7">Last 7 days</option>
<option value="30">Last 30 days</option>
<option value="90">Last 90 days</option>
<option value="180">Last 6 months</option>
<option value="365">Last year</option>
<option value="730">Last 2 years</option>
<option value="1825">Last 5 years</option>
<option value="36500">Entire discography</option>
</select>
</div>
</div>
<!-- Linked Provider Section -->
<div class="config-section" id="watchlist-linked-provider-section" style="display:none">
<h3 class="config-section-title">Linked Artist</h3>
<p class="config-section-subtitle">The metadata provider artist linked to this watchlist entry</p>
<div id="watchlist-linked-provider-content">
<!-- Dynamically populated -->
</div>
</div>
</div>
<div class="watchlist-artist-config-footer">
<div class="config-modal-actions">
<button class="config-modal-btn config-modal-btn-secondary"
onclick="closeWatchlistArtistConfigModal()">
Cancel
</button>
<button class="config-modal-btn config-modal-btn-primary" id="save-artist-config-btn">
Save Preferences
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Watchlist Global Config Modal -->
<div class="modal-overlay hidden" id="watchlist-global-config-modal-overlay">
<div class="watchlist-artist-config-modal" id="watchlist-global-config-modal">
<div class="watchlist-artist-config-header" style="padding: 24px 28px;">
<div style="display: flex; align-items: center; gap: 12px;">
<span style="font-size: 28px;">⚙️</span>
<div>
<h2 style="color: #fff; margin: 0; font-size: 22px;">Global Watchlist Settings</h2>
<p style="color: #b3b3b3; margin: 4px 0 0; font-size: 13px;">
Override per-artist settings for all watchlist scans
</p>
</div>
</div>
<span class="watchlist-artist-config-close" onclick="closeWatchlistGlobalSettingsModal()">&times;</span>
</div>
<div class="watchlist-artist-config-content">
<div class="watchlist-artist-config-body">
<!-- Global Override Toggle -->
<div class="config-section">
<label class="config-option global-override-toggle" id="global-override-toggle-label">
<input type="checkbox" id="global-override-enabled" onchange="toggleGlobalOverrideOptions()">
<div class="config-option-content">
<div class="config-option-icon">🌐</div>
<div class="config-option-text">
<span class="config-option-title">Enable Global Override</span>
<span class="config-option-description">When enabled, the settings below apply to ALL
artists, ignoring individual artist configs</span>
</div>
</div>
</label>
</div>
<div id="global-override-options" style="opacity: 0.4; pointer-events: none; transition: opacity 0.3s;">
<!-- Release Types -->
<div class="config-section">
<h3 class="config-section-title">Release Types</h3>
<p class="config-section-subtitle">Select which release types to download for all artists</p>
<div class="config-options">
<label class="config-option">
<input type="checkbox" id="global-include-albums" checked>
<div class="config-option-content">
<div class="config-option-icon">💿</div>
<div class="config-option-text">
<span class="config-option-title">Albums</span>
<span class="config-option-description">Full-length studio albums</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="global-include-eps" checked>
<div class="config-option-content">
<div class="config-option-icon">🎵</div>
<div class="config-option-text">
<span class="config-option-title">EPs</span>
<span class="config-option-description">Extended plays (4-6 tracks)</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="global-include-singles" checked>
<div class="config-option-content">
<div class="config-option-icon">🎶</div>
<div class="config-option-text">
<span class="config-option-title">Singles</span>
<span class="config-option-description">Single tracks and 2-3 track
releases</span>
</div>
</div>
</label>
</div>
</div>
<!-- Content Filters -->
<div class="config-section">
<h3 class="config-section-title">Content Filters</h3>
<p class="config-section-subtitle">Check to INCLUDE, leave unchecked to EXCLUDE (default: all
excluded)</p>
<div class="config-options">
<label class="config-option">
<input type="checkbox" id="global-include-live">
<div class="config-option-content">
<div class="config-option-icon">🎤</div>
<div class="config-option-text">
<span class="config-option-title">Include Live Versions</span>
<span class="config-option-description">Live performances and concerts</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="global-include-remixes">
<div class="config-option-content">
<div class="config-option-icon">🎧</div>
<div class="config-option-text">
<span class="config-option-title">Include Remixes</span>
<span class="config-option-description">Remix versions and edits</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="global-include-acoustic">
<div class="config-option-content">
<div class="config-option-icon">🎸</div>
<div class="config-option-text">
<span class="config-option-title">Include Acoustic Versions</span>
<span class="config-option-description">Acoustic and stripped versions</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="global-include-compilations">
<div class="config-option-content">
<div class="config-option-icon">📀</div>
<div class="config-option-text">
<span class="config-option-title">Include Compilations</span>
<span class="config-option-description">Greatest hits and collections</span>
</div>
</div>
</label>
<label class="config-option">
<input type="checkbox" id="global-include-instrumentals">
<div class="config-option-content">
<div class="config-option-icon">🎹</div>
<div class="config-option-text">
<span class="config-option-title">Include Instrumentals</span>
<span class="config-option-description">Instrumental, karaoke, and backing track versions</span>
</div>
</div>
</label>
</div>
</div>
<!-- Include Everything shortcut -->
<div class="config-section">
<label class="config-option include-everything-option">
<input type="checkbox" id="global-include-all" onchange="toggleGlobalIncludeAll()">
<div class="config-option-content">
<div class="config-option-icon"></div>
<div class="config-option-text">
<span class="config-option-title">Include Everything</span>
<span class="config-option-description">Enable all release types AND content filters
(live, remixes, acoustic, compilations, instrumentals)</span>
</div>
</div>
</label>
</div>
</div>
<!-- Custom Exclusion Terms (always visible, independent of global override) -->
<div class="config-section">
<h3 class="config-section-title">Custom Exclusion Terms</h3>
<p class="config-section-subtitle">Comma-separated terms — tracks or albums matching any term will be skipped during scans</p>
<div class="config-exclude-terms-container">
<input type="text" id="global-exclude-terms" class="config-exclude-terms-input"
placeholder="e.g. commentary, interlude, skit, demo, a cappella">
<p class="config-exclude-terms-hint">Applied globally to all watchlist scans regardless of override setting. Case-insensitive substring matching.</p>
</div>
</div>
</div>
</div>
<div class="watchlist-artist-config-footer">
<div class="config-modal-actions">
<button class="config-modal-btn config-modal-btn-secondary"
onclick="closeWatchlistGlobalSettingsModal()">
Cancel
</button>
<button class="config-modal-btn config-modal-btn-primary" id="save-global-config-btn"
onclick="saveWatchlistGlobalConfig()">
Save Global Settings
</button>
</div>
</div>
</div>
</div>
<!-- Genre Browser Modal -->
<div class="genre-browser-modal-overlay" id="genre-browser-modal">
<div class="genre-browser-modal-container">
<div class="genre-browser-modal-header">
<h2 class="genre-browser-modal-title">🎵 Browse by Genre</h2>
<button class="genre-browser-modal-close" id="genre-browser-modal-close">
<span class="genre-browser-close-icon">×</span>
</button>
</div>
<div class="genre-browser-modal-content">
<div class="genre-browser-search-section">
<div class="genre-browser-search-container">
<input type="text" class="genre-browser-search-input" placeholder="Search genres..."
id="genre-browser-search">
<span class="genre-browser-search-icon">🔍</span>
</div>
</div>
<div class="genre-browser-genres-section">
<div class="genre-browser-genres-grid" id="genre-browser-genres-grid">
<!-- Loading placeholder -->
<div class="genre-browser-loading-container">
<div class="genre-browser-loading-spinner"></div>
<p class="genre-browser-loading-text">🔍 Discovering current Beatport genres...</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Tool Help Modal -->
<!-- Retag Tool Modal -->
<div class="retag-modal-overlay" id="retag-modal">
<div class="retag-modal-container">
<div class="retag-modal-header">
<div class="retag-modal-header-left">
<h2 class="retag-modal-title">Retag Tool</h2>
</div>
<div class="retag-header-actions">
<button class="retag-clear-all-btn" id="retag-clear-all-btn" onclick="clearAllRetagGroups(this)">Clear All</button>
<button class="retag-modal-close" onclick="closeRetagModal()">&times;</button>
</div>
</div>
<div class="retag-batch-bar" id="retag-batch-bar" style="display: none;">
<span class="retag-batch-count" id="retag-batch-count">0 selected</span>
<button class="retag-batch-remove-btn" onclick="batchRemoveRetagGroups()">Remove Selected</button>
</div>
<div class="retag-modal-body" id="retag-modal-body">
<div class="retag-loading">Loading downloads...</div>
</div>
</div>
</div>
<!-- Retag Search Sub-Modal -->
<div class="retag-search-overlay" id="retag-search-modal">
<div class="retag-search-container">
<div class="retag-search-header">
<h3 class="retag-search-title" id="retag-search-title">Search for Correct Album</h3>
<button class="retag-search-close" onclick="closeRetagSearch()">&times;</button>
</div>
<div class="retag-search-input-section">
<input type="text" id="retag-search-input" placeholder="Search albums..." autocomplete="off">
</div>
<div class="retag-search-results" id="retag-search-results"></div>
</div>
</div>
<div class="tool-help-modal" id="tool-help-modal">
<div class="tool-help-modal-content">
<div class="tool-help-modal-header">
<h3 id="tool-help-modal-title">Tool Information</h3>
<button class="tool-help-modal-close">&times;</button>
</div>
<div class="tool-help-modal-body" id="tool-help-modal-body">
<!-- Content will be dynamically inserted -->
</div>
</div>
</div>
<!-- Library History Modal -->
<div class="modal-overlay hidden" id="library-history-overlay" onclick="if(event.target===this)closeLibraryHistoryModal()">
<div class="library-history-modal">
<div class="library-history-modal-header">
<h3>Library History</h3>
<button class="library-history-modal-close" onclick="closeLibraryHistoryModal()">&times;</button>
</div>
<div class="library-history-tabs">
<button class="library-history-tab active" data-tab="download" onclick="switchHistoryTab('download')">
Downloads <span class="library-history-tab-count" id="history-download-count">0</span>
</button>
<button class="library-history-tab" data-tab="import" onclick="switchHistoryTab('import')">
Server Imports <span class="library-history-tab-count" id="history-import-count">0</span>
</button>
</div>
<div class="library-history-list" id="library-history-list"></div>
<div class="library-history-pagination" id="library-history-pagination"></div>
</div>
</div>
<!-- Sync History Modal -->
<div class="modal-overlay hidden" id="sync-history-overlay" onclick="if(event.target===this)closeSyncHistoryModal()">
<div class="sync-history-modal">
<div class="sync-history-modal-header">
<h3>Sync History</h3>
<button class="sync-history-modal-close" onclick="closeSyncHistoryModal()">&times;</button>
</div>
<div class="sync-history-tabs" id="sync-history-tabs"></div>
<div class="sync-history-list" id="sync-history-list"></div>
<div class="sync-history-pagination" id="sync-history-pagination"></div>
</div>
</div>
<!-- Metadata Cache Browse Modal -->
<div class="mcache-modal-overlay" id="mcache-browse-modal" style="display:none;">
<div class="mcache-modal">
<div class="mcache-modal-header">
<h3 class="mcache-modal-title">Metadata Cache Browser</h3>
<div class="mcache-modal-header-actions">
<div class="mcache-clear-dropdown" id="mcache-clear-dropdown">
<button class="mcache-btn-clear" onclick="toggleMcacheClearDropdown(event)" title="Clear cached data">Clear ▾</button>
<div class="mcache-clear-dropdown-menu" id="mcache-clear-dropdown-menu">
<button onclick="clearMetadataCacheBySource('spotify')"><span class="mcache-source-badge spotify" style="margin-right:6px">spotify</span>Clear Spotify</button>
<button onclick="clearMetadataCacheBySource('itunes')"><span class="mcache-source-badge itunes" style="margin-right:6px">itunes</span>Clear iTunes</button>
<button onclick="clearMetadataCacheBySource('deezer')"><span class="mcache-source-badge deezer" style="margin-right:6px">deezer</span>Clear Deezer</button>
<button onclick="clearMetadataCacheBySource('beatport')"><span class="mcache-source-badge beatport" style="margin-right:6px">beatport</span>Clear Beatport</button>
<div style="border-top:1px solid rgba(255,255,255,0.08);margin:4px 0"></div>
<button onclick="clearMetadataCache()">Clear All</button>
</div>
</div>
<button class="mcache-modal-close" onclick="closeMetadataCacheModal()">&times;</button>
</div>
</div>
<div class="mcache-stats-bar" id="mcache-stats-bar">
<div class="mcache-stat-pill">
<span class="mcache-stat-pill-label">Spotify</span>
<span class="mcache-stat-pill-value" id="mcache-browse-spotify-count">0</span>
</div>
<div class="mcache-stat-pill">
<span class="mcache-stat-pill-label">iTunes</span>
<span class="mcache-stat-pill-value" id="mcache-browse-itunes-count">0</span>
</div>
<div class="mcache-stat-pill">
<span class="mcache-stat-pill-label">Deezer</span>
<span class="mcache-stat-pill-value" id="mcache-browse-deezer-count">0</span>
</div>
<div class="mcache-stat-pill">
<span class="mcache-stat-pill-label">Beatport</span>
<span class="mcache-stat-pill-value" id="mcache-browse-beatport-count">0</span>
</div>
<div class="mcache-stat-pill">
<span class="mcache-stat-pill-label">Total Hits</span>
<span class="mcache-stat-pill-value" id="mcache-browse-hits">0</span>
</div>
<div class="mcache-stat-pill">
<span class="mcache-stat-pill-label">Searches</span>
<span class="mcache-stat-pill-value" id="mcache-browse-searches">0</span>
</div>
</div>
<div class="mcache-tabs">
<button class="mcache-tab active" data-tab="artist" onclick="switchMetadataCacheTab('artist')">Artists</button>
<button class="mcache-tab" data-tab="album" onclick="switchMetadataCacheTab('album')">Albums</button>
<button class="mcache-tab" data-tab="track" onclick="switchMetadataCacheTab('track')">Tracks</button>
</div>
<div class="mcache-filters">
<input type="text" class="mcache-search-input" id="mcache-search" placeholder="Search cached metadata..." oninput="debouncedMetadataCacheSearch()">
<select class="mcache-source-filter" id="mcache-source-filter" onchange="loadMetadataCacheBrowse()">
<option value="">All Sources</option>
<option value="spotify">Spotify</option>
<option value="itunes">iTunes</option>
<option value="deezer">Deezer</option>
<option value="beatport">Beatport</option>
</select>
<select class="mcache-sort-filter" id="mcache-sort-filter" onchange="loadMetadataCacheBrowse()">
<option value="last_accessed_at">Recently Accessed</option>
<option value="updated_at">Recently Updated</option>
<option value="created_at">Recently Added</option>
<option value="access_count">Most Accessed</option>
<option value="name">Name (A-Z)</option>
</select>
</div>
<div class="mcache-grid" id="mcache-grid">
<!-- Cards populated by JS -->
</div>
<div class="mcache-pagination" id="mcache-pagination"></div>
</div>
</div>
<!-- Metadata Cache Detail Modal (overlays browse modal) -->
<div class="mcache-detail-overlay" id="mcache-detail-modal" style="display:none;">
<div class="mcache-detail-modal">
<div class="mcache-detail-header">
<h3 class="mcache-detail-title" id="mcache-detail-title">Entity Detail</h3>
<button class="mcache-modal-close" onclick="closeMetadataCacheDetail()">&times;</button>
</div>
<div class="mcache-detail-body" id="mcache-detail-body">
<!-- Detail content populated by JS -->
</div>
</div>
</div>
<!-- Library Maintenance Modal -->
<div class="repair-modal-overlay" id="repair-modal" style="display:none;" onclick="if(event.target===this)closeRepairModal()">
<div class="repair-modal">
<div class="repair-modal-header">
<div class="repair-modal-header-left">
<img src="/static/whisoul.png" alt="" class="repair-modal-logo" />
<div class="repair-modal-header-text">
<h2 class="repair-modal-title">Library Maintenance</h2>
<p class="repair-modal-subtitle">Scan, detect, and fix issues in your music library</p>
</div>
</div>
<div class="repair-modal-header-actions">
<label class="repair-master-toggle">
<input type="checkbox" id="repair-master-toggle" onchange="toggleRepairMaster()">
<span class="repair-toggle-slider"></span>
<span class="repair-toggle-label" id="repair-master-label">Enabled</span>
</label>
<button class="repair-modal-close" onclick="closeRepairModal()">&times;</button>
</div>
</div>
<div class="repair-tabs">
<button class="repair-tab active" data-tab="jobs" onclick="switchRepairTab('jobs')">Jobs</button>
<button class="repair-tab" data-tab="findings" onclick="switchRepairTab('findings')">
Findings <span class="repair-tab-badge" id="repair-findings-tab-badge" style="display:none">0</span>
</button>
<button class="repair-tab" data-tab="history" onclick="switchRepairTab('history')">History</button>
</div>
<div class="repair-tab-content" id="repair-tab-jobs">
<div class="repair-jobs-list" id="repair-jobs-list">
<div class="repair-loading">Loading jobs...</div>
</div>
</div>
<div class="repair-tab-content" id="repair-tab-findings" style="display:none;">
<!-- Summary dashboard -->
<div class="repair-findings-dashboard" id="repair-findings-dashboard"></div>
<!-- Toolbar: filters + bulk actions -->
<div class="repair-findings-toolbar">
<div class="repair-findings-filters">
<select id="repair-findings-job-filter" onchange="_repairFindingsPage=0;loadRepairFindings()">
<option value="">All Jobs</option>
</select>
<select id="repair-findings-severity-filter" onchange="_repairFindingsPage=0;loadRepairFindings()">
<option value="">All Severity</option>
<option value="info">Info</option>
<option value="warning">Warning</option>
</select>
<select id="repair-findings-status-filter" onchange="_repairFindingsPage=0;loadRepairFindings()">
<option value="pending">Pending</option>
<option value="">All Status</option>
<option value="resolved">Resolved</option>
<option value="dismissed">Dismissed</option>
</select>
</div>
<label class="repair-select-all" title="Select all on this page">
<input type="checkbox" id="repair-select-all-cb" onchange="toggleSelectAllFindings(this.checked)">
<span>Select All</span>
</label>
<div class="repair-findings-bulk" id="repair-findings-bulk" style="display:none;">
<span class="repair-bulk-count" id="repair-bulk-count"></span>
<button class="repair-bulk-btn fix" onclick="bulkFixFindings()">Fix Selected</button>
<button class="repair-bulk-btn" onclick="bulkRepairAction('dismiss')">Dismiss Selected</button>
<button class="repair-bulk-btn fix-all" id="repair-fix-all-btn" style="display:none;" onclick="fixAllMatchingFindings()">Fix All</button>
</div>
<button class="repair-clear-btn" onclick="clearRepairFindings()" title="Clear findings matching current filters">Clear Findings</button>
</div>
<div class="repair-findings-list" id="repair-findings-list">
<div class="repair-loading">Loading findings...</div>
</div>
<div class="repair-findings-pagination" id="repair-findings-pagination"></div>
</div>
<div class="repair-tab-content" id="repair-tab-history" style="display:none;">
<div class="repair-history-list" id="repair-history-list">
<div class="repair-loading">Loading history...</div>
</div>
</div>
</div>
</div>
<!-- Right Sidebar Download Indicator (Global - outside all containers) -->
<div class="discover-download-sidebar" id="discover-download-sidebar">
<div class="discover-download-sidebar-header">
<span class="discover-download-sidebar-icon">🎵</span>
<span class="discover-download-sidebar-title">Downloads</span>
<span class="discover-download-sidebar-count" id="discover-download-count">0</span>
</div>
<div class="discover-download-bubbles" id="discover-download-bubbles">
<!-- Download bubbles will be added here dynamically -->
</div>
</div>
<!-- Confirm Dialog Modal -->
<div class="modal-overlay hidden" id="confirm-modal-overlay">
<div class="confirm-modal">
<div class="confirm-modal-header">
<h2 id="confirm-modal-title">Confirm</h2>
<button class="confirm-modal-close" onclick="resolveConfirmDialog(false)"></button>
</div>
<div class="confirm-modal-content">
<p id="confirm-modal-message"></p>
</div>
<div class="confirm-modal-actions">
<button class="modal-button modal-button--secondary" id="confirm-modal-cancel"
onclick="resolveConfirmDialog(false)">Cancel</button>
<button class="modal-button modal-button--primary" id="confirm-modal-confirm"
onclick="resolveConfirmDialog(true)">Confirm</button>
</div>
</div>
</div>
<!-- Spotify Rate Limit Modal -->
<div class="modal-overlay hidden" id="rate-limit-modal-overlay">
<div class="confirm-modal rate-limit-modal">
<div class="confirm-modal-header rate-limit-modal-header">
<div class="rate-limit-title-row">
<svg class="rate-limit-icon" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
<line x1="12" y1="9" x2="12" y2="13"/>
<line x1="12" y1="17" x2="12.01" y2="17"/>
</svg>
<h2>Spotify Rate Limited</h2>
</div>
<button class="confirm-modal-close" onclick="closeRateLimitModal()"></button>
</div>
<div class="confirm-modal-content rate-limit-modal-content">
<p class="rate-limit-description">Spotify has temporarily blocked API access. All Spotify features (search, enrichment, playlists) are paused until the ban expires.</p>
<div class="rate-limit-details">
<div class="rate-limit-detail-row">
<span class="rate-limit-label">Ban Duration</span>
<span class="rate-limit-value" id="rate-limit-ban-duration"></span>
</div>
<div class="rate-limit-detail-row">
<span class="rate-limit-label">Triggered By</span>
<span class="rate-limit-value rate-limit-endpoint" id="rate-limit-endpoint"></span>
</div>
<div class="rate-limit-detail-row">
<span class="rate-limit-label">Time Remaining</span>
<span class="rate-limit-value rate-limit-countdown" id="rate-limit-countdown"></span>
</div>
</div>
<p class="rate-limit-hint">You can wait for the ban to expire (the app uses Apple Music in the meantime) or disconnect Spotify to clear the ban immediately.</p>
</div>
<div class="confirm-modal-actions rate-limit-modal-actions">
<button class="modal-button modal-button--secondary" onclick="closeRateLimitModal()">Dismiss</button>
<button class="modal-button rate-limit-disconnect-btn" onclick="disconnectSpotifyFromRateLimit()">
Disconnect Spotify
<span class="rate-limit-disconnect-sub">Clear ban, pause enrichment &amp; switch to fallback source</span>
</button>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='vendor/socket.io.min.js') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
<script src="{{ url_for('static', filename='script.js') }}"></script>
<!-- Floating helper toggle — always accessible above modals -->
<button class="helper-float-btn" id="helper-float-btn" onclick="toggleHelperMode()" title="Interactive Help — click any element to learn about it">
<span>?</span>
</button>
<script src="{{ url_for('static', filename='docs.js') }}"></script>
<script src="{{ url_for('static', filename='helper.js') }}"></script>
<script src="{{ url_for('static', filename='particles.js') }}"></script>
<script src="{{ url_for('static', filename='worker-orbs.js') }}"></script>
</body>
</html>