Fix web player overlay template cycling during match video

- Fix overlay controller to properly handle video phase state machine
- Stop template rotation during match/result phases
- Fetch match data with fighter names from database for match phase
- Send match data to match_video template via matchUpdate message
- Handle result_data properly in result phase
- Update match_video template to process match data from web player
- Update overlay-web-adapter to emit match data directly

This fixes the issue where the web player overlay was cycling between
fixtures and match templates instead of showing the match_video template
during match video playback, and not showing results either.
parent a72b1640
...@@ -596,6 +596,18 @@ ...@@ -596,6 +596,18 @@
if (data && data.timer_update) { if (data && data.timer_update) {
console.log('DEBUG: [MATCH_VIDEO] Timer update received:', data.timer_update); console.log('DEBUG: [MATCH_VIDEO] Timer update received:', data.timer_update);
} }
// Handle match data from web player
if (data && data.match) {
console.log('DEBUG: [MATCH_VIDEO] Match data received:', data.match);
const hadMatchData = !!matchData;
matchData = data.match;
updateMatchInfo();
// Show entrance animation only on first match data
if (!hadMatchData && !entranceAnimationShown) {
console.log('DEBUG: [MATCH_VIDEO] Showing entrance animation from dataUpdated');
showEntranceAnimation();
}
}
}); });
} }
return; return;
...@@ -619,6 +631,18 @@ ...@@ -619,6 +631,18 @@
webServerUrlReceived = true; webServerUrlReceived = true;
console.log('DEBUG: [MATCH_VIDEO] Web server base URL updated to:', webServerBaseUrl); console.log('DEBUG: [MATCH_VIDEO] Web server base URL updated to:', webServerBaseUrl);
} }
// Handle match data from web player
if (data && data.match) {
console.log('DEBUG: [MATCH_VIDEO] Match data received (fallback):', data.match);
const hadMatchData = !!matchData;
matchData = data.match;
updateMatchInfo();
// Show entrance animation only on first match data
if (!hadMatchData && !entranceAnimationShown) {
console.log('DEBUG: [MATCH_VIDEO] Showing entrance animation from dataUpdated (fallback)');
showEntranceAnimation();
}
}
}); });
} else { } else {
console.log('DEBUG: [MATCH_VIDEO] Overlay object not available in WebChannel'); console.log('DEBUG: [MATCH_VIDEO] Overlay object not available in WebChannel');
......
...@@ -10739,6 +10739,61 @@ def get_overlay_data(): ...@@ -10739,6 +10739,61 @@ def get_overlay_data():
if hasattr(headless_player, 'result_data'): if hasattr(headless_player, 'result_data'):
overlay_data['result_data'] = headless_player.result_data overlay_data['result_data'] = headless_player.result_data
# If we have result_data, fetch match details from database for fighter names
if headless_player.result_data and headless_player.result_data.get('match_id'):
try:
from ..database.models import MatchModel
session = api_bp.db_manager.get_session()
try:
match = session.query(MatchModel).filter_by(id=headless_player.result_data['match_id']).first()
if match:
# Add match data with fighter names to result_data
overlay_data['result_data']['match'] = {
'id': match.id,
'match_number': match.match_number,
'fighter1_township': match.fighter1_township,
'fighter2_township': match.fighter2_township,
'venue_kampala_township': match.venue_kampala_township
}
# Also add under_over_result if available
if match.under_over_result:
overlay_data['result_data']['under_over_result'] = match.under_over_result
# Add winning outcomes if available
if match.winning_outcomes:
import json
try:
winning_outcomes = json.loads(match.winning_outcomes) if isinstance(match.winning_outcomes, str) else match.winning_outcomes
overlay_data['result_data']['winningOutcomes'] = [{'outcome': o} for o in winning_outcomes]
except (json.JSONDecodeError, TypeError):
pass
finally:
session.close()
except Exception as match_error:
logger.error(f"Failed to fetch match data for result: {match_error}")
# For match phase, fetch match details from database for fighter names
current_phase = overlay_data.get('video_phase', 'idle')
current_match_id = overlay_data.get('current_match_id')
if current_phase == 'match' and current_match_id:
try:
from ..database.models import MatchModel
session = api_bp.db_manager.get_session()
try:
match = session.query(MatchModel).filter_by(id=current_match_id).first()
if match:
# Add match data with fighter names for match_video template
overlay_data['match'] = {
'id': match.id,
'match_number': match.match_number,
'fighter1_township': match.fighter1_township,
'fighter2_township': match.fighter2_township,
'venue_kampala_township': match.venue_kampala_township
}
finally:
session.close()
except Exception as match_error:
logger.error(f"Failed to fetch match data for match phase: {match_error}")
# Add some default data if not available # Add some default data if not available
if not overlay_data: if not overlay_data:
overlay_data = { overlay_data = {
......
...@@ -475,8 +475,11 @@ ...@@ -475,8 +475,11 @@
break; break;
case 'matchUpdate': case 'matchUpdate':
debugLog('Match update received'); debugLog('Match update received:', message);
window.overlay.dataUpdated.emit({ match_update: message }); // Update overlay data with match info
overlayData = { ...overlayData, ...message };
// Emit the match data directly so templates can access data.match
window.overlay.dataUpdated.emit(message);
break; break;
case 'resultUpdate': case 'resultUpdate':
......
...@@ -408,6 +408,26 @@ class WebOverlayController { ...@@ -408,6 +408,26 @@ class WebOverlayController {
licenseText: this.licenseText licenseText: this.licenseText
}); });
} }
// CRITICAL: For results template, send the stored result data
// This ensures the template gets the data AFTER it loads
if (normalizedName === 'results' && this.resultData) {
this.log('Sending stored result data to results template:', this.resultData);
// Send as resultUpdate first (primary method)
this.sendMessageToOverlay('resultUpdate', this.resultData);
// Also send as dataUpdated for compatibility
this.sendMessageToOverlay('dataUpdated', this.resultData);
}
// CRITICAL: For match_video template, send the stored match data
// This ensures the template gets the data AFTER it loads
if (normalizedName === 'match_video' && this.matchData) {
this.log('Sending stored match data to match_video template:', this.matchData);
// Send as matchUpdate first (primary method)
this.sendMessageToOverlay('matchUpdate', this.matchData);
// Also send as dataUpdated for compatibility
this.sendMessageToOverlay('dataUpdated', this.matchData);
}
}, 100); // Small delay to ensure iframe is ready }, 100); // Small delay to ensure iframe is ready
}; };
...@@ -639,11 +659,26 @@ class WebOverlayController { ...@@ -639,11 +659,26 @@ class WebOverlayController {
this.phaseStartTime = newPhaseStartTime; this.phaseStartTime = newPhaseStartTime;
this.phaseVideoDuration = newPhaseVideoDuration; this.phaseVideoDuration = newPhaseVideoDuration;
// For result phase, extract result_data and merge with response
// The API returns result_data with match info when in result phase
if (newPhase === 'result' && response.result_data) {
this.log('Found result_data in API response:', response.result_data);
// Merge result_data into the response for handlePhaseTransition
Object.assign(response, response.result_data);
// Store for later use when template loads
this.resultData = response.result_data;
}
// Handle phase transitions with delay compensation // Handle phase transitions with delay compensation
// Only trigger transition if phase actually changed // Only trigger transition if phase actually changed
if (newPhase !== previousPhase) { if (newPhase !== previousPhase) {
this.log(`Video phase changed: ${previousPhase} -> ${newPhase}`); this.log(`Video phase changed: ${previousPhase} -> ${newPhase}`);
this.handlePhaseTransition(previousPhase, newPhase, response); this.handlePhaseTransition(previousPhase, newPhase, response);
} else if (newPhase === 'result' && this.resultData && this.currentTemplate === 'results') {
// Even if phase hasn't changed, send updated result data to template
this.log('Sending updated result data to results template');
this.sendMessageToOverlay('resultUpdate', this.resultData);
this.sendMessageToOverlay('dataUpdated', this.resultData);
} }
// Legacy support: Also check is_playing_match_video flag // Legacy support: Also check is_playing_match_video flag
...@@ -727,9 +762,30 @@ class WebOverlayController { ...@@ -727,9 +762,30 @@ class WebOverlayController {
// Match phase - stop rotation and show match template // Match phase - stop rotation and show match template
this.log('Match phase - stopping rotation, loading match_video template'); this.log('Match phase - stopping rotation, loading match_video template');
this.stopTemplateRotation(); this.stopTemplateRotation();
// Build match data with fighter names
const matchData = {
match_id: data.match_id,
fixture_id: data.fixture_id,
result: data.result,
// Ensure match data is properly included with fighter names
match: data.match || (this.overlayData ? this.overlayData.match : null) || null
};
// Log the match data for debugging
this.log('Match data for match_video template:', matchData.match);
// Store match data for the template to fetch after loading
this.matchData = matchData;
// Only load if not already showing match_video // Only load if not already showing match_video
if (this.currentTemplate !== 'match_video') { if (this.currentTemplate !== 'match_video') {
this.loadTemplate('match_video'); this.loadTemplate('match_video');
} else {
// Template already loaded, just send the data update
this.log('Match_video template already loaded, sending data update');
this.sendMessageToOverlay('matchUpdate', matchData);
this.sendMessageToOverlay('dataUpdated', matchData);
} }
break; break;
...@@ -739,6 +795,7 @@ class WebOverlayController { ...@@ -739,6 +795,7 @@ class WebOverlayController {
this.stopTemplateRotation(); this.stopTemplateRotation();
// Build result data with proper match information // Build result data with proper match information
// The match data should have fighter1_township and fighter2_township fields
const resultData = { const resultData = {
outcome: data.outcome || data.result, outcome: data.outcome || data.result,
result: data.result, result: data.result,
...@@ -752,22 +809,29 @@ class WebOverlayController { ...@@ -752,22 +809,29 @@ class WebOverlayController {
// Log the match data for debugging // Log the match data for debugging
this.log('Result data match info:', resultData.match); this.log('Result data match info:', resultData.match);
this.log('Sending resultUpdate to overlay:', resultData); this.log('Full result data:', JSON.stringify(resultData, null, 2));
// Send result data to template before loading // Store result data for the template to fetch after loading
this.sendMessageToOverlay('resultUpdate', resultData); this.resultData = resultData;
// Also send as dataUpdated for compatibility
this.sendMessageToOverlay('dataUpdated', resultData);
// Only load if not already showing results // Only load if not already showing results for this match
if (this.currentTemplate !== 'results') { if (this.currentTemplate !== 'results' || this.lastResultMatchId !== data.match_id) {
this.lastResultMatchId = data.match_id;
this.loadTemplate('results'); this.loadTemplate('results');
} else {
// Template already loaded, just send the data update
this.log('Results template already loaded, sending data update');
this.sendMessageToOverlay('resultUpdate', resultData);
this.sendMessageToOverlay('dataUpdated', resultData);
} }
break; break;
case 'idle': case 'idle':
// Idle phase - return to intro rotation after result completes // Idle phase - return to intro rotation after result completes
this.log('Idle phase - will restart template rotation after delay'); this.log('Idle phase - will restart template rotation after delay');
// Clear result data when returning to idle
this.resultData = null;
this.lastResultMatchId = null;
// Don't immediately start rotation - wait for next intro phase // Don't immediately start rotation - wait for next intro phase
break; break;
...@@ -976,6 +1040,16 @@ class WebOverlayController { ...@@ -976,6 +1040,16 @@ class WebOverlayController {
timerState: this.timerState, timerState: this.timerState,
licenseText: this.licenseText licenseText: this.licenseText
}); });
// Also send result data if this is the results template
if (this.resultData) {
this.log('Sending result data with initialData:', this.resultData);
this.sendMessageToOverlay('resultUpdate', this.resultData);
}
// Also send match data if this is the match_video template
if (this.matchData) {
this.log('Sending match data with initialData:', this.matchData);
this.sendMessageToOverlay('matchUpdate', this.matchData);
}
break; break;
case 'requestFixtureData': case 'requestFixtureData':
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment