Add activity logging for key user actions

Introduces activity item logging for settings saves, connection tests, auto-detects, authentication flows, searches, batch cancellations, and discovery operations in web_server.py. Updates dashboard connection test API and UI to use a new endpoint and function, ensuring user actions are tracked and surfaced in the activity feed.
pull/15/head
Broque Thomas 8 months ago
parent 94d1c82674
commit ae9f426361

@ -1518,6 +1518,12 @@ def handle_settings():
config_manager.set(f'{service}.{key}', value)
print("✅ Settings saved successfully via Web UI.")
# Add activity for settings save
changed_services = list(new_settings.keys())
services_text = ", ".join(changed_services)
add_activity_item("⚙️", "Settings Updated", f"{services_text} configuration saved", "Now")
spotify_client._setup_client()
plex_client.server = None
jellyfin_client.server = None
@ -1553,6 +1559,42 @@ def test_connection_endpoint():
service = active_server # use the actual server name for the test
success, message = run_service_test(service, test_config)
# Add activity for connection test
if success:
add_activity_item("", "Connection Test", f"{service.title()} connection successful", "Now")
else:
add_activity_item("", "Connection Test", f"{service.title()} connection failed", "Now")
return jsonify({"success": success, "error": "" if success else message, "message": message if success else ""})
@app.route('/api/test-dashboard-connection', methods=['POST'])
def test_dashboard_connection_endpoint():
"""Test connection from dashboard - creates specific dashboard activity items"""
data = request.get_json()
service = data.get('service')
if not service:
return jsonify({"success": False, "error": "No service specified."}), 400
print(f"Received dashboard test connection request for: {service}")
# Get the current settings from the main config manager to test with
test_config = config_manager.get(service, {})
# For media servers, the service name might be 'server'
if service == 'server':
active_server = config_manager.get_active_media_server()
test_config = config_manager.get(active_server, {})
service = active_server # use the actual server name for the test
success, message = run_service_test(service, test_config)
# Add activity for dashboard connection test (different from settings test)
if success:
add_activity_item("🎛️", "Dashboard Test", f"{service.title()} service verified", "Now")
else:
add_activity_item("⚠️", "Dashboard Test", f"{service.title()} service check failed", "Now")
return jsonify({"success": success, "error": "" if success else message, "message": message if success else ""})
@app.route('/api/detect-media-server', methods=['POST'])
@ -1560,19 +1602,30 @@ def detect_media_server_endpoint():
data = request.get_json()
server_type = data.get('server_type')
print(f"Received auto-detect request for: {server_type}")
# Add activity for auto-detect start
add_activity_item("🔍", "Auto-Detect Started", f"Searching for {server_type} server", "Now")
found_url = run_detection(server_type)
if found_url:
add_activity_item("", "Auto-Detect Complete", f"{server_type} found at {found_url}", "Now")
return jsonify({"success": True, "found_url": found_url})
else:
add_activity_item("", "Auto-Detect Failed", f"No {server_type} server found", "Now")
return jsonify({"success": False, "error": f"No {server_type} server found on common local addresses."})
@app.route('/api/detect-soulseek', methods=['POST'])
def detect_soulseek_endpoint():
print("Received auto-detect request for slskd")
# Add activity for soulseek auto-detect start
add_activity_item("🔍", "Auto-Detect Started", "Searching for slskd server", "Now")
found_url = run_detection('slskd')
if found_url:
add_activity_item("", "Auto-Detect Complete", f"slskd found at {found_url}", "Now")
return jsonify({"success": True, "found_url": found_url})
else:
add_activity_item("", "Auto-Detect Failed", "No slskd server found", "Now")
return jsonify({"success": False, "error": "No slskd server found on common local addresses."})
# --- Full Tidal Authentication Flow ---
@ -1595,12 +1648,17 @@ def auth_tidal():
print(" tidal_client.authenticate() to start the flow.")
print("Please follow the instructions in the console to log in to Tidal.")
# Add activity for authentication start
add_activity_item("🔐", "Tidal Auth Started", "Initiating authentication flow", "Now")
if temp_tidal_client.authenticate():
# Re-initialize the main client instance after successful auth
global tidal_client
tidal_client = TidalClient()
add_activity_item("", "Tidal Auth Complete", "Successfully authenticated with Tidal", "Now")
return "<h1>✅ Tidal Authentication Successful!</h1><p>You can now close this window and return to the SoulSync application.</p>"
else:
add_activity_item("", "Tidal Auth Failed", "Authentication with Tidal failed", "Now")
return "<h1>❌ Tidal Authentication Failed</h1><p>Please check the console output of the server for a login URL and follow the instructions.</p>", 400
@ -1674,6 +1732,9 @@ def search_music():
print(f"Web UI Search for: '{query}'")
# Add activity for search start
add_activity_item("🔍", "Search Started", f"'{query}'", "Now")
try:
tracks, albums = asyncio.run(soulseek_client.search(query))
@ -1694,6 +1755,10 @@ def search_music():
# Sort by quality score
all_results = sorted(processed_albums + processed_tracks, key=lambda x: x.get('quality_score', 0), reverse=True)
# Add activity for search completion
total_results = len(all_results)
add_activity_item("", "Search Complete", f"'{query}' - {total_results} results", "Now")
return jsonify({"results": all_results})
except Exception as e:
@ -7970,6 +8035,10 @@ def cancel_batch(batch_id):
task['status'] = 'cancelled'
cancelled_count += 1
# Add activity for batch cancellation
playlist_name = download_batches[batch_id].get('playlist_name', 'Unknown Playlist')
add_activity_item("🚫", "Batch Cancelled", f"'{playlist_name}' - {cancelled_count} downloads cancelled", "Now")
print(f"✅ Cancelled batch {batch_id} with {cancelled_count} tasks")
return jsonify({"success": True, "cancelled_tasks": cancelled_count})
@ -8449,6 +8518,9 @@ def start_tidal_discovery(playlist_id):
}
tidal_discovery_states[playlist_id] = state
# Add activity for discovery start
add_activity_item("🔍", "Tidal Discovery Started", f"'{target_playlist.name}' - {len(target_playlist.tracks)} tracks", "Now")
# Start discovery worker
future = tidal_discovery_executor.submit(_run_tidal_discovery_worker, playlist_id)
state['discovery_future'] = future
@ -8714,6 +8786,9 @@ def _run_tidal_discovery_worker(playlist_id):
state['status'] = 'discovered'
state['discovery_progress'] = 100
# Add activity for discovery completion
add_activity_item("", "Tidal Discovery Complete", f"'{playlist.name}' - {successful_discoveries}/{len(playlist.tracks)} tracks found", "Now")
print(f"✅ Tidal discovery complete: {successful_discoveries}/{len(playlist.tracks)} tracks found")
except Exception as e:
@ -8993,6 +9068,11 @@ def start_youtube_discovery(url_hash):
state['discovery_progress'] = 0
state['spotify_matches'] = 0
# Add activity for discovery start
playlist_name = state['playlist']['name']
track_count = len(state['playlist']['tracks'])
add_activity_item("🔍", "YouTube Discovery Started", f"'{playlist_name}' - {track_count} tracks", "Now")
# Start discovery worker
future = youtube_discovery_executor.submit(_run_youtube_discovery_worker, url_hash)
state['discovery_future'] = future
@ -9146,6 +9226,10 @@ def _run_youtube_discovery_worker(url_hash):
state['status'] = 'complete'
state['discovery_progress'] = 100
# Add activity for discovery completion
playlist_name = playlist['name']
add_activity_item("", "YouTube Discovery Complete", f"'{playlist_name}' - {state['spotify_matches']}/{len(tracks)} tracks found", "Now")
print(f"✅ YouTube discovery complete: {state['spotify_matches']}/{len(tracks)} tracks matched")
except Exception as e:

@ -168,7 +168,7 @@
<p class="service-card-status-text" id="spotify-status-text">Disconnected</p>
<p class="service-card-response-time" id="spotify-response-time">Response: --</p>
<div class="service-card-footer">
<button class="service-card-button" onclick="testConnection('spotify')">Test Connection</button>
<button class="service-card-button" onclick="testDashboardConnection('spotify')">Test Connection</button>
</div>
</div>
<div class="service-card" id="media-server-service-card">
@ -179,7 +179,7 @@
<p class="service-card-status-text" id="media-server-status-text">Disconnected</p>
<p class="service-card-response-time" id="media-server-response-time">Response: --</p>
<div class="service-card-footer">
<button class="service-card-button" onclick="testConnection('server')">Test Connection</button>
<button class="service-card-button" onclick="testDashboardConnection('server')">Test Connection</button>
</div>
</div>
<div class="service-card" id="soulseek-service-card">
@ -190,7 +190,7 @@
<p class="service-card-status-text" id="soulseek-status-text">Disconnected</p>
<p class="service-card-response-time" id="soulseek-response-time">Response: --</p>
<div class="service-card-footer">
<button class="service-card-button" onclick="testConnection('soulseek')">Test Connection</button>
<button class="service-card-button" onclick="testDashboardConnection('soulseek')">Test Connection</button>
</div>
</div>
</div>

@ -259,6 +259,7 @@ const API = {
config: '/config',
settings: '/api/settings',
testConnection: '/api/test-connection',
testDashboardConnection: '/api/test-dashboard-connection',
playlists: '/api/playlists',
sync: '/api/sync',
search: '/api/search',
@ -1511,6 +1512,32 @@ async function testConnection(service) {
}
}
// Dashboard-specific test functions that create activity items
async function testDashboardConnection(service) {
try {
showLoadingOverlay(`Testing ${service} service...`);
const response = await fetch(API.testDashboardConnection, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ service })
});
const result = await response.json();
if (result.success) {
showToast(`${service} service verified`, 'success');
} else {
showToast(`${service} service check failed: ${result.error}`, 'error');
}
} catch (error) {
console.error(`Error testing ${service} service:`, error);
showToast(`Failed to test ${service} service`, 'error');
} finally {
hideLoadingOverlay();
}
}
// Individual Auto-detect functions - same as GUI
async function autoDetectPlex() {
try {

Loading…
Cancel
Save