Add edited media report page and fix image versioning

- New route GET /players/<id>/edited-media-report with tabular report
- New template edited_media_report.html with User/Filename/Date/Link columns
- Launch Report button (green) next to View All Edited Media button
- Fix: on first edit, move original file to versionized folder as original_<name>
- Fix: content lookup fallback via PlayerEdit records and path regex
- Keep Content.filename pointing to latest edit for player playlist
This commit is contained in:
2026-07-21 09:56:14 +03:00
parent 2af04e1db3
commit 05194a19b6
7 changed files with 718 additions and 54 deletions
+102 -13
View File
@@ -28,10 +28,23 @@ def list():
status_info = get_player_status_info(player.id)
player_statuses[player.id] = status_info
# Build a JSON-safe dict of deployment statuses for the polling JS
import json
from datetime import datetime as dt
player_statuses_json = {}
for player in players:
player_statuses_json[str(player.id)] = {
'deployment_status': player.deployment_status,
'last_deployment_status': player.last_deployment_status,
'last_deployment_at': player.last_deployment_at.isoformat() if player.last_deployment_at else None,
'last_deployment_message': player.last_deployment_message,
}
return render_template('players/players_list.html',
players=players,
playlists=playlists,
player_statuses=player_statuses)
player_statuses=player_statuses,
player_statuses_json=json.dumps(player_statuses_json))
except Exception as e:
log_action('error', f'Error loading players list: {str(e)}')
flash('Error loading players list.', 'danger')
@@ -148,6 +161,13 @@ def add_player():
host = f"{detected_ip}:{port_part}" if port_part else detected_ip
server_url = f"{flask_request.scheme}://{host}"
# Mark deployment as "in progress" immediately so the UI polling picks it up
from datetime import datetime
new_player.deployment_status = 'deploying'
new_player.last_deployment_at = datetime.utcnow()
new_player.last_deployment_message = 'Deployment in progress...'
db.session.commit()
# Generate API key for player authentication
import hashlib
api_key = hashlib.sha256(f'{name}:{hostname}'.encode()).hexdigest()[:32]
@@ -173,19 +193,30 @@ def add_player():
log_action('error', f'Failed to initiate background deployment for player "{name}": {str(deploy_err)}')
# Flash detailed success message
success_msg = f'''
Player "{name}" created successfully!<br>
<strong>Auth Code:</strong> <code style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px;">{auth_code}</code><br>
<strong>Hostname:</strong> {hostname}<br>
<strong>Quick Connect:</strong> {quickconnect_code}<br>
'''
if deployment_initiated:
success_msg += f'<strong style="color: #0275d8;">&#8987; Deployment in Progress</strong> Deploying to {ssh_hostname} in background...<br>'
success_msg += '<small>Check player status to see deployment completion</small><br>'
success_msg += '<small>Configure the player with these credentials in app_config.json</small>'
flash(success_msg, 'success')
success_msg = f'''
Player "{name}" created successfully!<br>
<strong>Auth Code:</strong> <code style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px;">{auth_code}</code><br>
<strong>Hostname:</strong> {hostname}<br>
<strong>Quick Connect:</strong> {quickconnect_code}<br>
<br>
<strong style="color: #0275d8;">&#8987; Deployment in Progress</strong><br>
Deploying to <strong>{ssh_hostname}</strong> in background...<br>
<small>The deployment status will update automatically on the players list.</small>
'''
flash(success_msg, 'success')
else:
success_msg = f'''
Player "{name}" created successfully!<br>
<strong>Auth Code:</strong> <code style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px;">{auth_code}</code><br>
<strong>Hostname:</strong> {hostname}<br>
<strong>Quick Connect:</strong> {quickconnect_code}<br>
<br>
<small>Configure the player with these credentials in app_config.json</small>
'''
flash(success_msg, 'success')
if deploy_player:
flash('Player was created but deployment could not be started. You can deploy manually from the Manage page.', 'warning')
return redirect(url_for('players.list'))
@@ -453,6 +484,40 @@ def edited_media(player_id: int):
return redirect(url_for('players.manage_player', player_id=player_id))
@players_bp.route('/<int:player_id>/edited-media-report')
@login_required
def edited_media_report(player_id: int):
"""Display a tabular report of all edited media from this player."""
try:
player = Player.query.get_or_404(player_id)
from app.models.player_edit import PlayerEdit
from app.models.player_user import PlayerUser
edited_media = PlayerEdit.query.filter_by(player_id=player_id)\
.order_by(PlayerEdit.created_at.desc())\
.all()
# Build user display name mapping
user_mappings = {}
for edit in edited_media:
if edit.user and edit.user not in user_mappings:
player_user = PlayerUser.query.filter_by(user_code=edit.user).first()
if player_user and player_user.user_name:
user_mappings[edit.user] = player_user.user_name
else:
user_mappings[edit.user] = edit.user
return render_template('players/edited_media_report.html',
player=player,
edited_media=edited_media,
user_mappings=user_mappings)
except Exception as e:
log_action('error', f'Error loading edited media report for player {player_id}: {str(e)}')
flash('Error loading edited media report.', 'danger')
return redirect(url_for('players.manage_player', player_id=player_id))
@players_bp.route('/<int:player_id>/fullscreen')
def player_fullscreen(player_id: int):
"""Display player fullscreen view (no authentication required for players)."""
@@ -598,6 +663,30 @@ def bulk_assign_playlist():
return jsonify({'success': False, 'error': str(e)}), 500
@players_bp.route('/deployment-status')
@login_required
def deployment_status():
"""Return deployment status for all players (used by polling JS)."""
try:
players = Player.query.with_entities(
Player.id, Player.deployment_status,
Player.last_deployment_status, Player.last_deployment_at,
Player.last_deployment_message
).all()
data = {}
for p in players:
data[p.id] = {
'deployment_status': p.deployment_status,
'last_deployment_status': p.last_deployment_status,
'last_deployment_at': p.last_deployment_at.isoformat() if p.last_deployment_at else None,
'last_deployment_message': p.last_deployment_message,
}
return jsonify(data)
except Exception as e:
log_action('error', f'Error fetching deployment status: {str(e)}')
return jsonify({'error': str(e)}), 500
@players_bp.route('/<int:player_id>/playlist/reorder', methods=['POST'])
@login_required
def reorder_playlist(player_id: int):