diff --git a/app/app.py b/app/app.py index 9505a25..b76d0d2 100755 --- a/app/app.py +++ b/app/app.py @@ -6,9 +6,20 @@ import requests import logging import threading import time +import datetime +import hashlib # Configure logging -logging.basicConfig(level=logging.INFO) +LOG_FOLDER = './logs' +os.makedirs(LOG_FOLDER, exist_ok=True) +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler(os.path.join(LOG_FOLDER, 'app.log')), + logging.StreamHandler() + ] +) Logger = logging.getLogger(__name__) app = Flask(__name__, static_folder='static') @@ -63,22 +74,38 @@ def download_media_files(playlist): file_url = media.get('url', '') file_path = os.path.join(RESOURCES_FOLDER, file_name) + if not file_name or not file_url: + Logger.error(f"Invalid media entry: {media}") + continue + + Logger.debug(f"Downloading file: {file_name} from {file_url}") + try: - response = requests.get(file_url) + response = requests.get(file_url, stream=True) if response.status_code == 200: with open(file_path, 'wb') as file: - file.write(response.content) + for chunk in response.iter_content(chunk_size=8192): + file.write(chunk) Logger.info(f"Downloaded {file_name} to {file_path}") else: Logger.error(f"Failed to download {file_name}: {response.status_code}") except requests.exceptions.RequestException as e: Logger.error(f"Failed to download {file_name}: {e}") +def calculate_file_hash(file_path): + """Calculate the SHA256 hash of a file.""" + if not os.path.exists(file_path): + return None + sha256 = hashlib.sha256() + with open(file_path, 'rb') as file: + while chunk := file.read(8192): + sha256.update(chunk) + return sha256.hexdigest() + # Download playlist files from server def download_playlist_files_from_server(): Logger.info("Starting playlist file download using app configuration...") - # Load app configuration app_config = load_app_config() server_address = app_config.get('server_address', '') port = app_config.get('port', 1025) @@ -89,7 +116,6 @@ def download_playlist_files_from_server(): Logger.error("Missing required configuration values.") return - # Construct the request URL and parameters server_ip = f'{server_address}:{port}' url = f'http://{server_ip}/api/playlists' params = { @@ -98,21 +124,64 @@ def download_playlist_files_from_server(): } try: - # Send request to fetch the playlist response = requests.get(url, params=params) Logger.debug(f"Status Code: {response.status_code}") Logger.debug(f"Response Content: {response.text}") if response.status_code == 200: try: - playlist = response.json().get('playlist', []) - Logger.info("Playlist retrieved successfully.") - save_playlist(playlist) # Save the playlist locally - download_media_files(playlist) # Download media files + server_playlist = response.json().get('playlist', []) + if not server_playlist: + Logger.error("Playlist is empty or missing in the server response.") + return + + Logger.info("Server playlist retrieved successfully.") + + # Calculate the hash of the current local playlist + local_playlist_hash = calculate_file_hash(PLAYLIST_FILE) + + # Calculate the hash of the server playlist + server_playlist_hash = hashlib.sha256(json.dumps(server_playlist, sort_keys=True).encode()).hexdigest() + + # Compare hashes to determine if the playlist has changed + if local_playlist_hash == server_playlist_hash: + Logger.info("No changes detected in the server playlist. Skipping download.") + return + + Logger.info("Changes detected in the server playlist. Downloading updated files...") + + # Compare and download missing or updated files + for media in server_playlist: + file_name = media.get('file_name', '') + file_url = media.get('url', '') + file_path = os.path.join(RESOURCES_FOLDER, file_name) + + if not file_name or not file_url: + Logger.error(f"Invalid media entry: {media}") + continue + + # Check if the file exists locally and matches the server version + if not os.path.exists(file_path) or os.path.getsize(file_path) != media.get('size', 0): + Logger.info(f"Downloading file: {file_name}") + response = requests.get(file_url, stream=True) + if response.status_code == 200: + with open(file_path, 'wb') as file: + for chunk in response.iter_content(chunk_size=8192): + file.write(chunk) + Logger.info(f"Downloaded {file_name} to {file_path}") + else: + Logger.error(f"Failed to download {file_name}: {response.status_code}") + + # Save the server playlist locally + save_playlist(server_playlist) + + # Update the updated_playlist.json + create_updated_playlist() + except json.JSONDecodeError as e: - Logger.error(f"Failed to parse playlist JSON: {e}") + Logger.error(f"Failed to parse server playlist JSON: {e}") else: - Logger.error(f"Failed to retrieve playlist: {response.text}") + Logger.error(f"Failed to retrieve server playlist: {response.text}") except requests.exceptions.RequestException as e: Logger.error(f"Failed to connect to server: {e}") @@ -183,9 +252,9 @@ def create_updated_playlist(): """Create a new playlist file with local file paths.""" Logger.info("Creating updated playlist with local file paths...") - # Load the existing playlist + # Load the existing server playlist if not os.path.exists(PLAYLIST_FILE): - Logger.error(f"Playlist file not found: {PLAYLIST_FILE}") + Logger.error(f"Server playlist file not found: {PLAYLIST_FILE}") return with open(PLAYLIST_FILE, 'r') as file: @@ -216,7 +285,8 @@ def create_updated_playlist(): # Check and download playlist on app startup def initialize_playlist(): Logger.info("Initializing playlist...") - download_playlist_files_from_server() + download_playlist_files_from_server() # Download playlist from the server + create_updated_playlist() # Create the updated playlist with local file paths Logger.info("Playlist initialization complete.") # Function to check for playlist updates every 5 minutes @@ -224,10 +294,8 @@ def periodic_playlist_check(): while True: try: Logger.info("Checking for playlist updates...") - # Download the playlist files from the server - download_playlist_files_from_server() - # Create the updated playlist with local file paths - create_updated_playlist() + download_playlist_files_from_server() # Download playlist from the server + create_updated_playlist() # Create the updated playlist with local file paths Logger.info("Playlist check complete.") except Exception as e: Logger.error(f"Error during playlist check: {e}") @@ -238,8 +306,30 @@ def start_playlist_check_thread(): thread = threading.Thread(target=periodic_playlist_check, daemon=True) thread.start() +# Function to delete log files older than 2 days +def delete_old_logs(log_folder='./logs', days=2): + Logger.info("Checking for old log files to delete...") + if not os.path.exists(log_folder): + Logger.warning(f"Log folder does not exist: {log_folder}") + return + + now = time.time() + cutoff = now - (days * 86400) # Convert days to seconds + + for file_name in os.listdir(log_folder): + file_path = os.path.join(log_folder, file_name) + if os.path.isfile(file_path): + file_modified_time = os.path.getmtime(file_path) + if file_modified_time < cutoff: + try: + os.remove(file_path) + Logger.info(f"Deleted old log file: {file_path}") + except Exception as e: + Logger.error(f"Failed to delete log file {file_path}: {e}") + if __name__ == '__main__': initialize_playlist() # Check and download playlist on startup create_updated_playlist() # Create the updated playlist start_playlist_check_thread() # Start the background thread for periodic checks - app.run(host='0.0.0.0', port=1025) \ No newline at end of file + app.run(host='0.0.0.0', port=1025) +