Full Cleanup automation: combined housekeeping sweep for quarantine, downloads, staging, and search history

pull/253/head
Broque Thomas 2 months ago
parent 6e16596b9d
commit 07a79e7af6

@ -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
},
]

@ -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…
Cancel
Save