Fix library maintenance path fixes failing silently (#207)
fix_finding() was using a potentially stale transfer_folder that was only refreshed during scheduled job runs, not on manual fix attempts. Now re-reads the transfer path from config before each fix, matching the same logic used by _run_next_job(). Also surfaces fix failure reasons to the user — bulk fix now logs each failure with finding ID and error, and the frontend toast shows the actual error message instead of just "X failed".
This commit is contained in:
parent
20452859c5
commit
98463e5d43
4 changed files with 32 additions and 6 deletions
|
|
@ -757,6 +757,11 @@ class RepairWorker:
|
||||||
finding_id: ID of the finding to fix
|
finding_id: ID of the finding to fix
|
||||||
fix_action: Optional action override (e.g. 'staging' or 'delete' for orphan files)
|
fix_action: Optional action override (e.g. 'staging' or 'delete' for orphan files)
|
||||||
"""
|
"""
|
||||||
|
# Refresh transfer folder from config before each fix — same logic as _run_next_job
|
||||||
|
if self._config_manager:
|
||||||
|
raw = self._config_manager.get('soulseek.transfer_path', './Transfer')
|
||||||
|
self.transfer_folder = self._resolve_path(raw)
|
||||||
|
|
||||||
conn = None
|
conn = None
|
||||||
try:
|
try:
|
||||||
conn = self.db._get_connection()
|
conn = self.db._get_connection()
|
||||||
|
|
@ -2301,14 +2306,18 @@ class RepairWorker:
|
||||||
|
|
||||||
fixed = 0
|
fixed = 0
|
||||||
failed = 0
|
failed = 0
|
||||||
|
errors = []
|
||||||
for fid in ids_to_fix:
|
for fid in ids_to_fix:
|
||||||
result = self.fix_finding(fid, fix_action=fix_action)
|
result = self.fix_finding(fid, fix_action=fix_action)
|
||||||
if result.get('success'):
|
if result.get('success'):
|
||||||
fixed += 1
|
fixed += 1
|
||||||
else:
|
else:
|
||||||
|
error_msg = result.get('error', 'unknown error')
|
||||||
|
logger.warning("Fix failed for finding #%s: %s", fid, error_msg)
|
||||||
|
errors.append({'id': fid, 'error': error_msg})
|
||||||
failed += 1
|
failed += 1
|
||||||
|
|
||||||
return {'fixed': fixed, 'failed': failed, 'total': len(ids_to_fix)}
|
return {'fixed': fixed, 'failed': failed, 'total': len(ids_to_fix), 'errors': errors}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Error bulk fixing findings: %s", e, exc_info=True)
|
logger.error("Error bulk fixing findings: %s", e, exc_info=True)
|
||||||
return {'fixed': 0, 'failed': 0, 'total': 0, 'error': str(e)}
|
return {'fixed': 0, 'failed': 0, 'total': 0, 'error': str(e)}
|
||||||
|
|
|
||||||
|
|
@ -18999,6 +18999,15 @@ def get_version_info():
|
||||||
"title": "What's New in SoulSync",
|
"title": "What's New in SoulSync",
|
||||||
"subtitle": f"Version {SOULSYNC_VERSION} — Latest Changes",
|
"subtitle": f"Version {SOULSYNC_VERSION} — Latest Changes",
|
||||||
"sections": [
|
"sections": [
|
||||||
|
{
|
||||||
|
"title": "🔧 Fix Library Maintenance Path Fixes Failing Silently",
|
||||||
|
"description": "Path mismatch fixes now use fresh config and report errors to the UI",
|
||||||
|
"features": [
|
||||||
|
"• Transfer folder path is re-read from config before each fix attempt",
|
||||||
|
"• Fix failure reasons are now shown in the toast notification",
|
||||||
|
"• Bulk fix failures are logged individually with finding ID and error details"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "🔧 Fix Spotify Manual Match Storing Wrong IDs",
|
"title": "🔧 Fix Spotify Manual Match Storing Wrong IDs",
|
||||||
"description": "Manual match modals no longer store iTunes/Deezer IDs in Spotify ID columns",
|
"description": "Manual match modals no longer store iTunes/Deezer IDs in Spotify ID columns",
|
||||||
|
|
@ -44034,7 +44043,8 @@ def repair_findings_bulk_fix():
|
||||||
'success': True,
|
'success': True,
|
||||||
'fixed': result.get('fixed', 0),
|
'fixed': result.get('fixed', 0),
|
||||||
'failed': result.get('failed', 0),
|
'failed': result.get('failed', 0),
|
||||||
'total': result.get('total', 0)
|
'total': result.get('total', 0),
|
||||||
|
'errors': result.get('errors', [])
|
||||||
}), 200
|
}), 200
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error bulk fixing findings: {e}")
|
logger.error(f"Error bulk fixing findings: {e}")
|
||||||
|
|
|
||||||
|
|
@ -3403,6 +3403,7 @@ function closeHelperSearch() {
|
||||||
const WHATS_NEW = {
|
const WHATS_NEW = {
|
||||||
'2.1': [
|
'2.1': [
|
||||||
// Newest features first
|
// Newest features first
|
||||||
|
{ title: 'Fix Path Mismatch Fixes', desc: 'Library Maintenance path fixes now use fresh config and show error reasons in the toast' },
|
||||||
{ title: 'Fix Wrong Spotify IDs', desc: 'Manual match no longer stores iTunes IDs as Spotify IDs — detects actual provider from results' },
|
{ title: 'Fix Wrong Spotify IDs', desc: 'Manual match no longer stores iTunes IDs as Spotify IDs — detects actual provider from results' },
|
||||||
{ title: 'Spotify Enrichment Budget', desc: 'Background enrichment worker caps at 3,000 items/day to prevent rate limit bans — resets at midnight' },
|
{ title: 'Spotify Enrichment Budget', desc: 'Background enrichment worker caps at 3,000 items/day to prevent rate limit bans — resets at midnight' },
|
||||||
{ title: 'Per-Artist Library Sync', desc: 'Validate files and clean stale entries per artist from the enhanced library view', page: 'library', selector: '.library-controls' },
|
{ title: 'Per-Artist Library Sync', desc: 'Validate files and clean stale entries per artist from the enhanced library view', page: 'library', selector: '.library-controls' },
|
||||||
|
|
|
||||||
|
|
@ -55821,7 +55821,11 @@ async function fixAllMatchingFindings() {
|
||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
showToast(`Fixed ${result.fixed}${result.failed ? `, ${result.failed} failed` : ''} of ${result.total}`, result.fixed > 0 ? 'success' : 'error');
|
let msg = `Fixed ${result.fixed}${result.failed ? `, ${result.failed} failed` : ''} of ${result.total}`;
|
||||||
|
if (result.errors && result.errors.length > 0) {
|
||||||
|
msg += `: ${result.errors[0].error}`;
|
||||||
|
}
|
||||||
|
showToast(msg, result.fixed > 0 ? 'success' : 'error');
|
||||||
} else {
|
} else {
|
||||||
showToast(result.error || 'Bulk fix failed', 'error');
|
showToast(result.error || 'Bulk fix failed', 'error');
|
||||||
}
|
}
|
||||||
|
|
@ -56013,7 +56017,7 @@ async function bulkFixFindings() {
|
||||||
if (!orphanFixAction) return;
|
if (!orphanFixAction) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let fixed = 0, failed = 0;
|
let fixed = 0, failed = 0, lastError = '';
|
||||||
showToast(`Fixing ${ids.length} findings...`, 'info');
|
showToast(`Fixing ${ids.length} findings...`, 'info');
|
||||||
|
|
||||||
for (const id of ids) {
|
for (const id of ids) {
|
||||||
|
|
@ -56030,14 +56034,16 @@ async function bulkFixFindings() {
|
||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) fixed++;
|
if (result.success) fixed++;
|
||||||
else failed++;
|
else { failed++; lastError = result.error || 'unknown error'; }
|
||||||
} catch {
|
} catch {
|
||||||
failed++;
|
failed++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_repairSelectedFindings.clear();
|
_repairSelectedFindings.clear();
|
||||||
showToast(`Fixed ${fixed}${failed ? `, ${failed} failed` : ''}`, fixed > 0 ? 'success' : 'error');
|
let fixMsg = `Fixed ${fixed}${failed ? `, ${failed} failed` : ''}`;
|
||||||
|
if (failed && lastError) fixMsg += `: ${lastError}`;
|
||||||
|
showToast(fixMsg, fixed > 0 ? 'success' : 'error');
|
||||||
loadRepairFindingsDashboard();
|
loadRepairFindingsDashboard();
|
||||||
loadRepairFindings();
|
loadRepairFindings();
|
||||||
updateRepairStatus();
|
updateRepairStatus();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue