diff --git a/core/repair_jobs/quality_upgrade.py b/core/repair_jobs/quality_upgrade.py index a6ee6522..00af7cf1 100644 --- a/core/repair_jobs/quality_upgrade.py +++ b/core/repair_jobs/quality_upgrade.py @@ -476,25 +476,26 @@ class QualityUpgradeJob(RepairJob): icon = 'repair-icon-lossy' default_enabled = False default_interval_hours = 168 - default_settings = {'scope': 'watchlist', 'min_confidence': 0.7, 'deep_audio_verify': False, 'require_top_target': False} - setting_options = {'scope': ['watchlist', 'all'], 'deep_audio_verify': [True, False], 'require_top_target': [True, False]} + default_settings = {'scope': 'all', 'min_confidence': 0.7, 'deep_audio_verify': False, 'require_top_target': False} + setting_options = {'scope': ['all', 'watchlist'], 'deep_audio_verify': [True, False], 'require_top_target': [True, False]} auto_fix = False def _get_settings(self, context: JobContext) -> Dict[str, Any]: - cfg = context.config_manager - scope = 'watchlist' - min_conf = 0.7 - deep_verify = False - require_top = False - if cfg: - scope = cfg.get(self.get_config_key('settings.scope'), 'watchlist') or 'watchlist' + merged = dict(self.default_settings) + if context.config_manager: try: - min_conf = float(cfg.get(self.get_config_key('settings.min_confidence'), 0.7)) - except (TypeError, ValueError): - min_conf = 0.7 - deep_verify = cfg.get(self.get_config_key('settings.deep_audio_verify'), False) is True - require_top = cfg.get(self.get_config_key('settings.require_top_target'), False) is True - return {'scope': scope, 'min_confidence': min_conf, 'deep_audio_verify': deep_verify, 'require_top_target': require_top} + cfg = context.config_manager.get(f'repair.jobs.{self.job_id}.settings', {}) + if isinstance(cfg, dict): + merged.update(cfg) + except Exception as e: + logger.debug("settings read failed: %s", e) + try: + merged['min_confidence'] = float(merged.get('min_confidence', 0.7)) + except (TypeError, ValueError): + merged['min_confidence'] = 0.7 + merged['deep_audio_verify'] = merged.get('deep_audio_verify') is True + merged['require_top_target'] = merged.get('require_top_target') is True + return merged def _load_tracks(self, db: Any, scope: str) -> List[dict]: conn = db._get_connection() diff --git a/webui/static/pages-extra.js b/webui/static/pages-extra.js index 23e8756e..64e34add 100644 --- a/webui/static/pages-extra.js +++ b/webui/static/pages-extra.js @@ -2611,6 +2611,7 @@ let _verifQuarLoading = false; // Expanded π detail panels, keyed by quarantine entry id β survives the // polling re-render (which rebuilds the rows every few seconds). const _verifQuarOpenDetails = new Set(); +const _verifQuarOpenGroups = new Set(); // group keys whose alt-members are expanded // null = not fetched yet (assume enabled). Without an AcoustID API key // nothing ever gets a verification status, so the review queue collapses // to quarantine-only. @@ -2668,7 +2669,7 @@ const _VERIF_QUAR_TRIGGERS = { bit_depth: ['BIT DEPTH FILTER', 'verif-rb-int'], }; -function _verifQuarRowHtml(q, idx) { +function _verifQuarRowHtml(q, idx, extraAction = '') { const title = _adlEsc(q.expected_track || q.original_filename || q.filename || 'Unknown file'); const meta = [_adlEsc(q.expected_artist || ''), _adlEsc(q.original_filename || '')].filter(Boolean).join(' β '); const [trigLabel, trigClass] = _VERIF_QUAR_TRIGGERS[q.trigger] || ['QUARANTINED', 'verif-rb-unv']; @@ -2702,10 +2703,21 @@ function _verifQuarRowHtml(q, idx) { ${approveBtn} + ${extraAction} `; } +function _verifQuarToggleGroup(btn) { + const key = btn.dataset.groupKey; + const open = !_verifQuarOpenGroups.has(key); + if (open) _verifQuarOpenGroups.add(key); else _verifQuarOpenGroups.delete(key); + const wrapper = btn.closest('.verif-quar-alt-wrapper'); + if (wrapper) wrapper.querySelector('.verif-quar-alt-members')?.classList.toggle('vqg-open', open); + btn.classList.toggle('open', open); + btn.textContent = open ? `β΄ ${btn.dataset.altCount} more` : `βΎ ${btn.dataset.altCount} more`; +} + function _verifQuarRows() { if (!_verifQuarLoaded) return '