Full Cleanup automation: combined housekeeping sweep for quarantine, downloads, staging, and search history
This commit is contained in:
parent
6e16596b9d
commit
07a79e7af6
3 changed files with 158 additions and 1 deletions
|
|
@ -98,6 +98,13 @@ SYSTEM_AUTOMATIONS = [
|
|||
'action_type': 'backup_database',
|
||||
'initial_delay': 600, # 10 min after startup
|
||||
},
|
||||
{
|
||||
'name': 'Full Cleanup',
|
||||
'trigger_type': 'schedule',
|
||||
'trigger_config': {'interval': 12, 'unit': 'hours'},
|
||||
'action_type': 'full_cleanup',
|
||||
'initial_delay': 900, # 15 min after startup
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
119
web_server.py
119
web_server.py
|
|
@ -1041,6 +1041,123 @@ def _register_automation_handlers():
|
|||
except Exception as e:
|
||||
return {'status': 'error', 'reason': str(e)}
|
||||
|
||||
def _auto_full_cleanup(config):
|
||||
"""Run all cleanup tasks: quarantine, download queue, empty dirs, staging, search history."""
|
||||
import shutil as _shutil
|
||||
automation_id = config.get('_automation_id')
|
||||
steps = []
|
||||
|
||||
# --- 1. Clear quarantine ---
|
||||
_update_automation_progress(automation_id, phase='Clearing quarantine...', progress=0)
|
||||
quarantine_path = os.path.join(docker_resolve_path(config_manager.get('soulseek.download_path', './downloads')), 'ss_quarantine')
|
||||
q_removed = 0
|
||||
if os.path.exists(quarantine_path):
|
||||
for f in os.listdir(quarantine_path):
|
||||
fp = os.path.join(quarantine_path, f)
|
||||
try:
|
||||
if os.path.isfile(fp):
|
||||
os.remove(fp)
|
||||
q_removed += 1
|
||||
elif os.path.isdir(fp):
|
||||
_shutil.rmtree(fp)
|
||||
q_removed += 1
|
||||
except Exception:
|
||||
pass
|
||||
steps.append(f'Quarantine: removed {q_removed} items')
|
||||
_update_automation_progress(automation_id,
|
||||
log_line=f'Quarantine: removed {q_removed} items', log_type='success' if q_removed else 'info')
|
||||
|
||||
# --- 2. Clear completed/errored/cancelled downloads from Soulseek queue ---
|
||||
_update_automation_progress(automation_id, phase='Clearing download queue...', progress=20)
|
||||
has_active_batches = False
|
||||
has_post_processing = False
|
||||
with tasks_lock:
|
||||
for batch_data in download_batches.values():
|
||||
if batch_data.get('phase') not in ['complete', 'error', 'cancelled', None]:
|
||||
has_active_batches = True
|
||||
break
|
||||
if not has_active_batches:
|
||||
for task_data in download_tasks.values():
|
||||
if task_data.get('status') == 'post_processing':
|
||||
has_post_processing = True
|
||||
break
|
||||
if has_active_batches:
|
||||
steps.append('Download queue: skipped (active batches)')
|
||||
_update_automation_progress(automation_id,
|
||||
log_line='Download queue: skipped (active batches)', log_type='skip')
|
||||
else:
|
||||
try:
|
||||
run_async(soulseek_client.clear_all_completed_downloads())
|
||||
steps.append('Download queue: cleared')
|
||||
_update_automation_progress(automation_id,
|
||||
log_line='Download queue: cleared', log_type='success')
|
||||
except Exception as e:
|
||||
steps.append(f'Download queue: error ({e})')
|
||||
_update_automation_progress(automation_id,
|
||||
log_line=f'Download queue: error ({e})', log_type='error')
|
||||
|
||||
# --- 3. Sweep empty download directories ---
|
||||
_update_automation_progress(automation_id, phase='Sweeping empty directories...', progress=40)
|
||||
if has_active_batches or has_post_processing:
|
||||
reason = 'active batches' if has_active_batches else 'post-processing active'
|
||||
steps.append(f'Empty directories: skipped ({reason})')
|
||||
_update_automation_progress(automation_id,
|
||||
log_line=f'Empty directories: skipped ({reason})', log_type='skip')
|
||||
else:
|
||||
dirs_removed = _sweep_empty_download_directories()
|
||||
steps.append(f'Empty directories: removed {dirs_removed}')
|
||||
_update_automation_progress(automation_id,
|
||||
log_line=f'Empty directories: removed {dirs_removed}', log_type='success' if dirs_removed else 'info')
|
||||
|
||||
# --- 4. Clear staging folder ---
|
||||
_update_automation_progress(automation_id, phase='Clearing staging folder...', progress=60)
|
||||
staging_path = _get_staging_path()
|
||||
s_removed = 0
|
||||
if os.path.isdir(staging_path):
|
||||
for f in os.listdir(staging_path):
|
||||
fp = os.path.join(staging_path, f)
|
||||
try:
|
||||
if os.path.isfile(fp):
|
||||
os.remove(fp)
|
||||
s_removed += 1
|
||||
elif os.path.isdir(fp):
|
||||
_shutil.rmtree(fp)
|
||||
s_removed += 1
|
||||
except Exception:
|
||||
pass
|
||||
steps.append(f'Staging: removed {s_removed} items')
|
||||
_update_automation_progress(automation_id,
|
||||
log_line=f'Staging: removed {s_removed} items', log_type='success' if s_removed else 'info')
|
||||
|
||||
# --- 5. Clean search history ---
|
||||
_update_automation_progress(automation_id, phase='Cleaning search history...', progress=80)
|
||||
try:
|
||||
run_async(soulseek_client.maintain_search_history_with_buffer(
|
||||
keep_searches=50, trigger_threshold=200
|
||||
))
|
||||
steps.append('Search history: cleaned')
|
||||
_update_automation_progress(automation_id,
|
||||
log_line='Search history: cleaned', log_type='success')
|
||||
except Exception as e:
|
||||
steps.append(f'Search history: error ({e})')
|
||||
_update_automation_progress(automation_id,
|
||||
log_line=f'Search history: error ({e})', log_type='error')
|
||||
|
||||
total_removed = q_removed + s_removed
|
||||
_update_automation_progress(automation_id, status='finished', progress=100,
|
||||
phase='Complete',
|
||||
log_line=f'Full cleanup complete — {total_removed} items removed', log_type='success')
|
||||
return {
|
||||
'status': 'completed',
|
||||
'quarantine_removed': str(q_removed),
|
||||
'staging_removed': str(s_removed),
|
||||
'total_removed': str(total_removed),
|
||||
'steps': steps,
|
||||
'_manages_own_progress': True,
|
||||
}
|
||||
|
||||
automation_engine.register_action_handler('full_cleanup', _auto_full_cleanup)
|
||||
|
||||
automation_engine.register_action_handler('start_database_update', _auto_start_database_update,
|
||||
lambda: db_update_state.get('status') == 'running')
|
||||
automation_engine.register_action_handler('deep_scan_library', _auto_deep_scan_library,
|
||||
|
|
@ -4542,6 +4659,8 @@ def get_automation_blocks():
|
|||
"description": "Remove old searches from Soulseek", "available": True},
|
||||
{"type": "clean_completed_downloads", "label": "Clean Completed Downloads", "icon": "check-square",
|
||||
"description": "Clear completed downloads and empty directories", "available": True},
|
||||
{"type": "full_cleanup", "label": "Full Cleanup", "icon": "trash",
|
||||
"description": "Clear quarantine, download queue, staging folder, and search history in one sweep", "available": True},
|
||||
{"type": "deep_scan_library", "label": "Deep Scan Library", "icon": "search",
|
||||
"description": "Full library comparison without losing enrichment data", "available": True},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -17574,6 +17574,30 @@ const TOOL_HELP_CONTENT = {
|
|||
</ul>
|
||||
`
|
||||
},
|
||||
'auto-full_cleanup': {
|
||||
title: 'Full Cleanup',
|
||||
content: `
|
||||
<h4>What does this action do?</h4>
|
||||
<p>Runs all housekeeping tasks in a single sweep:</p>
|
||||
<ol>
|
||||
<li><strong>Clear Quarantine</strong> — permanently deletes all quarantined files</li>
|
||||
<li><strong>Clear Download Queue</strong> — removes completed, errored, and cancelled downloads from Soulseek</li>
|
||||
<li><strong>Sweep Empty Directories</strong> — removes empty folders left behind in the downloads directory</li>
|
||||
<li><strong>Clear Staging Folder</strong> — deletes all files and folders from the import staging area</li>
|
||||
<li><strong>Clean Search History</strong> — trims old Soulseek search queries</li>
|
||||
</ol>
|
||||
|
||||
<h4>Safety</h4>
|
||||
<p>Skips download queue cleanup if batches are actively downloading or post-processing. Each step runs independently — a failure in one step won't stop the others.</p>
|
||||
|
||||
<h4>Good for</h4>
|
||||
<ul>
|
||||
<li>Scheduled housekeeping every 12 hours</li>
|
||||
<li>Keeping disk usage and queue clutter under control</li>
|
||||
<li>Running after large batch downloads complete</li>
|
||||
</ul>
|
||||
`
|
||||
},
|
||||
'auto-deep_scan_library': {
|
||||
title: 'Deep Scan Library',
|
||||
content: `
|
||||
|
|
@ -47064,6 +47088,7 @@ const _autoIcons = {
|
|||
refresh_beatport_cache: '\uD83C\uDFB5',
|
||||
clean_search_history: '\uD83D\uDDD1\uFE0F',
|
||||
clean_completed_downloads: '\u2705',
|
||||
full_cleanup: '\uD83E\uDDF9',
|
||||
};
|
||||
|
||||
// --- Load & Render List ---
|
||||
|
|
@ -47234,7 +47259,8 @@ function _autoFormatAction(type) {
|
|||
update_discovery_pool: 'Update Discovery', start_quality_scan: 'Run Quality Scan',
|
||||
backup_database: 'Backup Database',
|
||||
refresh_beatport_cache: 'Refresh Beatport Cache', clean_search_history: 'Clean Search History',
|
||||
clean_completed_downloads: 'Clean Completed Downloads' };
|
||||
clean_completed_downloads: 'Clean Completed Downloads',
|
||||
full_cleanup: 'Full Cleanup' };
|
||||
return labels[type] || type || 'Unknown';
|
||||
}
|
||||
function _autoFormatNotify(type) {
|
||||
|
|
@ -47455,6 +47481,11 @@ const _RESULT_DISPLAY_MAP = {
|
|||
'cleanup_wishlist': [
|
||||
{ key: 'removed', label: 'Duplicates Removed' },
|
||||
],
|
||||
'full_cleanup': [
|
||||
{ key: 'quarantine_removed', label: 'Quarantine Removed' },
|
||||
{ key: 'staging_removed', label: 'Staging Removed' },
|
||||
{ key: 'total_removed', label: 'Total Items Removed' },
|
||||
],
|
||||
};
|
||||
|
||||
function _renderResultStats(resultJson, actionType) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue