- Created 10 SVG icon files in app/static/icons/ (Feather Icons style) - Updated base.html with SVG icons in navigation and dark mode toggle - Updated dashboard.html with icons in stats cards and quick actions - Updated content_list_new.html (playlist management) with SVG icons - Updated upload_media.html with upload-related icons - Updated manage_player.html with player management icons - Icons use currentColor for automatic theme adaptation - Removed emoji dependency for better Raspberry Pi compatibility - Added ICON_INTEGRATION.md documentation
233 lines
8.1 KiB
Python
233 lines
8.1 KiB
Python
"""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
|
|
import os
|
|
|
|
from app.extensions import db, cache
|
|
from app.models import Player, Content
|
|
from app.utils.logger import log_action
|
|
|
|
playlist_bp = Blueprint('playlist', __name__, url_prefix='/playlist')
|
|
|
|
|
|
@playlist_bp.route('/<int:player_id>')
|
|
@login_required
|
|
def manage_playlist(player_id: int):
|
|
"""Manage playlist for a specific player."""
|
|
player = Player.query.get_or_404(player_id)
|
|
|
|
# Get all content for this player, ordered by position
|
|
playlist_content = Content.query.filter_by(
|
|
player_id=player_id
|
|
).order_by(Content.position).all()
|
|
|
|
# Get available content (files not already in this player's playlist)
|
|
all_files = db.session.query(Content.filename).distinct().all()
|
|
playlist_filenames = {c.filename for c in playlist_content}
|
|
available_files = [f[0] for f in all_files if f[0] not in playlist_filenames]
|
|
|
|
return render_template('playlist/manage_playlist.html',
|
|
player=player,
|
|
playlist_content=playlist_content,
|
|
available_files=available_files)
|
|
|
|
|
|
@playlist_bp.route('/<int:player_id>/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)
|
|
|
|
try:
|
|
filename = request.form.get('filename')
|
|
duration = request.form.get('duration', type=int, default=10)
|
|
|
|
if not filename:
|
|
flash('Please provide a filename.', 'warning')
|
|
return redirect(url_for('playlist.manage_playlist', player_id=player_id))
|
|
|
|
# Get max position
|
|
max_position = db.session.query(db.func.max(Content.position)).filter_by(
|
|
player_id=player_id
|
|
).scalar() or 0
|
|
|
|
# Get file info from existing content
|
|
existing_content = Content.query.filter_by(filename=filename).first()
|
|
if not existing_content:
|
|
flash('File not found.', 'danger')
|
|
return redirect(url_for('playlist.manage_playlist', player_id=player_id))
|
|
|
|
# Create new content entry for this player
|
|
new_content = Content(
|
|
filename=filename,
|
|
content_type=existing_content.content_type,
|
|
duration=duration,
|
|
file_size=existing_content.file_size,
|
|
player_id=player_id,
|
|
position=max_position + 1
|
|
)
|
|
db.session.add(new_content)
|
|
|
|
# Increment playlist version
|
|
player.playlist_version += 1
|
|
|
|
db.session.commit()
|
|
cache.clear()
|
|
|
|
log_action('info', f'Added "{filename}" to playlist for player "{player.name}"')
|
|
flash(f'Added "{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('/<int:player_id>/remove/<int:content_id>', 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)
|
|
content = Content.query.get_or_404(content_id)
|
|
|
|
if content.player_id != player_id:
|
|
flash('Content does not belong to this player.', 'danger')
|
|
return redirect(url_for('playlist.manage_playlist', player_id=player_id))
|
|
|
|
try:
|
|
filename = content.filename
|
|
|
|
# Delete content
|
|
db.session.delete(content)
|
|
|
|
# Reorder remaining content
|
|
remaining_content = Content.query.filter_by(
|
|
player_id=player_id
|
|
).order_by(Content.position).all()
|
|
|
|
for idx, item in enumerate(remaining_content, start=1):
|
|
item.position = idx
|
|
|
|
# Increment playlist version
|
|
player.playlist_version += 1
|
|
|
|
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('/<int:player_id>/reorder', methods=['POST'])
|
|
@login_required
|
|
def reorder_playlist(player_id: int):
|
|
"""Reorder playlist items."""
|
|
player = Player.query.get_or_404(player_id)
|
|
|
|
try:
|
|
# 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
|
|
for idx, content_id in enumerate(content_ids, start=1):
|
|
content = Content.query.get(content_id)
|
|
if content and content.player_id == player_id:
|
|
content.position = idx
|
|
|
|
# Increment playlist version
|
|
player.playlist_version += 1
|
|
|
|
db.session.commit()
|
|
cache.clear()
|
|
|
|
log_action('info', f'Reordered playlist for player "{player.name}" (version {player.playlist_version})')
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'message': 'Playlist reordered successfully',
|
|
'version': player.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('/<int:player_id>/update-duration/<int:content_id>', 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)
|
|
content = Content.query.get_or_404(content_id)
|
|
|
|
if content.player_id != player_id:
|
|
return jsonify({'success': False, 'message': 'Content does not belong to this player'}), 403
|
|
|
|
try:
|
|
duration = request.form.get('duration', type=int)
|
|
|
|
if not duration or duration < 1:
|
|
return jsonify({'success': False, 'message': 'Invalid duration'}), 400
|
|
|
|
content.duration = duration
|
|
player.playlist_version += 1
|
|
|
|
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': player.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('/<int:player_id>/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)
|
|
|
|
try:
|
|
# Delete all content for this player
|
|
Content.query.filter_by(player_id=player_id).delete()
|
|
|
|
# Increment playlist version
|
|
player.playlist_version += 1
|
|
|
|
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))
|