diff --git a/config/settings.py b/config/settings.py index a9d8740c..6820b9fc 100644 --- a/config/settings.py +++ b/config/settings.py @@ -460,6 +460,9 @@ class ConfigManager: "enabled": True, "poll_interval": 30 }, + "library": { + "music_paths": [] + }, "import": { "staging_path": "./Staging", "replace_lower_quality": False diff --git a/web_server.py b/web_server.py index 9dda191f..303db8a5 100644 --- a/web_server.py +++ b/web_server.py @@ -12913,6 +12913,18 @@ def _resolve_library_file_path(file_path): except Exception: pass + # Check user-configured music library paths (Settings > Library) + try: + music_paths = config_manager.get('library.music_paths', []) + if isinstance(music_paths, list): + for p in music_paths: + if p and isinstance(p, str): + resolved_p = docker_resolve_path(p.strip()) + if resolved_p: + library_dirs.add(resolved_p) + except Exception: + pass + path_parts = file_path.replace('\\', '/').split('/') # Try progressively shorter path suffixes against each candidate directory diff --git a/webui/index.html b/webui/index.html index eeb8afe3..8344ca8a 100644 --- a/webui/index.html +++ b/webui/index.html @@ -5693,6 +5693,25 @@ + +
+

📂 Music Library Paths

+ +
+ Tell SoulSync where your music files live. Required for tag writing, streaming, and file detection + when your media server stores files at a different path than SoulSync can see. + Docker users: mount your music folder(s) into the SoulSync container with read-write access, + then add the container-side path here (e.g. /music). +
+ +
+ +
+ +
+
+ +

🔞 Content Filter

diff --git a/webui/static/script.js b/webui/static/script.js index 11521810..22427901 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -5969,6 +5969,10 @@ async function loadSettingsData() { document.getElementById('lossy-copy-options').style.display = settings.lossy_copy?.enabled ? 'block' : 'none'; + // Populate Music Library Paths + const _musicPaths = settings.library?.music_paths || []; + renderMusicPaths(_musicPaths); + // Populate Content Filter settings document.getElementById('allow-explicit').checked = settings.content_filter?.allow_explicit !== false; @@ -6606,6 +6610,49 @@ async function toggleHydrabaseFromSettings() { } } +// ── Music Library Paths ── +function renderMusicPaths(paths) { + const container = document.getElementById('music-paths-list'); + if (!container) return; + if (!paths || paths.length === 0) { + container.innerHTML = '
No paths configured. Click "Add Path" to add your music folder(s).
'; + return; + } + container.innerHTML = paths.map((p, i) => ` +
+ + +
+ `).join(''); +} + +function addMusicPathRow() { + const container = document.getElementById('music-paths-list'); + if (!container) return; + // Clear the "no paths" message if present + const placeholder = container.querySelector('div[style*="color: rgba"]'); + if (placeholder && !container.querySelector('.music-path-row')) placeholder.remove(); + const row = document.createElement('div'); + row.className = 'form-group music-path-row'; + row.style.marginBottom = '4px'; + row.innerHTML = ` + + + `; + container.appendChild(row); + row.querySelector('input').focus(); +} + +function collectMusicPaths() { + const inputs = document.querySelectorAll('.music-path-input'); + const paths = []; + inputs.forEach(input => { + const val = input.value.trim(); + if (val) paths.push(val); + }); + return paths; +} + // ── Database Maintenance ── async function loadDbMaintenanceInfo() { try { @@ -7079,6 +7126,9 @@ async function saveSettings(quiet = false) { content_filter: { allow_explicit: document.getElementById('allow-explicit').checked }, + library: { + music_paths: collectMusicPaths() + }, import: { replace_lower_quality: document.getElementById('import-replace-lower-quality').checked },