Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
MBetterc
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Mbetter
MBetterc
Commits
e8a91c81
Commit
e8a91c81
authored
Nov 22, 2025
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
All working up to match template...
parent
7b742d32
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
56 additions
and
21 deletions
+56
-21
games_thread.py
mbetterclient/core/games_thread.py
+28
-7
migrations.py
mbetterclient/database/migrations.py
+1
-1
player.py
mbetterclient/qt_player/player.py
+25
-11
routes.py
mbetterclient/web_dashboard/routes.py
+2
-2
No files found.
mbetterclient/core/games_thread.py
View file @
e8a91c81
...
@@ -1232,17 +1232,21 @@ class GamesThread(ThreadedComponent):
...
@@ -1232,17 +1232,21 @@ class GamesThread(ThreadedComponent):
import
os
import
os
from
pathlib
import
Path
from
pathlib
import
Path
logger
.
info
(
f
"DEBUG: Starting ZIP extraction for match {match_id}"
)
session
=
self
.
db_manager
.
get_session
()
session
=
self
.
db_manager
.
get_session
()
try
:
try
:
# Get the match from database
# Get the match from database
match
=
session
.
query
(
MatchModel
)
.
filter_by
(
id
=
match_id
)
.
first
()
match
=
session
.
query
(
MatchModel
)
.
filter_by
(
id
=
match_id
)
.
first
()
if
not
match
:
if
not
match
:
logger
.
warning
(
f
"
Match {match_id} not found
, skipping ZIP extraction"
)
logger
.
warning
(
f
"
DEBUG: Match {match_id} not found in database
, skipping ZIP extraction"
)
return
return
logger
.
info
(
f
"DEBUG: Found match {match_id}, zip_filename: {match.zip_filename}"
)
if
not
match
.
zip_filename
:
if
not
match
.
zip_filename
:
logger
.
info
(
f
"Match {match_id} has no associated ZIP file, skipping extraction"
)
logger
.
info
(
f
"
DEBUG:
Match {match_id} has no associated ZIP file, skipping extraction"
)
return
return
# Determine ZIP file location (ZIP files are stored in the zip_files directory)
# Determine ZIP file location (ZIP files are stored in the zip_files directory)
...
@@ -1250,21 +1254,32 @@ class GamesThread(ThreadedComponent):
...
@@ -1250,21 +1254,32 @@ class GamesThread(ThreadedComponent):
user_data_dir
=
get_user_data_dir
()
user_data_dir
=
get_user_data_dir
()
zip_file_path
=
user_data_dir
/
"zip_files"
/
match
.
zip_filename
zip_file_path
=
user_data_dir
/
"zip_files"
/
match
.
zip_filename
logger
.
info
(
f
"DEBUG: Looking for ZIP file at: {zip_file_path}"
)
logger
.
info
(
f
"DEBUG: ZIP file exists: {zip_file_path.exists()}"
)
if
not
zip_file_path
.
exists
():
if
not
zip_file_path
.
exists
():
logger
.
warning
(
f
"ZIP file not found: {zip_file_path}"
)
logger
.
warning
(
f
"
DEBUG:
ZIP file not found: {zip_file_path}"
)
return
return
logger
.
info
(
f
"DEBUG: ZIP file size: {zip_file_path.stat().st_size} bytes"
)
# Create temporary directory for extraction
# Create temporary directory for extraction
temp_dir
=
Path
(
tempfile
.
mkdtemp
(
prefix
=
f
"match_{match_id}_"
))
temp_dir
=
Path
(
tempfile
.
mkdtemp
(
prefix
=
f
"match_{match_id}_"
))
logger
.
info
(
f
"
Extracting ZIP file {zip_file_path} to temporary
directory: {temp_dir}"
)
logger
.
info
(
f
"
DEBUG: Created temp
directory: {temp_dir}"
)
# Extract the ZIP file
# Extract the ZIP file
logger
.
info
(
f
"DEBUG: Starting ZIP extraction..."
)
with
zipfile
.
ZipFile
(
str
(
zip_file_path
),
'r'
)
as
zip_ref
:
with
zipfile
.
ZipFile
(
str
(
zip_file_path
),
'r'
)
as
zip_ref
:
file_list
=
zip_ref
.
namelist
()
logger
.
info
(
f
"DEBUG: ZIP contains {len(file_list)} files: {file_list}"
)
zip_ref
.
extractall
(
str
(
temp_dir
))
zip_ref
.
extractall
(
str
(
temp_dir
))
# Log extraction results
# Log extraction results
extracted_files
=
list
(
temp_dir
.
rglob
(
"*"
))
extracted_files
=
list
(
temp_dir
.
rglob
(
"*"
))
logger
.
info
(
f
"Successfully extracted {len(extracted_files)} files from {match.zip_filename}"
)
logger
.
info
(
f
"DEBUG: Successfully extracted {len(extracted_files)} files from {match.zip_filename}"
)
for
extracted_file
in
extracted_files
:
if
extracted_file
.
is_file
():
logger
.
info
(
f
"DEBUG: Extracted file: {extracted_file} (size: {extracted_file.stat().st_size} bytes)"
)
# Store the temporary directory path for potential cleanup
# Store the temporary directory path for potential cleanup
# In a real implementation, you might want to track this for cleanup
# In a real implementation, you might want to track this for cleanup
...
@@ -1273,13 +1288,15 @@ class GamesThread(ThreadedComponent):
...
@@ -1273,13 +1288,15 @@ class GamesThread(ThreadedComponent):
# Update match in database with temp path (optional)
# Update match in database with temp path (optional)
session
.
commit
()
session
.
commit
()
logger
.
info
(
f
"ZIP extraction completed for match {match_id}"
)
logger
.
info
(
f
"
DEBUG:
ZIP extraction completed for match {match_id}"
)
finally
:
finally
:
session
.
close
()
session
.
close
()
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
error
(
f
"Failed to unzip ZIP file for match {match_id}: {e}"
)
logger
.
error
(
f
"DEBUG: Failed to unzip ZIP file for match {match_id}: {e}"
)
import
traceback
logger
.
error
(
f
"DEBUG: Full traceback: {traceback.format_exc()}"
)
def
_calculate_match_result
(
self
,
fixture_id
:
str
,
match_id
:
int
)
->
str
:
def
_calculate_match_result
(
self
,
fixture_id
:
str
,
match_id
:
int
)
->
str
:
"""Calculate match result based on betting patterns and CAP logic"""
"""Calculate match result based on betting patterns and CAP logic"""
...
@@ -1539,6 +1556,10 @@ class GamesThread(ThreadedComponent):
...
@@ -1539,6 +1556,10 @@ class GamesThread(ThreadedComponent):
# Get video filename based on result
# Get video filename based on result
video_filename
=
self
.
_get_match_video_filename
(
match_id
,
result
)
video_filename
=
self
.
_get_match_video_filename
(
match_id
,
result
)
# Unzip the match ZIP file before sending PLAY_VIDEO_MATCH
logger
.
info
(
f
"Unzipping ZIP file for match {match_id} before sending PLAY_VIDEO_MATCH"
)
self
.
_unzip_match_zip_file
(
match_id
)
# Send PLAY_VIDEO_MATCH message with result
# Send PLAY_VIDEO_MATCH message with result
play_message
=
MessageBuilder
.
play_video_match
(
play_message
=
MessageBuilder
.
play_video_match
(
sender
=
self
.
name
,
sender
=
self
.
name
,
...
...
mbetterclient/database/migrations.py
View file @
e8a91c81
...
@@ -2033,7 +2033,7 @@ class Migration_027_AddDefaultIntroTemplatesConfig(DatabaseMigration):
...
@@ -2033,7 +2033,7 @@ class Migration_027_AddDefaultIntroTemplatesConfig(DatabaseMigration):
}
}
],
],
"default_show_time"
:
"00:15"
,
"default_show_time"
:
"00:15"
,
"rotating_time"
:
"0
5:00
"
"rotating_time"
:
"0
0:15
"
}
}
import
json
import
json
...
...
mbetterclient/qt_player/player.py
View file @
e8a91c81
...
@@ -1634,7 +1634,7 @@ class PlayerWindow(QMainWindow):
...
@@ -1634,7 +1634,7 @@ class PlayerWindow(QMainWindow):
# Template rotation state
# Template rotation state
self
.
template_sequence
=
[]
self
.
template_sequence
=
[]
self
.
rotating_time
=
'0
5:00
'
self
.
rotating_time
=
'0
0:15
'
self
.
current_template_index
=
0
self
.
current_template_index
=
0
self
.
template_rotation_timer
=
None
self
.
template_rotation_timer
=
None
...
@@ -1669,8 +1669,8 @@ class PlayerWindow(QMainWindow):
...
@@ -1669,8 +1669,8 @@ class PlayerWindow(QMainWindow):
minutes
,
seconds
=
map
(
int
,
self
.
rotating_time
.
split
(
':'
))
minutes
,
seconds
=
map
(
int
,
self
.
rotating_time
.
split
(
':'
))
rotation_interval_ms
=
(
minutes
*
60
+
seconds
)
*
1000
# Convert to milliseconds
rotation_interval_ms
=
(
minutes
*
60
+
seconds
)
*
1000
# Convert to milliseconds
except
(
ValueError
,
AttributeError
):
except
(
ValueError
,
AttributeError
):
logger
.
warning
(
f
"Invalid rotating_time format '{self.rotating_time}', using default
5 minute
s"
)
logger
.
warning
(
f
"Invalid rotating_time format '{self.rotating_time}', using default
15 second
s"
)
rotation_interval_ms
=
5
*
60
*
1000
# Default 5 minute
s
rotation_interval_ms
=
15
*
1000
# Default 15 second
s
logger
.
info
(
f
"Starting template rotation every {rotation_interval_ms}ms ({self.rotating_time})"
)
logger
.
info
(
f
"Starting template rotation every {rotation_interval_ms}ms ({self.rotating_time})"
)
...
@@ -1746,7 +1746,7 @@ class PlayerWindow(QMainWindow):
...
@@ -1746,7 +1746,7 @@ class PlayerWindow(QMainWindow):
# Handle template rotation for intro videos
# Handle template rotation for intro videos
self
.
template_sequence
=
loop_data
.
get
(
'template_sequence'
,
[])
self
.
template_sequence
=
loop_data
.
get
(
'template_sequence'
,
[])
self
.
rotating_time
=
loop_data
.
get
(
'rotating_time'
,
'0
5:00
'
)
self
.
rotating_time
=
loop_data
.
get
(
'rotating_time'
,
'0
0:15
'
)
self
.
current_template_index
=
0
self
.
current_template_index
=
0
if
self
.
template_sequence
:
if
self
.
template_sequence
:
...
@@ -1766,7 +1766,7 @@ class PlayerWindow(QMainWindow):
...
@@ -1766,7 +1766,7 @@ class PlayerWindow(QMainWindow):
self
.
loop_count
=
0
self
.
loop_count
=
0
self
.
current_loop_iteration
=
0
self
.
current_loop_iteration
=
0
self
.
template_sequence
=
[]
self
.
template_sequence
=
[]
self
.
rotating_time
=
'0
5:00
'
self
.
rotating_time
=
'0
0:15
'
self
.
current_template_index
=
0
self
.
current_template_index
=
0
with
QMutexLocker
(
self
.
mutex
):
with
QMutexLocker
(
self
.
mutex
):
...
@@ -3301,7 +3301,7 @@ class QtVideoPlayer(QObject):
...
@@ -3301,7 +3301,7 @@ class QtVideoPlayer(QObject):
'infinite_loop'
:
True
,
# Loop indefinitely until PLAY_VIDEO_MATCH
'infinite_loop'
:
True
,
# Loop indefinitely until PLAY_VIDEO_MATCH
'continuous_playback'
:
True
,
'continuous_playback'
:
True
,
'template_sequence'
:
template_sequence
,
'template_sequence'
:
template_sequence
,
'rotating_time'
:
intro_templates
.
get
(
'rotating_time'
,
'0
5:00
'
)
'rotating_time'
:
intro_templates
.
get
(
'rotating_time'
,
'0
0:15
'
)
}
}
# Start with first template in sequence
# Start with first template in sequence
...
@@ -3513,7 +3513,7 @@ class QtVideoPlayer(QObject):
...
@@ -3513,7 +3513,7 @@ class QtVideoPlayer(QObject):
return
{
return
{
'templates'
:
[],
'templates'
:
[],
'default_show_time'
:
'00:30'
,
'default_show_time'
:
'00:30'
,
'rotating_time'
:
'0
5:00
'
'rotating_time'
:
'0
0:15
'
}
}
def
_find_intro_video_file
(
self
,
match_id
:
int
)
->
Optional
[
Path
]:
def
_find_intro_video_file
(
self
,
match_id
:
int
)
->
Optional
[
Path
]:
...
@@ -3576,15 +3576,29 @@ class QtVideoPlayer(QObject):
...
@@ -3576,15 +3576,29 @@ class QtVideoPlayer(QObject):
# Look for temp directories created by _unzip_match_zip_file
# Look for temp directories created by _unzip_match_zip_file
temp_base
=
Path
(
tempfile
.
gettempdir
())
temp_base
=
Path
(
tempfile
.
gettempdir
())
temp_dir_pattern
=
f
"match_{match_id}_"
temp_dir_pattern
=
f
"match_{match_id}_"
logger
.
debug
(
f
"DEBUG: Looking for temp directories with pattern '{temp_dir_pattern}' in {temp_base}"
)
for
temp_dir
in
temp_base
.
glob
(
f
"{temp_dir_pattern}*"
):
matching_dirs
=
list
(
temp_base
.
glob
(
f
"{temp_dir_pattern}*"
))
logger
.
debug
(
f
"DEBUG: Found {len(matching_dirs)} directories matching pattern"
)
for
temp_dir
in
matching_dirs
:
if
temp_dir
.
is_dir
():
if
temp_dir
.
is_dir
():
logger
.
debug
(
f
"DEBUG: Checking directory: {temp_dir}"
)
video_file
=
temp_dir
/
video_filename
video_file
=
temp_dir
/
video_filename
logger
.
debug
(
f
"DEBUG: Looking for video file: {video_file}"
)
if
video_file
.
exists
():
if
video_file
.
exists
():
logger
.
info
(
f
"Found match video: {video_file}"
)
logger
.
info
(
f
"Found match video: {video_file}
(size: {video_file.stat().st_size} bytes)
"
)
return
video_file
return
video_file
else
:
logger
.
debug
(
f
"DEBUG: Video file not found in this directory"
)
logger
.
warning
(
f
"Match video not found: {video_filename} for match {match_id}"
)
logger
.
warning
(
f
"Match video not found: {video_filename} for match {match_id}"
)
logger
.
warning
(
f
"DEBUG: Searched in {len(matching_dirs)} directories:"
)
for
temp_dir
in
matching_dirs
:
logger
.
warning
(
f
" - {temp_dir}"
)
if
temp_dir
.
exists
():
contents
=
list
(
temp_dir
.
iterdir
())
if
temp_dir
.
is_dir
()
else
[]
logger
.
warning
(
f
" Contents: {[str(f) for f in contents]}"
)
return
None
return
None
except
Exception
as
e
:
except
Exception
as
e
:
...
@@ -3603,8 +3617,8 @@ class QtVideoPlayer(QObject):
...
@@ -3603,8 +3617,8 @@ class QtVideoPlayer(QObject):
minutes
,
seconds
=
map
(
int
,
self
.
rotating_time
.
split
(
':'
))
minutes
,
seconds
=
map
(
int
,
self
.
rotating_time
.
split
(
':'
))
rotation_interval_ms
=
(
minutes
*
60
+
seconds
)
*
1000
# Convert to milliseconds
rotation_interval_ms
=
(
minutes
*
60
+
seconds
)
*
1000
# Convert to milliseconds
except
(
ValueError
,
AttributeError
):
except
(
ValueError
,
AttributeError
):
logger
.
warning
(
f
"Invalid rotating_time format '{self.rotating_time}', using default
5 minute
s"
)
logger
.
warning
(
f
"Invalid rotating_time format '{self.rotating_time}', using default
15 second
s"
)
rotation_interval_ms
=
5
*
60
*
1000
# Default 5 minute
s
rotation_interval_ms
=
15
*
1000
# Default 15 second
s
logger
.
info
(
f
"Starting template rotation every {rotation_interval_ms}ms ({self.rotating_time})"
)
logger
.
info
(
f
"Starting template rotation every {rotation_interval_ms}ms ({self.rotating_time})"
)
...
...
mbetterclient/web_dashboard/routes.py
View file @
e8a91c81
...
@@ -1824,7 +1824,7 @@ def get_intro_templates():
...
@@ -1824,7 +1824,7 @@ def get_intro_templates():
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
error
(
f
"Error loading intro templates: {str(e)}"
)
logger
.
error
(
f
"Error loading intro templates: {str(e)}"
)
return
jsonify
({
'templates'
:
[],
'default_show_time'
:
'00:30'
,
'rotating_time'
:
'0
5:00
'
})
return
jsonify
({
'templates'
:
[],
'default_show_time'
:
'00:30'
,
'rotating_time'
:
'0
0:15
'
})
@
api_bp
.
route
(
'/intro-templates'
,
methods
=
[
'POST'
])
@
api_bp
.
route
(
'/intro-templates'
,
methods
=
[
'POST'
])
...
@@ -1843,7 +1843,7 @@ def save_intro_templates():
...
@@ -1843,7 +1843,7 @@ def save_intro_templates():
# Validate data structure
# Validate data structure
templates
=
data
.
get
(
'templates'
,
[])
templates
=
data
.
get
(
'templates'
,
[])
default_show_time
=
data
.
get
(
'default_show_time'
,
'00:30'
)
default_show_time
=
data
.
get
(
'default_show_time'
,
'00:30'
)
rotating_time
=
data
.
get
(
'rotating_time'
,
'0
5:00
'
)
rotating_time
=
data
.
get
(
'rotating_time'
,
'0
0:15
'
)
logger
.
info
(
f
"WebDashboard: Saving intro templates - received {len(templates)} templates"
)
logger
.
info
(
f
"WebDashboard: Saving intro templates - received {len(templates)} templates"
)
logger
.
debug
(
f
"WebDashboard: Templates data: {templates}"
)
logger
.
debug
(
f
"WebDashboard: Templates data: {templates}"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment