""" Main Blueprint - Dashboard and Home Routes """ from flask import Blueprint, render_template, redirect, url_for from flask_login import login_required, current_user from app.extensions import db, cache from app.models.player import Player from app.models.playlist import Playlist from app.models.content import Content from app.utils.logger import get_recent_logs import os main_bp = Blueprint('main', __name__) @main_bp.route('/') @login_required @cache.cached(timeout=60, unless=lambda: current_user.role != 'viewer') def dashboard(): """Main dashboard page""" # Get statistics total_players = Player.query.count() total_playlists = Playlist.query.count() total_content = Content.query.count() # Calculate storage usage upload_folder = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'static', 'uploads') storage_mb = 0 if os.path.exists(upload_folder): for filename in os.listdir(upload_folder): filepath = os.path.join(upload_folder, filename) if os.path.isfile(filepath): storage_mb += os.path.getsize(filepath) storage_mb = round(storage_mb / (1024 * 1024), 2) # Convert to MB server_logs = get_recent_logs(20) # Per-playlist overview for the dashboard: which playlist holds what, and # which players consume it. This answers "where should I add media / which # playlist needs editing?" without opening each playlist. # # Players are fetched in a single query and grouped in Python rather than # relying on Playlist.players per row, to avoid one query per playlist. playlists = Playlist.query.order_by(Playlist.name).all() players_by_playlist = {} for player in Player.query.order_by(Player.name).all(): if player.playlist_id is not None: players_by_playlist.setdefault(player.playlist_id, []).append(player) playlist_overview = [] for playlist in playlists: assigned = players_by_playlist.get(playlist.id, []) playlist_overview.append({ 'playlist': playlist, 'content_count': playlist.contents.count(), 'total_duration': playlist.total_duration, 'players': assigned, 'player_count': len(assigned), 'online_count': sum(1 for p in assigned if p.is_online), }) # Players with no playlist produce no content on screen, so surface them. unassigned_players = Player.query.filter(Player.playlist_id.is_(None))\ .order_by(Player.name).all() return render_template( 'dashboard.html', total_players=total_players, total_playlists=total_playlists, total_content=total_content, storage_mb=storage_mb, recent_logs=server_logs, playlist_overview=playlist_overview, unassigned_players=unassigned_players ) @main_bp.route('/health') def health(): """Health check endpoint""" from flask import jsonify import os try: # Check database db.session.execute(db.text('SELECT 1')) # Check disk space upload_folder = os.path.join( main_bp.root_path or '.', 'static/uploads' ) if os.path.exists(upload_folder): stat = os.statvfs(upload_folder) free_space_gb = (stat.f_bavail * stat.f_frsize) / (1024**3) else: free_space_gb = 0 return jsonify({ 'status': 'healthy', 'database': 'ok', 'disk_space_gb': round(free_space_gb, 2) }), 200 except Exception as e: return jsonify({ 'status': 'unhealthy', 'error': str(e) }), 500