Fixture template works now

parent 0632ac59
......@@ -595,7 +595,9 @@ class MbetterClientApplication:
def _process_core_message(self, message: Message):
"""Process messages received by the core component"""
try:
logger.debug(f"Core processing message: {message}")
# Suppress debug logging for VIDEO_PROGRESS messages to reduce log noise
if message.type != MessageType.VIDEO_PROGRESS:
logger.debug(f"Core processing message: {message}")
if message.type == MessageType.SYSTEM_STATUS:
self._handle_system_status(message)
......@@ -608,7 +610,9 @@ class MbetterClientApplication:
elif message.type == MessageType.SYSTEM_SHUTDOWN:
self._handle_shutdown_message(message)
else:
logger.debug(f"Unhandled message type in core: {message.type}")
# 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}")
except Exception as e:
logger.error(f"Failed to process core message: {e}")
......
......@@ -47,11 +47,12 @@ logger = logging.getLogger(__name__)
class OverlayWebChannel(QObject):
"""QObject for WebChannel communication with overlay HTML/JS"""
# Signals to send data to JavaScript
dataUpdated = pyqtSignal(dict)
positionChanged = pyqtSignal(float, float) # position, duration in seconds
videoInfoChanged = pyqtSignal(dict)
fixtureDataUpdated = pyqtSignal(dict) # Signal for fixture data updates
def __init__(self):
super().__init__()
......@@ -178,6 +179,43 @@ class OverlayWebChannel(QObject):
else:
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):
"""Background worker for video processing tasks"""
......@@ -419,6 +457,11 @@ class OverlayWebView(QWebEngineView):
from PyQt6.QtCore import QTimer
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:
logger.debug(f"GREEN SCREEN DEBUG: Template load initiated - {template_path}")
logger.info(f"Loaded template: {template_path} (source: {template_source})")
......@@ -619,6 +662,51 @@ class OverlayWebView(QWebEngineView):
if self.overlay_channel:
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):
"""Native Qt overlay widget - no WebEngine to prevent freezing"""
......@@ -1271,6 +1359,11 @@ class PlayerWindow(QMainWindow):
self.overlay_timer.timeout.connect(self.update_overlay_periodically)
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)
self.template_rotation_timer = QTimer()
self.template_rotation_timer.timeout.connect(self._rotate_template)
......@@ -1744,9 +1837,89 @@ class PlayerWindow(QMainWindow):
}
if time_data and current_time: # Ensure we have valid data
self._update_overlay_safe(overlay_view, time_data)
except Exception as 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):
"""Show controls on mouse movement"""
......@@ -2019,6 +2192,10 @@ class QtVideoPlayer(QObject):
self.window: Optional[PlayerWindow] = None
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
self.web_dashboard_url = "http://localhost:5000" # Default web dashboard URL
......@@ -2038,22 +2215,103 @@ class QtVideoPlayer(QObject):
# Default web server configuration - matches main.py defaults
host = "127.0.0.1" # Default host
port = 5001 # Default port
# Try to get web server settings if available
if hasattr(self, '_message_bus') and self._message_bus:
# Check if we can get web server info from message bus or settings
# For now, use defaults since we don't have direct access to web settings
pass
# Construct base URL
base_url = f"http://{host}:{port}"
logger.debug(f"Web server base URL determined as: {base_url}")
return base_url
except Exception as e:
logger.error(f"Failed to determine web server base URL: {e}")
# Return default fallback
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:
"""Initialize PyQt6 application and components"""
......@@ -2253,6 +2511,12 @@ class QtVideoPlayer(QObject):
'showStats': False,
'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:
logger.debug("Window not ready for overlay loading")
......@@ -2630,21 +2894,25 @@ class QtVideoPlayer(QObject):
logger.error(f"Failed to handle video loaded event: {e}")
def _send_progress_update(self):
"""Send video progress update"""
try:
if self.window and self.window.media_player.duration() > 0:
position = self.window.media_player.position()
duration = self.window.media_player.duration()
percentage = (position / duration) * 100 if duration > 0 else 0
progress_message = MessageBuilder.video_progress(
sender=self.name,
position=position / 1000.0, # Convert to seconds
duration=duration / 1000.0, # Convert to seconds
percentage=percentage
)
self.message_bus.publish(progress_message, broadcast=True)
"""Send video progress update (throttled to 2 per second)"""
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:
position = self.window.media_player.position()
duration = self.window.media_player.duration()
percentage = (position / duration) * 100 if duration > 0 else 0
progress_message = MessageBuilder.video_progress(
sender=self.name,
position=position / 1000.0, # Convert to seconds
duration=duration / 1000.0, # Convert to seconds
percentage=percentage
)
self.message_bus.publish(progress_message, broadcast=True)
self.last_progress_update_time = current_time
except Exception as e:
logger.error(f"Failed to send progress update: {e}")
......@@ -2764,16 +3032,16 @@ class QtVideoPlayer(QObject):
template_data = message.data.get("template_data", {})
reload_template = template_data.get("reload_template", False)
load_specific_template = template_data.get("load_specific_template", "")
if self.debug_overlay:
logger.debug(f"GREEN SCREEN DEBUG: Template change message received")
logger.debug(f"GREEN SCREEN DEBUG: Template name: {template_name}")
logger.debug(f"GREEN SCREEN DEBUG: Reload template: {reload_template}")
logger.debug(f"GREEN SCREEN DEBUG: Load specific template: {load_specific_template}")
if self.window and hasattr(self.window, 'window_overlay'):
overlay_view = self.window.window_overlay
# Check video player state before template change
if hasattr(self.window, 'media_player') and self.debug_overlay:
video_state = self.window.media_player.playbackState()
......@@ -2785,7 +3053,7 @@ class QtVideoPlayer(QObject):
if hasattr(self.window, 'overlay_window') and self.debug_overlay:
logger.debug(f"GREEN SCREEN DEBUG: Overlay window geometry: {self.window.overlay_window.geometry()}")
logger.debug(f"GREEN SCREEN DEBUG: Overlay window visible: {self.window.overlay_window.isVisible()}")
# CRITICAL FIX: Protect video context during template changes
video_widget = None
if hasattr(self.window, 'video_widget') and hasattr(self.window.video_widget, 'get_video_widget'):
......@@ -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 FIX: Protecting video rendering during template load")
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:
logger.debug(f"GREEN SCREEN DEBUG: Specific template load initiated")
# Otherwise reload current template if requested and using WebEngine overlay
......@@ -2809,7 +3090,7 @@ class QtVideoPlayer(QObject):
overlay_view.reload_current_template()
if self.debug_overlay:
logger.debug(f"GREEN SCREEN DEBUG: Current template reload initiated")
# CRITICAL FIX: Force video widget refresh after template change
if video_widget and self.debug_overlay:
logger.debug(f"GREEN SCREEN FIX: Forcing video widget refresh after template change")
......
......@@ -76,11 +76,11 @@
}
.fixtures-table th {
padding: 15px 10px;
padding: 8px 4px;
text-align: center;
background: rgba(255, 255, 255, 0.1);
font-weight: bold;
font-size: 14px;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 1px;
border-radius: 8px;
......@@ -89,7 +89,7 @@
}
.fixtures-table td {
padding: 12px 10px;
padding: 8px 6px;
text-align: center;
background: rgba(255, 255, 255, 0.05);
border-radius: 6px;
......@@ -100,6 +100,45 @@
.fixtures-table tbody tr:hover td {
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 {
font-weight: bold;
......@@ -244,6 +283,8 @@
<div class="overlay-container">
<div class="fixtures-panel" id="fixturesPanel">
<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 id="fixturesContent" style="display: none;">
<table class="fixtures-table" id="fixturesTable">
......@@ -275,108 +316,109 @@
let overlayData = {};
let fixturesData = null;
let outcomesData = null;
let countdownInterval = null;
let nextMatchStartTime = null;
// Web server configuration - will be set via WebChannel
let webServerBaseUrl = 'http://127.0.0.1:5001'; // Default fallback
// Debug logging function that sends messages to Qt application logs
function debugLog(message, level = 'info') {
try {
// Try to send to Qt WebChannel if available
if (typeof qt !== 'undefined' && qt.webChannelTransport) {
// Send debug message to Qt application
if (window.sendDebugMessage) {
window.sendDebugMessage(`[FIXTURES] ${message}`);
}
}
} catch (e) {
// Fallback to console if WebChannel not available - use original console to avoid recursion
originalConsoleLog(`[FIXTURES FALLBACK] ${message}`);
}
// Always log to console as well for browser debugging - use original to avoid recursion
originalConsoleLog(`🔍 DEBUG: ${message}`);
}
// Store original console.log before overriding
const originalConsoleLog = console.log;
// Override console.log to redirect all console.log calls to Qt application logs
console.log = function(...args) {
// Convert arguments to string message
const message = args.map(arg =>
typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
).join(' ');
// Send to Qt application logs directly (avoid calling debugLog to prevent recursion)
try {
if (typeof qt !== 'undefined' && qt.webChannelTransport) {
if (window.sendDebugMessage) {
window.sendDebugMessage(`[FIXTURES] ${message}`);
}
}
} catch (e) {
// Fallback handled below
}
// Always call original console.log for browser debugging
originalConsoleLog.apply(console, args);
};
// Function to receive fixture data from Qt WebChannel
function updateFixtureData(data) {
console.log('🔍 DEBUG: Received fixture data from WebChannel:', data);
if (data && data.fixtures) {
fixturesData = data.fixtures;
renderFixtures();
} else if (data && data.matches) {
fixturesData = data.matches;
renderFixtures();
} else {
console.log('🔍 DEBUG: No valid fixture data in WebChannel update, showing fallback');
showFallbackMatches();
}
}
// 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);
}
// Check if we have fixtures data
// Check if we have fixtures data from WebChannel
if (data && data.fixtures) {
console.log('🔍 DEBUG: Received fixtures data from WebChannel');
fixturesData = data.fixtures;
renderFixtures();
} else if (data && data.matches) {
console.log('🔍 DEBUG: Received matches data from WebChannel');
fixturesData = data.matches;
renderFixtures();
} else {
// Fetch fixtures data from API
fetchFixturesData();
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
}
}
// Fetch fixtures data from the API
async function fetchFixturesData() {
try {
console.log('Fetching fixtures data from API...');
// Try multiple API endpoints with different authentication levels
const apiEndpoints = [
`${webServerBaseUrl}/api/cashier/pending-matches`,
`${webServerBaseUrl}/api/fixtures`,
`${webServerBaseUrl}/api/status` // Fallback to basic status endpoint
];
let apiData = null;
let usedEndpoint = null;
for (const endpoint of apiEndpoints) {
try {
console.log(`Trying API endpoint: ${endpoint}`);
const response = await fetch(endpoint, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include' // Include cookies for authentication
});
if (response.ok) {
const data = await response.json();
console.log(`API Response from ${endpoint}:`, data);
if (data.success) {
apiData = data;
usedEndpoint = endpoint;
break;
}
} else {
console.warn(`API endpoint ${endpoint} returned status ${response.status}`);
}
} catch (endpointError) {
console.warn(`Failed to fetch from ${endpoint}:`, endpointError);
continue;
}
}
if (apiData && apiData.matches && apiData.matches.length > 0) {
console.log(`Found ${apiData.matches.length} matches from ${usedEndpoint}`);
fixturesData = apiData.matches;
renderFixtures();
return Promise.resolve();
} else if (apiData && apiData.fixtures && apiData.fixtures.length > 0) {
// Handle fixtures endpoint format
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();
return Promise.resolve();
}
}
// If we reach here, no valid data was found
console.log('No fixture data available from any API endpoint, will show fallback');
return Promise.reject('No API data available');
} catch (error) {
console.error('Error fetching fixtures data:', error);
return Promise.reject(error);
}
// Function to receive fixture data from Qt WebChannel (called by Python) - new method
function updateFixtures(data) {
console.log('🔍 DEBUG: Received fixtures data from WebChannel via updateFixtures:', data);
updateFixtureData(data);
}
// Show fallback sample matches when API is not available
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 = [
{
id: 1,
......@@ -407,6 +449,7 @@
]
}
];
console.log('🔍 DEBUG: Fallback data loaded, rendering fixtures');
renderFixtures();
}
......@@ -583,54 +626,102 @@
// Initialize when DOM is loaded
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
document.getElementById('fixturesContent').style.display = 'none';
document.getElementById('noMatches').style.display = 'none';
document.getElementById('loadingMessage').style.display = 'block';
document.getElementById('loadingMessage').textContent = 'Loading live fixture data...';
// Start fetching real data immediately
fetchFixturesData().then(() => {
// If API fails completely, show fallback data after a short delay
setTimeout(() => {
if (!fixturesData || fixturesData.length === 0) {
console.log('No data loaded after API attempts, forcing fallback display');
showFallbackMatches();
// Wait for WebChannel to provide data - no direct API fetching
console.log('🔍 DEBUG: Waiting for Python side to provide fixture data via WebChannel');
// 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);
}
}, 2000);
}).catch(() => {
console.log('API fetch failed, showing fallback data');
showFallbackMatches();
});
// Refresh data every 30 seconds
setInterval(function() {
console.log('Refreshing fixture data...');
fetchFixturesData();
}, 30000);
}
}, 1000);
// Fallback timeout - if no data received after 10 seconds, show fallback
setTimeout(() => {
if (!fixturesData || fixturesData.length === 0) {
console.log('🔍 DEBUG: No data received from WebChannel after timeout, showing fallback');
showFallbackMatches();
} else {
console.log('🔍 DEBUG: Data received from WebChannel, no fallback needed');
}
}, 10000);
});
// Qt WebChannel initialization (when available)
if (typeof QWebChannel !== 'undefined') {
console.log('🔍 DEBUG: QWebChannel available, initializing...');
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
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) {
console.log('🔍 DEBUG: Overlay dataChanged signal received:', 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
if (channel.objects.overlay.getCurrentData) {
console.log('🔍 DEBUG: Calling getCurrentData for initial data');
channel.objects.overlay.getCurrentData(function(data) {
console.log('🔍 DEBUG: Received initial data from getCurrentData:', 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>
......
......@@ -1925,7 +1925,6 @@ def save_intro_templates():
@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():
"""Get all fixtures/matches grouped by fixture_id with calculated status"""
try:
......@@ -2040,10 +2039,14 @@ def calculate_fixture_status(matches, today):
@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():
"""Get pending matches from the correct fixture for cashier dashboard"""
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 datetime import datetime, date, timedelta
......@@ -2190,10 +2193,14 @@ def start_games():
@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):
"""Get all matches in a fixture by fixture_id"""
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 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