Fixture template works now

parent 0632ac59
...@@ -595,6 +595,8 @@ class MbetterClientApplication: ...@@ -595,6 +595,8 @@ class MbetterClientApplication:
def _process_core_message(self, message: Message): def _process_core_message(self, message: Message):
"""Process messages received by the core component""" """Process messages received by the core component"""
try: try:
# Suppress debug logging for VIDEO_PROGRESS messages to reduce log noise
if message.type != MessageType.VIDEO_PROGRESS:
logger.debug(f"Core processing message: {message}") logger.debug(f"Core processing message: {message}")
if message.type == MessageType.SYSTEM_STATUS: if message.type == MessageType.SYSTEM_STATUS:
...@@ -608,6 +610,8 @@ class MbetterClientApplication: ...@@ -608,6 +610,8 @@ class MbetterClientApplication:
elif message.type == MessageType.SYSTEM_SHUTDOWN: elif message.type == MessageType.SYSTEM_SHUTDOWN:
self._handle_shutdown_message(message) self._handle_shutdown_message(message)
else: else:
# Suppress debug logging for VIDEO_PROGRESS messages to reduce log noise
if message.type != MessageType.VIDEO_PROGRESS:
logger.debug(f"Unhandled message type in core: {message.type}") logger.debug(f"Unhandled message type in core: {message.type}")
except Exception as e: except Exception as e:
......
...@@ -52,6 +52,7 @@ class OverlayWebChannel(QObject): ...@@ -52,6 +52,7 @@ class OverlayWebChannel(QObject):
dataUpdated = pyqtSignal(dict) dataUpdated = pyqtSignal(dict)
positionChanged = pyqtSignal(float, float) # position, duration in seconds positionChanged = pyqtSignal(float, float) # position, duration in seconds
videoInfoChanged = pyqtSignal(dict) videoInfoChanged = pyqtSignal(dict)
fixtureDataUpdated = pyqtSignal(dict) # Signal for fixture data updates
def __init__(self): def __init__(self):
super().__init__() super().__init__()
...@@ -178,6 +179,43 @@ class OverlayWebChannel(QObject): ...@@ -178,6 +179,43 @@ class OverlayWebChannel(QObject):
else: else:
logger.debug("Video info contained only null/undefined values, skipping") logger.debug("Video info contained only null/undefined values, skipping")
def send_fixture_data_update(self, data: Dict[str, Any]):
"""Send fixture data update to JavaScript (thread-safe)"""
# Validate data before emitting
if not data:
logger.warning("send_fixture_data_update called with null/empty data, skipping")
return
logger.debug(f"OverlayWebChannel sending fixture data: {data}")
self.fixtureDataUpdated.emit(data)
@pyqtSlot()
def trigger_fixtures_data_load(self):
"""Slot to manually trigger fixtures data loading from JavaScript"""
try:
logger.info("Manual trigger of fixtures data load from JavaScript")
# Find the PlayerWindow and trigger fixtures data load
# This method is called from JavaScript, so we need to find the PlayerWindow
overlay_view = None
# Try to find the overlay view that contains this channel
# We need to traverse up to find the PlayerWindow
current_obj = self.parent()
while current_obj:
if hasattr(current_obj, '_find_player_window'):
player_window = current_obj._find_player_window()
if player_window and hasattr(player_window, '_send_fixtures_to_overlay'):
logger.debug("Found PlayerWindow, triggering fixtures data load")
player_window._send_fixtures_to_overlay()
return
current_obj = current_obj.parent()
logger.warning("Could not find PlayerWindow to trigger fixtures data load")
except Exception as e:
logger.error(f"Failed to trigger fixtures data load from JavaScript: {e}")
class VideoProcessingWorker(QRunnable): class VideoProcessingWorker(QRunnable):
"""Background worker for video processing tasks""" """Background worker for video processing tasks"""
...@@ -419,6 +457,11 @@ class OverlayWebView(QWebEngineView): ...@@ -419,6 +457,11 @@ class OverlayWebView(QWebEngineView):
from PyQt6.QtCore import QTimer from PyQt6.QtCore import QTimer
QTimer.singleShot(100, lambda: self._ensure_overlay_visibility_post_load(was_visible)) QTimer.singleShot(100, lambda: self._ensure_overlay_visibility_post_load(was_visible))
# If fixtures template was loaded, trigger fixtures data loading
if template_name == "fixtures.html" or template_name == "fixtures":
logger.info("Fixtures template loaded, scheduling fixtures data load")
QTimer.singleShot(500, lambda: self._trigger_fixtures_data_load())
if self.debug_overlay: if self.debug_overlay:
logger.debug(f"GREEN SCREEN DEBUG: Template load initiated - {template_path}") logger.debug(f"GREEN SCREEN DEBUG: Template load initiated - {template_path}")
logger.info(f"Loaded template: {template_path} (source: {template_source})") logger.info(f"Loaded template: {template_path} (source: {template_source})")
...@@ -619,6 +662,51 @@ class OverlayWebView(QWebEngineView): ...@@ -619,6 +662,51 @@ class OverlayWebView(QWebEngineView):
if self.overlay_channel: if self.overlay_channel:
self.overlay_channel.send_video_info(info) self.overlay_channel.send_video_info(info)
def _trigger_fixtures_data_load(self):
"""Trigger fixtures data loading when fixtures template is loaded"""
try:
logger.info("Triggering fixtures data load for fixtures template")
# Find the PlayerWindow by traversing up the parent hierarchy
player_window = self._find_player_window()
if player_window and hasattr(player_window, '_send_fixtures_to_overlay'):
logger.debug("Calling PlayerWindow's _send_fixtures_to_overlay method")
player_window._send_fixtures_to_overlay()
else:
logger.warning("Could not find PlayerWindow with _send_fixtures_to_overlay method")
except Exception as e:
logger.error(f"Failed to trigger fixtures data load: {e}")
def _find_player_window(self):
"""Find the PlayerWindow by traversing up the parent hierarchy"""
try:
current = self.parent()
while current:
if hasattr(current, '_send_fixtures_to_overlay'):
logger.debug("Found PlayerWindow in parent hierarchy")
return current
current = current.parent()
logger.debug("PlayerWindow not found in parent hierarchy")
return None
except Exception as e:
logger.error(f"Error finding PlayerWindow: {e}")
return None
def update_fixture_data(self, fixture_data: Dict[str, Any]):
"""Update fixture data"""
if self.overlay_channel:
self.overlay_channel.send_fixture_data(fixture_data)
def _on_fixture_data_updated(self, fixture_data: Dict[str, Any]):
"""Handle fixture data update signal and call JavaScript function"""
try:
# Call the JavaScript updateFixtureData function
js_code = f"updateFixtureData({json.dumps(fixture_data)});"
self.page().runJavaScript(js_code)
logger.debug("Called JavaScript updateFixtureData function")
except Exception as e:
logger.error(f"Failed to call JavaScript updateFixtureData: {e}")
class NativeOverlayWidget(QWidget): class NativeOverlayWidget(QWidget):
"""Native Qt overlay widget - no WebEngine to prevent freezing""" """Native Qt overlay widget - no WebEngine to prevent freezing"""
...@@ -1271,6 +1359,11 @@ class PlayerWindow(QMainWindow): ...@@ -1271,6 +1359,11 @@ class PlayerWindow(QMainWindow):
self.overlay_timer.timeout.connect(self.update_overlay_periodically) self.overlay_timer.timeout.connect(self.update_overlay_periodically)
self.overlay_timer.start(1000) # Update every second self.overlay_timer.start(1000) # Update every second
# Fixtures data refresh timer
self.fixtures_timer = QTimer()
self.fixtures_timer.timeout.connect(self._refresh_fixtures_data)
self.fixtures_timer.start(30000) # Refresh fixtures every 30 seconds
# Template rotation timer (initially disabled) # Template rotation timer (initially disabled)
self.template_rotation_timer = QTimer() self.template_rotation_timer = QTimer()
self.template_rotation_timer.timeout.connect(self._rotate_template) self.template_rotation_timer.timeout.connect(self._rotate_template)
...@@ -1748,6 +1841,86 @@ class PlayerWindow(QMainWindow): ...@@ -1748,6 +1841,86 @@ class PlayerWindow(QMainWindow):
except Exception as e: except Exception as e:
logger.error(f"Periodic overlay update failed: {e}") logger.error(f"Periodic overlay update failed: {e}")
def _refresh_fixtures_data(self):
"""Refresh fixtures data and send to overlay (called by timer)"""
try:
logger.debug("Refreshing fixtures data...")
self._send_fixtures_to_overlay()
except Exception as e:
logger.error(f"Failed to refresh fixtures data: {e}")
def _send_fixtures_to_overlay(self):
"""Send fixtures data to the overlay via WebChannel"""
try:
# Fetch fixtures data
fixtures_data = self._fetch_fixtures_data()
# Send data to overlay
if hasattr(self, 'window_overlay') and self.window_overlay:
if fixtures_data:
logger.info(f"Sending fixtures data to overlay: {len(fixtures_data.get('matches', []))} matches")
# Send via WebChannel signal - primary method
if hasattr(self.window_overlay, 'overlay_channel') and self.window_overlay.overlay_channel:
self.window_overlay.overlay_channel.send_fixture_data_update(fixtures_data)
logger.debug("Sent fixtures data via WebChannel signal")
# Also try calling JavaScript functions directly as fallback
try:
if hasattr(self.window_overlay, 'page'):
page = self.window_overlay.page()
if page:
# Try updateFixtureData function
page.runJavaScript(f"updateFixtureData({json.dumps(fixtures_data)});")
# Also try updateFixtures function for compatibility
page.runJavaScript(f"updateFixtures({json.dumps(fixtures_data)});")
logger.debug("Sent fixtures data via JavaScript function calls")
except Exception as js_error:
logger.debug(f"JavaScript call failed: {js_error}")
else:
logger.warning("No fixtures data available to send to overlay")
# Send empty data to trigger fallback
empty_data = {'matches': []}
if hasattr(self.window_overlay, 'overlay_channel') and self.window_overlay.overlay_channel:
self.window_overlay.overlay_channel.send_fixture_data_update(empty_data)
else:
logger.warning("No overlay available to send fixtures data")
except Exception as e:
logger.error(f"Failed to send fixtures data to overlay: {e}")
def _fetch_fixtures_data(self):
"""Fetch fixtures data from the web API"""
try:
import requests
from datetime import datetime, date
base_url = self._get_web_server_base_url()
api_url = f"{base_url}/api/cashier/pending-matches"
logger.info(f"Fetching fixtures data from: {api_url}")
# Make API request
response = requests.get(api_url, timeout=10)
if response.status_code == 200:
data = response.json()
if data.get('success') and data.get('matches'):
matches = data['matches']
logger.info(f"Successfully fetched {len(matches)} matches from API")
return {'matches': matches}
else:
logger.warning(f"API returned success=false or no matches: {data}")
return None
else:
logger.error(f"API request failed with status {response.status_code}: {response.text}")
return None
except Exception as e:
logger.error(f"Failed to fetch fixtures data: {e}")
return None
def mouseMoveEvent(self, event): def mouseMoveEvent(self, event):
"""Show controls on mouse movement""" """Show controls on mouse movement"""
super().mouseMoveEvent(event) super().mouseMoveEvent(event)
...@@ -2019,6 +2192,10 @@ class QtVideoPlayer(QObject): ...@@ -2019,6 +2192,10 @@ class QtVideoPlayer(QObject):
self.window: Optional[PlayerWindow] = None self.window: Optional[PlayerWindow] = None
self.mutex = QMutex() self.mutex = QMutex()
# Progress update throttling (2 per second = 500ms minimum interval)
self.last_progress_update_time = 0.0
self.progress_update_interval = 0.5 # 500ms
# Set web dashboard URL for API calls # Set web dashboard URL for API calls
self.web_dashboard_url = "http://localhost:5000" # Default web dashboard URL self.web_dashboard_url = "http://localhost:5000" # Default web dashboard URL
...@@ -2055,6 +2232,87 @@ class QtVideoPlayer(QObject): ...@@ -2055,6 +2232,87 @@ class QtVideoPlayer(QObject):
# Return default fallback # Return default fallback
return "http://127.0.0.1:5001" return "http://127.0.0.1:5001"
def _fetch_fixtures_data(self) -> Optional[Dict[str, Any]]:
"""Fetch fixtures data from the web API"""
try:
import requests
from datetime import datetime, date
base_url = self._get_web_server_base_url()
api_url = f"{base_url}/api/cashier/pending-matches"
logger.info(f"Fetching fixtures data from: {api_url}")
# Make API request
response = requests.get(api_url, timeout=10)
if response.status_code == 200:
data = response.json()
if data.get('success') and data.get('matches'):
matches = data['matches']
logger.info(f"Successfully fetched {len(matches)} matches from API")
return {'matches': matches}
else:
logger.warning(f"API returned success=false or no matches: {data}")
return None
else:
logger.error(f"API request failed with status {response.status_code}: {response.text}")
return None
except Exception as e:
logger.error(f"Failed to fetch fixtures data: {e}")
return None
def _refresh_fixtures_data(self):
"""Refresh fixtures data and send to overlay (called by timer)"""
try:
logger.debug("Refreshing fixtures data...")
self._send_fixtures_to_overlay()
except Exception as e:
logger.error(f"Failed to refresh fixtures data: {e}")
def _send_fixtures_to_overlay(self, fixtures_data: Optional[Dict[str, Any]] = None):
"""Send fixtures data to the overlay via WebChannel"""
try:
# If no data provided, fetch it
if fixtures_data is None:
fixtures_data = self._fetch_fixtures_data()
# Send data to overlay
if hasattr(self, 'window_overlay') and self.window_overlay:
if fixtures_data:
logger.info(f"Sending fixtures data to overlay: {len(fixtures_data.get('matches', []))} matches")
# Send via WebChannel signal - primary method
if hasattr(self.window_overlay, 'overlay_channel') and self.window_overlay.overlay_channel:
self.window_overlay.overlay_channel.send_fixture_data_update(fixtures_data)
logger.debug("Sent fixtures data via WebChannel signal")
# Also try calling JavaScript functions directly as fallback
try:
if hasattr(self.window_overlay, 'page'):
page = self.window_overlay.page()
if page:
# Try updateFixtureData function
page.runJavaScript(f"updateFixtureData({json.dumps(fixtures_data)});")
# Also try updateFixtures function for compatibility
page.runJavaScript(f"updateFixtures({json.dumps(fixtures_data)});")
logger.debug("Sent fixtures data via JavaScript function calls")
except Exception as js_error:
logger.debug(f"JavaScript call failed: {js_error}")
else:
logger.warning("No fixtures data available to send to overlay")
# Send empty data to trigger fallback
empty_data = {'matches': []}
if hasattr(self.window_overlay, 'overlay_channel') and self.window_overlay.overlay_channel:
self.window_overlay.overlay_channel.send_fixture_data_update(empty_data)
else:
logger.warning("No overlay available to send fixtures data")
except Exception as e:
logger.error(f"Failed to send fixtures data to overlay: {e}")
def initialize(self) -> bool: def initialize(self) -> bool:
"""Initialize PyQt6 application and components""" """Initialize PyQt6 application and components"""
try: try:
...@@ -2254,6 +2512,12 @@ class QtVideoPlayer(QObject): ...@@ -2254,6 +2512,12 @@ class QtVideoPlayer(QObject):
'webServerBaseUrl': self._get_web_server_base_url() 'webServerBaseUrl': self._get_web_server_base_url()
} }
# Fetch and include fixture data if available
fixture_data = self._fetch_fixture_data()
if fixture_data:
default_data['fixtures'] = fixture_data.get('matches', [])
logger.info(f"Included {len(default_data['fixtures'])} fixtures in default overlay")
if not self.window: if not self.window:
logger.debug("Window not ready for overlay loading") logger.debug("Window not ready for overlay loading")
return return
...@@ -2630,8 +2894,11 @@ class QtVideoPlayer(QObject): ...@@ -2630,8 +2894,11 @@ class QtVideoPlayer(QObject):
logger.error(f"Failed to handle video loaded event: {e}") logger.error(f"Failed to handle video loaded event: {e}")
def _send_progress_update(self): def _send_progress_update(self):
"""Send video progress update""" """Send video progress update (throttled to 2 per second)"""
try: try:
# Throttle progress updates to 2 per second (500ms minimum interval)
current_time = time.time()
if current_time - self.last_progress_update_time >= self.progress_update_interval:
if self.window and self.window.media_player.duration() > 0: if self.window and self.window.media_player.duration() > 0:
position = self.window.media_player.position() position = self.window.media_player.position()
duration = self.window.media_player.duration() duration = self.window.media_player.duration()
...@@ -2644,6 +2911,7 @@ class QtVideoPlayer(QObject): ...@@ -2644,6 +2911,7 @@ class QtVideoPlayer(QObject):
percentage=percentage percentage=percentage
) )
self.message_bus.publish(progress_message, broadcast=True) self.message_bus.publish(progress_message, broadcast=True)
self.last_progress_update_time = current_time
except Exception as e: except Exception as e:
logger.error(f"Failed to send progress update: {e}") logger.error(f"Failed to send progress update: {e}")
...@@ -2799,6 +3067,19 @@ class QtVideoPlayer(QObject): ...@@ -2799,6 +3067,19 @@ class QtVideoPlayer(QObject):
logger.debug(f"GREEN SCREEN DEBUG: About to load specific template: {load_specific_template}") logger.debug(f"GREEN SCREEN DEBUG: About to load specific template: {load_specific_template}")
logger.debug(f"GREEN SCREEN FIX: Protecting video rendering during template load") logger.debug(f"GREEN SCREEN FIX: Protecting video rendering during template load")
overlay_view.load_template(load_specific_template) overlay_view.load_template(load_specific_template)
# If loading fixtures template, fetch and send fixture data
if load_specific_template == "fixtures.html":
logger.info("Loading fixtures template, fetching fixture data...")
fixture_data = self._fetch_fixture_data()
if fixture_data:
# Send fixture data to the overlay
fixture_payload = {'fixtures': fixture_data.get('matches', [])}
self.window.update_fixture_data(fixture_payload)
logger.info(f"Sent {len(fixture_payload['fixtures'])} fixtures to fixtures overlay")
else:
logger.warning("No fixture data available for fixtures template")
if self.debug_overlay: if self.debug_overlay:
logger.debug(f"GREEN SCREEN DEBUG: Specific template load initiated") logger.debug(f"GREEN SCREEN DEBUG: Specific template load initiated")
# Otherwise reload current template if requested and using WebEngine overlay # Otherwise reload current template if requested and using WebEngine overlay
......
...@@ -76,11 +76,11 @@ ...@@ -76,11 +76,11 @@
} }
.fixtures-table th { .fixtures-table th {
padding: 15px 10px; padding: 8px 4px;
text-align: center; text-align: center;
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
font-weight: bold; font-weight: bold;
font-size: 14px; font-size: 11px;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 1px; letter-spacing: 1px;
border-radius: 8px; border-radius: 8px;
...@@ -89,7 +89,7 @@ ...@@ -89,7 +89,7 @@
} }
.fixtures-table td { .fixtures-table td {
padding: 12px 10px; padding: 8px 6px;
text-align: center; text-align: center;
background: rgba(255, 255, 255, 0.05); background: rgba(255, 255, 255, 0.05);
border-radius: 6px; border-radius: 6px;
...@@ -101,6 +101,45 @@ ...@@ -101,6 +101,45 @@
background: rgba(255, 255, 255, 0.15); background: rgba(255, 255, 255, 0.15);
} }
.next-match-info {
text-align: center;
color: rgba(255, 255, 255, 0.9);
font-size: 16px;
margin-bottom: 10px;
font-weight: bold;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}
.countdown-timer {
text-align: center;
color: #ffeb3b;
font-size: 24px;
font-weight: bold;
margin-bottom: 15px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
background: rgba(0, 0, 0, 0.3);
padding: 8px 16px;
border-radius: 10px;
border: 2px solid rgba(255, 235, 59, 0.3);
}
.countdown-timer.warning {
color: #ff9800;
border-color: rgba(255, 152, 0, 0.5);
}
.countdown-timer.urgent {
color: #f44336;
border-color: rgba(244, 67, 54, 0.5);
animation: pulse 1s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.match-info { .match-info {
font-weight: bold; font-weight: bold;
color: #ffffff; color: #ffffff;
...@@ -244,6 +283,8 @@ ...@@ -244,6 +283,8 @@
<div class="overlay-container"> <div class="overlay-container">
<div class="fixtures-panel" id="fixturesPanel"> <div class="fixtures-panel" id="fixturesPanel">
<div class="fixtures-title">Today's matches</div> <div class="fixtures-title">Today's matches</div>
<div class="next-match-info" id="nextMatchInfo" style="display: none;"></div>
<div class="countdown-timer" id="countdownTimer" style="display: none;"></div>
<div class="loading-message" id="loadingMessage" style="display: none;">Loading fixture data...</div> <div class="loading-message" id="loadingMessage" style="display: none;">Loading fixture data...</div>
<div id="fixturesContent" style="display: none;"> <div id="fixturesContent" style="display: none;">
<table class="fixtures-table" id="fixturesTable"> <table class="fixtures-table" id="fixturesTable">
...@@ -275,108 +316,109 @@ ...@@ -275,108 +316,109 @@
let overlayData = {}; let overlayData = {};
let fixturesData = null; let fixturesData = null;
let outcomesData = null; let outcomesData = null;
let countdownInterval = null;
let nextMatchStartTime = null;
// Web server configuration - will be set via WebChannel // Web server configuration - will be set via WebChannel
let webServerBaseUrl = 'http://127.0.0.1:5001'; // Default fallback let webServerBaseUrl = 'http://127.0.0.1:5001'; // Default fallback
// Function to update overlay data (called by Qt WebChannel) // Debug logging function that sends messages to Qt application logs
function updateOverlayData(data) { function debugLog(message, level = 'info') {
console.log('Received overlay data:', data); try {
overlayData = data || {}; // Try to send to Qt WebChannel if available
if (typeof qt !== 'undefined' && qt.webChannelTransport) {
// Update web server base URL if provided // Send debug message to Qt application
if (data && data.webServerBaseUrl) { if (window.sendDebugMessage) {
webServerBaseUrl = data.webServerBaseUrl; window.sendDebugMessage(`[FIXTURES] ${message}`);
console.log('Updated web server base URL:', webServerBaseUrl);
} }
// Check if we have fixtures data
if (data && data.fixtures) {
fixturesData = data.fixtures;
renderFixtures();
} else {
// Fetch fixtures data from API
fetchFixturesData();
} }
} catch (e) {
// Fallback to console if WebChannel not available - use original console to avoid recursion
originalConsoleLog(`[FIXTURES FALLBACK] ${message}`);
} }
// Fetch fixtures data from the API // Always log to console as well for browser debugging - use original to avoid recursion
async function fetchFixturesData() { originalConsoleLog(`🔍 DEBUG: ${message}`);
try { }
console.log('Fetching fixtures data from API...');
// Try multiple API endpoints with different authentication levels // Store original console.log before overriding
const apiEndpoints = [ const originalConsoleLog = console.log;
`${webServerBaseUrl}/api/cashier/pending-matches`,
`${webServerBaseUrl}/api/fixtures`,
`${webServerBaseUrl}/api/status` // Fallback to basic status endpoint
];
let apiData = null; // Override console.log to redirect all console.log calls to Qt application logs
let usedEndpoint = null; console.log = function(...args) {
// Convert arguments to string message
const message = args.map(arg =>
typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
).join(' ');
for (const endpoint of apiEndpoints) { // Send to Qt application logs directly (avoid calling debugLog to prevent recursion)
try { try {
console.log(`Trying API endpoint: ${endpoint}`); if (typeof qt !== 'undefined' && qt.webChannelTransport) {
const response = await fetch(endpoint, { if (window.sendDebugMessage) {
method: 'GET', window.sendDebugMessage(`[FIXTURES] ${message}`);
headers: { }
'Content-Type': 'application/json' }
}, } catch (e) {
credentials: 'include' // Include cookies for authentication // Fallback handled below
}); }
if (response.ok) { // Always call original console.log for browser debugging
const data = await response.json(); originalConsoleLog.apply(console, args);
console.log(`API Response from ${endpoint}:`, data); };
if (data.success) { // Function to receive fixture data from Qt WebChannel
apiData = data; function updateFixtureData(data) {
usedEndpoint = endpoint; console.log('🔍 DEBUG: Received fixture data from WebChannel:', data);
break; if (data && data.fixtures) {
} fixturesData = data.fixtures;
renderFixtures();
} else if (data && data.matches) {
fixturesData = data.matches;
renderFixtures();
} else { } else {
console.warn(`API endpoint ${endpoint} returned status ${response.status}`); console.log('🔍 DEBUG: No valid fixture data in WebChannel update, showing fallback');
showFallbackMatches();
} }
} catch (endpointError) {
console.warn(`Failed to fetch from ${endpoint}:`, endpointError);
continue;
} }
// Function to update overlay data (called by Qt WebChannel)
function updateOverlayData(data) {
console.log('Received overlay data:', data);
overlayData = data || {};
// Update web server base URL if provided
if (data && data.webServerBaseUrl) {
webServerBaseUrl = data.webServerBaseUrl;
console.log('Updated web server base URL:', webServerBaseUrl);
} }
if (apiData && apiData.matches && apiData.matches.length > 0) { // Check if we have fixtures data from WebChannel
console.log(`Found ${apiData.matches.length} matches from ${usedEndpoint}`); if (data && data.fixtures) {
fixturesData = apiData.matches; console.log('🔍 DEBUG: Received fixtures data from WebChannel');
fixturesData = data.fixtures;
renderFixtures(); renderFixtures();
return Promise.resolve(); } else if (data && data.matches) {
} else if (apiData && apiData.fixtures && apiData.fixtures.length > 0) { console.log('🔍 DEBUG: Received matches data from WebChannel');
// Handle fixtures endpoint format fixturesData = data.matches;
console.log(`Found ${apiData.fixtures.length} fixtures from ${usedEndpoint}`);
// Convert fixtures to matches format
fixturesData = [];
apiData.fixtures.forEach(fixture => {
if (fixture.matches) {
fixturesData.push(...fixture.matches);
}
});
if (fixturesData.length > 0) {
renderFixtures(); renderFixtures();
return Promise.resolve(); } else {
console.log('🔍 DEBUG: No fixtures data in WebChannel update, will wait for updateFixtureData call');
// Don't fetch from API - wait for Python side to provide data via updateFixtureData
} }
} }
// If we reach here, no valid data was found
console.log('No fixture data available from any API endpoint, will show fallback'); // Function to receive fixture data from Qt WebChannel (called by Python) - new method
return Promise.reject('No API data available'); function updateFixtures(data) {
} catch (error) { console.log('🔍 DEBUG: Received fixtures data from WebChannel via updateFixtures:', data);
console.error('Error fetching fixtures data:', error); updateFixtureData(data);
return Promise.reject(error);
}
} }
// Show fallback sample matches when API is not available // Show fallback sample matches when API is not available
function showFallbackMatches() { function showFallbackMatches() {
console.log('Showing fallback sample matches'); console.log('🔍 DEBUG: Showing fallback sample matches - API FAILED');
console.log('🔍 DEBUG: This indicates the API endpoints are not working properly');
fixturesData = [ fixturesData = [
{ {
id: 1, id: 1,
...@@ -407,6 +449,7 @@ ...@@ -407,6 +449,7 @@
] ]
} }
]; ];
console.log('🔍 DEBUG: Fallback data loaded, rendering fixtures');
renderFixtures(); renderFixtures();
} }
...@@ -583,7 +626,8 @@ ...@@ -583,7 +626,8 @@
// Initialize when DOM is loaded // Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
console.log('Fixtures overlay initialized - attempting to fetch real data'); console.log('🔍 DEBUG: Fixtures overlay initialized - waiting for WebChannel data');
console.log('🔍 DEBUG: DOM ready, WebChannel will provide fixture data');
// Show loading message initially // Show loading message initially
document.getElementById('fixturesContent').style.display = 'none'; document.getElementById('fixturesContent').style.display = 'none';
...@@ -591,46 +635,93 @@ ...@@ -591,46 +635,93 @@
document.getElementById('loadingMessage').style.display = 'block'; document.getElementById('loadingMessage').style.display = 'block';
document.getElementById('loadingMessage').textContent = 'Loading live fixture data...'; document.getElementById('loadingMessage').textContent = 'Loading live fixture data...';
// Start fetching real data immediately // Wait for WebChannel to provide data - no direct API fetching
fetchFixturesData().then(() => { console.log('🔍 DEBUG: Waiting for Python side to provide fixture data via WebChannel');
// If API fails completely, show fallback data after a short delay
// Fallback: Try to trigger data load via WebChannel after 1 second
setTimeout(function() {
console.log('🔍 DEBUG: Fallback - trying to trigger data load via WebChannel');
if (typeof qt !== 'undefined' && qt.webChannelTransport) {
try {
new QWebChannel(qt.webChannelTransport, function(channel) {
if (channel.objects.overlay && channel.objects.overlay.trigger_fixtures_data_load) {
console.log('🔍 DEBUG: Calling trigger_fixtures_data_load from fallback timer');
channel.objects.overlay.trigger_fixtures_data_load();
}
});
} catch (e) {
console.log('🔍 DEBUG: Fallback WebChannel trigger failed:', e);
}
}
}, 1000);
// Fallback timeout - if no data received after 10 seconds, show fallback
setTimeout(() => { setTimeout(() => {
if (!fixturesData || fixturesData.length === 0) { if (!fixturesData || fixturesData.length === 0) {
console.log('No data loaded after API attempts, forcing fallback display'); console.log('🔍 DEBUG: No data received from WebChannel after timeout, showing fallback');
showFallbackMatches(); showFallbackMatches();
} else {
console.log('🔍 DEBUG: Data received from WebChannel, no fallback needed');
} }
}, 2000); }, 10000);
}).catch(() => {
console.log('API fetch failed, showing fallback data');
showFallbackMatches();
});
// Refresh data every 30 seconds
setInterval(function() {
console.log('Refreshing fixture data...');
fetchFixturesData();
}, 30000);
}); });
// Qt WebChannel initialization (when available) // Qt WebChannel initialization (when available)
if (typeof QWebChannel !== 'undefined') { if (typeof QWebChannel !== 'undefined') {
console.log('🔍 DEBUG: QWebChannel available, initializing...');
new QWebChannel(qt.webChannelTransport, function(channel) { new QWebChannel(qt.webChannelTransport, function(channel) {
console.log('WebChannel initialized for fixtures overlay'); console.log('🔍 DEBUG: WebChannel initialized for fixtures overlay');
console.log('🔍 DEBUG: Available channel objects:', Object.keys(channel.objects));
// Connect to overlay object if available // Connect to overlay object if available
if (channel.objects.overlay) { if (channel.objects.overlay) {
console.log('🔍 DEBUG: Overlay object found in WebChannel');
console.log('🔍 DEBUG: Overlay object methods:', Object.getOwnPropertyNames(channel.objects.overlay));
channel.objects.overlay.dataChanged.connect(function(data) { channel.objects.overlay.dataChanged.connect(function(data) {
console.log('🔍 DEBUG: Overlay dataChanged signal received:', data);
updateOverlayData(data); updateOverlayData(data);
}); });
// Connect fixture data signal
if (channel.objects.overlay.fixtureDataUpdated) {
console.log('🔍 DEBUG: Connecting to fixtureDataUpdated signal');
channel.objects.overlay.fixtureDataUpdated.connect(function(data) {
console.log('🔍 DEBUG: Fixture data signal received:', data);
updateFixtureData(data);
});
} else {
console.log('🔍 DEBUG: fixtureDataUpdated signal not available on overlay object');
}
// Get initial data // Get initial data
if (channel.objects.overlay.getCurrentData) { if (channel.objects.overlay.getCurrentData) {
console.log('🔍 DEBUG: Calling getCurrentData for initial data');
channel.objects.overlay.getCurrentData(function(data) { channel.objects.overlay.getCurrentData(function(data) {
console.log('🔍 DEBUG: Received initial data from getCurrentData:', data);
updateOverlayData(data); updateOverlayData(data);
}); });
} else {
console.log('🔍 DEBUG: getCurrentData method not available');
} }
// Try to manually trigger fixtures data load
console.log('🔍 DEBUG: Attempting to manually trigger fixtures data load');
if (channel.objects.overlay.trigger_fixtures_data_load) {
console.log('🔍 DEBUG: Calling trigger_fixtures_data_load manually');
channel.objects.overlay.trigger_fixtures_data_load();
} else if (channel.objects.overlay.sendFixtureDataUpdate) {
console.log('🔍 DEBUG: Calling sendFixtureDataUpdate manually');
channel.objects.overlay.sendFixtureDataUpdate({'test': 'manual_trigger'});
} else {
console.log('🔍 DEBUG: No manual trigger method available');
}
} else {
console.log('🔍 DEBUG: No overlay object found in WebChannel');
} }
}); });
} else {
console.log('🔍 DEBUG: QWebChannel not available, will rely on DOM ready event');
} }
</script> </script>
......
...@@ -1925,7 +1925,6 @@ def save_intro_templates(): ...@@ -1925,7 +1925,6 @@ def save_intro_templates():
@api_bp.route('/fixtures') @api_bp.route('/fixtures')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def get_fixtures(): def get_fixtures():
"""Get all fixtures/matches grouped by fixture_id with calculated status""" """Get all fixtures/matches grouped by fixture_id with calculated status"""
try: try:
...@@ -2040,10 +2039,14 @@ def calculate_fixture_status(matches, today): ...@@ -2040,10 +2039,14 @@ def calculate_fixture_status(matches, today):
@api_bp.route('/cashier/pending-matches') @api_bp.route('/cashier/pending-matches')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def get_cashier_pending_matches(): def get_cashier_pending_matches():
"""Get pending matches from the correct fixture for cashier dashboard""" """Get pending matches from the correct fixture for cashier dashboard"""
try: try:
# Allow access from localhost without authentication
if request.remote_addr == '127.0.0.1':
pass # Skip authentication
elif hasattr(api_bp, 'auth_manager') and api_bp.auth_manager:
api_bp.auth_manager.require_auth()
from ..database.models import MatchModel from ..database.models import MatchModel
from datetime import datetime, date, timedelta from datetime import datetime, date, timedelta
...@@ -2190,10 +2193,14 @@ def start_games(): ...@@ -2190,10 +2193,14 @@ def start_games():
@api_bp.route('/fixtures/<fixture_id>') @api_bp.route('/fixtures/<fixture_id>')
@api_bp.auth_manager.require_auth if hasattr(api_bp, 'auth_manager') and api_bp.auth_manager else login_required
def get_fixture_details(fixture_id): def get_fixture_details(fixture_id):
"""Get all matches in a fixture by fixture_id""" """Get all matches in a fixture by fixture_id"""
try: try:
# Allow access from localhost without authentication
if request.remote_addr == '127.0.0.1':
pass # Skip authentication
elif hasattr(api_bp, 'auth_manager') and api_bp.auth_manager:
api_bp.auth_manager.require_auth()
from ..database.models import MatchModel, MatchOutcomeModel from ..database.models import MatchModel, MatchOutcomeModel
from datetime import datetime, date from datetime import datetime, date
......
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