"""Playlist blueprint for managing player playlists.""" from flask import (Blueprint, render_template, request, redirect, url_for, flash, jsonify, current_app) from flask_login import login_required from sqlalchemy import desc, update import os from app.extensions import db, cache from app.models import Player, Content, Playlist from app.models.playlist import playlist_content from app.utils.logger import log_action playlist_bp = Blueprint('playlist', __name__, url_prefix='/playlist') @playlist_bp.route('/') @login_required def manage_playlist(player_id: int): """Manage playlist for a specific player.""" player = Player.query.get_or_404(player_id) # Get content from player's assigned playlist playlist_items = [] if player.playlist_id: playlist = Playlist.query.get(player.playlist_id) if playlist: playlist_items = playlist.get_content_ordered() # Get available content (all content not in current playlist) all_content = Content.query.all() playlist_content_ids = {item.id for item in playlist_items} available_content = [c for c in all_content if c.id not in playlist_content_ids] return render_template('playlist/manage_playlist.html', player=player, playlist_content=playlist_items, available_content=available_content) @playlist_bp.route('//add', methods=['POST']) @login_required def add_to_playlist(player_id: int): """Add content to player's playlist.""" player = Player.query.get_or_404(player_id) if not player.playlist_id: flash('Player has no playlist assigned.', 'warning') return redirect(url_for('playlist.manage_playlist', player_id=player_id)) try: content_id = request.form.get('content_id', type=int) duration = request.form.get('duration', type=int, default=10) if not content_id: flash('Please select content.', 'warning') return redirect(url_for('playlist.manage_playlist', player_id=player_id)) content = Content.query.get_or_404(content_id) playlist = Playlist.query.get(player.playlist_id) # Get max position from sqlalchemy import select, func max_pos = db.session.execute( select(func.max(playlist_content.c.position)).where( playlist_content.c.playlist_id == playlist.id ) ).scalar() or 0 # Add to playlist_content association table stmt = playlist_content.insert().values( playlist_id=playlist.id, content_id=content.id, position=max_pos + 1, duration=duration ) db.session.execute(stmt) # Increment playlist version playlist.increment_version() db.session.commit() cache.clear() log_action('info', f'Added "{content.filename}" to playlist for player "{player.name}"') flash(f'Added "{content.filename}" to playlist.', 'success') except Exception as e: db.session.rollback() log_action('error', f'Error adding to playlist: {str(e)}') flash('Error adding to playlist.', 'danger') return redirect(url_for('playlist.manage_playlist', player_id=player_id)) @playlist_bp.route('//remove/', methods=['POST']) @login_required def remove_from_playlist(player_id: int, content_id: int): """Remove content from player's playlist.""" player = Player.query.get_or_404(player_id) if not player.playlist_id: flash('Player has no playlist assigned.', 'danger') return redirect(url_for('playlist.manage_playlist', player_id=player_id)) try: content = Content.query.get_or_404(content_id) playlist = Playlist.query.get(player.playlist_id) filename = content.filename # Remove from playlist_content association table from sqlalchemy import delete stmt = delete(playlist_content).where( (playlist_content.c.playlist_id == playlist.id) & (playlist_content.c.content_id == content_id) ) db.session.execute(stmt) # Reorder remaining content from sqlalchemy import select remaining = db.session.execute( select(playlist_content.c.content_id, playlist_content.c.position).where( playlist_content.c.playlist_id == playlist.id ).order_by(playlist_content.c.position) ).fetchall() for idx, row in enumerate(remaining, start=1): stmt = update(playlist_content).where( (playlist_content.c.playlist_id == playlist.id) & (playlist_content.c.content_id == row.content_id) ).values(position=idx) db.session.execute(stmt) # Increment playlist version playlist.increment_version() db.session.commit() cache.clear() log_action('info', f'Removed "{filename}" from playlist for player "{player.name}"') flash(f'Removed "{filename}" from playlist.', 'success') except Exception as e: db.session.rollback() log_action('error', f'Error removing from playlist: {str(e)}') flash('Error removing from playlist.', 'danger') return redirect(url_for('playlist.manage_playlist', player_id=player_id)) @playlist_bp.route('//reorder', methods=['POST']) @login_required def reorder_playlist(player_id: int): """Reorder playlist items.""" player = Player.query.get_or_404(player_id) if not player.playlist_id: return jsonify({'success': False, 'message': 'Player has no playlist'}), 400 try: playlist = Playlist.query.get(player.playlist_id) # Get new order from JSON data = request.get_json() content_ids = data.get('content_ids', []) if not content_ids: return jsonify({'success': False, 'message': 'No content IDs provided'}), 400 # Update positions in association table for idx, content_id in enumerate(content_ids, start=1): stmt = update(playlist_content).where( (playlist_content.c.playlist_id == playlist.id) & (playlist_content.c.content_id == content_id) ).values(position=idx) db.session.execute(stmt) # Increment playlist version playlist.increment_version() db.session.commit() cache.clear() log_action('info', f'Reordered playlist for player "{player.name}" (version {playlist.version})') return jsonify({ 'success': True, 'message': 'Playlist reordered successfully', 'version': playlist.version }) except Exception as e: db.session.rollback() log_action('error', f'Error reordering playlist: {str(e)}') return jsonify({'success': False, 'message': str(e)}), 500 @playlist_bp.route('//update-duration/', methods=['POST']) @login_required def update_duration(player_id: int, content_id: int): """Update content duration in playlist.""" player = Player.query.get_or_404(player_id) if not player.playlist_id: return jsonify({'success': False, 'message': 'Player has no playlist'}), 400 try: playlist = Playlist.query.get(player.playlist_id) content = Content.query.get_or_404(content_id) duration = request.form.get('duration', type=int) if not duration or duration < 1: return jsonify({'success': False, 'message': 'Invalid duration'}), 400 # Update duration in association table stmt = update(playlist_content).where( (playlist_content.c.playlist_id == playlist.id) & (playlist_content.c.content_id == content_id) ).values(duration=duration) db.session.execute(stmt) # Increment playlist version playlist.increment_version() db.session.commit() cache.clear() log_action('info', f'Updated duration for "{content.filename}" in player "{player.name}" playlist') return jsonify({ 'success': True, 'message': 'Duration updated', 'version': playlist.version }) except Exception as e: db.session.rollback() log_action('error', f'Error updating duration: {str(e)}') return jsonify({'success': False, 'message': str(e)}), 500 @playlist_bp.route('//update-muted/', methods=['POST']) @login_required def update_muted(player_id: int, content_id: int): """Update content muted setting in playlist.""" player = Player.query.get_or_404(player_id) if not player.playlist_id: return jsonify({'success': False, 'message': 'Player has no playlist'}), 400 try: playlist = Playlist.query.get(player.playlist_id) content = Content.query.get_or_404(content_id) muted = request.form.get('muted', 'true').lower() == 'true' # Update muted in association table stmt = update(playlist_content).where( (playlist_content.c.playlist_id == playlist.id) & (playlist_content.c.content_id == content_id) ).values(muted=muted) db.session.execute(stmt) # Increment playlist version playlist.increment_version() db.session.commit() cache.clear() log_action('info', f'Updated muted={muted} for "{content.filename}" in player "{player.name}" playlist') return jsonify({ 'success': True, 'message': 'Audio setting updated', 'muted': muted, 'version': playlist.version }) except Exception as e: db.session.rollback() log_action('error', f'Error updating muted setting: {str(e)}') return jsonify({'success': False, 'message': str(e)}), 500 @playlist_bp.route('//clear', methods=['POST']) @login_required def clear_playlist(player_id: int): """Clear all content from player's playlist.""" player = Player.query.get_or_404(player_id) if not player.playlist_id: flash('Player has no playlist assigned.', 'warning') return redirect(url_for('playlist.manage_playlist', player_id=player_id)) try: playlist = Playlist.query.get(player.playlist_id) # Delete all content from playlist from sqlalchemy import delete stmt = delete(playlist_content).where( playlist_content.c.playlist_id == playlist.id ) db.session.execute(stmt) # Increment playlist version playlist.increment_version() db.session.commit() cache.clear() log_action('info', f'Cleared playlist for player "{player.name}"') flash('Playlist cleared successfully.', 'success') except Exception as e: db.session.rollback() log_action('error', f'Error clearing playlist: {str(e)}') flash('Error clearing playlist.', 'danger') return redirect(url_for('playlist.manage_playlist', player_id=player_id))