updated digiserver 2
This commit is contained in:
@@ -14,8 +14,9 @@ players_bp = Blueprint('players', __name__, url_prefix='/players')
|
||||
|
||||
|
||||
@players_bp.route('/')
|
||||
@players_bp.route('/list')
|
||||
@login_required
|
||||
def players_list():
|
||||
def list():
|
||||
"""Display list of all players."""
|
||||
try:
|
||||
players = Player.query.order_by(Player.name).all()
|
||||
@@ -27,7 +28,7 @@ def players_list():
|
||||
status_info = get_player_status_info(player.id)
|
||||
player_statuses[player.id] = status_info
|
||||
|
||||
return render_template('players_list.html',
|
||||
return render_template('players/players_list.html',
|
||||
players=players,
|
||||
groups=groups,
|
||||
player_statuses=player_statuses)
|
||||
@@ -43,11 +44,15 @@ def add_player():
|
||||
"""Add a new player."""
|
||||
if request.method == 'GET':
|
||||
groups = Group.query.order_by(Group.name).all()
|
||||
return render_template('add_player.html', groups=groups)
|
||||
return render_template('players/add_player.html', groups=groups)
|
||||
|
||||
try:
|
||||
name = request.form.get('name', '').strip()
|
||||
hostname = request.form.get('hostname', '').strip()
|
||||
location = request.form.get('location', '').strip()
|
||||
password = request.form.get('password', '').strip()
|
||||
quickconnect_code = request.form.get('quickconnect_code', '').strip()
|
||||
orientation = request.form.get('orientation', 'Landscape')
|
||||
group_id = request.form.get('group_id')
|
||||
|
||||
# Validation
|
||||
@@ -55,23 +60,59 @@ def add_player():
|
||||
flash('Player name must be at least 3 characters long.', 'warning')
|
||||
return redirect(url_for('players.add_player'))
|
||||
|
||||
if not hostname or len(hostname) < 3:
|
||||
flash('Hostname must be at least 3 characters long.', 'warning')
|
||||
return redirect(url_for('players.add_player'))
|
||||
|
||||
# Check if hostname already exists
|
||||
existing_player = Player.query.filter_by(hostname=hostname).first()
|
||||
if existing_player:
|
||||
flash(f'A player with hostname "{hostname}" already exists.', 'warning')
|
||||
return redirect(url_for('players.add_player'))
|
||||
|
||||
if not quickconnect_code:
|
||||
flash('Quick Connect Code is required.', 'warning')
|
||||
return redirect(url_for('players.add_player'))
|
||||
|
||||
# Generate unique auth code
|
||||
auth_code = secrets.token_urlsafe(16)
|
||||
auth_code = secrets.token_urlsafe(32)
|
||||
|
||||
# Create player
|
||||
new_player = Player(
|
||||
name=name,
|
||||
hostname=hostname,
|
||||
location=location or None,
|
||||
auth_code=auth_code,
|
||||
orientation=orientation,
|
||||
group_id=int(group_id) if group_id else None
|
||||
)
|
||||
|
||||
# Set password if provided
|
||||
if password:
|
||||
new_player.set_password(password)
|
||||
else:
|
||||
# Use quickconnect code as default password
|
||||
new_player.set_password(quickconnect_code)
|
||||
|
||||
# Set quickconnect code
|
||||
new_player.set_quickconnect_code(quickconnect_code)
|
||||
|
||||
db.session.add(new_player)
|
||||
db.session.commit()
|
||||
|
||||
log_action('info', f'Player "{name}" created with auth code {auth_code}')
|
||||
flash(f'Player "{name}" created successfully. Auth code: {auth_code}', 'success')
|
||||
log_action('info', f'Player "{name}" (hostname: {hostname}) created')
|
||||
|
||||
return redirect(url_for('players.players_list'))
|
||||
# Flash detailed success message
|
||||
success_msg = f'''
|
||||
Player "{name}" created successfully!<br>
|
||||
<strong>Auth Code:</strong> {auth_code}<br>
|
||||
<strong>Hostname:</strong> {hostname}<br>
|
||||
<strong>Quick Connect:</strong> {quickconnect_code}<br>
|
||||
<small>Configure the player with these credentials in app_config.json</small>
|
||||
'''
|
||||
flash(success_msg, 'success')
|
||||
|
||||
return redirect(url_for('players.list'))
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
@@ -88,7 +129,7 @@ def edit_player(player_id: int):
|
||||
|
||||
if request.method == 'GET':
|
||||
groups = Group.query.order_by(Group.name).all()
|
||||
return render_template('edit_player.html', player=player, groups=groups)
|
||||
return render_template('players/edit_player.html', player=player, groups=groups)
|
||||
|
||||
try:
|
||||
name = request.form.get('name', '').strip()
|
||||
@@ -112,7 +153,7 @@ def edit_player(player_id: int):
|
||||
log_action('info', f'Player "{name}" (ID: {player_id}) updated')
|
||||
flash(f'Player "{name}" updated successfully.', 'success')
|
||||
|
||||
return redirect(url_for('players.players_list'))
|
||||
return redirect(url_for('players.list'))
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
@@ -146,7 +187,7 @@ def delete_player(player_id: int):
|
||||
log_action('error', f'Error deleting player: {str(e)}')
|
||||
flash('Error deleting player. Please try again.', 'danger')
|
||||
|
||||
return redirect(url_for('players.players_list'))
|
||||
return redirect(url_for('players.list'))
|
||||
|
||||
|
||||
@players_bp.route('/<int:player_id>/regenerate-auth', methods=['POST'])
|
||||
@@ -169,7 +210,7 @@ def regenerate_auth_code(player_id: int):
|
||||
log_action('error', f'Error regenerating auth code: {str(e)}')
|
||||
flash('Error regenerating auth code. Please try again.', 'danger')
|
||||
|
||||
return redirect(url_for('players.players_list'))
|
||||
return redirect(url_for('players.list'))
|
||||
|
||||
|
||||
@players_bp.route('/<int:player_id>')
|
||||
@@ -191,7 +232,7 @@ def player_page(player_id: int):
|
||||
.limit(10)\
|
||||
.all()
|
||||
|
||||
return render_template('player_page.html',
|
||||
return render_template('players/player_page.html',
|
||||
player=player,
|
||||
playlist=playlist,
|
||||
status_info=status_info,
|
||||
@@ -199,7 +240,7 @@ def player_page(player_id: int):
|
||||
except Exception as e:
|
||||
log_action('error', f'Error loading player page: {str(e)}')
|
||||
flash('Error loading player page.', 'danger')
|
||||
return redirect(url_for('players.players_list'))
|
||||
return redirect(url_for('players.list'))
|
||||
|
||||
|
||||
@players_bp.route('/<int:player_id>/fullscreen')
|
||||
@@ -217,7 +258,7 @@ def player_fullscreen(player_id: int):
|
||||
# Get player's playlist
|
||||
playlist = get_player_playlist(player_id)
|
||||
|
||||
return render_template('player_fullscreen.html',
|
||||
return render_template('players/player_fullscreen.html',
|
||||
player=player,
|
||||
playlist=playlist)
|
||||
except Exception as e:
|
||||
@@ -227,7 +268,7 @@ def player_fullscreen(player_id: int):
|
||||
|
||||
@cache.memoize(timeout=300) # Cache for 5 minutes
|
||||
def get_player_playlist(player_id: int) -> List[dict]:
|
||||
"""Get playlist for a player based on their group assignment.
|
||||
"""Get playlist for a player based on their direct content assignment.
|
||||
|
||||
Args:
|
||||
player_id: The player's database ID
|
||||
@@ -239,16 +280,10 @@ def get_player_playlist(player_id: int) -> List[dict]:
|
||||
if not player:
|
||||
return []
|
||||
|
||||
# Get content from player's group
|
||||
if player.group_id:
|
||||
group = Group.query.get(player.group_id)
|
||||
if group:
|
||||
contents = group.contents.order_by(Content.position).all()
|
||||
else:
|
||||
contents = []
|
||||
else:
|
||||
# Player not in a group - show all content
|
||||
contents = Content.query.order_by(Content.position).all()
|
||||
# Get content directly assigned to this player
|
||||
contents = Content.query.filter_by(player_id=player_id)\
|
||||
.order_by(Content.position, Content.uploaded_at)\
|
||||
.all()
|
||||
|
||||
# Build playlist
|
||||
playlist = []
|
||||
@@ -366,3 +401,97 @@ def bulk_assign_group():
|
||||
db.session.rollback()
|
||||
log_action('error', f'Error bulk assigning players: {str(e)}')
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@players_bp.route('/<int:player_id>/playlist/reorder', methods=['POST'])
|
||||
@login_required
|
||||
def reorder_playlist(player_id: int):
|
||||
"""Reorder items in player's playlist."""
|
||||
try:
|
||||
data = request.get_json()
|
||||
content_id = data.get('content_id')
|
||||
direction = data.get('direction') # 'up' or 'down'
|
||||
|
||||
if not content_id or not direction:
|
||||
return jsonify({'success': False, 'message': 'Missing parameters'}), 400
|
||||
|
||||
# Get the content item
|
||||
content = Content.query.filter_by(id=content_id, player_id=player_id).first()
|
||||
if not content:
|
||||
return jsonify({'success': False, 'message': 'Content not found'}), 404
|
||||
|
||||
# Get all content for this player, ordered by position
|
||||
all_content = Content.query.filter_by(player_id=player_id)\
|
||||
.order_by(Content.position, Content.uploaded_at).all()
|
||||
|
||||
# Find current index
|
||||
current_index = None
|
||||
for idx, item in enumerate(all_content):
|
||||
if item.id == content_id:
|
||||
current_index = idx
|
||||
break
|
||||
|
||||
if current_index is None:
|
||||
return jsonify({'success': False, 'message': 'Content not in playlist'}), 404
|
||||
|
||||
# Swap positions
|
||||
if direction == 'up' and current_index > 0:
|
||||
# Swap with previous item
|
||||
all_content[current_index].position, all_content[current_index - 1].position = \
|
||||
all_content[current_index - 1].position, all_content[current_index].position
|
||||
elif direction == 'down' and current_index < len(all_content) - 1:
|
||||
# Swap with next item
|
||||
all_content[current_index].position, all_content[current_index + 1].position = \
|
||||
all_content[current_index + 1].position, all_content[current_index].position
|
||||
|
||||
db.session.commit()
|
||||
cache.delete_memoized(get_player_playlist, player_id)
|
||||
|
||||
log_action('info', f'Reordered playlist for player {player_id}')
|
||||
return jsonify({'success': True})
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
log_action('error', f'Error reordering playlist: {str(e)}')
|
||||
return jsonify({'success': False, 'message': str(e)}), 500
|
||||
|
||||
|
||||
@players_bp.route('/<int:player_id>/playlist/remove', methods=['POST'])
|
||||
@login_required
|
||||
def remove_from_playlist(player_id: int):
|
||||
"""Remove content from player's playlist."""
|
||||
try:
|
||||
data = request.get_json()
|
||||
content_id = data.get('content_id')
|
||||
|
||||
if not content_id:
|
||||
return jsonify({'success': False, 'message': 'Missing content_id'}), 400
|
||||
|
||||
# Get the content item
|
||||
content = Content.query.filter_by(id=content_id, player_id=player_id).first()
|
||||
if not content:
|
||||
return jsonify({'success': False, 'message': 'Content not found'}), 404
|
||||
|
||||
filename = content.filename
|
||||
|
||||
# Delete from database
|
||||
db.session.delete(content)
|
||||
|
||||
# Increment playlist version
|
||||
player = Player.query.get(player_id)
|
||||
if player:
|
||||
player.playlist_version += 1
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Clear cache
|
||||
cache.delete_memoized(get_player_playlist, player_id)
|
||||
|
||||
log_action('info', f'Removed "{filename}" from player {player_id} playlist (version {player.playlist_version})')
|
||||
return jsonify({'success': True, 'message': f'Removed "{filename}" from playlist'})
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
log_action('error', f'Error removing from playlist: {str(e)}')
|
||||
return jsonify({'success': False, 'message': str(e)}), 500
|
||||
|
||||
|
||||
Reference in New Issue
Block a user