diff --git a/webui/static/docs.js b/webui/static/docs.js index 0a8f54d3..a7d8ac32 100644 --- a/webui/static/docs.js +++ b/webui/static/docs.js @@ -82,6 +82,9 @@ const DOCS_SECTIONS = [ { id: 'dash-overview', title: 'Overview & Stats' }, { id: 'dash-workers', title: 'Enrichment Workers' }, { id: 'dash-tools', title: 'Tool Cards' }, + { id: 'dash-retag', title: 'Retag Tool' }, + { id: 'dash-backup', title: 'Backup Manager' }, + { id: 'dash-repair', title: 'Repair & Maintenance' }, { id: 'dash-activity', title: 'Activity Feed' } ], content: () => ` @@ -104,6 +107,7 @@ const DOCS_SECTIONS = [

Genius

Lyrics, descriptions, alternate names, song artwork

ℹ️
Workers retry "not found" items every 30 days and errored items every 7 days. You can pause/resume any worker from the dashboard.
+

Rate Limit Protection: Workers include smart rate limiting for all APIs. If Spotify returns a rate limit (429), a global ban activates — all Spotify calls are suppressed and searches automatically fall back to iTunes. A countdown modal appears showing ban duration, and the worker auto-resumes when the ban expires. You can manually disconnect Spotify from the modal to clear the ban immediately.

Tool Cards

@@ -122,6 +126,40 @@ const DOCS_SECTIONS = [
💡
Each tool card has a help button (?) that opens detailed instructions for that specific tool.
+
+

Retag Tool

+

The Retag Tool lets you fix incorrect metadata tags on files already in your library. This is useful when files were downloaded with wrong or incomplete tags.

+
    +
  1. Open the Retag Tool card on the Dashboard
  2. +
  3. Select an artist and album from the dropdown filters
  4. +
  5. The tool displays all tracks in the album with their current file tags alongside the correct metadata from Spotify or iTunes
  6. +
  7. Review the tag differences — mismatches are highlighted
  8. +
  9. Click Retag to write the corrected metadata to the audio files
  10. +
+

The retag operation writes title, artist, album artist, album, track number, disc number, year, and genre. Cover art can optionally be re-embedded.

+
+
+

Backup Manager

+

The Backup Manager protects your SoulSync database (all library data, watchlists, playlists, automations, and settings).

+ +

The system automation Auto-Backup Database creates a backup every 3 days automatically. You can adjust the interval in Automations.

+
+
+

Repair & Maintenance

+

Additional maintenance tools accessible from the dashboard:

+ +

Activity Feed

The activity feed at the bottom of the dashboard shows recent system events: downloads completed, syncs started, settings changed, automation runs, and errors. Events appear in real-time via WebSocket.

@@ -137,8 +175,10 @@ const DOCS_SECTIONS = [ { id: 'sync-spotify', title: 'Spotify Playlists' }, { id: 'sync-youtube', title: 'YouTube Playlists' }, { id: 'sync-tidal', title: 'Tidal Playlists' }, + { id: 'sync-listenbrainz', title: 'ListenBrainz' }, { id: 'sync-beatport', title: 'Beatport' }, { id: 'sync-mirrored', title: 'Mirrored Playlists' }, + { id: 'sync-m3u', title: 'M3U Export' }, { id: 'sync-discovery', title: 'Discovery Pipeline' } ], content: () => ` @@ -166,9 +206,31 @@ const DOCS_SECTIONS = [

Tidal Playlists

Requires Tidal authentication in Settings. Once connected, refresh to load your Tidal playlists. You can also select Tidal download quality: HQ (320kbps), HiFi (FLAC 16-bit), or HiFi Plus (up to 24-bit).

+
+

ListenBrainz

+

If ListenBrainz is configured in Settings, the Sync page includes a ListenBrainz tab for browsing and importing playlists from your ListenBrainz account:

+ +

ListenBrainz tracks are matched against Spotify/iTunes using a 4-strategy search: direct match, swapped artist/title, album-based lookup, and extended fuzzy search. Discovered tracks can be synced to your library like any other playlist.

+

Beatport

-

The Beatport tab has three views: Browse (featured content, genre browsing), Charts (Top 100, Hype charts), and My Playlists. Browse 12+ electronic music genres and download directly from chart listings.

+

The Beatport tab provides deep integration with electronic music content across three views:

+

Browse — Featured content organized into sections:

+ +

Genre Browser — Browse 12+ electronic music genres (House, Techno, Drum & Bass, Trance, etc.) with per-genre views: Top 10 tracks, staff picks, hype rankings, latest releases, and new charts.

+

Charts — Top 100 and Hype charts with full track listings. Each track can be manually matched against Spotify for metadata, then synced and downloaded.

+
ℹ️
Beatport data is cached with a configurable TTL. The system automation Refresh Beatport Cache runs every 24 hours to keep content fresh.

Mirrored Playlists

@@ -180,6 +242,11 @@ const DOCS_SECTIONS = [
  • Each profile has its own mirrored playlists
  • +
    +

    M3U Export

    +

    Export any mirrored playlist as an M3U file for use in external media players or media servers. Enable M3U export in Settings and use the export button on any playlist card.

    +

    M3U files reference the actual file paths in your library, so they work with any M3U-compatible player. Auto-save can be enabled to regenerate M3U files automatically when playlists are updated.

    +

    Discovery Pipeline

    For non-Spotify playlists (YouTube, Tidal), tracks need to be discovered before syncing. Discovery matches raw titles to official Spotify/iTunes metadata using fuzzy matching with a 0.7 confidence threshold.

    @@ -200,7 +267,9 @@ const DOCS_SECTIONS = [ children: [ { id: 'search-enhanced', title: 'Enhanced Search' }, { id: 'search-basic', title: 'Basic Search' }, + { id: 'search-sources', title: 'Download Sources' }, { id: 'search-downloading', title: 'Downloading Music' }, + { id: 'search-postprocess', title: 'Post-Processing Pipeline' }, { id: 'search-quality', title: 'Quality Profiles' }, { id: 'search-manager', title: 'Download Manager' } ], @@ -219,15 +288,44 @@ const DOCS_SECTIONS = [

    Toggle to Basic Search mode for direct Soulseek queries. This shows raw search results with detailed info: format, bitrate, quality score, file size, uploader name, upload speed, and availability.

    Filters let you narrow results by type (Albums/Singles), format (FLAC/MP3/OGG/AAC/WMA), and sort by relevance, quality, size, bitrate, duration, or uploader speed.

    +
    +

    Download Sources

    +

    SoulSync supports multiple download sources, configurable in Settings → Download Settings:

    + + + + + + + + +
    SourceDescriptionBest For
    SoulseekP2P network via slskd — largest selection of lossless and rare musicFLAC, rare tracks, DJ sets
    YouTubeYouTube audio extraction via yt-dlpLive performances, remixes, tracks not on Soulseek
    TidalTidal HiFi streaming rip (requires auth)Guaranteed quality, official releases
    HybridTries your primary source first, then automatically falls back to alternatesBest overall success rate
    +
    💡
    Hybrid mode is recommended for most users. It tries Soulseek first (best quality), then falls back to YouTube or Tidal if no suitable results are found. The fallback order respects your quality profile settings.
    +

    YouTube settings include cookies browser selection (for bot detection bypass), download delay (seconds between requests), and minimum confidence threshold for title matching.

    +

    Downloading Music

    When you select an album or track to download, a modal appears with:

    -

    After downloading, files go through post-processing: optional AcoustID fingerprint verification, automatic metadata tagging (title, artist, album, track number, genre, cover art), and organized file placement in your library.

    +

    Downloads can be started from multiple places: Enhanced Search results, artist discography, Download Missing modal, wishlist auto-processing, and playlist sync.

    +
    +
    +

    Post-Processing Pipeline

    +

    After a file is downloaded, it goes through an automatic pipeline before appearing in your library:

    +
      +
    1. AcoustID Fingerprint Verification — If AcoustID is configured, the downloaded file is fingerprinted and compared against the expected track. Title and artist are fuzzy-matched (title ≥ 70% similarity, artist ≥ 60%). Files that fail verification are quarantined instead of added to your library.
    2. +
    3. Metadata Tagging — The file is tagged with official metadata: title, artist, album artist, album, track number, disc number, year, genre, and composer. Tags are written using Mutagen (supports MP3, FLAC, OGG, M4A).
    4. +
    5. Cover Art Embedding — Album artwork is downloaded from the metadata source and embedded directly into the audio file.
    6. +
    7. File Organization — The file is renamed and moved to your transfer path following the template: Artist/Album/TrackNum - Title.ext. For multi-disc albums, a Disc N/ subfolder is automatically created when the album has more than one disc.
    8. +
    9. Lyrics (LRC) — Synced lyrics are fetched from the LRClib API and saved as .lrc sidecar files alongside the audio file. Compatible media players (foobar2000, MusicBee, Plex, etc.) will display time-synced lyrics automatically. Falls back to plain-text lyrics if synced versions aren't available.
    10. +
    11. Lossy Copy — If enabled in settings, a lower-bitrate copy is created alongside the original (useful for mobile device syncing).
    12. +
    13. Media Server Scan — Your media server (Plex/Jellyfin) is notified to scan for the new file. Navidrome auto-detects changes.
    14. +
    +
    ℹ️
    Quarantine: Files that fail AcoustID verification are moved to a quarantine folder instead of your library. You can review quarantined files and manually approve or delete them. The automation engine can trigger notifications when files are quarantined.

    Quality Profiles

    @@ -271,15 +369,23 @@ const DOCS_SECTIONS = [
    -

    Discovery Playlists

    -

    SoulSync generates curated playlists from your discovery pool (50 similar artists refreshed during watchlist scans):

    - -

    Each playlist can be downloaded or synced to your media server.

    +

    Discovery & Personalized Playlists

    +

    SoulSync generates playlists from two sources: your discovery pool (50 similar artists refreshed during watchlist scans) and your library listening data:

    + + + + + + + + + + + + + +
    PlaylistSourceDescription
    Popular PicksDiscovery PoolTop tracks from discovery pool artists
    Hidden GemsDiscovery PoolRare and deeper cuts from pool artists
    Discovery ShuffleDiscovery PoolRandomized mix across all pool artists
    Recently AddedLibraryTracks most recently added to your collection
    Top TracksLibraryYour most-played or highest-rated tracks
    Forgotten FavoritesLibraryTracks you haven't listened to in a while
    Decade MixesLibraryTracks grouped by release decade (70s, 80s, 90s, etc.)
    Daily MixesLibraryAuto-generated daily playlists based on your taste profile
    Familiar FavoritesLibraryWell-known tracks from artists you follow
    +

    Each playlist can be played in the media player, downloaded, or synced to your media server. Genre browsers let you filter discovery pool content by specific genres.

    Build Custom Playlist

    @@ -306,15 +412,28 @@ const DOCS_SECTIONS = [ icon: '/static/artists.png', children: [ { id: 'art-search', title: 'Artist Search' }, + { id: 'art-detail', title: 'Artist Detail & Discography' }, { id: 'art-watchlist', title: 'Watchlist' }, { id: 'art-scanning', title: 'New Release Scanning' }, + { id: 'art-wishlist', title: 'Wishlist' }, { id: 'art-settings', title: 'Watchlist Settings' } ], content: () => ` +
    +

    Artist Detail & Discography

    +

    The artist detail page shows a full discography organized by category:

    + +

    At the top, View on buttons link to the artist on each matched external service (Spotify, Apple Music, MusicBrainz, Deezer, AudioDB, Last.fm, Genius). Service badges on artist cards also indicate which services have matched this artist.

    +

    Similar Artists appear as clickable bubbles below the discography for further exploration and discovery.

    Watchlist

    @@ -336,6 +455,19 @@ const DOCS_SECTIONS = [
  • Stats: artists scanned, new tracks found, tracks added to wishlist
  • +
    +

    Wishlist

    +

    The wishlist is the queue of tracks waiting to be downloaded. Tracks are added to the wishlist from multiple sources:

    + +

    Auto-Processing: The system automation runs every 30 minutes, picking up wishlist items and attempting to download them from your configured source. Failed items are retried with increasing backoff.

    +

    Manual Processing: Use the Process Wishlist automation action to trigger processing on demand. Options include processing all items, albums only, or singles only.

    +

    Cleanup: The Cleanup Wishlist action removes duplicates (same track added multiple times) and items you already own in your library.

    +
    ℹ️
    Each wishlist item tracks its source (watchlist scan, playlist sync, manual), number of retry attempts, last error message, and status (pending, downloading, failed, complete).
    +

    Watchlist Settings

    Per-Artist Settings — Click the config icon on any watched artist to customize what release types to include: Albums, EPs, Singles, Live versions, Remixes, Acoustic versions, Compilations.

    @@ -434,6 +566,7 @@ const DOCS_SECTIONS = [
  • Fire Signal — Emit a custom signal that other automations can listen for
  • All notification messages support variable substitution: {name}, {status}, {time}, {run_count}, and context-specific variables from the action result.

    +

    Test Notifications: Use the test button next to any notification then-action to send a test message before saving. This verifies your webhook URL, API key, or bot token is working correctly.

    ℹ️
    Signal chaining lets you build multi-step workflows. Safety features include cycle detection (DFS), a 5-level chain depth limit, and a 10-second cooldown between signal fires.
    @@ -516,6 +649,7 @@ const DOCS_SECTIONS = [

    Download Missing Tracks

    From any album card showing missing tracks, click Download Missing to open a modal listing all tracks not in your library. Select tracks, choose a download source, and start the download. Progress is tracked per-track with status indicators.

    +

    Multi-Disc Albums: Albums with multiple discs are handled automatically. Tracks are organized into Disc N/ subfolders within the album directory, preventing track number collisions (e.g., Disc 1 Track 1 vs Disc 2 Track 1). The disc structure is detected from Spotify or iTunes metadata.

    ` }, @@ -525,7 +659,9 @@ const DOCS_SECTIONS = [ icon: '/static/import.png', children: [ { id: 'imp-setup', title: 'Staging Setup' }, - { id: 'imp-workflow', title: 'Import Workflow' } + { id: 'imp-workflow', title: 'Import Workflow' }, + { id: 'imp-singles', title: 'Singles Import' }, + { id: 'imp-matching', title: 'Track Matching' } ], content: () => `
    @@ -542,7 +678,20 @@ const DOCS_SECTIONS = [
  • Match tracks — Drag-and-drop staged files onto album track slots, or let auto-match attempt it
  • Review the match and click Confirm to import — files are tagged, organized, and added to your library
  • -

    The Singles tab handles individual tracks that aren't part of an album.

    +
    +
    +

    Singles Import

    +

    The Singles tab handles individual tracks that aren't part of an album structure. Files in the staging root (not in subfolders) appear here. Search for the correct track on Spotify/iTunes, confirm the match, and import. The file is tagged, renamed, and placed in your library.

    +
    +
    +

    Track Matching

    +

    The import matching system compares staged files against official album track lists:

    +
      +
    • Auto-Match — Attempts to match files to tracks automatically based on filename, duration, and track order
    • +
    • Drag & Drop — Manually drag staged files onto the correct album track slots
    • +
    • Conflict Detection — Highlights when a file matches multiple tracks or when tracks are unmatched
    • +
    +

    After matching, the import process tags files with the official metadata (title, artist, album, track number, cover art) and moves them to your transfer path following the standard file organization template.

    ` }, @@ -612,12 +761,13 @@ const DOCS_SECTIONS = [

    Download Settings

      -
    • Download Source Mode — Soulseek, YouTube, Tidal, or Hybrid (tries one, falls back to another)
    • -
    • Download Path — Where files are initially downloaded
    • -
    • Transfer Path — Where processed files are moved (should be your media server's monitored folder)
    • -
    • Staging Path — Folder for the Import feature
    • -
    • iTunes Country — Storefront region for iTunes lookups (US, GB, FR, etc.). Changes apply immediately.
    • -
    • Lossy Copy — Optionally create a lower-bitrate copy of every download (for mobile syncing)
    • +
    • Download Source Mode — Soulseek, YouTube, Tidal, or Hybrid. Hybrid tries your primary source first, then falls back to alternates. See Download Sources in the Music Downloads section for details.
    • +
    • Download Path — Where files are initially downloaded and processed
    • +
    • Transfer Path — Where processed files are moved after tagging and organization. Should point to your media server's monitored folder.
    • +
    • Staging Path — Folder for the Import feature (files placed here appear on the Import page)
    • +
    • iTunes Country — Storefront region for iTunes/Apple Music lookups (US, GB, FR, JP, etc.). Changes apply immediately to all searches without restarting.
    • +
    • Lossy Copy — When enabled, creates a lower-bitrate copy (MP3) of every downloaded file alongside the original. Useful for syncing to mobile devices or streaming servers with bandwidth constraints. The copy is placed in a configurable output folder.
    • +
    • Content Filtering — Toggle explicit content filtering to control whether explicit tracks appear in search results and downloads.
    @@ -627,10 +777,11 @@ const DOCS_SECTIONS = [

    Other Settings

      -
    • Content Filtering — Toggle explicit content allowance
    • -
    • YouTube — Cookies browser (for bot detection), download delay, minimum confidence
    • -
    • UI Appearance — Custom accent colors with persistent preference
    • -
    • API Keys — Generate and manage API keys for the REST API (Bearer token auth)
    • +
    • YouTube Configuration — Select cookies browser (Chrome, Firefox, Edge) for bot detection bypass, set download delay (seconds between requests), and minimum confidence threshold for title matching
    • +
    • UI Appearance — Custom accent colors with persistent preference. Changes apply immediately across the entire interface.
    • +
    • API Keys — Generate and manage API keys for the REST API. Keys use a sk_ prefix and are shown once at creation — only a SHA-256 hash is stored for security.
    • +
    • Path Templates — Configure how files are organized in your library. The default template is Artist/Album/TrackNum - Title.ext
    • +
    • WebSocket — Real-time status updates are delivered via WebSocket. All downloads, enrichment progress, scan status, and system events push to the UI without polling.
    ` @@ -675,7 +826,8 @@ const DOCS_SECTIONS = [ icon: '/static/settings.png', children: [ { id: 'api-auth', title: 'Authentication' }, - { id: 'api-endpoints', title: 'Key Endpoints' } + { id: 'api-endpoints', title: 'Key Endpoints' }, + { id: 'api-websocket', title: 'WebSocket Events' } ], content: () => `
    @@ -704,7 +856,24 @@ const DOCS_SECTIONS = [ POST /api/database/backupCreate a backup -

    The full API has 90+ endpoints. Use reverse proxy support for external access.

    +

    The full API has 90+ endpoints covering library, downloads, playlists, automations, settings, and more. Use a reverse proxy (Nginx, Caddy, Traefik) for external access with HTTPS.

    +
    +
    +

    WebSocket Events

    +

    SoulSync uses Socket.IO for real-time communication. The frontend connects automatically and receives live updates without polling:

    + + + + + + + + + + + +
    EventDescription
    download_progressPer-track download progress (speed, ETA, percentage)
    download_completeTrack finished downloading and post-processing
    batch_progressAlbum/playlist batch download status
    worker_statusEnrichment worker status (Spotify, MusicBrainz, Deezer, etc.)
    scan_progressLibrary scan, quality scan, or duplicate scan progress
    system_statusService connectivity changes (Spotify rate limit, slskd disconnect)
    activitySystem activity feed entries
    +

    All UI elements that show live progress (download bars, worker icons, scan counters) are driven by these WebSocket events.

    ` } @@ -753,6 +922,26 @@ function initializeDocsPage() { }); content.innerHTML = contentHTML; + // Suppress scroll spy during click-initiated scrolls + let _scrollSpySuppressed = false; + + function suppressScrollSpy() { + _scrollSpySuppressed = true; + clearTimeout(suppressScrollSpy._timer); + suppressScrollSpy._timer = setTimeout(() => { _scrollSpySuppressed = false; }, 800); + } + + // Helper: get element offset relative to a scrollable ancestor + function getOffsetRelativeTo(el, ancestor) { + let offset = 0; + let current = el; + while (current && current !== ancestor) { + offset += current.offsetTop; + current = current.offsetParent; + } + return offset; + } + // Section title click → expand/collapse children + scroll nav.querySelectorAll('.docs-nav-section-title').forEach(title => { title.addEventListener('click', () => { @@ -770,7 +959,8 @@ function initializeDocsPage() { if (children) children.classList.add('expanded'); } - // Scroll to section + // Scroll to section (suppress scroll spy so it doesn't fight) + suppressScrollSpy(); const target = document.getElementById('docs-' + sectionId); if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' }); }); @@ -778,9 +968,13 @@ function initializeDocsPage() { // Child click → scroll to subsection nav.querySelectorAll('.docs-nav-child').forEach(child => { - child.addEventListener('click', () => { + child.addEventListener('click', (e) => { + e.stopPropagation(); nav.querySelectorAll('.docs-nav-child').forEach(c => c.classList.remove('active')); child.classList.add('active'); + + // Keep parent section expanded + suppressScrollSpy(); const target = document.getElementById(child.dataset.target); if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' }); }); @@ -811,20 +1005,30 @@ function initializeDocsPage() { const docsContent = document.getElementById('docs-content'); if (docsContent) { docsContent.addEventListener('scroll', () => { - const scrollTop = docsContent.scrollTop + 100; + if (_scrollSpySuppressed) return; + + const containerRect = docsContent.getBoundingClientRect(); + const threshold = containerRect.top + 120; let activeSection = null; let activeChild = null; + // Find which section is currently in view using getBoundingClientRect DOCS_SECTIONS.forEach(section => { const el = document.getElementById('docs-' + section.id); - if (el && el.offsetTop <= scrollTop) { - activeSection = section.id; + if (el) { + const rect = el.getBoundingClientRect(); + if (rect.top <= threshold) { + activeSection = section.id; + } } if (section.children) { section.children.forEach(child => { const childEl = document.getElementById(child.id); - if (childEl && childEl.offsetTop <= scrollTop) { - activeChild = child.id; + if (childEl) { + const childRect = childEl.getBoundingClientRect(); + if (childRect.top <= threshold) { + activeChild = child.id; + } } }); }