Add audio parameter to player playlist and auto-mark deployment status
- api.py: include 'audio' (on/off) field in playlist JSON served to players, derived from the muted toggle in the manage playlist page - players.py: include 'audio' field in legacy player playlist builder - api.py: auto-flip deployment_status pending/deploying -> deployed when a player sends feedback (player is confirmed running) - players_list.html: poll deployment status for pending/failed badges so the players list live-updates once a player starts sending feedback - .dockerignore: exclude runtime data/ volume dir from build context
This commit is contained in:
@@ -44,6 +44,9 @@ flask_session/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# Runtime data volumes (mounted at runtime, NOT part of the image)
|
||||||
|
data/
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
BLUEPRINT_GUIDE.md
|
BLUEPRINT_GUIDE.md
|
||||||
ICON_INTEGRATION.md
|
ICON_INTEGRATION.md
|
||||||
|
|||||||
+18
-1
@@ -413,7 +413,12 @@ def get_cached_playlist(player_id: int) -> List[Dict]:
|
|||||||
'position': content._playlist_position or idx,
|
'position': content._playlist_position or idx,
|
||||||
'url': item_url, # Web page URL for weblinks, file download URL otherwise
|
'url': item_url, # Web page URL for weblinks, file download URL otherwise
|
||||||
'description': content.description,
|
'description': content.description,
|
||||||
'edit_on_player': getattr(content, '_playlist_edit_on_player_enabled', False)
|
'edit_on_player': getattr(content, '_playlist_edit_on_player_enabled', False),
|
||||||
|
'muted': getattr(content, '_playlist_muted', True),
|
||||||
|
# Player expects 'audio' = "on"/"off" to set playback sound.
|
||||||
|
# The playlist_content.muted column stores True when audio is muted,
|
||||||
|
# so audio is "on" exactly when muted is False.
|
||||||
|
'audio': 'off' if getattr(content, '_playlist_muted', True) else 'on'
|
||||||
})
|
})
|
||||||
|
|
||||||
return playlist_data
|
return playlist_data
|
||||||
@@ -502,6 +507,18 @@ def receive_player_feedback():
|
|||||||
player.last_seen = datetime.utcnow()
|
player.last_seen = datetime.utcnow()
|
||||||
player.status = status
|
player.status = status
|
||||||
|
|
||||||
|
# A player that is sending feedback is running, so its deployment
|
||||||
|
# finished successfully. Auto-mark it as deployed so the players list
|
||||||
|
# doesn't stay stuck on "pending"/"deploying" after deployment is done.
|
||||||
|
if player.deployment_status in ('pending', 'deploying'):
|
||||||
|
player.deployment_status = 'deployed'
|
||||||
|
player.last_deployment_status = 'success'
|
||||||
|
player.last_deployment_message = (
|
||||||
|
'Player is running and sending feedback'
|
||||||
|
)
|
||||||
|
if not player.last_deployment_at:
|
||||||
|
player.last_deployment_at = datetime.utcnow()
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
log_action('info', f'Feedback received from {player.name} ({player.hostname}): {status} - {message}')
|
log_action('info', f'Feedback received from {player.name} ({player.hostname}): {status} - {message}')
|
||||||
|
|||||||
@@ -578,6 +578,7 @@ def get_player_playlist(player_id: int) -> List[dict]:
|
|||||||
'duration': getattr(content, '_playlist_duration', content.duration or 10),
|
'duration': getattr(content, '_playlist_duration', content.duration or 10),
|
||||||
'position': getattr(content, '_playlist_position', 0),
|
'position': getattr(content, '_playlist_position', 0),
|
||||||
'muted': getattr(content, '_playlist_muted', True),
|
'muted': getattr(content, '_playlist_muted', True),
|
||||||
|
'audio': 'off' if getattr(content, '_playlist_muted', True) else 'on',
|
||||||
'filename': content.filename
|
'filename': content.filename
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -279,9 +279,13 @@
|
|||||||
var POLL_INTERVAL = 5000; // 5 seconds
|
var POLL_INTERVAL = 5000; // 5 seconds
|
||||||
var polling = false;
|
var polling = false;
|
||||||
|
|
||||||
// Initial check: are there any "Deploying..." badges?
|
// Initial check: any badge that isn't yet "Deployed" yet (deploying,
|
||||||
var deployingBadges = document.querySelectorAll('.deploy-badge.deploying');
|
// pending, or failed). We keep polling so that players which send
|
||||||
if (deployingBadges.length > 0) {
|
// feedback auto-transition from "pending" to "deployed".
|
||||||
|
var activeBadges = document.querySelectorAll(
|
||||||
|
'.deploy-badge.deploying, .deploy-badge.pending, .deploy-badge.failed'
|
||||||
|
);
|
||||||
|
if (activeBadges.length > 0) {
|
||||||
polling = true;
|
polling = true;
|
||||||
schedulePoll();
|
schedulePoll();
|
||||||
}
|
}
|
||||||
@@ -296,7 +300,7 @@
|
|||||||
fetch('{{ url_for("players.deployment_status") }}')
|
fetch('{{ url_for("players.deployment_status") }}')
|
||||||
.then(function(r) { return r.json(); })
|
.then(function(r) { return r.json(); })
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
var anyDeploying = false;
|
var anyActive = false;
|
||||||
|
|
||||||
for (var playerId in data) {
|
for (var playerId in data) {
|
||||||
if (!data.hasOwnProperty(playerId)) continue;
|
if (!data.hasOwnProperty(playerId)) continue;
|
||||||
@@ -314,19 +318,25 @@
|
|||||||
if (ds === 'deployed') {
|
if (ds === 'deployed') {
|
||||||
cell.innerHTML = '<span class="deploy-badge deployed" title="' + escapeHtml(msg) + '">\u2705 Deployed<span class="deploy-timestamp">' + ts + '</span></span>';
|
cell.innerHTML = '<span class="deploy-badge deployed" title="' + escapeHtml(msg) + '">\u2705 Deployed<span class="deploy-timestamp">' + ts + '</span></span>';
|
||||||
} else if (ds === 'failed') {
|
} else if (ds === 'failed') {
|
||||||
|
// Terminal state - no need to keep polling for this one
|
||||||
cell.innerHTML = '<span class="deploy-badge failed deploy-tooltip" title="' + escapeHtml(msg) + '">\u274c Failed<span class="deploy-timestamp">' + ts + '</span></span>';
|
cell.innerHTML = '<span class="deploy-badge failed deploy-tooltip" title="' + escapeHtml(msg) + '">\u274c Failed<span class="deploy-timestamp">' + ts + '</span></span>';
|
||||||
} else if (ds === 'deploying') {
|
} else if (ds === 'deploying') {
|
||||||
anyDeploying = true;
|
anyActive = true;
|
||||||
if (!cell.querySelector('.deploying')) {
|
if (!cell.querySelector('.deploying')) {
|
||||||
cell.innerHTML = '<span class="deploy-badge deploying"><span class="spinner"></span>Deploying...</span>';
|
cell.innerHTML = '<span class="deploy-badge deploying"><span class="spinner"></span>Deploying...</span>';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No deployment or not started (pending, null, etc.)
|
// pending / null - deployment not confirmed yet.
|
||||||
// Don't set anyDeploying = false here — we only care about active deployments
|
// Keep polling: the player may start sending feedback,
|
||||||
|
// which flips the status to "deployed" on the server.
|
||||||
|
anyActive = true;
|
||||||
|
if (!cell.querySelector('.pending')) {
|
||||||
|
cell.innerHTML = '<span class="deploy-badge pending" title="Awaiting deployment">\u23f3 Pending</span>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anyDeploying) {
|
if (anyActive) {
|
||||||
schedulePoll();
|
schedulePoll();
|
||||||
} else {
|
} else {
|
||||||
polling = false;
|
polling = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user