Add configurable music library paths for file resolution
New setting in Settings > Library lets users add folder paths where their music files live. The file resolver checks these paths when looking for library files, solving Docker path mismatches and multi- folder libraries. Required for tag writing, streaming, and orphan detection when the media server reports paths that differ from what SoulSync can see. Docker users mount their music folder(s) with read-write access and add the container-side path. Default is empty — existing users see no change.
This commit is contained in:
parent
195484441e
commit
f603f92868
4 changed files with 84 additions and 0 deletions
|
|
@ -460,6 +460,9 @@ class ConfigManager:
|
|||
"enabled": True,
|
||||
"poll_interval": 30
|
||||
},
|
||||
"library": {
|
||||
"music_paths": []
|
||||
},
|
||||
"import": {
|
||||
"staging_path": "./Staging",
|
||||
"replace_lower_quality": False
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -5693,6 +5693,25 @@
|
|||
</div>
|
||||
|
||||
|
||||
<!-- Music Library Paths -->
|
||||
<div class="settings-group" data-stg="library">
|
||||
<h3>📂 Music Library Paths</h3>
|
||||
|
||||
<div class="help-text" style="margin-bottom: 12px;">
|
||||
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.
|
||||
<strong>Docker users:</strong> mount your music folder(s) into the SoulSync container with read-write access,
|
||||
then add the <em>container-side</em> path here (e.g. <code>/music</code>).
|
||||
</div>
|
||||
|
||||
<div id="music-paths-list"></div>
|
||||
|
||||
<div class="form-actions" style="margin-top: 8px;">
|
||||
<button class="test-button" onclick="addMusicPathRow()">+ Add Path</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Content Filter Settings -->
|
||||
<div class="settings-group" data-stg="library">
|
||||
<h3>🔞 Content Filter</h3>
|
||||
|
|
|
|||
|
|
@ -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 = '<div style="color: rgba(255,255,255,0.3); font-size: 0.85em; padding: 4px 0;">No paths configured. Click "Add Path" to add your music folder(s).</div>';
|
||||
return;
|
||||
}
|
||||
container.innerHTML = paths.map((p, i) => `
|
||||
<div class="form-group music-path-row" style="margin-bottom: 4px;">
|
||||
<input type="text" class="music-path-input" value="${escapeHtml(p)}" placeholder="/music or C:\\Music" style="flex:1;">
|
||||
<button class="test-button" onclick="this.closest('.music-path-row').remove()" style="padding: 8px 12px; color: #ef5350; border-color: rgba(239,83,80,0.3);">×</button>
|
||||
</div>
|
||||
`).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 = `
|
||||
<input type="text" class="music-path-input" value="" placeholder="/music or C:\\Music" style="flex:1;">
|
||||
<button class="test-button" onclick="this.closest('.music-path-row').remove()" style="padding: 8px 12px; color: #ef5350; border-color: rgba(239,83,80,0.3);">×</button>
|
||||
`;
|
||||
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
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue