diff --git a/builds/dev.build.sh b/builds/dev.build.sh deleted file mode 100755 index 35a2804..0000000 --- a/builds/dev.build.sh +++ /dev/null @@ -1 +0,0 @@ -docker buildx build --push --platform linux/amd64,linux/arm64 --build-arg CACHE_BUST=$(date +%s) --tag cooldockerizer93/spotizerr:dev . \ No newline at end of file diff --git a/builds/latest.build.sh b/builds/latest.build.sh deleted file mode 100755 index 104761e..0000000 --- a/builds/latest.build.sh +++ /dev/null @@ -1 +0,0 @@ -docker buildx build --push --platform linux/amd64,linux/arm64 --build-arg CACHE_BUST=$(date +%s) --tag cooldockerizer93/spotizerr:latest . diff --git a/routes/utils/celery_tasks.py b/routes/utils/celery_tasks.py index 0f88897..ac72774 100644 --- a/routes/utils/celery_tasks.py +++ b/routes/utils/celery_tasks.py @@ -160,6 +160,28 @@ def _log_task_to_history(task_id, final_status_str, error_msg=None): logger.warning(f"History: No task_info found for task_id {task_id}. Cannot log to history.") return + # Determine service_used and quality_profile + main_service_name = str(task_info.get('main', 'Unknown')).capitalize() # e.g. Spotify, Deezer from their respective .env values + fallback_service_name = str(task_info.get('fallback', '')).capitalize() + + service_used_str = main_service_name + if task_info.get('fallback') and fallback_service_name: # Check if fallback was configured + # Try to infer actual service used if possible, otherwise show configured. + # This part is a placeholder for more accurate determination if deezspot gives explicit feedback. + # For now, we assume 'main' was used unless an error hints otherwise. + # A more robust solution would involve deezspot callback providing this. + service_used_str = f"{main_service_name} (Fallback: {fallback_service_name})" + # If error message indicates fallback, we could try to parse it. + # e.g. if error_msg and "fallback" in error_msg.lower(): service_used_str = f"{fallback_service_name} (Used Fallback)" + + # Determine quality profile (primarily from the 'quality' field) + # 'quality' usually holds the primary service's quality (e.g., spotifyQuality, deezerQuality) + quality_profile_str = str(task_info.get('quality', 'N/A')) + + # Get convertTo and bitrate + convert_to_str = str(task_info.get('convertTo', '')) # Empty string if None or not present + bitrate_str = str(task_info.get('bitrate', '')) # Empty string if None or not present + # Extract Spotify ID from item URL if possible spotify_id = None item_url = task_info.get('url', '') @@ -185,7 +207,11 @@ def _log_task_to_history(task_id, final_status_str, error_msg=None): 'timestamp_added': task_info.get('created_at', time.time()), 'timestamp_completed': last_status_obj.get('timestamp', time.time()) if last_status_obj else time.time(), 'original_request_json': json.dumps(task_info.get('original_request', {})), - 'last_status_obj_json': json.dumps(last_status_obj if last_status_obj else {}) + 'last_status_obj_json': json.dumps(last_status_obj if last_status_obj else {}), + 'service_used': service_used_str, + 'quality_profile': quality_profile_str, + 'convert_to': convert_to_str if convert_to_str else None, # Store None if empty string + 'bitrate': bitrate_str if bitrate_str else None # Store None if empty string } add_entry_to_history(history_entry) except Exception as e: diff --git a/routes/utils/history_manager.py b/routes/utils/history_manager.py index d3c55d7..346c680 100644 --- a/routes/utils/history_manager.py +++ b/routes/utils/history_manager.py @@ -29,7 +29,11 @@ def init_history_db(): timestamp_added REAL, timestamp_completed REAL, original_request_json TEXT, - last_status_obj_json TEXT + last_status_obj_json TEXT, + service_used TEXT, + quality_profile TEXT, + convert_to TEXT, + bitrate TEXT ) """) conn.commit() @@ -51,7 +55,8 @@ def add_entry_to_history(history_data: dict): 'task_id', 'download_type', 'item_name', 'item_artist', 'item_album', 'item_url', 'spotify_id', 'status_final', 'error_message', 'timestamp_added', 'timestamp_completed', 'original_request_json', - 'last_status_obj_json' + 'last_status_obj_json', 'service_used', 'quality_profile', + 'convert_to', 'bitrate' ] # Ensure all keys are present, filling with None if not for key in required_keys: @@ -66,14 +71,17 @@ def add_entry_to_history(history_data: dict): task_id, download_type, item_name, item_artist, item_album, item_url, spotify_id, status_final, error_message, timestamp_added, timestamp_completed, original_request_json, - last_status_obj_json - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + last_status_obj_json, service_used, quality_profile, + convert_to, bitrate + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( history_data['task_id'], history_data['download_type'], history_data['item_name'], history_data['item_artist'], history_data['item_album'], history_data['item_url'], history_data['spotify_id'], history_data['status_final'], history_data['error_message'], history_data['timestamp_added'], history_data['timestamp_completed'], - history_data['original_request_json'], history_data['last_status_obj_json'] + history_data['original_request_json'], history_data['last_status_obj_json'], + history_data['service_used'], history_data['quality_profile'], + history_data['convert_to'], history_data['bitrate'] )) conn.commit() logger.info(f"Added/Updated history for task_id: {history_data['task_id']}, status: {history_data['status_final']}") @@ -131,7 +139,8 @@ def get_history_entries(limit=25, offset=0, sort_by='timestamp_completed', sort_ # Validate sort_by and sort_order to prevent SQL injection valid_sort_columns = [ 'task_id', 'download_type', 'item_name', 'item_artist', 'item_album', - 'item_url', 'status_final', 'timestamp_added', 'timestamp_completed' + 'item_url', 'status_final', 'timestamp_added', 'timestamp_completed', + 'service_used', 'quality_profile', 'convert_to', 'bitrate' ] if sort_by not in valid_sort_columns: sort_by = 'timestamp_completed' # Default sort @@ -175,7 +184,11 @@ if __name__ == '__main__': 'timestamp_added': time.time() - 3600, 'timestamp_completed': time.time(), 'original_request_json': json.dumps({'param1': 'value1'}), - 'last_status_obj_json': json.dumps({'status': 'complete', 'message': 'Finished!'}) + 'last_status_obj_json': json.dumps({'status': 'complete', 'message': 'Finished!'}), + 'service_used': 'Spotify (Primary)', + 'quality_profile': 'NORMAL', + 'convert_to': None, + 'bitrate': None } add_entry_to_history(sample_data_complete) @@ -192,7 +205,11 @@ if __name__ == '__main__': 'timestamp_added': time.time() - 7200, 'timestamp_completed': time.time() - 60, 'original_request_json': json.dumps({'param2': 'value2'}), - 'last_status_obj_json': json.dumps({'status': 'error', 'error': 'Network issue'}) + 'last_status_obj_json': json.dumps({'status': 'error', 'error': 'Network issue'}), + 'service_used': 'Deezer', + 'quality_profile': 'MP3_320', + 'convert_to': 'mp3', + 'bitrate': '320' } add_entry_to_history(sample_data_error) @@ -210,7 +227,11 @@ if __name__ == '__main__': 'timestamp_added': time.time() - 3600, 'timestamp_completed': time.time() + 100, # Updated completion time 'original_request_json': json.dumps({'param1': 'value1', 'new_param': 'added'}), - 'last_status_obj_json': json.dumps({'status': 'complete', 'message': 'Finished! With update.'}) + 'last_status_obj_json': json.dumps({'status': 'complete', 'message': 'Finished! With update.'}), + 'service_used': 'Spotify (Deezer Fallback)', + 'quality_profile': 'HIGH', + 'convert_to': 'flac', + 'bitrate': None } add_entry_to_history(updated_data_complete) diff --git a/src/js/history.ts b/src/js/history.ts index ed13c34..7816feb 100644 --- a/src/js/history.ts +++ b/src/js/history.ts @@ -41,10 +41,11 @@ document.addEventListener('DOMContentLoaded', () => { totalEntries = data.total_count; currentPage = Math.floor(offset / limit) + 1; updatePagination(); + updateSortIndicators(); } catch (error) { console.error('Error fetching history:', error); if (historyTableBody) { - historyTableBody.innerHTML = 'Error loading history.'; + historyTableBody.innerHTML = 'Error loading history.'; } } } @@ -54,7 +55,7 @@ document.addEventListener('DOMContentLoaded', () => { historyTableBody.innerHTML = ''; // Clear existing rows if (!entries || entries.length === 0) { - historyTableBody.innerHTML = 'No history entries found.'; + historyTableBody.innerHTML = 'No history entries found.'; return; } @@ -63,6 +64,19 @@ document.addEventListener('DOMContentLoaded', () => { row.insertCell().textContent = entry.item_name || 'N/A'; row.insertCell().textContent = entry.item_artist || 'N/A'; row.insertCell().textContent = entry.download_type ? entry.download_type.charAt(0).toUpperCase() + entry.download_type.slice(1) : 'N/A'; + row.insertCell().textContent = entry.service_used || 'N/A'; + // Construct Quality display string + let qualityDisplay = entry.quality_profile || 'N/A'; + if (entry.convert_to) { + qualityDisplay = `${entry.convert_to.toUpperCase()}`; + if (entry.bitrate) { + qualityDisplay += ` ${entry.bitrate}k`; + } + qualityDisplay += ` (${entry.quality_profile || 'Original'})`; + } else if (entry.bitrate) { // Case where convert_to might not be set, but bitrate is (e.g. for OGG Vorbis quality settings) + qualityDisplay = `${entry.bitrate}k (${entry.quality_profile || 'Profile'})`; + } + row.insertCell().textContent = qualityDisplay; const statusCell = row.insertCell(); statusCell.textContent = entry.status_final || 'N/A'; @@ -91,7 +105,7 @@ document.addEventListener('DOMContentLoaded', () => { errorDetailsDiv = document.createElement('div'); errorDetailsDiv.className = 'error-details'; const newCell = row.insertCell(); // This will append to the end of the row - newCell.colSpan = 7; // Span across all columns + newCell.colSpan = 9; // Span across all columns newCell.appendChild(errorDetailsDiv); // Visually, this new cell will be after the 'Details' button cell. // To make it appear as part of the status cell or below the row, more complex DOM manipulation or CSS would be needed. @@ -122,6 +136,10 @@ document.addEventListener('DOMContentLoaded', () => { `Album: ${entry.item_album || 'N/A'}\n` + `URL: ${entry.item_url}\n` + `Spotify ID: ${entry.spotify_id || 'N/A'}\n` + + `Service Used: ${entry.service_used || 'N/A'}\n` + + `Quality Profile (Original): ${entry.quality_profile || 'N/A'}\n` + + `ConvertTo: ${entry.convert_to || 'N/A'}\n` + + `Bitrate: ${entry.bitrate ? entry.bitrate + 'k' : 'N/A'}\n` + `Status: ${entry.status_final}\n` + `Error: ${entry.error_message || 'None'}\n` + `Added: ${new Date(entry.timestamp_added * 1000).toLocaleString()}\n` + @@ -146,6 +164,16 @@ document.addEventListener('DOMContentLoaded', () => { }); }); + function updateSortIndicators() { + document.querySelectorAll('th[data-sort]').forEach(headerCell => { + const th = headerCell as HTMLElement; + th.classList.remove('sort-asc', 'sort-desc'); + if (th.dataset.sort === currentSortBy) { + th.classList.add(currentSortOrder === 'ASC' ? 'sort-asc' : 'sort-desc'); + } + }); + } + prevButton?.addEventListener('click', () => fetchHistory(currentPage - 1)); nextButton?.addEventListener('click', () => fetchHistory(currentPage + 1)); limitSelect?.addEventListener('change', (e) => { diff --git a/static/html/history.html b/static/html/history.html index 6a42101..875b550 100644 --- a/static/html/history.html +++ b/static/html/history.html @@ -46,6 +46,8 @@ Name Artist Type + Service + Quality Status Date Added Date Completed/Ended