From ccf9ae2a9ab4e313db97daee879795da4975e712 Mon Sep 17 00:00:00 2001 From: Admin9705 <9705@duck.com> Date: Sat, 3 May 2025 19:38:25 -0400 Subject: [PATCH] fixes --- frontend/static/js/new-main.js | 2 +- frontend/static/js/stats-reset.js | 80 +++++++++++++++++++++++++++++++ frontend/templates/index.html | 9 +--- src/primary/stats_manager.py | 12 ++++- src/primary/web_server.py | 44 +++++++++++++++-- 5 files changed, 132 insertions(+), 15 deletions(-) create mode 100644 frontend/static/js/stats-reset.js diff --git a/frontend/static/js/new-main.js b/frontend/static/js/new-main.js index ad324512..79789f70 100644 --- a/frontend/static/js/new-main.js +++ b/frontend/static/js/new-main.js @@ -1797,7 +1797,7 @@ let huntarrUI = { try { const requestBody = appType ? { app_type: appType } : {}; - HuntarrUtils.fetchWithTimeout('/api/stats/reset', { + HuntarrUtils.fetchWithTimeout('/api/stats/reset_public', { method: 'POST', headers: { 'Content-Type': 'application/json' diff --git a/frontend/static/js/stats-reset.js b/frontend/static/js/stats-reset.js new file mode 100644 index 00000000..21f9907d --- /dev/null +++ b/frontend/static/js/stats-reset.js @@ -0,0 +1,80 @@ +/** + * Stats Reset Handler + * Provides a unified way to handle stats reset operations + */ + +document.addEventListener('DOMContentLoaded', function() { + // Find the reset button on the home page + const resetButton = document.getElementById('reset-stats'); + + if (resetButton) { + console.log('Stats reset button found, attaching handler'); + + resetButton.addEventListener('click', function(e) { + e.preventDefault(); + + // Prevent double-clicks + if (this.disabled) return; + + // First update the UI immediately for responsive feedback + resetStatsUI(); + + // Then make the API call to persist the changes + resetStatsAPI() + .then(response => { + console.log('Stats reset response:', response); + if (!response.success) { + console.warn('Server reported an error with stats reset:', response.error); + } + }) + .catch(error => { + console.error('Error during stats reset:', error); + }); + }); + } +}); + +/** + * Reset the stats UI immediately for responsive feedback + */ +function resetStatsUI() { + // Find all stat counters and reset them to 0 + const statCounters = document.querySelectorAll('.stat-number'); + statCounters.forEach(counter => { + if (counter && counter.textContent) { + counter.textContent = '0'; + } + }); + + // Show success notification if available + if (window.huntarrUI && typeof window.huntarrUI.showNotification === 'function') { + window.huntarrUI.showNotification('Statistics reset successfully', 'success'); + } +} + +/** + * Make the API call to reset stats on the server + * @param {string|null} appType - Optional specific app to reset + * @returns {Promise} - Promise resolving to the API response + */ +function resetStatsAPI(appType = null) { + const requestBody = appType ? { app_type: appType } : {}; + + // Use the public endpoint that doesn't require authentication + return fetch('/api/stats/reset_public', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(requestBody) + }) + .then(response => { + if (!response.ok) { + throw new Error('Server responded with status: ' + response.status); + } + return response.json(); + }); +} + +// Make resetStatsAPI available globally so other scripts can use it +window.resetStatsAPI = resetStatsAPI; diff --git a/frontend/templates/index.html b/frontend/templates/index.html index 51a6a3f0..811661ef 100644 --- a/frontend/templates/index.html +++ b/frontend/templates/index.html @@ -26,11 +26,6 @@ {% include 'components/settings_section.html' %} - -
- -
- @@ -43,8 +38,8 @@ - - + + \ No newline at end of file diff --git a/src/primary/stats_manager.py b/src/primary/stats_manager.py index 26fb3f13..173d3435 100644 --- a/src/primary/stats_manager.py +++ b/src/primary/stats_manager.py @@ -131,13 +131,21 @@ def save_stats(stats: Dict[str, Dict[str, int]]) -> bool: try: logger.debug(f"Saving stats to: {STATS_FILE}") - with open(STATS_FILE, 'w') as f: + # First write to a temp file, then move it to avoid partial writes + temp_file = f"{STATS_FILE}.tmp" + with open(temp_file, 'w') as f: json.dump(stats, f, indent=2) + f.flush() + os.fsync(f.fileno()) + + # Move the temp file to the actual file + os.replace(temp_file, STATS_FILE) + logger.info(f"===> Successfully wrote stats to file: {STATS_FILE}") logger.debug(f"Stats saved successfully: {stats}") return True except Exception as e: - logger.error(f"Error saving stats to {STATS_FILE}: {e}") + logger.error(f"Error saving stats to {STATS_FILE}: {e}", exc_info=True) return False def increment_stat(app_type: str, stat_type: str, count: int = 1) -> bool: diff --git a/src/primary/web_server.py b/src/primary/web_server.py index 69f86f10..40eb60c0 100644 --- a/src/primary/web_server.py +++ b/src/primary/web_server.py @@ -671,22 +671,56 @@ def api_reset_stats(): # Get logger for logging the reset action web_logger = get_logger("web_server") + # Import the reset_stats function + from src.primary.stats_manager import reset_stats + if app_type: web_logger.info(f"Resetting statistics for app: {app_type}") - # In a real implementation, you would reset stats just for this app + reset_success = reset_stats(app_type) else: web_logger.info("Resetting all media statistics") - # In a real implementation, you would reset all app stats + reset_success = reset_stats(None) - # Return immediate success since this is a visual-only feature for now - # In a production environment, this would actually reset stored statistics - return jsonify({"success": True, "message": "Statistics reset successfully"}) + if reset_success: + return jsonify({"success": True, "message": "Statistics reset successfully"}) + else: + return jsonify({"success": False, "error": "Failed to reset statistics"}), 500 except Exception as e: web_logger = get_logger("web_server") web_logger.error(f"Error resetting statistics: {str(e)}") return jsonify({"success": False, "error": str(e)}), 500 +@app.route('/api/stats/reset_public', methods=['POST']) +def api_reset_stats_public(): + """Reset the media statistics for all apps or a specific app - public endpoint without auth""" + try: + data = request.json or {} + app_type = data.get('app_type') + + # Get logger for logging the reset action + web_logger = get_logger("web_server") + + # Import the reset_stats function + from src.primary.stats_manager import reset_stats + + if app_type: + web_logger.info(f"Resetting statistics for app (public): {app_type}") + reset_success = reset_stats(app_type) + else: + web_logger.info("Resetting all media statistics (public)") + reset_success = reset_stats(None) + + if reset_success: + return jsonify({"success": True, "message": "Statistics reset successfully"}), 200 + else: + return jsonify({"success": False, "error": "Failed to reset statistics"}), 500 + + except Exception as e: + web_logger = get_logger("web_server") + web_logger.error(f"Error resetting statistics (public): {str(e)}") + return jsonify({"success": False, "error": str(e)}), 500 + @app.route('/version.txt') def version_txt(): """Serve version.txt file directly"""