templates license and results bottom bar

parent 0a38c586
Collecting flask-cors
Using cached flask_cors-6.0.1-py3-none-any.whl.metadata (5.3 kB)
Requirement already satisfied: flask>=0.9 in ./venv/lib/python3.13/site-packages (from flask-cors) (3.1.2)
Requirement already satisfied: Werkzeug>=0.7 in ./venv/lib/python3.13/site-packages (from flask-cors) (3.1.3)
Requirement already satisfied: blinker>=1.9.0 in ./venv/lib/python3.13/site-packages (from flask>=0.9->flask-cors) (1.9.0)
Requirement already satisfied: click>=8.1.3 in ./venv/lib/python3.13/site-packages (from flask>=0.9->flask-cors) (8.3.1)
Requirement already satisfied: itsdangerous>=2.2.0 in ./venv/lib/python3.13/site-packages (from flask>=0.9->flask-cors) (2.2.0)
Requirement already satisfied: jinja2>=3.1.2 in ./venv/lib/python3.13/site-packages (from flask>=0.9->flask-cors) (3.1.6)
Requirement already satisfied: markupsafe>=2.1.1 in ./venv/lib/python3.13/site-packages (from flask>=0.9->flask-cors) (3.0.3)
Using cached flask_cors-6.0.1-py3-none-any.whl (13 kB)
Installing collected packages: flask-cors
Successfully installed flask-cors-6.0.1
...@@ -280,6 +280,113 @@ class OverlayWebChannel(QObject): ...@@ -280,6 +280,113 @@ class OverlayWebChannel(QObject):
logger.error(f"QtWebChannel: Full traceback: {traceback.format_exc()}") logger.error(f"QtWebChannel: Full traceback: {traceback.format_exc()}")
return json.dumps([]) return json.dumps([])
@pyqtSlot(result=str)
def getLicenseText(self) -> str:
"""Provide license text to JavaScript via WebChannel"""
try:
logger.info("QtWebChannel: Getting license text from database")
if not self.db_manager:
logger.error("QtWebChannel: Database manager not available")
return json.dumps({"license_text": "License text not available - database not connected"})
# Get license text from database
license_text = self._get_license_text_from_database()
logger.debug(f"QtWebChannel: Retrieved license text from database")
return json.dumps({"license_text": license_text})
except Exception as e:
logger.error(f"QtWebChannel: Failed to get license text: {e}")
return json.dumps({"license_text": "License text not available - error occurred"})
@pyqtSlot(result=str)
def getCompletedMatches(self) -> str:
"""Provide last 3 completed matches to JavaScript via WebChannel"""
try:
logger.info("QtWebChannel: Getting completed matches from database")
if not self.db_manager:
logger.error("QtWebChannel: Database manager not available")
return json.dumps([])
# Get completed matches from database
completed_matches = self._get_completed_matches_from_database()
logger.debug(f"QtWebChannel: Retrieved {len(completed_matches)} completed matches from database")
return json.dumps(completed_matches)
except Exception as e:
logger.error(f"QtWebChannel: Failed to get completed matches: {e}")
return json.dumps([])
def _get_license_text_from_database(self) -> str:
"""Get license text from database configuration"""
try:
if not self.db_manager:
logger.error("Database manager not available for getting license text")
return "License text not available - database not connected"
# Get license text from configuration
license_text = self.db_manager.get_config_value('license_text', 'License text not configured')
logger.debug(f"Retrieved license text from database: {license_text}")
return license_text
except Exception as e:
logger.error(f"Failed to get license text from database: {e}")
return "License text not available - database error"
def _get_completed_matches_from_database(self) -> Optional[List[Dict[str, Any]]]:
"""Get last 3 completed matches from database"""
try:
from ..database.models import MatchModel
from datetime import datetime
# Use the database manager passed to this channel
if not self.db_manager:
logger.error("Database manager not initialized")
return []
session = self.db_manager.get_session()
try:
# Get last 3 completed matches (status = 'done' and result is not null)
# Ordered by end_time descending (most recent first)
completed_matches = session.query(MatchModel).filter(
MatchModel.status == 'done',
MatchModel.result.isnot(None),
MatchModel.end_time.isnot(None)
).order_by(MatchModel.end_time.desc()).limit(3).all()
if not completed_matches:
logger.debug("No completed matches found")
return []
matches_data = []
for match in completed_matches:
match_data = {
'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,
'result': match.result,
'under_over_result': match.under_over_result,
'end_time': match.end_time.isoformat() if match.end_time else None
}
matches_data.append(match_data)
logger.debug(f"Retrieved {len(matches_data)} completed matches from database")
return matches_data
finally:
session.close()
except Exception as e:
logger.error(f"Failed to get completed matches from database: {e}")
return []
def _get_fixture_data_from_games_thread(self) -> Optional[List[Dict[str, Any]]]: def _get_fixture_data_from_games_thread(self) -> Optional[List[Dict[str, Any]]]:
"""Get fixture data from the games thread""" """Get fixture data from the games thread"""
try: try:
......
...@@ -256,26 +256,42 @@ ...@@ -256,26 +256,42 @@
let currentMessage = 'Waiting for game to start...'; let currentMessage = 'Waiting for game to start...';
let currentIcon = '🥊'; let currentIcon = '🥊';
let licenseText = 'Loading license text...'; let licenseText = 'Loading license text...';
let licenseTextFromData = '';
// Function to update overlay data (called by Qt WebChannel) // Function to update overlay data (called by Qt WebChannel)
function updateOverlayData(data) { function updateOverlayData(data) {
console.log('Received text overlay data:', data); console.log('Received text overlay data:', data);
overlayData = data || {}; overlayData = data || {};
if (data && data.title) { let contentChanged = false;
if (data && data.title && data.title !== currentTitle) {
currentTitle = data.title; currentTitle = data.title;
contentChanged = true;
} }
if (data && data.message) { if (data && data.message && data.message !== currentMessage) {
currentMessage = data.message; currentMessage = data.message;
contentChanged = true;
} }
if (data && data.icon) { if (data && data.icon && data.icon !== currentIcon) {
currentIcon = data.icon; currentIcon = data.icon;
contentChanged = true;
}
// Handle license text from overlay data
if (data && data.license_text) {
licenseTextFromData = data.license_text;
licenseText = licenseTextFromData;
updateLicenseDisplay();
} }
// Only update message display and restart animations if content actually changed
if (contentChanged) {
updateMessageDisplay(); updateMessageDisplay();
} }
}
// Update the message display // Update the message display
function updateMessageDisplay() { function updateMessageDisplay() {
...@@ -292,21 +308,35 @@ ...@@ -292,21 +308,35 @@
restartAnimations(); restartAnimations();
} }
// Fetch license text from API // Fetch license text from WebChannel
async function fetchLicenseText() { async function fetchLicenseText() {
try { try {
// Try to fetch from the web dashboard API // Only fetch from WebChannel if we don't have license text from overlay data yet
const response = await fetch('http://127.0.0.1:5001/api/config/license-text'); if (!licenseTextFromData || licenseTextFromData.trim() === '') {
if (response.ok) { // Try to get license text from Qt WebChannel
const data = await response.json(); if (window.overlay && window.overlay.getLicenseText) {
if (data.success) { const licenseTextData = await window.overlay.getLicenseText();
licenseText = data.license_text || ''; if (licenseTextData) {
// Parse the JSON response to extract license_text
try {
const licenseData = JSON.parse(licenseTextData);
if (licenseData.license_text) {
licenseText = licenseData.license_text;
updateLicenseDisplay(); updateLicenseDisplay();
console.log('License text loaded from WebChannel:', licenseText);
} else {
console.log('No license_text found in WebChannel response');
}
} catch (parseError) {
console.log('Failed to parse license text JSON from WebChannel:', parseError);
// Keep default text if parsing fails
}
}
} }
} }
} catch (error) { } catch (error) {
console.log('Could not fetch license text from API, using default'); console.log('Could not fetch license text from WebChannel, using default');
// Keep default text if API is not available // Keep default text if WebChannel is not available
} }
} }
...@@ -347,28 +377,79 @@ ...@@ -347,28 +377,79 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
console.log('Text message overlay initialized'); console.log('Text message overlay initialized');
updateMessageDisplay(); updateMessageDisplay();
fetchLicenseText();
// Setup WebChannel communication
setupWebChannel();
}); });
// Qt WebChannel initialization (when available) // Setup WebChannel communication
if (typeof QWebChannel !== 'undefined') { function setupWebChannel() {
new QWebChannel(qt.webChannelTransport, function(channel) { // Check if WebChannel is already set up by overlay.js
console.log('WebChannel initialized for text message overlay'); if (window.overlay) {
console.log('WebChannel already set up by overlay.js');
// Connect to overlay object if available // Listen for data updates from Python
if (channel.objects.overlay) { if (window.overlay.dataUpdated) {
channel.objects.overlay.dataChanged.connect(function(data) { window.overlay.dataUpdated.connect(function(data) {
console.log('Received data update from Python:', data);
updateOverlayData(data); updateOverlayData(data);
}); });
}
// Get initial data // Wait for WebChannel to be ready before fetching license text
if (channel.objects.overlay.getCurrentData) { waitForWebChannelReady(() => {
channel.objects.overlay.getCurrentData(function(data) { fetchLicenseText(); // Fetch license text now that WebChannel is ready
updateOverlayData(data);
}); });
return;
} }
// Fallback: setup WebChannel if overlay.js didn't do it
if (typeof qt !== 'undefined' && qt.webChannelTransport) {
try {
new QWebChannel(qt.webChannelTransport, function(channel) {
console.log('WebChannel connected successfully (fallback)');
// Connect to overlay object
window.overlay = channel.objects.overlay;
// Listen for data updates from Python
if (window.overlay && window.overlay.dataUpdated) {
window.overlay.dataUpdated.connect(function(data) {
console.log('Received data update from Python (fallback):', data);
updateOverlayData(data);
});
} }
// Wait for WebChannel to be ready before fetching license text
waitForWebChannelReady(() => {
fetchLicenseText(); // Fetch license text now that WebChannel is ready
}); });
});
} catch (e) {
console.log('Failed to setup WebChannel:', e);
}
} else {
console.log('WebChannel not available, using default settings');
}
}
// Wait for WebChannel to be ready
function waitForWebChannelReady(callback, maxWait = 10000) {
const startTime = Date.now();
const checkReady = () => {
if (window.overlay && window.overlay.getLicenseText) {
console.log('WebChannel ready for license text fetch');
callback();
} else if (Date.now() - startTime > maxWait) {
console.warn('WebChannel not ready after max wait time, proceeding anyway');
callback();
} else {
setTimeout(checkReady, 100);
}
};
checkReady();
} }
// Export functions for external use // Export functions for external use
...@@ -377,7 +458,7 @@ ...@@ -377,7 +458,7 @@
</script> </script>
<!-- <!--
IMPORTANT: When creating or editing custom templates, always maintain these two script tags: IMPORTANT: When creating or editing custom templates, always maintain these script tags:
1. qrc:///qtwebchannel/qwebchannel.js - Required for Qt WebChannel communication 1. qrc:///qtwebchannel/qwebchannel.js - Required for Qt WebChannel communication
2. overlay://overlay.js - Required for overlay functionality and data updates 2. overlay://overlay.js - Required for overlay functionality and data updates
......
...@@ -143,32 +143,23 @@ ...@@ -143,32 +143,23 @@
.results-bottom-bar { .results-bottom-bar {
position: absolute; position: absolute;
bottom: 20px; bottom: 20px;
left: 50%; left: 20px;
transform: translateX(-50%); right: 20px;
background: rgba(0, 123, 255, 0.85); background: rgba(0, 123, 255, 0.85);
border-radius: 15px; border-radius: 15px;
padding: 15px 20px; padding: 15px 20px;
max-width: 90%;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
border: 2px solid rgba(255, 255, 255, 0.1); border: 2px solid rgba(255, 255, 255, 0.1);
color: white; color: white;
font-size: 24px; font-size: 24px;
text-align: left; text-align: center;
z-index: 1001; z-index: 1001;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.results-title {
font-weight: bold;
margin-bottom: 10px;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}
.match-result {
font-size: 20px;
margin: 5px 0;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}
.fixtures-title { .fixtures-title {
color: white; color: white;
...@@ -417,7 +408,6 @@ ...@@ -417,7 +408,6 @@
<!-- Results Bottom Bar --> <!-- Results Bottom Bar -->
<div class="results-bottom-bar" id="resultsBottomBar" style="display: none;"> <div class="results-bottom-bar" id="resultsBottomBar" style="display: none;">
<div class="results-title">RESULTS of the last 3 matches:</div>
<div id="resultsContent"> <div id="resultsContent">
<!-- Match results will be populated by JavaScript --> <!-- Match results will be populated by JavaScript -->
</div> </div>
...@@ -822,21 +812,32 @@ ...@@ -822,21 +812,32 @@
]; ];
} }
// Fetch license text from API // Fetch license text from WebChannel
async function fetchLicenseText() { async function fetchLicenseText() {
try { try {
// Try to fetch from the web dashboard API // Try to get license text from Qt WebChannel
const response = await fetch('http://127.0.0.1:5001/api/config/license-text'); if (window.overlay && window.overlay.getLicenseText) {
if (response.ok) { const licenseTextData = await window.overlay.getLicenseText();
const data = await response.json(); if (licenseTextData) {
if (data.success) { // Parse the JSON response to extract license_text
licenseText = data.license_text || ''; try {
const licenseData = JSON.parse(licenseTextData);
if (licenseData.license_text) {
licenseText = licenseData.license_text;
updateLicenseDisplay(); updateLicenseDisplay();
console.log('License text loaded from WebChannel:', licenseText);
} else {
console.log('No license_text found in WebChannel response');
}
} catch (parseError) {
console.log('Failed to parse license text JSON from WebChannel:', parseError);
// Keep default text if parsing fails
}
} }
} }
} catch (error) { } catch (error) {
console.log('Could not fetch license text from API, using default'); console.log('Could not fetch license text from WebChannel, using default');
// Keep default text if API is not available // Keep default text if WebChannel is not available
} }
} }
...@@ -1090,7 +1091,19 @@ ...@@ -1090,7 +1091,19 @@
try { try {
debugTime('Fetching last match results'); debugTime('Fetching last match results');
// Try to fetch from web API if webServerBaseUrl is available // Try WebChannel first (same as license text)
if (window.overlay && typeof window.overlay.getCompletedMatches === 'function') {
const completedMatchesJson = await window.overlay.getCompletedMatches();
const completedMatches = JSON.parse(completedMatchesJson);
if (completedMatches && completedMatches.length > 0) {
// Take last 3 matches (already ordered by end_time desc in WebChannel)
const lastMatches = completedMatches.slice(0, 3);
renderMatchResults(lastMatches);
return;
}
}
// Fallback: try to fetch from web API if webServerBaseUrl is available
if (webServerBaseUrl) { if (webServerBaseUrl) {
const response = await fetch(`${webServerBaseUrl}/api/fixtures`); const response = await fetch(`${webServerBaseUrl}/api/fixtures`);
if (response.ok) { if (response.ok) {
...@@ -1125,17 +1138,6 @@ ...@@ -1125,17 +1138,6 @@
} }
} }
// Fallback: try WebChannel if available
if (window.overlay && typeof window.overlay.getCompletedMatches === 'function') {
const completedMatchesJson = await window.overlay.getCompletedMatches();
const completedMatches = JSON.parse(completedMatchesJson);
if (completedMatches && completedMatches.length > 0) {
const lastMatches = completedMatches.slice(-3); // Last 3 matches
renderMatchResults(lastMatches);
return;
}
}
// No results available // No results available
debugTime('No match results available'); debugTime('No match results available');
document.getElementById('resultsBottomBar').style.display = 'none'; document.getElementById('resultsBottomBar').style.display = 'none';
...@@ -1151,9 +1153,9 @@ ...@@ -1151,9 +1153,9 @@
const resultsContent = document.getElementById('resultsContent'); const resultsContent = document.getElementById('resultsContent');
if (!resultsContent) return; if (!resultsContent) return;
let resultsHTML = ''; let resultsText = 'RESULTS: ';
matches.forEach(match => { matches.forEach((match, index) => {
const matchNumber = match.match_number || match.id || 'N/A'; const matchNumber = match.match_number || match.id || 'N/A';
const fighter1 = match.fighter1_township || match.fighter1 || 'Fighter 1'; const fighter1 = match.fighter1_township || match.fighter1 || 'Fighter 1';
const fighter2 = match.fighter2_township || match.fighter2 || 'Fighter 2'; const fighter2 = match.fighter2_township || match.fighter2 || 'Fighter 2';
...@@ -1162,10 +1164,13 @@ ...@@ -1162,10 +1164,13 @@
const resultText = underOver ? `${result} (${underOver})` : result; const resultText = underOver ? `${result} (${underOver})` : result;
resultsHTML += `<div class="match-result">Match #${matchNumber}:: ${fighter1} vs ${fighter2}: ${resultText}</div>`; resultsText += `Match #${matchNumber}: ${fighter1} vs ${fighter2} = ${resultText}`;
if (index < matches.length - 1) {
resultsText += ' | ';
}
}); });
resultsContent.innerHTML = resultsHTML; resultsContent.textContent = resultsText;
document.getElementById('resultsBottomBar').style.display = 'block'; document.getElementById('resultsBottomBar').style.display = 'block';
debugTime(`Rendered ${matches.length} match results`); debugTime(`Rendered ${matches.length} match results`);
...@@ -1179,9 +1184,6 @@ ...@@ -1179,9 +1184,6 @@
// Setup WebChannel first // Setup WebChannel first
setupWebChannel(); setupWebChannel();
// Fetch license text
fetchLicenseText();
// Show loading message initially // Show loading message initially
document.getElementById('fixturesContent').style.display = 'none'; document.getElementById('fixturesContent').style.display = 'none';
document.getElementById('noMatches').style.display = 'none'; document.getElementById('noMatches').style.display = 'none';
...@@ -1190,9 +1192,12 @@ ...@@ -1190,9 +1192,12 @@
debugTime('UI initialized - Loading message displayed'); debugTime('UI initialized - Loading message displayed');
// Wait briefly for WebChannel to connect // Wait for WebChannel to be ready before fetching data
setTimeout(() => { waitForWebChannelReady(() => {
console.log('🔍 DEBUG: Starting fixture data fetch via WebChannel'); console.log('🔍 DEBUG: WebChannel ready, starting data fetch');
// Fetch license text now that WebChannel is ready
fetchLicenseText();
// Fetch fixtures data via WebChannel // Fetch fixtures data via WebChannel
debugTime('Fetching fixtures data via WebChannel'); debugTime('Fetching fixtures data via WebChannel');
...@@ -1213,8 +1218,27 @@ ...@@ -1213,8 +1218,27 @@
debugTime('Data was received before 5 second timeout'); debugTime('Data was received before 5 second timeout');
} }
}, 5000); }, 5000);
}, 50); // Wait 50ms for WebChannel setup
}); });
});
// Wait for WebChannel to be ready
function waitForWebChannelReady(callback, maxWait = 10000) {
const startTime = Date.now();
const checkReady = () => {
if (window.overlay && window.overlay.getLicenseText) {
console.log('🔍 DEBUG: WebChannel ready for license text fetch');
callback();
} else if (Date.now() - startTime > maxWait) {
console.warn('🔍 DEBUG: WebChannel not ready after max wait time, proceeding anyway');
callback();
} else {
setTimeout(checkReady, 100);
}
};
checkReady();
}
</script> </script>
......
...@@ -32,6 +32,31 @@ ...@@ -32,6 +32,31 @@
z-index: 9999; z-index: 9999;
} }
/* License Top Bar */
.license-bar {
position: absolute;
top: 0;
left: 0;
right: 0;
background: rgba(0, 123, 255, 0.85);
border-radius: 0 0 20px 20px;
padding: 15px 30px;
text-align: center;
z-index: 1000;
backdrop-filter: blur(10px);
border: 2px solid rgba(255, 255, 255, 0.1);
border-top: none;
}
.license-text {
color: rgba(255, 255, 255, 0.95);
font-size: 16px;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.6);
line-height: 1.4;
word-wrap: break-word;
}
/* Debug console overlay */ /* Debug console overlay */
#debugConsole { #debugConsole {
position: absolute; position: absolute;
...@@ -118,32 +143,23 @@ ...@@ -118,32 +143,23 @@
.results-bottom-bar { .results-bottom-bar {
position: absolute; position: absolute;
bottom: 20px; bottom: 20px;
left: 50%; left: 20px;
transform: translateX(-50%); right: 20px;
background: rgba(0, 123, 255, 0.85); background: rgba(0, 123, 255, 0.85);
border-radius: 15px; border-radius: 15px;
padding: 15px 20px; padding: 15px 20px;
max-width: 90%;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
border: 2px solid rgba(255, 255, 255, 0.1); border: 2px solid rgba(255, 255, 255, 0.1);
color: white; color: white;
font-size: 24px; font-size: 24px;
text-align: left; text-align: center;
z-index: 1001; z-index: 1001;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.results-title {
font-weight: bold;
margin-bottom: 10px;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}
.match-result {
font-size: 20px;
margin: 5px 0;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
}
.match-title { .match-title {
font-size: 48px; font-size: 48px;
...@@ -457,6 +473,11 @@ ...@@ -457,6 +473,11 @@
<div id="consoleOutput"></div> <div id="consoleOutput"></div>
</div> </div>
<!-- License Top Bar -->
<div class="license-bar" id="licenseBar">
<div class="license-text" id="licenseText">Loading license text...</div>
</div>
<div class="overlay-container"> <div class="overlay-container">
<div class="fixtures-panel" id="fixturesPanel"> <div class="fixtures-panel" id="fixturesPanel">
<!-- Title --> <!-- Title -->
...@@ -493,7 +514,6 @@ ...@@ -493,7 +514,6 @@
<!-- Results Bottom Bar --> <!-- Results Bottom Bar -->
<div class="results-bottom-bar" id="resultsBottomBar" style="display: none;"> <div class="results-bottom-bar" id="resultsBottomBar" style="display: none;">
<div class="results-title">RESULTS of the last 3 matches:</div>
<div id="resultsContent"> <div id="resultsContent">
<!-- Match results will be populated by JavaScript --> <!-- Match results will be populated by JavaScript -->
</div> </div>
...@@ -508,6 +528,7 @@ ...@@ -508,6 +528,7 @@
let countdownInterval = null; let countdownInterval = null;
let nextMatchStartTime = null; let nextMatchStartTime = null;
let startTime = null; let startTime = null;
let licenseText = 'Loading license text...';
// Apply console.log override immediately with buffering // Apply console.log override immediately with buffering
(function() { (function() {
...@@ -947,6 +968,50 @@ ...@@ -947,6 +968,50 @@
]; ];
} }
// Fetch license text from WebChannel
async function fetchLicenseText() {
try {
// Try to get license text from Qt WebChannel
if (window.overlay && window.overlay.getLicenseText) {
const licenseTextData = await window.overlay.getLicenseText();
if (licenseTextData) {
// Parse the JSON response to extract license_text
try {
const licenseData = JSON.parse(licenseTextData);
if (licenseData.license_text) {
licenseText = licenseData.license_text;
updateLicenseDisplay();
console.log('License text loaded from WebChannel:', licenseText);
} else {
console.log('No license_text found in WebChannel response');
}
} catch (parseError) {
console.log('Failed to parse license text JSON from WebChannel:', parseError);
// Keep default text if parsing fails
}
}
}
} catch (error) {
console.log('Could not fetch license text from WebChannel, using default');
// Keep default text if WebChannel is not available
}
}
// Update license text display
function updateLicenseDisplay() {
const licenseBar = document.getElementById('licenseBar');
const licenseElement = document.getElementById('licenseText');
if (licenseElement && licenseBar) {
if (licenseText && licenseText.trim() !== '') {
licenseElement.textContent = licenseText;
licenseBar.style.display = 'block';
} else {
licenseBar.style.display = 'none';
}
}
}
// Get timer state and start countdown (same as fixtures.html) // Get timer state and start countdown (same as fixtures.html)
async function getTimerStateAndStartCountdown() { async function getTimerStateAndStartCountdown() {
console.log('🔍 DEBUG: getTimerStateAndStartCountdown called'); console.log('🔍 DEBUG: getTimerStateAndStartCountdown called');
...@@ -1042,10 +1107,7 @@ ...@@ -1042,10 +1107,7 @@
const loadingMessage = document.getElementById('loadingMessage'); const loadingMessage = document.getElementById('loadingMessage');
const matchContent = document.getElementById('matchContent'); const matchContent = document.getElementById('matchContent');
const noMatches = document.getElementById('noMatches'); const noMatches = document.getElementById('noMatches');
const matchTitle = document.getElementById('matchTitle');
const matchVenue = document.getElementById('matchVenue'); const matchVenue = document.getElementById('matchVenue');
const outcomesHeader = document.getElementById('outcomesHeader');
const outcomesBody = document.getElementById('outcomesBody');
loadingMessage.style.display = 'none'; loadingMessage.style.display = 'none';
noMatches.style.display = 'none'; noMatches.style.display = 'none';
...@@ -1074,6 +1136,11 @@ ...@@ -1074,6 +1136,11 @@
console.log('DEBUG: nextMatch.fighter2_township =', nextMatch.fighter2_township); console.log('DEBUG: nextMatch.fighter2_township =', nextMatch.fighter2_township);
console.log('DEBUG: nextMatch.venue_kampala_township =', nextMatch.venue_kampala_township); console.log('DEBUG: nextMatch.venue_kampala_township =', nextMatch.venue_kampala_township);
// Update match title with match number
const matchTitle = document.querySelector('.match-title');
const matchNumber = nextMatch.match_number || nextMatch.id || 'N/A';
if (matchTitle) matchTitle.textContent = `Next Match: #${matchNumber}`;
// Update fighter names in top left (multi-line layout) // Update fighter names in top left (multi-line layout)
const fighter1 = nextMatch.fighter1_township || nextMatch.fighter1 || 'Fighter 1'; const fighter1 = nextMatch.fighter1_township || nextMatch.fighter1 || 'Fighter 1';
const fighter2 = nextMatch.fighter2_township || nextMatch.fighter2 || 'Fighter 2'; const fighter2 = nextMatch.fighter2_township || nextMatch.fighter2 || 'Fighter 2';
...@@ -1186,7 +1253,19 @@ ...@@ -1186,7 +1253,19 @@
try { try {
debugTime('Fetching last match results'); debugTime('Fetching last match results');
// Try to fetch from web API if webServerBaseUrl is available // Try WebChannel first (same as license text)
if (window.overlay && typeof window.overlay.getCompletedMatches === 'function') {
const completedMatchesJson = await window.overlay.getCompletedMatches();
const completedMatches = JSON.parse(completedMatchesJson);
if (completedMatches && completedMatches.length > 0) {
// Take last 3 matches (already ordered by end_time desc in WebChannel)
const lastMatches = completedMatches.slice(0, 3);
renderMatchResults(lastMatches);
return;
}
}
// Fallback: try to fetch from web API if webServerBaseUrl is available
if (webServerBaseUrl) { if (webServerBaseUrl) {
const response = await fetch(`${webServerBaseUrl}/api/fixtures`); const response = await fetch(`${webServerBaseUrl}/api/fixtures`);
if (response.ok) { if (response.ok) {
...@@ -1221,17 +1300,6 @@ ...@@ -1221,17 +1300,6 @@
} }
} }
// Fallback: try WebChannel if available
if (window.overlay && typeof window.overlay.getCompletedMatches === 'function') {
const completedMatchesJson = await window.overlay.getCompletedMatches();
const completedMatches = JSON.parse(completedMatchesJson);
if (completedMatches && completedMatches.length > 0) {
const lastMatches = completedMatches.slice(-3); // Last 3 matches
renderMatchResults(lastMatches);
return;
}
}
// No results available // No results available
debugTime('No match results available'); debugTime('No match results available');
document.getElementById('resultsBottomBar').style.display = 'none'; document.getElementById('resultsBottomBar').style.display = 'none';
...@@ -1247,9 +1315,9 @@ ...@@ -1247,9 +1315,9 @@
const resultsContent = document.getElementById('resultsContent'); const resultsContent = document.getElementById('resultsContent');
if (!resultsContent) return; if (!resultsContent) return;
let resultsHTML = ''; let resultsText = 'RESULTS: ';
matches.forEach(match => { matches.forEach((match, index) => {
const matchNumber = match.match_number || match.id || 'N/A'; const matchNumber = match.match_number || match.id || 'N/A';
const fighter1 = match.fighter1_township || match.fighter1 || 'Fighter 1'; const fighter1 = match.fighter1_township || match.fighter1 || 'Fighter 1';
const fighter2 = match.fighter2_township || match.fighter2 || 'Fighter 2'; const fighter2 = match.fighter2_township || match.fighter2 || 'Fighter 2';
...@@ -1258,10 +1326,13 @@ ...@@ -1258,10 +1326,13 @@
const resultText = underOver ? `${result} (${underOver})` : result; const resultText = underOver ? `${result} (${underOver})` : result;
resultsHTML += `<div class="match-result">Match #${matchNumber}:: ${fighter1} vs ${fighter2}: ${resultText}</div>`; resultsText += `Match #${matchNumber}: ${fighter1} vs ${fighter2} = ${resultText}`;
if (index < matches.length - 1) {
resultsText += ' | ';
}
}); });
resultsContent.innerHTML = resultsHTML; resultsContent.textContent = resultsText;
document.getElementById('resultsBottomBar').style.display = 'block'; document.getElementById('resultsBottomBar').style.display = 'block';
debugTime(`Rendered ${matches.length} match results`); debugTime(`Rendered ${matches.length} match results`);
...@@ -1289,6 +1360,9 @@ ...@@ -1289,6 +1360,9 @@
console.log('🔍 DEBUG: WebServerBaseUrl not received via WebChannel, proceeding with WebChannel data fetch'); console.log('🔍 DEBUG: WebServerBaseUrl not received via WebChannel, proceeding with WebChannel data fetch');
} }
// Fetch license text now that WebChannel is ready
fetchLicenseText();
// Check for timer state immediately when page loads // Check for timer state immediately when page loads
debugTime('Checking timer state on page load'); debugTime('Checking timer state on page load');
getTimerStateAndStartCountdown(); getTimerStateAndStartCountdown();
......
...@@ -19,6 +19,31 @@ ...@@ -19,6 +19,31 @@
position: relative; position: relative;
} }
/* License Top Bar */
.license-bar {
position: absolute;
top: 0;
left: 0;
right: 0;
background: rgba(0, 123, 255, 0.40);
border-radius: 0 0 20px 20px;
padding: 15px 30px;
text-align: center;
z-index: 1000;
backdrop-filter: blur(10px);
border: 2px solid rgba(255, 255, 255, 0.2);
border-top: none;
}
.license-text {
color: rgba(255, 255, 255, 0.95);
font-size: 16px;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.6);
line-height: 1.4;
word-wrap: break-word;
}
/* Debug indicator to verify CSS is loaded */ /* Debug indicator to verify CSS is loaded */
body::before { body::before {
content: 'Text Message Overlay v1.0 loaded'; content: 'Text Message Overlay v1.0 loaded';
...@@ -211,6 +236,11 @@ ...@@ -211,6 +236,11 @@
</style> </style>
</head> </head>
<body> <body>
<!-- License Top Bar -->
<div class="license-bar" id="licenseBar">
<div class="license-text" id="licenseText">Loading license text...</div>
</div>
<div class="overlay-container"> <div class="overlay-container">
<div class="message-panel" id="messagePanel"> <div class="message-panel" id="messagePanel">
<div class="message-icon" id="messageIcon">📢</div> <div class="message-icon" id="messageIcon">📢</div>
...@@ -225,26 +255,90 @@ ...@@ -225,26 +255,90 @@
let currentTitle = 'Announcement'; let currentTitle = 'Announcement';
let currentMessage = 'This is a custom message from the system.'; let currentMessage = 'This is a custom message from the system.';
let currentIcon = '📢'; let currentIcon = '📢';
let licenseText = 'Loading license text...';
let licenseTextFromData = '';
// Function to update overlay data (called by Qt WebChannel) // Function to update overlay data (called by Qt WebChannel)
function updateOverlayData(data) { function updateOverlayData(data) {
console.log('Received text overlay data:', data); console.log('Received text overlay data:', data);
overlayData = data || {}; overlayData = data || {};
if (data && data.title) { let contentChanged = false;
if (data && data.title && data.title !== currentTitle) {
currentTitle = data.title; currentTitle = data.title;
contentChanged = true;
} }
if (data && data.message) { if (data && data.message && data.message !== currentMessage) {
currentMessage = data.message; currentMessage = data.message;
contentChanged = true;
} }
if (data && data.icon) { if (data && data.icon && data.icon !== currentIcon) {
currentIcon = data.icon; currentIcon = data.icon;
contentChanged = true;
}
// Handle license text from overlay data
if (data && data.license_text) {
licenseTextFromData = data.license_text;
licenseText = licenseTextFromData;
updateLicenseDisplay();
} }
// Only update message display and restart animations if content actually changed
if (contentChanged) {
updateMessageDisplay(); updateMessageDisplay();
} }
}
// Fetch license text from WebChannel
async function fetchLicenseText() {
try {
// Only fetch from WebChannel if we don't have license text from overlay data yet
if (!licenseTextFromData || licenseTextFromData.trim() === '') {
// Try to get license text from Qt WebChannel
if (window.overlay && window.overlay.getLicenseText) {
const licenseTextData = await window.overlay.getLicenseText();
if (licenseTextData) {
// Parse the JSON response to extract license_text
try {
const licenseData = JSON.parse(licenseTextData);
if (licenseData.license_text) {
licenseText = licenseData.license_text;
updateLicenseDisplay();
console.log('License text loaded from WebChannel:', licenseText);
} else {
console.log('No license_text found in WebChannel response');
}
} catch (parseError) {
console.log('Failed to parse license text JSON from WebChannel:', parseError);
// Keep default text if parsing fails
}
}
}
}
} catch (error) {
console.log('Could not fetch license text from WebChannel, using default');
// Keep default text if WebChannel is not available
}
}
// Update license text display
function updateLicenseDisplay() {
const licenseBar = document.getElementById('licenseBar');
const licenseElement = document.getElementById('licenseText');
if (licenseElement && licenseBar) {
if (licenseText && licenseText.trim() !== '') {
licenseElement.textContent = licenseText;
licenseBar.style.display = 'block';
} else {
licenseBar.style.display = 'none';
}
}
}
// Update the message display // Update the message display
function updateMessageDisplay() { function updateMessageDisplay() {
...@@ -283,27 +377,79 @@ ...@@ -283,27 +377,79 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
console.log('Text message overlay initialized'); console.log('Text message overlay initialized');
updateMessageDisplay(); updateMessageDisplay();
// Setup WebChannel communication
setupWebChannel();
}); });
// Qt WebChannel initialization (when available) // Setup WebChannel communication
if (typeof QWebChannel !== 'undefined') { function setupWebChannel() {
new QWebChannel(qt.webChannelTransport, function(channel) { // Check if WebChannel is already set up by overlay.js
console.log('WebChannel initialized for text message overlay'); if (window.overlay) {
console.log('WebChannel already set up by overlay.js');
// Connect to overlay object if available // Listen for data updates from Python
if (channel.objects.overlay) { if (window.overlay.dataUpdated) {
channel.objects.overlay.dataChanged.connect(function(data) { window.overlay.dataUpdated.connect(function(data) {
console.log('Received data update from Python:', data);
updateOverlayData(data); updateOverlayData(data);
}); });
}
// Get initial data // Wait for WebChannel to be ready before fetching license text
if (channel.objects.overlay.getCurrentData) { waitForWebChannelReady(() => {
channel.objects.overlay.getCurrentData(function(data) { fetchLicenseText(); // Fetch license text now that WebChannel is ready
updateOverlayData(data);
}); });
return;
} }
// Fallback: setup WebChannel if overlay.js didn't do it
if (typeof qt !== 'undefined' && qt.webChannelTransport) {
try {
new QWebChannel(qt.webChannelTransport, function(channel) {
console.log('WebChannel connected successfully (fallback)');
// Connect to overlay object
window.overlay = channel.objects.overlay;
// Listen for data updates from Python
if (window.overlay && window.overlay.dataUpdated) {
window.overlay.dataUpdated.connect(function(data) {
console.log('Received data update from Python (fallback):', data);
updateOverlayData(data);
});
} }
// Wait for WebChannel to be ready before fetching license text
waitForWebChannelReady(() => {
fetchLicenseText(); // Fetch license text now that WebChannel is ready
});
}); });
} catch (e) {
console.log('Failed to setup WebChannel:', e);
}
} else {
console.log('WebChannel not available, using default settings');
}
}
// Wait for WebChannel to be ready
function waitForWebChannelReady(callback, maxWait = 10000) {
const startTime = Date.now();
const checkReady = () => {
if (window.overlay && window.overlay.getLicenseText) {
console.log('WebChannel ready for license text fetch');
callback();
} else if (Date.now() - startTime > maxWait) {
console.warn('WebChannel not ready after max wait time, proceeding anyway');
callback();
} else {
setTimeout(checkReady, 100);
}
};
checkReady();
} }
// Export functions for external use // Export functions for external use
...@@ -312,7 +458,7 @@ ...@@ -312,7 +458,7 @@
</script> </script>
<!-- <!--
IMPORTANT: When creating or editing custom templates, always maintain these two script tags: IMPORTANT: When creating or editing custom templates, always maintain these script tags:
1. qrc:///qtwebchannel/qwebchannel.js - Required for Qt WebChannel communication 1. qrc:///qtwebchannel/qwebchannel.js - Required for Qt WebChannel communication
2. overlay://overlay.js - Required for overlay functionality and data updates 2. overlay://overlay.js - Required for overlay functionality and data updates
......
#!/usr/bin/env python3
"""
Test script to check API endpoints for debugging fixtures overlay
"""
import requests
import json
import sys
from pathlib import Path
def test_fixtures_api():
"""Test the fixtures API endpoint"""
print("Testing fixtures API endpoint...")
# Try different possible URLs
urls_to_test = [
"http://127.0.0.1:5001/api/fixtures",
"http://localhost:5001/api/fixtures",
"http://127.0.0.1:5000/api/fixtures",
"http://localhost:5000/api/fixtures"
]
for url in urls_to_test:
print(f"\nTrying URL: {url}")
try:
response = requests.get(url, timeout=5)
print(f"Status Code: {response.status_code}")
if response.status_code == 200:
try:
data = response.json()
print("Response data:")
print(json.dumps(data, indent=2))
if 'success' in data and data['success']:
print(f"✅ SUCCESS: Found {data.get('total', 0)} fixtures")
return True
else:
print("❌ API returned success=false")
return False
except json.JSONDecodeError as e:
print(f"❌ Invalid JSON response: {e}")
print(f"Raw response: {response.text[:500]}...")
return False
else:
print(f"❌ HTTP Error: {response.status_code}")
print(f"Response: {response.text[:200]}...")
except requests.exceptions.ConnectionError:
print("❌ Connection refused - server not running")
except requests.exceptions.Timeout:
print("❌ Request timed out")
except Exception as e:
print(f"❌ Error: {e}")
print("\n❌ All URLs failed - API server may not be running")
return False
def test_web_dashboard_status():
"""Test if web dashboard is running"""
print("\nTesting web dashboard status...")
urls_to_test = [
"http://127.0.0.1:5001/",
"http://localhost:5001/",
"http://127.0.0.1:5000/",
"http://localhost:5000/"
]
for url in urls_to_test:
print(f"Trying URL: {url}")
try:
response = requests.get(url, timeout=5)
print(f"Status Code: {response.status_code}")
if response.status_code == 200:
print("✅ Web dashboard is running")
return True
except Exception as e:
print(f"❌ Error: {e}")
print("❌ Web dashboard not accessible")
return False
def main():
print("=== MbetterClient API Test ===")
# Test web dashboard first
web_running = test_web_dashboard_status()
if not web_running:
print("\n⚠️ Web dashboard is not running. Make sure to start the web server first.")
print("Run: python main.py --web-only")
sys.exit(1)
# Test fixtures API
api_working = test_fixtures_api()
if api_working:
print("\n✅ API test completed successfully")
else:
print("\n❌ API test failed")
print("\nTroubleshooting tips:")
print("1. Make sure the web server is running")
print("2. Check if the database has fixture data")
print("3. Check the web server logs for errors")
print("4. Verify the fixtures endpoint is properly configured")
if __name__ == "__main__":
main()
\ No newline at end of file
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