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
a596debf
You need to sign in or sign up before continuing.
Commit
a596debf
authored
Dec 03, 2025
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
- Removed native overlay
parent
d697940f
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
586 additions
and
794 deletions
+586
-794
PyQt6_UPGRADE_SUMMARY.md
PyQt6_UPGRADE_SUMMARY.md
+9
-5
main.py
main.py
+0
-6
client.py
mbetterclient/api_client/client.py
+7
-7
settings.py
mbetterclient/config/settings.py
+0
-1
application.py
mbetterclient/core/application.py
+0
-2
match_timer.py
mbetterclient/core/match_timer.py
+1
-1
screen_cast.py
mbetterclient/core/screen_cast.py
+5
-5
thread_manager.py
mbetterclient/core/thread_manager.py
+6
-5
__init__.py
mbetterclient/qt_player/__init__.py
+0
-3
overlay_engine.py
mbetterclient/qt_player/overlay_engine.py
+0
-492
player.py
mbetterclient/qt_player/player.py
+248
-224
template_watcher.py
mbetterclient/qt_player/template_watcher.py
+18
-9
app.py
mbetterclient/web_dashboard/app.py
+9
-8
overlay.js
mbetterclient/web_dashboard/static/overlay.js
+283
-26
No files found.
PyQt6_UPGRADE_SUMMARY.md
View file @
a596debf
...
@@ -33,10 +33,11 @@ Successfully replaced the PyQt5 video player implementation with a comprehensive
...
@@ -33,10 +33,11 @@ Successfully replaced the PyQt5 video player implementation with a comprehensive
-
**Responsive Design**
: Automatic scaling for different resolutions
-
**Responsive Design**
: Automatic scaling for different resolutions
-
**GSAP-ready Structure**
: Animation framework integration support
-
**GSAP-ready Structure**
: Animation framework integration support
### 3. Legacy Compatibility (mbetterclient/qt_player/overlay_engine.py)
### 3. Legacy Removal (overlay_engine.py)
**UPDATED**
PyQt5 → PyQt6 imports and constants for compatibility:
**REMOVED**
the legacy native overlay engine entirely:
-
Updated Qt enums to PyQt6 format (Qt.AlignLeft → Qt.AlignmentFlag.AlignLeft)
-
The native Qt overlay implementation was replaced by the superior QWebEngineView system
-
Maintained backward compatibility for any remaining legacy code
-
All overlay functionality now uses HTML/CSS/JavaScript templates exclusively
-
Removed OverlayEngine and OverlayRenderer classes as they are no longer needed
### 4. Message Bus Integration
### 4. Message Bus Integration
**ENHANCED**
message handling for complete thread communication:
**ENHANCED**
message handling for complete thread communication:
...
@@ -135,7 +136,10 @@ message_bus.publish(overlay_message)
...
@@ -135,7 +136,10 @@ message_bus.publish(overlay_message)
### Modified Files:
### Modified Files:
-
`mbetterclient/qt_player/player.py`
-
**COMPLETELY REPLACED**
with PyQt6 implementation
-
`mbetterclient/qt_player/player.py`
-
**COMPLETELY REPLACED**
with PyQt6 implementation
-
`mbetterclient/qt_player/overlay_engine.py`
- Updated PyQt5 → PyQt6 imports
-
`mbetterclient/qt_player/__init__.py`
- Removed legacy overlay engine imports
### Removed Files:
-
`mbetterclient/qt_player/overlay_engine.py`
- Legacy native overlay implementation removed
### Unchanged Files:
### Unchanged Files:
-
`mbetterclient/core/application.py`
- Works seamlessly with new implementation
-
`mbetterclient/core/application.py`
- Works seamlessly with new implementation
...
...
main.py
View file @
a596debf
...
@@ -173,11 +173,6 @@ Examples:
...
@@ -173,11 +173,6 @@ Examples:
help
=
'Disable web dashboard (PyQt interface only)'
help
=
'Disable web dashboard (PyQt interface only)'
)
)
parser
.
add_argument
(
'--native-overlay'
,
action
=
'store_true'
,
help
=
'Use native Qt overlay instead of QWebEngineView (prevents freezing on some systems)'
)
# Screen cast options
# Screen cast options
parser
.
add_argument
(
parser
.
add_argument
(
...
@@ -275,7 +270,6 @@ def main():
...
@@ -275,7 +270,6 @@ def main():
settings
.
debug_overlay
=
args
.
debug_overlay
settings
.
debug_overlay
=
args
.
debug_overlay
settings
.
enable_qt
=
not
args
.
no_qt
settings
.
enable_qt
=
not
args
.
no_qt
settings
.
enable_web
=
not
args
.
no_web
settings
.
enable_web
=
not
args
.
no_web
settings
.
qt
.
use_native_overlay
=
args
.
native_overlay
# Timer settings
# Timer settings
if
args
.
start_timer
is
not
None
:
if
args
.
start_timer
is
not
None
:
...
...
mbetterclient/api_client/client.py
View file @
a596debf
...
@@ -922,27 +922,27 @@ class APIClient(ThreadedComponent):
...
@@ -922,27 +922,27 @@ class APIClient(ThreadedComponent):
self
.
message_bus
.
publish
(
ready_message
)
self
.
message_bus
.
publish
(
ready_message
)
# Main execution loop
# Main execution loop
while
self
.
running
:
while
self
.
running
and
not
self
.
shutdown_event
.
is_set
()
:
try
:
try
:
# Update heartbeat at the start of each loop
# Update heartbeat at the start of each loop
self
.
heartbeat
()
self
.
heartbeat
()
# Process messages
# Process messages
message
=
self
.
message_bus
.
get_message
(
self
.
name
,
timeout
=
1.0
)
message
=
self
.
message_bus
.
get_message
(
self
.
name
,
timeout
=
1.0
)
if
message
:
if
message
:
self
.
_process_message
(
message
)
self
.
_process_message
(
message
)
# Update heartbeat before potentially long operations
# Update heartbeat before potentially long operations
self
.
heartbeat
()
self
.
heartbeat
()
# Execute scheduled API requests
# Execute scheduled API requests
self
.
_execute_scheduled_requests
()
self
.
_execute_scheduled_requests
()
# Update heartbeat after operations
# Update heartbeat after operations
self
.
heartbeat
()
self
.
heartbeat
()
time
.
sleep
(
1.0
)
time
.
sleep
(
1.0
)
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
error
(
f
"APIClient run loop error: {e}"
)
logger
.
error
(
f
"APIClient run loop error: {e}"
)
# Update heartbeat even in error cases
# Update heartbeat even in error cases
...
...
mbetterclient/config/settings.py
View file @
a596debf
...
@@ -228,7 +228,6 @@ class QtConfig:
...
@@ -228,7 +228,6 @@ class QtConfig:
overlay_enabled
:
bool
=
True
overlay_enabled
:
bool
=
True
default_template
:
str
=
"news_template"
default_template
:
str
=
"news_template"
overlay_opacity
:
float
=
0.9
overlay_opacity
:
float
=
0.9
use_native_overlay
:
bool
=
False
# Use native Qt widgets instead of QWebEngineView
# Performance settings
# Performance settings
hardware_acceleration
:
bool
=
True
hardware_acceleration
:
bool
=
True
...
...
mbetterclient/core/application.py
View file @
a596debf
...
@@ -133,8 +133,6 @@ class MbetterClientApplication:
...
@@ -133,8 +133,6 @@ class MbetterClientApplication:
stored_settings
.
enable_web
=
self
.
settings
.
enable_web
stored_settings
.
enable_web
=
self
.
settings
.
enable_web
stored_settings
.
enable_screen_cast
=
self
.
settings
.
enable_screen_cast
# Preserve screen cast setting
stored_settings
.
enable_screen_cast
=
self
.
settings
.
enable_screen_cast
# Preserve screen cast setting
# Preserve command line Qt overlay setting
stored_settings
.
qt
.
use_native_overlay
=
self
.
settings
.
qt
.
use_native_overlay
# Preserve command line debug settings
# Preserve command line debug settings
stored_settings
.
debug_overlay
=
self
.
settings
.
debug_overlay
stored_settings
.
debug_overlay
=
self
.
settings
.
debug_overlay
...
...
mbetterclient/core/match_timer.py
View file @
a596debf
...
@@ -63,7 +63,7 @@ class MatchTimerComponent(ThreadedComponent):
...
@@ -63,7 +63,7 @@ class MatchTimerComponent(ThreadedComponent):
"""Main timer loop"""
"""Main timer loop"""
logger
.
info
(
"MatchTimer component started"
)
logger
.
info
(
"MatchTimer component started"
)
while
self
.
running
:
while
self
.
running
and
not
self
.
shutdown_event
.
is_set
()
:
try
:
try
:
# Process any pending messages first
# Process any pending messages first
message
=
self
.
message_bus
.
get_message
(
self
.
name
,
timeout
=
0.1
)
message
=
self
.
message_bus
.
get_message
(
self
.
name
,
timeout
=
0.1
)
...
...
mbetterclient/core/screen_cast.py
View file @
a596debf
...
@@ -183,21 +183,21 @@ class ScreenCastComponent(ThreadedComponent):
...
@@ -183,21 +183,21 @@ class ScreenCastComponent(ThreadedComponent):
self
.
_connect_chromecast
()
self
.
_connect_chromecast
()
# Main loop - monitor and restart capture if needed
# Main loop - monitor and restart capture if needed
while
self
.
running
:
while
self
.
running
and
not
self
.
shutdown_event
.
is_set
()
:
try
:
try
:
# Process messages
# Process messages
message
=
self
.
message_bus
.
get_message
(
self
.
name
,
timeout
=
1.0
)
message
=
self
.
message_bus
.
get_message
(
self
.
name
,
timeout
=
1.0
)
if
message
:
if
message
:
self
.
_process_message
(
message
)
self
.
_process_message
(
message
)
# Check capture health
# Check capture health
self
.
_check_capture_health
()
self
.
_check_capture_health
()
# Update heartbeat
# Update heartbeat
self
.
heartbeat
()
self
.
heartbeat
()
time
.
sleep
(
1.0
)
time
.
sleep
(
1.0
)
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
error
(
f
"ScreenCast run loop error: {e}"
)
logger
.
error
(
f
"ScreenCast run loop error: {e}"
)
time
.
sleep
(
1.0
)
time
.
sleep
(
1.0
)
...
...
mbetterclient/core/thread_manager.py
View file @
a596debf
...
@@ -233,10 +233,11 @@ class ThreadManager:
...
@@ -233,10 +233,11 @@ class ThreadManager:
def
stop_all
(
self
,
timeout
:
float
=
10.0
)
->
bool
:
def
stop_all
(
self
,
timeout
:
float
=
10.0
)
->
bool
:
"""Stop all components"""
"""Stop all components"""
logger
.
info
(
"Stopping all components..."
)
logger
.
info
(
"Stopping all components..."
)
success
=
True
success
=
True
stop_timeout
=
timeout
/
max
(
len
(
self
.
components
),
1
)
# Distribute timeout
# Use at least 8 seconds per component, but distribute total timeout
stop_timeout
=
max
(
timeout
/
max
(
len
(
self
.
components
),
1
),
8.0
)
with
self
.
_lock
:
with
self
.
_lock
:
for
name
,
component
in
self
.
components
.
items
():
for
name
,
component
in
self
.
components
.
items
():
try
:
try
:
...
@@ -248,12 +249,12 @@ class ThreadManager:
...
@@ -248,12 +249,12 @@ class ThreadManager:
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
error
(
f
"Exception stopping component {name}: {e}"
)
logger
.
error
(
f
"Exception stopping component {name}: {e}"
)
success
=
False
success
=
False
if
success
:
if
success
:
logger
.
info
(
"All components stopped successfully"
)
logger
.
info
(
"All components stopped successfully"
)
else
:
else
:
logger
.
warning
(
"Some components failed to stop cleanly"
)
logger
.
warning
(
"Some components failed to stop cleanly"
)
return
success
return
success
def
restart_component
(
self
,
name
:
str
,
timeout
:
float
=
5.0
)
->
bool
:
def
restart_component
(
self
,
name
:
str
,
timeout
:
float
=
5.0
)
->
bool
:
...
...
mbetterclient/qt_player/__init__.py
View file @
a596debf
...
@@ -3,13 +3,10 @@ PyQt video player with overlay templates for MbetterClient
...
@@ -3,13 +3,10 @@ PyQt video player with overlay templates for MbetterClient
"""
"""
from
.player
import
QtVideoPlayer
from
.player
import
QtVideoPlayer
from
.overlay_engine
import
OverlayEngine
,
OverlayRenderer
from
.templates
import
TemplateManager
,
NewsTemplate
from
.templates
import
TemplateManager
,
NewsTemplate
__all__
=
[
__all__
=
[
'QtVideoPlayer'
,
'QtVideoPlayer'
,
'OverlayEngine'
,
'OverlayRenderer'
,
'TemplateManager'
,
'TemplateManager'
,
'NewsTemplate'
'NewsTemplate'
]
]
\ No newline at end of file
mbetterclient/qt_player/overlay_engine.py
deleted
100644 → 0
View file @
d697940f
"""
Overlay engine for rendering dynamic overlays on video content
NOTE: This is legacy code - the new PyQt6 implementation uses QWebEngineView for overlays
"""
import
time
import
logging
from
typing
import
Dict
,
Any
,
Optional
,
List
,
Tuple
from
pathlib
import
Path
from
PyQt6.QtCore
import
Qt
,
QRect
,
QPoint
,
QTimer
,
QPropertyAnimation
,
QEasingCurve
from
PyQt6.QtGui
import
(
QPainter
,
QPen
,
QBrush
,
QColor
,
QFont
,
QPixmap
,
QFontMetrics
,
QLinearGradient
,
QRadialGradient
,
QPolygon
)
from
PyQt6.QtWidgets
import
QGraphicsEffect
logger
=
logging
.
getLogger
(
__name__
)
class
OverlayElement
:
"""Base class for overlay elements"""
def
__init__
(
self
,
element_id
:
str
,
element_type
:
str
,
config
:
Dict
[
str
,
Any
]):
self
.
id
=
element_id
self
.
type
=
element_type
self
.
config
=
config
self
.
visible
=
config
.
get
(
'visible'
,
True
)
self
.
opacity
=
config
.
get
(
'opacity'
,
1.0
)
self
.
x
=
config
.
get
(
'x'
,
0
)
self
.
y
=
config
.
get
(
'y'
,
0
)
self
.
width
=
config
.
get
(
'width'
,
100
)
self
.
height
=
config
.
get
(
'height'
,
50
)
self
.
z_index
=
config
.
get
(
'z_index'
,
0
)
# Animation properties
self
.
animation_config
=
config
.
get
(
'animation'
,
{})
self
.
animation_start_time
=
None
def
get_rect
(
self
)
->
QRect
:
"""Get element rectangle"""
return
QRect
(
int
(
self
.
x
),
int
(
self
.
y
),
int
(
self
.
width
),
int
(
self
.
height
))
def
update_animation
(
self
,
elapsed_time
:
float
):
"""Update animation state"""
if
not
self
.
animation_config
:
return
animation_type
=
self
.
animation_config
.
get
(
'type'
)
if
animation_type
==
'scroll'
:
self
.
_update_scroll_animation
(
elapsed_time
)
elif
animation_type
==
'fade'
:
self
.
_update_fade_animation
(
elapsed_time
)
elif
animation_type
==
'bounce'
:
self
.
_update_bounce_animation
(
elapsed_time
)
def
_update_scroll_animation
(
self
,
elapsed_time
:
float
):
"""Update scrolling text animation"""
speed
=
self
.
animation_config
.
get
(
'speed'
,
100
)
# pixels per second
direction
=
self
.
animation_config
.
get
(
'direction'
,
'left'
)
if
direction
==
'left'
:
self
.
x
-=
speed
*
elapsed_time
/
1000.0
elif
direction
==
'right'
:
self
.
x
+=
speed
*
elapsed_time
/
1000.0
elif
direction
==
'up'
:
self
.
y
-=
speed
*
elapsed_time
/
1000.0
elif
direction
==
'down'
:
self
.
y
+=
speed
*
elapsed_time
/
1000.0
def
_update_fade_animation
(
self
,
elapsed_time
:
float
):
"""Update fade animation"""
duration
=
self
.
animation_config
.
get
(
'duration'
,
2000
)
# ms
fade_type
=
self
.
animation_config
.
get
(
'fade_type'
,
'in'
)
if
self
.
animation_start_time
is
None
:
self
.
animation_start_time
=
elapsed_time
progress
=
(
elapsed_time
-
self
.
animation_start_time
)
/
duration
progress
=
max
(
0.0
,
min
(
1.0
,
progress
))
if
fade_type
==
'in'
:
self
.
opacity
=
progress
elif
fade_type
==
'out'
:
self
.
opacity
=
1.0
-
progress
elif
fade_type
==
'in_out'
:
if
progress
<=
0.5
:
self
.
opacity
=
progress
*
2
else
:
self
.
opacity
=
(
1.0
-
progress
)
*
2
def
_update_bounce_animation
(
self
,
elapsed_time
:
float
):
"""Update bounce animation"""
import
math
frequency
=
self
.
animation_config
.
get
(
'frequency'
,
1.0
)
# Hz
amplitude
=
self
.
animation_config
.
get
(
'amplitude'
,
10
)
# pixels
offset
=
amplitude
*
math
.
sin
(
2
*
math
.
pi
*
frequency
*
elapsed_time
/
1000.0
)
self
.
y
=
self
.
config
.
get
(
'y'
,
0
)
+
offset
def
render
(
self
,
painter
:
QPainter
,
canvas_rect
:
QRect
):
"""Render element (to be implemented by subclasses)"""
pass
class
RectangleElement
(
OverlayElement
):
"""Rectangle overlay element"""
def
__init__
(
self
,
element_id
:
str
,
config
:
Dict
[
str
,
Any
]):
super
()
.
__init__
(
element_id
,
'rectangle'
,
config
)
self
.
color
=
QColor
(
config
.
get
(
'color'
,
'#000000'
))
self
.
border_color
=
QColor
(
config
.
get
(
'border_color'
,
'#ffffff'
))
self
.
border_width
=
config
.
get
(
'border_width'
,
0
)
self
.
corner_radius
=
config
.
get
(
'corner_radius'
,
0
)
def
render
(
self
,
painter
:
QPainter
,
canvas_rect
:
QRect
):
"""Render rectangle"""
if
not
self
.
visible
or
self
.
opacity
<=
0
:
return
# Set opacity
painter
.
setOpacity
(
self
.
opacity
)
rect
=
self
.
get_rect
()
# Fill
if
self
.
color
.
alpha
()
>
0
:
brush
=
QBrush
(
self
.
color
)
painter
.
setBrush
(
brush
)
else
:
painter
.
setBrush
(
Qt
.
NoBrush
)
# Border
if
self
.
border_width
>
0
:
pen
=
QPen
(
self
.
border_color
,
self
.
border_width
)
painter
.
setPen
(
pen
)
else
:
painter
.
setPen
(
Qt
.
NoPen
)
# Draw
if
self
.
corner_radius
>
0
:
painter
.
drawRoundedRect
(
rect
,
self
.
corner_radius
,
self
.
corner_radius
)
else
:
painter
.
drawRect
(
rect
)
class
TextElement
(
OverlayElement
):
"""Text overlay element"""
def
__init__
(
self
,
element_id
:
str
,
config
:
Dict
[
str
,
Any
]):
super
()
.
__init__
(
element_id
,
'text'
,
config
)
self
.
text
=
config
.
get
(
'text'
,
''
)
self
.
font_family
=
config
.
get
(
'font_family'
,
'Arial'
)
self
.
font_size
=
config
.
get
(
'font_size'
,
16
)
self
.
font_weight
=
config
.
get
(
'font_weight'
,
'normal'
)
self
.
color
=
QColor
(
config
.
get
(
'color'
,
'#ffffff'
))
self
.
background_color
=
QColor
(
config
.
get
(
'background_color'
,
'transparent'
))
self
.
alignment
=
config
.
get
(
'alignment'
,
'left'
)
self
.
word_wrap
=
config
.
get
(
'word_wrap'
,
False
)
# Scrolling text properties
self
.
scroll_position
=
0
self
.
text_width
=
0
def
set_text
(
self
,
text
:
str
):
"""Update text content"""
self
.
text
=
text
self
.
scroll_position
=
0
# Reset scroll position
def
render
(
self
,
painter
:
QPainter
,
canvas_rect
:
QRect
):
"""Render text"""
if
not
self
.
visible
or
self
.
opacity
<=
0
or
not
self
.
text
:
return
# Set opacity
painter
.
setOpacity
(
self
.
opacity
)
# Setup font
font
=
QFont
(
self
.
font_family
,
self
.
font_size
)
if
self
.
font_weight
==
'bold'
:
font
.
setBold
(
True
)
painter
.
setFont
(
font
)
# Get text metrics
metrics
=
QFontMetrics
(
font
)
self
.
text_width
=
metrics
.
boundingRect
(
self
.
text
)
.
width
()
rect
=
self
.
get_rect
()
# Background
if
self
.
background_color
.
alpha
()
>
0
:
painter
.
fillRect
(
rect
,
self
.
background_color
)
# Text color
painter
.
setPen
(
self
.
color
)
# Handle scrolling animation
if
self
.
animation_config
.
get
(
'type'
)
==
'scroll'
:
self
.
_render_scrolling_text
(
painter
,
rect
,
metrics
)
else
:
self
.
_render_static_text
(
painter
,
rect
)
def
_render_scrolling_text
(
self
,
painter
:
QPainter
,
rect
:
QRect
,
metrics
:
QFontMetrics
):
"""Render scrolling text"""
if
self
.
text_width
<=
rect
.
width
():
# Text fits, no scrolling needed
self
.
_render_static_text
(
painter
,
rect
)
return
# Calculate scroll position
direction
=
self
.
animation_config
.
get
(
'direction'
,
'left'
)
if
direction
==
'left'
:
# Reset position when text fully scrolled out
if
self
.
x
<
-
self
.
text_width
:
self
.
x
=
rect
.
right
()
# Draw text at current position
text_rect
=
QRect
(
int
(
self
.
x
),
rect
.
y
(),
self
.
text_width
,
rect
.
height
())
else
:
text_rect
=
rect
# Draw text
alignment
=
Qt
.
AlignVCenter
if
self
.
alignment
==
'center'
:
alignment
|=
Qt
.
AlignHCenter
elif
self
.
alignment
==
'right'
:
alignment
|=
Qt
.
AlignRight
else
:
alignment
|=
Qt
.
AlignLeft
painter
.
drawText
(
text_rect
,
alignment
,
self
.
text
)
def
_render_static_text
(
self
,
painter
:
QPainter
,
rect
:
QRect
):
"""Render static text"""
# Text alignment
alignment
=
Qt
.
AlignVCenter
if
self
.
alignment
==
'center'
:
alignment
|=
Qt
.
AlignHCenter
elif
self
.
alignment
==
'right'
:
alignment
|=
Qt
.
AlignRight
else
:
alignment
|=
Qt
.
AlignLeft
if
self
.
word_wrap
:
alignment
|=
Qt
.
TextWordWrap
painter
.
drawText
(
rect
,
alignment
,
self
.
text
)
class
ImageElement
(
OverlayElement
):
"""Image overlay element"""
def
__init__
(
self
,
element_id
:
str
,
config
:
Dict
[
str
,
Any
]):
super
()
.
__init__
(
element_id
,
'image'
,
config
)
self
.
source
=
config
.
get
(
'source'
,
''
)
self
.
fit
=
config
.
get
(
'fit'
,
'contain'
)
# contain, cover, fill, scale-down
self
.
pixmap
:
Optional
[
QPixmap
]
=
None
self
.
_load_image
()
def
_load_image
(
self
):
"""Load image from source"""
try
:
if
self
.
source
:
# Handle both absolute and relative paths
if
Path
(
self
.
source
)
.
is_absolute
():
image_path
=
Path
(
self
.
source
)
else
:
# Relative to project root
project_root
=
Path
(
__file__
)
.
parent
.
parent
.
parent
image_path
=
project_root
/
self
.
source
if
image_path
.
exists
():
self
.
pixmap
=
QPixmap
(
str
(
image_path
))
logger
.
debug
(
f
"Loaded image: {image_path}"
)
else
:
logger
.
warning
(
f
"Image not found: {image_path}"
)
except
Exception
as
e
:
logger
.
error
(
f
"Failed to load image {self.source}: {e}"
)
def
set_source
(
self
,
source
:
str
):
"""Update image source"""
self
.
source
=
source
self
.
_load_image
()
def
render
(
self
,
painter
:
QPainter
,
canvas_rect
:
QRect
):
"""Render image"""
if
not
self
.
visible
or
self
.
opacity
<=
0
or
not
self
.
pixmap
:
return
# Set opacity
painter
.
setOpacity
(
self
.
opacity
)
rect
=
self
.
get_rect
()
# Scale pixmap based on fit mode
scaled_pixmap
=
self
.
_scale_pixmap
(
self
.
pixmap
,
rect
)
# Calculate position for centering if needed
if
self
.
fit
in
[
'contain'
,
'scale-down'
]:
x_offset
=
(
rect
.
width
()
-
scaled_pixmap
.
width
())
//
2
y_offset
=
(
rect
.
height
()
-
scaled_pixmap
.
height
())
//
2
draw_pos
=
QPoint
(
rect
.
x
()
+
x_offset
,
rect
.
y
()
+
y_offset
)
else
:
draw_pos
=
rect
.
topLeft
()
painter
.
drawPixmap
(
draw_pos
,
scaled_pixmap
)
def
_scale_pixmap
(
self
,
pixmap
:
QPixmap
,
target_rect
:
QRect
)
->
QPixmap
:
"""Scale pixmap according to fit mode"""
if
self
.
fit
==
'fill'
:
return
pixmap
.
scaled
(
target_rect
.
size
(),
Qt
.
IgnoreAspectRatio
,
Qt
.
SmoothTransformation
)
elif
self
.
fit
==
'cover'
:
return
pixmap
.
scaled
(
target_rect
.
size
(),
Qt
.
KeepAspectRatioByExpanding
,
Qt
.
SmoothTransformation
)
elif
self
.
fit
==
'contain'
:
return
pixmap
.
scaled
(
target_rect
.
size
(),
Qt
.
KeepAspectRatio
,
Qt
.
SmoothTransformation
)
elif
self
.
fit
==
'scale-down'
:
if
pixmap
.
width
()
<=
target_rect
.
width
()
and
pixmap
.
height
()
<=
target_rect
.
height
():
return
pixmap
# No scaling needed
else
:
return
pixmap
.
scaled
(
target_rect
.
size
(),
Qt
.
KeepAspectRatio
,
Qt
.
SmoothTransformation
)
else
:
return
pixmap
class
OverlayRenderer
:
"""Manages rendering of overlay elements"""
def
__init__
(
self
):
self
.
elements
:
Dict
[
str
,
OverlayElement
]
=
{}
self
.
start_time
=
time
.
time
()
*
1000
# milliseconds
self
.
last_update_time
=
self
.
start_time
def
add_element
(
self
,
element
:
OverlayElement
):
"""Add overlay element"""
self
.
elements
[
element
.
id
]
=
element
def
remove_element
(
self
,
element_id
:
str
):
"""Remove overlay element"""
if
element_id
in
self
.
elements
:
del
self
.
elements
[
element_id
]
def
get_element
(
self
,
element_id
:
str
)
->
Optional
[
OverlayElement
]:
"""Get overlay element by ID"""
return
self
.
elements
.
get
(
element_id
)
def
clear_elements
(
self
):
"""Clear all elements"""
self
.
elements
.
clear
()
def
update_element_data
(
self
,
element_id
:
str
,
data
:
Dict
[
str
,
Any
]):
"""Update element data"""
element
=
self
.
get_element
(
element_id
)
if
element
:
if
element
.
type
==
'text'
and
'text'
in
data
:
element
.
set_text
(
data
[
'text'
])
elif
element
.
type
==
'image'
and
'source'
in
data
:
element
.
set_source
(
data
[
'source'
])
# Update other properties
for
key
,
value
in
data
.
items
():
if
hasattr
(
element
,
key
):
setattr
(
element
,
key
,
value
)
def
render
(
self
,
painter
:
QPainter
,
canvas_rect
:
QRect
):
"""Render all overlay elements"""
current_time
=
time
.
time
()
*
1000
elapsed_time
=
current_time
-
self
.
last_update_time
self
.
last_update_time
=
current_time
# Sort elements by z-index
sorted_elements
=
sorted
(
self
.
elements
.
values
(),
key
=
lambda
e
:
e
.
z_index
)
# Render elements
for
element
in
sorted_elements
:
try
:
# Update animations
element
.
update_animation
(
current_time
-
self
.
start_time
)
# Render element
element
.
render
(
painter
,
canvas_rect
)
except
Exception
as
e
:
logger
.
error
(
f
"Failed to render element {element.id}: {e}"
)
class
OverlayEngine
:
"""Main overlay engine"""
def
__init__
(
self
):
self
.
renderer
=
OverlayRenderer
()
self
.
template_config
:
Optional
[
Dict
[
str
,
Any
]]
=
None
self
.
overlay_data
:
Dict
[
str
,
Any
]
=
{}
self
.
playback_position
=
0
self
.
playback_duration
=
0
def
load_template
(
self
,
template_config
:
Dict
[
str
,
Any
],
overlay_data
:
Dict
[
str
,
Any
]
=
None
):
"""Load overlay template"""
try
:
self
.
template_config
=
template_config
self
.
overlay_data
=
overlay_data
or
{}
# Clear existing elements
self
.
renderer
.
clear_elements
()
# Create elements from template
elements_config
=
template_config
.
get
(
'elements'
,
[])
for
element_config
in
elements_config
:
element
=
self
.
_create_element
(
element_config
)
if
element
:
self
.
renderer
.
add_element
(
element
)
# Apply overlay data
self
.
_apply_overlay_data
()
logger
.
info
(
f
"Loaded template with {len(elements_config)} elements"
)
except
Exception
as
e
:
logger
.
error
(
f
"Failed to load template: {e}"
)
def
_create_element
(
self
,
config
:
Dict
[
str
,
Any
])
->
Optional
[
OverlayElement
]:
"""Create overlay element from configuration"""
element_type
=
config
.
get
(
'type'
)
element_id
=
config
.
get
(
'id'
)
if
not
element_type
or
not
element_id
:
logger
.
warning
(
"Element missing type or id"
)
return
None
try
:
if
element_type
==
'rectangle'
:
return
RectangleElement
(
element_id
,
config
)
elif
element_type
==
'text'
:
return
TextElement
(
element_id
,
config
)
elif
element_type
==
'image'
:
return
ImageElement
(
element_id
,
config
)
else
:
logger
.
warning
(
f
"Unknown element type: {element_type}"
)
return
None
except
Exception
as
e
:
logger
.
error
(
f
"Failed to create element {element_id}: {e}"
)
return
None
def
update_overlay_data
(
self
,
overlay_data
:
Dict
[
str
,
Any
]):
"""Update overlay data"""
self
.
overlay_data
.
update
(
overlay_data
)
self
.
_apply_overlay_data
()
def
_apply_overlay_data
(
self
):
"""Apply overlay data to elements"""
try
:
for
element_id
,
data
in
self
.
overlay_data
.
items
():
self
.
renderer
.
update_element_data
(
element_id
,
data
)
except
Exception
as
e
:
logger
.
error
(
f
"Failed to apply overlay data: {e}"
)
def
update_playback_position
(
self
,
position
:
int
,
duration
:
int
):
"""Update video playback position"""
self
.
playback_position
=
position
self
.
playback_duration
=
duration
def
render
(
self
,
painter
:
QPainter
,
canvas_rect
:
QRect
):
"""Render overlay"""
try
:
self
.
renderer
.
render
(
painter
,
canvas_rect
)
except
Exception
as
e
:
logger
.
error
(
f
"Overlay render failed: {e}"
)
def
get_element_by_id
(
self
,
element_id
:
str
)
->
Optional
[
OverlayElement
]:
"""Get element by ID"""
return
self
.
renderer
.
get_element
(
element_id
)
def
set_element_visibility
(
self
,
element_id
:
str
,
visible
:
bool
):
"""Set element visibility"""
element
=
self
.
get_element_by_id
(
element_id
)
if
element
:
element
.
visible
=
visible
def
set_element_text
(
self
,
element_id
:
str
,
text
:
str
):
"""Set text content for text element"""
element
=
self
.
get_element_by_id
(
element_id
)
if
element
and
element
.
type
==
'text'
:
element
.
set_text
(
text
)
def
set_element_image
(
self
,
element_id
:
str
,
image_source
:
str
):
"""Set image source for image element"""
element
=
self
.
get_element_by_id
(
element_id
)
if
element
and
element
.
type
==
'image'
:
element
.
set_source
(
image_source
)
\ No newline at end of file
mbetterclient/qt_player/player.py
View file @
a596debf
...
@@ -647,16 +647,44 @@ class OverlayWebView(QWebEngineView):
...
@@ -647,16 +647,44 @@ class OverlayWebView(QWebEngineView):
self
.
setup_web_view
()
self
.
setup_web_view
()
self
.
_setup_custom_scheme
()
self
.
_setup_custom_scheme
()
logger
.
info
(
f
"OverlayWebView initialized - builtin: {self.builtin_templates_dir}, uploaded: {self.uploaded_templates_dir}"
)
logger
.
info
(
f
"OverlayWebView initialized - builtin: {self.builtin_templates_dir}, uploaded: {self.uploaded_templates_dir}"
)
def
setup_web_view
(
self
):
def
setup_web_view
(
self
):
"""Setup web view with proper transparency for overlay"""
"""Setup web view with proper transparency for overlay"""
logger
.
info
(
"OverlayWebView.setup_web_view() - Starting setup"
)
logger
.
info
(
"OverlayWebView.setup_web_view() - Starting setup"
)
# Enhanced GPU detection and logging
# Enhanced GPU detection and logging
for VirtualBox compatibility
import
os
import
os
is_mesa
=
os
.
environ
.
get
(
'LIBGL_ALWAYS_SOFTWARE'
)
==
'1'
or
\
is_mesa
=
os
.
environ
.
get
(
'LIBGL_ALWAYS_SOFTWARE'
)
==
'1'
or
\
os
.
environ
.
get
(
'MESA_GL_VERSION_OVERRIDE'
)
is
not
None
os
.
environ
.
get
(
'MESA_GL_VERSION_OVERRIDE'
)
is
not
None
# Check if running in VirtualBox
is_virtualbox
=
False
try
:
with
open
(
'/sys/devices/virtual/dmi/id/sys_vendor'
,
'r'
)
as
f
:
vendor
=
f
.
read
()
.
strip
()
.
lower
()
if
'virtualbox'
in
vendor
or
'innotek'
in
vendor
:
is_virtualbox
=
True
logger
.
warning
(
"VIRTUALBOX ENVIRONMENT DETECTED - Overlay compatibility issues likely"
)
except
:
# Check for VirtualBox guest additions
try
:
import
subprocess
result
=
subprocess
.
run
([
'lsmod'
],
capture_output
=
True
,
text
=
True
,
timeout
=
2
)
if
'vboxvideo'
in
result
.
stdout
or
'vboxguest'
in
result
.
stdout
:
is_virtualbox
=
True
logger
.
warning
(
"VIRTUALBOX GUEST MODULES DETECTED - Overlay compatibility issues likely"
)
except
:
pass
if
is_virtualbox
:
logger
.
warning
(
"=== VIRTUALBOX OVERLAY DIAGNOSTICS ==="
)
logger
.
warning
(
"VirtualBox detected - common overlay issues:"
)
logger
.
warning
(
"1. GPU acceleration disabled by default"
)
logger
.
warning
(
"2. Limited OpenGL support"
)
logger
.
warning
(
"3. Qt WebEngine may fallback to software rendering"
)
logger
.
warning
(
"4. Transparency effects may not work"
)
logger
.
warning
(
"5. WebChannel communication may be unreliable"
)
# Log GPU-related environment variables
# Log GPU-related environment variables
gpu_env_vars
=
[
gpu_env_vars
=
[
'LIBGL_ALWAYS_SOFTWARE'
,
'LIBGL_ALWAYS_SOFTWARE'
,
...
@@ -666,7 +694,9 @@ class OverlayWebView(QWebEngineView):
...
@@ -666,7 +694,9 @@ class OverlayWebView(QWebEngineView):
'DISPLAY'
,
'DISPLAY'
,
'XDG_SESSION_TYPE'
,
'XDG_SESSION_TYPE'
,
'QTWEBENGINE_DISABLE_SANDBOX'
,
'QTWEBENGINE_DISABLE_SANDBOX'
,
'QTWEBENGINE_REMOTE_DEBUGGING'
'QTWEBENGINE_REMOTE_DEBUGGING'
,
'QTWEBENGINE_DISABLE_GPU'
,
'QT_OPENGL'
]
]
logger
.
info
(
"=== GPU Environment Check ==="
)
logger
.
info
(
"=== GPU Environment Check ==="
)
...
@@ -684,7 +714,7 @@ class OverlayWebView(QWebEngineView):
...
@@ -684,7 +714,7 @@ class OverlayWebView(QWebEngineView):
gpu_info
=
result
.
stdout
.
strip
()
.
split
(
','
)
gpu_info
=
result
.
stdout
.
strip
()
.
split
(
','
)
logger
.
info
(
f
"NVIDIA GPU detected: {gpu_info[0]}, Memory: {gpu_info[1]}/{gpu_info[2]}MB, Driver: {gpu_info[3]}"
)
logger
.
info
(
f
"NVIDIA GPU detected: {gpu_info[0]}, Memory: {gpu_info[1]}/{gpu_info[2]}MB, Driver: {gpu_info[3]}"
)
else
:
else
:
logger
.
warning
(
"nvidia-smi not available or failed"
)
logger
.
warning
(
"nvidia-smi not available or failed
- likely VirtualBox or no NVIDIA GPU
"
)
# Check GPU processes specifically
# Check GPU processes specifically
proc_result
=
subprocess
.
run
([
'nvidia-smi'
,
'pmon'
],
capture_output
=
True
,
text
=
True
,
timeout
=
5
)
proc_result
=
subprocess
.
run
([
'nvidia-smi'
,
'pmon'
],
capture_output
=
True
,
text
=
True
,
timeout
=
5
)
...
@@ -696,12 +726,12 @@ class OverlayWebView(QWebEngineView):
...
@@ -696,12 +726,12 @@ class OverlayWebView(QWebEngineView):
for
proc
in
gpu_processes
[:
3
]:
# Log first 3 processes
for
proc
in
gpu_processes
[:
3
]:
# Log first 3 processes
logger
.
info
(
f
"GPU Process: {proc}"
)
logger
.
info
(
f
"GPU Process: {proc}"
)
else
:
else
:
logger
.
warning
(
"NO GPU PROCESSES FOUND - This indicates GPU is not being utilized"
)
logger
.
warning
(
"NO GPU PROCESSES FOUND - This indicates GPU is not being utilized
(common in VirtualBox)
"
)
else
:
else
:
logger
.
warning
(
"Could not check GPU processes"
)
logger
.
warning
(
"Could not check GPU processes
- likely VirtualBox environment
"
)
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
warning
(
f
"Could not check NVIDIA GPU: {e}"
)
logger
.
warning
(
f
"Could not check NVIDIA GPU: {e}
- likely VirtualBox or no GPU
"
)
# Check OpenGL
# Check OpenGL
try
:
try
:
...
@@ -710,9 +740,9 @@ class OverlayWebView(QWebEngineView):
...
@@ -710,9 +740,9 @@ class OverlayWebView(QWebEngineView):
if
result
.
returncode
==
0
:
if
result
.
returncode
==
0
:
logger
.
info
(
f
"OpenGL renderer: {result.stdout.strip()}"
)
logger
.
info
(
f
"OpenGL renderer: {result.stdout.strip()}"
)
else
:
else
:
logger
.
warning
(
"Could not get OpenGL renderer info"
)
logger
.
warning
(
"Could not get OpenGL renderer info
- VirtualBox may not support OpenGL properly
"
)
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
warning
(
f
"Could not check OpenGL: {e}"
)
logger
.
warning
(
f
"Could not check OpenGL: {e}
- VirtualBox OpenGL issues likely
"
)
# Check Vulkan (Qt WebEngine can use Vulkan on some systems)
# Check Vulkan (Qt WebEngine can use Vulkan on some systems)
try
:
try
:
...
@@ -720,13 +750,22 @@ class OverlayWebView(QWebEngineView):
...
@@ -720,13 +750,22 @@ class OverlayWebView(QWebEngineView):
if
result
.
returncode
==
0
:
if
result
.
returncode
==
0
:
logger
.
info
(
"Vulkan is available"
)
logger
.
info
(
"Vulkan is available"
)
else
:
else
:
logger
.
info
(
"Vulkan not available or not configured"
)
logger
.
info
(
"Vulkan not available or not configured
- VirtualBox typically lacks Vulkan support
"
)
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
debug
(
f
"Vulkan check failed: {e}"
)
logger
.
debug
(
f
"Vulkan check failed: {e}"
)
logger
.
info
(
f
"Mesa software rendering detection: {is_mesa}"
)
logger
.
info
(
f
"Mesa software rendering detection: {is_mesa}"
)
if
is_mesa
:
if
is_mesa
:
logger
.
warning
(
"MESA SOFTWARE RENDERING DETECTED - GPU acceleration may be disabled"
)
logger
.
warning
(
"MESA SOFTWARE RENDERING DETECTED - GPU acceleration may be disabled (VirtualBox default)"
)
elif
is_virtualbox
:
logger
.
warning
(
"VirtualBox detected but Mesa not forced - Qt WebEngine may still fallback to software rendering"
)
# Check Qt WebEngine version and capabilities
try
:
from
PyQt6.QtWebEngineCore
import
QWebEngineProfile
logger
.
info
(
"Qt WebEngine is available"
)
except
ImportError
as
e
:
logger
.
error
(
f
"Qt WebEngine not available: {e} - overlays will not work"
)
# Set transparent background on the web page
# Set transparent background on the web page
page
=
self
.
page
()
page
=
self
.
page
()
...
@@ -764,14 +803,20 @@ class OverlayWebView(QWebEngineView):
...
@@ -764,14 +803,20 @@ class OverlayWebView(QWebEngineView):
logger
.
info
(
f
"OverlayWebView setup completed - Mesa: {is_mesa}, transparency configured"
)
logger
.
info
(
f
"OverlayWebView setup completed - Mesa: {is_mesa}, transparency configured"
)
# Setup WebChannel
# Setup WebChannel
logger
.
info
(
"Setting up WebChannel for overlay communication"
)
self
.
web_channel
=
QWebChannel
()
self
.
web_channel
=
QWebChannel
()
# Get message bus from parent window
# Get message bus from parent window
message_bus
=
None
message_bus
=
None
if
hasattr
(
self
.
parent
(),
'_message_bus'
):
if
hasattr
(
self
.
parent
(),
'_message_bus'
):
message_bus
=
self
.
parent
()
.
_message_bus
message_bus
=
self
.
parent
()
.
_message_bus
logger
.
info
(
"Message bus found for WebChannel"
)
else
:
logger
.
warning
(
"No message bus available for WebChannel - overlay communication may be limited"
)
self
.
overlay_channel
=
OverlayWebChannel
(
db_manager
=
self
.
db_manager
,
message_bus
=
message_bus
)
self
.
overlay_channel
=
OverlayWebChannel
(
db_manager
=
self
.
db_manager
,
message_bus
=
message_bus
)
self
.
web_channel
.
registerObject
(
"overlay"
,
self
.
overlay_channel
)
self
.
web_channel
.
registerObject
(
"overlay"
,
self
.
overlay_channel
)
page
.
setWebChannel
(
self
.
web_channel
)
page
.
setWebChannel
(
self
.
web_channel
)
logger
.
info
(
"WebChannel setup completed - overlay object registered as 'overlay'"
)
# Monitor GPU processes after WebEngine initialization
# Monitor GPU processes after WebEngine initialization
self
.
_monitor_gpu_processes
()
self
.
_monitor_gpu_processes
()
...
@@ -800,6 +845,8 @@ class OverlayWebView(QWebEngineView):
...
@@ -800,6 +845,8 @@ class OverlayWebView(QWebEngineView):
# Check Qt WebEngine GPU acceleration status
# Check Qt WebEngine GPU acceleration status
QTimer
.
singleShot
(
2000
,
self
.
_check_qt_webengine_gpu_status
)
QTimer
.
singleShot
(
2000
,
self
.
_check_qt_webengine_gpu_status
)
logger
.
info
(
"OverlayWebView setup completed successfully"
)
def
_enable_debug_console
(
self
,
ok
=
None
):
def
_enable_debug_console
(
self
,
ok
=
None
):
"""Enable debug console and ensure JavaScript overrides are active"""
"""Enable debug console and ensure JavaScript overrides are active"""
try
:
try
:
...
@@ -937,6 +984,20 @@ class OverlayWebView(QWebEngineView):
...
@@ -937,6 +984,20 @@ class OverlayWebView(QWebEngineView):
"""Load a specific template file, prioritizing uploaded templates"""
"""Load a specific template file, prioritizing uploaded templates"""
try
:
try
:
logger
.
info
(
f
"=== LOADING OVERLAY TEMPLATE: {template_name} ==="
)
logger
.
info
(
f
"=== LOADING OVERLAY TEMPLATE: {template_name} ==="
)
# Check if running in VirtualBox for additional diagnostics
is_virtualbox
=
False
try
:
with
open
(
'/sys/devices/virtual/dmi/id/sys_vendor'
,
'r'
)
as
f
:
vendor
=
f
.
read
()
.
strip
()
.
lower
()
if
'virtualbox'
in
vendor
or
'innotek'
in
vendor
:
is_virtualbox
=
True
except
:
pass
if
is_virtualbox
:
logger
.
warning
(
"VIRTUALBOX: Loading overlay template - potential compatibility issues"
)
if
self
.
debug_overlay
:
if
self
.
debug_overlay
:
logger
.
debug
(
f
"GREEN SCREEN DEBUG: Starting template load - {template_name}"
)
logger
.
debug
(
f
"GREEN SCREEN DEBUG: Starting template load - {template_name}"
)
logger
.
debug
(
f
"GREEN SCREEN DEBUG: Current page URL before load: {self.url().toString()}"
)
logger
.
debug
(
f
"GREEN SCREEN DEBUG: Current page URL before load: {self.url().toString()}"
)
...
@@ -944,6 +1005,7 @@ class OverlayWebView(QWebEngineView):
...
@@ -944,6 +1005,7 @@ class OverlayWebView(QWebEngineView):
# CRITICAL FIX: Store visibility state before template load
# CRITICAL FIX: Store visibility state before template load
was_visible
=
self
.
isVisible
()
was_visible
=
self
.
isVisible
()
logger
.
info
(
f
"Overlay visibility before template load: {was_visible}"
)
# If no template name provided, use default
# If no template name provided, use default
if
not
template_name
:
if
not
template_name
:
...
@@ -983,6 +1045,16 @@ class OverlayWebView(QWebEngineView):
...
@@ -983,6 +1045,16 @@ class OverlayWebView(QWebEngineView):
if
template_path
and
template_path
.
exists
():
if
template_path
and
template_path
.
exists
():
logger
.
info
(
f
"TEMPLATE FOUND: {template_path} (source: {template_source})"
)
logger
.
info
(
f
"TEMPLATE FOUND: {template_path} (source: {template_source})"
)
# Check template file permissions and size
try
:
stat_info
=
template_path
.
stat
()
logger
.
info
(
f
"Template file size: {stat_info.st_size} bytes"
)
logger
.
info
(
f
"Template file permissions: {oct(stat_info.st_mode)}"
)
logger
.
info
(
f
"Template file readable: {template_path.stat().st_mode & 0o400 != 0}"
)
except
Exception
as
stat_error
:
logger
.
warning
(
f
"Could not check template file stats: {stat_error}"
)
if
self
.
debug_overlay
:
if
self
.
debug_overlay
:
logger
.
debug
(
f
"GREEN SCREEN DEBUG: About to load template file: {template_path}"
)
logger
.
debug
(
f
"GREEN SCREEN DEBUG: About to load template file: {template_path}"
)
logger
.
debug
(
f
"GREEN SCREEN DEBUG: Template source: {template_source}"
)
logger
.
debug
(
f
"GREEN SCREEN DEBUG: Template source: {template_source}"
)
...
@@ -1011,12 +1083,28 @@ class OverlayWebView(QWebEngineView):
...
@@ -1011,12 +1083,28 @@ class OverlayWebView(QWebEngineView):
self
.
_load_fallback_overlay
()
self
.
_load_fallback_overlay
()
return
return
# Check WebEngine page state before loading
page
=
self
.
page
()
if
page
:
logger
.
info
(
"WebEngine page state before template load:"
)
logger
.
info
(
f
" - Page URL: {page.url().toString()}"
)
logger
.
info
(
f
" - Page title: {page.title()}"
)
logger
.
info
(
f
" - Page background color: {page.backgroundColor()}"
)
logger
.
info
(
f
" - WebChannel available: {page.webChannel() is not None}"
)
else
:
logger
.
warning
(
"No WebEngine page available before template load"
)
logger
.
info
(
f
"LOADING TEMPLATE INTO WEBENGINE: {template_path}"
)
logger
.
info
(
f
"LOADING TEMPLATE INTO WEBENGINE: {template_path}"
)
load_start_time
=
time
.
time
()
self
.
load
(
QUrl
.
fromLocalFile
(
str
(
template_path
)))
self
.
load
(
QUrl
.
fromLocalFile
(
str
(
template_path
)))
load_end_time
=
time
.
time
()
logger
.
info
(
f
"Template load initiated in {load_end_time - load_start_time:.3f} seconds"
)
self
.
current_template
=
template_name
self
.
current_template
=
template_name
# CRITICAL FIX: Force visibility recovery after template load
# CRITICAL FIX: Force visibility recovery after template load
if
was_visible
and
not
self
.
isVisible
():
if
was_visible
and
not
self
.
isVisible
():
logger
.
warning
(
"Overlay lost visibility after template load, recovering"
)
if
self
.
debug_overlay
:
if
self
.
debug_overlay
:
logger
.
debug
(
f
"GREEN SCREEN FIX: Recovering overlay visibility after template load"
)
logger
.
debug
(
f
"GREEN SCREEN FIX: Recovering overlay visibility after template load"
)
self
.
show
()
self
.
show
()
...
@@ -1046,6 +1134,10 @@ class OverlayWebView(QWebEngineView):
...
@@ -1046,6 +1134,10 @@ class OverlayWebView(QWebEngineView):
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
"=== TEMPLATE LOAD COMPLETED: {template_path} (source: {template_source}) ==="
)
logger
.
info
(
f
"=== TEMPLATE LOAD COMPLETED: {template_path} (source: {template_source}) ==="
)
# Schedule a check for template load success
QTimer
.
singleShot
(
1000
,
lambda
:
self
.
_check_template_load_success
(
template_name
))
else
:
else
:
logger
.
error
(
f
"No template found: {template_name}"
)
logger
.
error
(
f
"No template found: {template_name}"
)
# Load fallback minimal overlay
# Load fallback minimal overlay
...
@@ -1364,10 +1456,86 @@ class OverlayWebView(QWebEngineView):
...
@@ -1364,10 +1456,86 @@ class OverlayWebView(QWebEngineView):
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
error
(
f
"Failed to start GPU process monitoring: {e}"
)
logger
.
error
(
f
"Failed to start GPU process monitoring: {e}"
)
def
_check_template_load_success
(
self
,
expected_template
:
str
):
"""Check if template loaded successfully"""
try
:
logger
.
info
(
f
"=== TEMPLATE LOAD SUCCESS CHECK: {expected_template} ==="
)
page
=
self
.
page
()
if
page
:
current_url
=
page
.
url
()
.
toString
()
logger
.
info
(
f
"Current page URL: {current_url}"
)
logger
.
info
(
f
"Expected template: {expected_template}"
)
# Check if URL contains the expected template
if
expected_template
in
current_url
or
expected_template
.
replace
(
'.html'
,
''
)
in
current_url
:
logger
.
info
(
"✓ Template URL matches expected template"
)
else
:
logger
.
warning
(
f
"✗ Template URL mismatch - expected {expected_template}, got {current_url}"
)
# Check page title
title
=
page
.
title
()
logger
.
info
(
f
"Page title: '{title}'"
)
# Run JavaScript to check page content
page
.
runJavaScript
(
"""
console.log('TEMPLATE CHECK: Page content validation');
console.log('Document readyState:', document.readyState);
console.log('Body exists:', !!document.body);
if (document.body) {
console.log('Body children count:', document.body.children.length);
console.log('Body innerHTML length:', document.body.innerHTML.length);
console.log('Body background:', window.getComputedStyle(document.body).background);
console.log('Body display:', window.getComputedStyle(document.body).display);
console.log('Body visibility:', window.getComputedStyle(document.body).visibility);
// Check for critical overlay elements
const criticalElements = ['titleMain', 'titleSubtitle', 'tickerText'];
criticalElements.forEach(id => {
const el = document.getElementById(id);
console.log(`Element ${id}: ${el ? 'EXISTS' : 'MISSING'}`);
if (el) {
console.log(` - display: ${window.getComputedStyle(el).display}`);
console.log(` - visibility: ${window.getComputedStyle(el).visibility}`);
console.log(` - textContent: '${el.textContent}'`);
}
});
}
console.log('TEMPLATE CHECK: Validation complete');
"""
)
# Check WebChannel status
web_channel
=
page
.
webChannel
()
logger
.
info
(
f
"WebChannel available: {web_channel is not None}"
)
if
web_channel
:
logger
.
info
(
"WebChannel is properly configured"
)
else
:
logger
.
warning
(
"WebChannel not available - overlay communication will fail"
)
else
:
logger
.
error
(
"No WebEngine page available for template check"
)
logger
.
info
(
f
"=== END TEMPLATE LOAD SUCCESS CHECK ==="
)
except
Exception
as
e
:
logger
.
error
(
f
"Failed to check template load success: {e}"
)
def
_check_overlay_visibility
(
self
):
def
_check_overlay_visibility
(
self
):
"""Check overlay window visibility and positioning"""
"""Check overlay window visibility and positioning"""
try
:
try
:
logger
.
info
(
"=== OVERLAY VISIBILITY CHECK ==="
)
logger
.
info
(
"=== OVERLAY VISIBILITY CHECK ==="
)
# Check if running in VirtualBox
is_virtualbox
=
False
try
:
with
open
(
'/sys/devices/virtual/dmi/id/sys_vendor'
,
'r'
)
as
f
:
vendor
=
f
.
read
()
.
strip
()
.
lower
()
if
'virtualbox'
in
vendor
or
'innotek'
in
vendor
:
is_virtualbox
=
True
logger
.
warning
(
"VIRTUALBOX: Running overlay visibility check"
)
except
:
pass
logger
.
info
(
f
"Overlay window exists: {self.parent() is not None}"
)
logger
.
info
(
f
"Overlay window exists: {self.parent() is not None}"
)
if
self
.
parent
():
if
self
.
parent
():
parent
=
self
.
parent
()
parent
=
self
.
parent
()
...
@@ -1380,6 +1548,15 @@ class OverlayWebView(QWebEngineView):
...
@@ -1380,6 +1548,15 @@ class OverlayWebView(QWebEngineView):
logger
.
info
(
f
"Overlay WebView size: {self.size()}"
)
logger
.
info
(
f
"Overlay WebView size: {self.size()}"
)
logger
.
info
(
f
"Overlay WebView pos: {self.pos()}"
)
logger
.
info
(
f
"Overlay WebView pos: {self.pos()}"
)
# Check if overlay is properly positioned over parent
if
self
.
parent
():
parent_geo
=
self
.
parent
()
.
geometry
()
overlay_geo
=
self
.
geometry
()
if
parent_geo
==
overlay_geo
:
logger
.
info
(
"✓ Overlay properly positioned over parent window"
)
else
:
logger
.
warning
(
f
"✗ Overlay positioning mismatch - parent: {parent_geo}, overlay: {overlay_geo}"
)
# Check WebEngine page status
# Check WebEngine page status
page
=
self
.
page
()
page
=
self
.
page
()
if
page
:
if
page
:
...
@@ -1389,11 +1566,30 @@ class OverlayWebView(QWebEngineView):
...
@@ -1389,11 +1566,30 @@ class OverlayWebView(QWebEngineView):
# Check if page has content
# Check if page has content
page
.
runJavaScript
(
"""
page
.
runJavaScript
(
"""
console.log('OVERLAY VISIBILITY: Checking page content');
console.log('OVERLAY VISIBILITY: Checking page content');
console.log('Document readyState:', document.readyState);
console.log('Body exists:', !!document.body);
console.log('Body exists:', !!document.body);
if (document.body) {
if (document.body) {
console.log('Body innerHTML length:', document.body.innerHTML.length);
console.log('Body innerHTML length:', document.body.innerHTML.length);
console.log('Body background:', window.getComputedStyle(document.body).background);
console.log('Body background:', window.getComputedStyle(document.body).background);
console.log('Body opacity:', window.getComputedStyle(document.body).opacity);
console.log('Body opacity:', window.getComputedStyle(document.body).opacity);
console.log('Body display:', window.getComputedStyle(document.body).display);
console.log('Body visibility:', window.getComputedStyle(document.body).visibility);
// Check for VirtualBox-specific issues
console.log('Window devicePixelRatio:', window.devicePixelRatio);
console.log('Canvas support:', !!document.createElement('canvas').getContext);
console.log('WebGL support check:');
try {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
console.log(' WebGL available:', !!gl);
if (gl) {
console.log(' WebGL renderer:', gl.getParameter(gl.RENDERER));
console.log(' WebGL vendor:', gl.getParameter(gl.VENDOR));
}
} catch (e) {
console.log(' WebGL check failed:', e.message);
}
}
}
console.log('OVERLAY VISIBILITY: Page check complete');
console.log('OVERLAY VISIBILITY: Page check complete');
"""
)
"""
)
...
@@ -1411,10 +1607,35 @@ class OverlayWebView(QWebEngineView):
...
@@ -1411,10 +1607,35 @@ class OverlayWebView(QWebEngineView):
logger
.
info
(
f
"GPU Process: {proc}"
)
logger
.
info
(
f
"GPU Process: {proc}"
)
else
:
else
:
logger
.
warning
(
"NO Qt WebEngine GPU processes found - overlay may not be using GPU acceleration"
)
logger
.
warning
(
"NO Qt WebEngine GPU processes found - overlay may not be using GPU acceleration"
)
if
is_virtualbox
:
logger
.
warning
(
"VIRTUALBOX: This is expected - VirtualBox typically disables GPU acceleration"
)
else
:
else
:
logger
.
warning
(
"Could not check GPU processes for Qt WebEngine"
)
logger
.
warning
(
"Could not check GPU processes for Qt WebEngine"
)
if
is_virtualbox
:
logger
.
warning
(
"VIRTUALBOX: GPU process check failed - common in VirtualBox environment"
)
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
debug
(
f
"GPU process check failed: {e}"
)
logger
.
debug
(
f
"GPU process check failed: {e}"
)
if
is_virtualbox
:
logger
.
warning
(
"VIRTUALBOX: GPU monitoring not available - this is normal"
)
# Check for VirtualBox-specific display issues
if
is_virtualbox
:
logger
.
info
(
"VIRTUALBOX: Checking for display compatibility issues"
)
try
:
import
os
display
=
os
.
environ
.
get
(
'DISPLAY'
,
'not set'
)
logger
.
info
(
f
"VIRTUALBOX: DISPLAY environment: {display}"
)
# Check if X11 forwarding is working
if
display
.
startswith
(
':'
):
logger
.
info
(
"VIRTUALBOX: Local X11 display detected"
)
elif
display
.
startswith
(
'localhost:'
)
or
display
.
startswith
(
'127.0.0.1:'
):
logger
.
warning
(
"VIRTUALBOX: X11 forwarding detected - may cause overlay issues"
)
else
:
logger
.
warning
(
"VIRTUALBOX: Unusual DISPLAY setting - may indicate display issues"
)
except
Exception
as
e
:
logger
.
debug
(
f
"VIRTUALBOX: Display check failed: {e}"
)
logger
.
info
(
"=== END OVERLAY VISIBILITY CHECK ==="
)
logger
.
info
(
"=== END OVERLAY VISIBILITY CHECK ==="
)
...
@@ -1520,205 +1741,15 @@ class OverlayWebView(QWebEngineView):
...
@@ -1520,205 +1741,15 @@ class OverlayWebView(QWebEngineView):
class
NativeOverlayWidget
(
QWidget
):
"""Native Qt overlay widget - no WebEngine to prevent freezing"""
def
__init__
(
self
,
parent
=
None
):
super
()
.
__init__
(
parent
)
# Initialize overlay_data BEFORE setup_ui() to prevent attribute errors
self
.
overlay_data
=
{
'title'
:
'MbetterClient PyQt6 Player'
,
'subtitle'
:
'Ready for Content'
,
'ticker'
:
'Welcome to MbetterClient • Professional Video Overlay System • Real-time Updates'
,
'currentTime'
:
'00:00:00'
}
# Add overlay_channel as None for compatibility with WebEngine interface
self
.
overlay_channel
=
None
self
.
setup_ui
()
logger
.
info
(
"NativeOverlayWidget initialized"
)
def
setup_ui
(
self
):
"""Setup native Qt overlay widgets with forced visibility"""
logger
.
debug
(
"NativeOverlayWidget.setup_ui() - Starting"
)
# FORCE widget to be completely visible with bright background
self
.
setStyleSheet
(
"""
NativeOverlayWidget {
background-color: rgba(255, 0, 255, 200);
border: 5px solid cyan;
}
"""
)
# Ensure widget is visible
self
.
setVisible
(
True
)
self
.
setAutoFillBackground
(
True
)
self
.
raise_
()
# Bring to front
# Main title label - ENHANCED VISIBILITY
self
.
title_label
=
QLabel
()
self
.
title_label
.
setStyleSheet
(
"""
QLabel {
color: white;
font-size: 32px;
font-weight: bold;
background: rgba(0,0,0,0.8);
padding: 15px;
border-radius: 8px;
border: 2px solid rgba(255,255,255,0.3);
text-shadow: 2px 2px 4px rgba(0,0,0,1.0);
}
"""
)
self
.
title_label
.
setAlignment
(
Qt
.
AlignmentFlag
.
AlignCenter
)
self
.
title_label
.
setWordWrap
(
True
)
# Subtitle label - ENHANCED VISIBILITY
self
.
subtitle_label
=
QLabel
()
self
.
subtitle_label
.
setStyleSheet
(
"""
QLabel {
color: #ffffff;
font-size: 18px;
background: rgba(0,0,0,0.7);
padding: 10px;
border-radius: 5px;
text-shadow: 1px 1px 2px rgba(0,0,0,1.0);
}
"""
)
self
.
subtitle_label
.
setAlignment
(
Qt
.
AlignmentFlag
.
AlignCenter
)
# Time display - ENHANCED VISIBILITY
self
.
time_label
=
QLabel
()
self
.
time_label
.
setStyleSheet
(
"""
QLabel {
color: yellow;
font-size: 16px;
font-weight: bold;
background: rgba(0,0,0,0.8);
padding: 8px;
border-radius: 5px;
border: 1px solid yellow;
}
"""
)
# Ticker label - ENHANCED VISIBILITY
self
.
ticker_label
=
QLabel
()
self
.
ticker_label
.
setStyleSheet
(
"""
QLabel {
color: white;
font-size: 14px;
font-weight: bold;
background: rgba(220, 53, 69, 0.95);
padding: 12px;
border-radius: 5px;
border: 2px solid rgba(255,255,255,0.3);
}
"""
)
self
.
ticker_label
.
setWordWrap
(
True
)
# Layout
layout
=
QVBoxLayout
(
self
)
layout
.
setContentsMargins
(
20
,
20
,
20
,
20
)
# Top section
layout
.
addStretch
(
1
)
layout
.
addWidget
(
self
.
title_label
)
layout
.
addWidget
(
self
.
subtitle_label
)
layout
.
addStretch
(
2
)
# Bottom section
layout
.
addWidget
(
self
.
ticker_label
)
layout
.
addSpacing
(
10
)
# Position time label in top-right corner
self
.
time_label
.
setParent
(
self
)
# Update display with initial data
self
.
update_display
()
logger
.
debug
(
"NativeOverlayWidget UI setup completed"
)
def
resizeEvent
(
self
,
event
):
"""Handle resize events"""
super
()
.
resizeEvent
(
event
)
# Position time label in top-right corner
if
hasattr
(
self
,
'time_label'
)
and
self
.
time_label
:
self
.
time_label
.
adjustSize
()
self
.
time_label
.
move
(
self
.
width
()
-
self
.
time_label
.
width
()
-
20
,
20
)
def
update_overlay_data
(
self
,
data
:
Dict
[
str
,
Any
]):
"""Update overlay display with new data"""
try
:
updated
=
False
if
'title'
in
data
:
self
.
overlay_data
[
'title'
]
=
data
[
'title'
]
updated
=
True
if
'subtitle'
in
data
:
self
.
overlay_data
[
'subtitle'
]
=
data
[
'subtitle'
]
updated
=
True
if
'ticker'
in
data
:
self
.
overlay_data
[
'ticker'
]
=
data
[
'ticker'
]
updated
=
True
if
'currentTime'
in
data
:
self
.
overlay_data
[
'currentTime'
]
=
data
[
'currentTime'
]
updated
=
True
if
updated
:
self
.
update_display
()
logger
.
debug
(
f
"Native overlay updated: {data}"
)
except
Exception
as
e
:
logger
.
error
(
f
"Failed to update native overlay: {e}"
)
def
update_display
(
self
):
"""Update all display elements"""
try
:
self
.
title_label
.
setText
(
self
.
overlay_data
.
get
(
'title'
,
''
))
self
.
subtitle_label
.
setText
(
self
.
overlay_data
.
get
(
'subtitle'
,
''
))
self
.
ticker_label
.
setText
(
self
.
overlay_data
.
get
(
'ticker'
,
''
))
self
.
time_label
.
setText
(
self
.
overlay_data
.
get
(
'currentTime'
,
''
))
# Adjust time label position
self
.
time_label
.
adjustSize
()
if
self
.
width
()
>
0
:
self
.
time_label
.
move
(
self
.
width
()
-
self
.
time_label
.
width
()
-
20
,
20
)
except
Exception
as
e
:
logger
.
error
(
f
"Failed to update native overlay display: {e}"
)
def
update_position
(
self
,
position
:
float
,
duration
:
float
):
"""Update playback position (compatible with WebEngine interface)"""
try
:
current_time
=
self
.
format_time
(
position
)
total_time
=
self
.
format_time
(
duration
)
self
.
update_overlay_data
({
'currentTime'
:
f
"{current_time} / {total_time}"
})
except
Exception
as
e
:
logger
.
error
(
f
"Failed to update position in native overlay: {e}"
)
def
update_video_info
(
self
,
info
:
Dict
[
str
,
Any
]):
"""Update video information (compatible with WebEngine interface)"""
# Native overlay doesn't show detailed video stats by default
# This is for compatibility with the WebEngine interface
pass
def
format_time
(
self
,
seconds
:
float
)
->
str
:
"""Format time in seconds to MM:SS"""
mins
=
int
(
seconds
//
60
)
secs
=
int
(
seconds
%
60
)
return
f
"{mins:02d}:{secs:02d}"
class
VideoWidget
(
QWidget
):
class
VideoWidget
(
QWidget
):
"""
Composite video widget with selectable overlay type
"""
"""
Video widget for Qt multimedia player
"""
def
__init__
(
self
,
parent
=
None
,
use_native_overlay
=
False
):
def
__init__
(
self
,
parent
=
None
):
super
()
.
__init__
(
parent
)
super
()
.
__init__
(
parent
)
self
.
use_native_overlay
=
use_native_overlay
self
.
setup_ui
()
self
.
setup_ui
()
logger
.
info
(
f
"VideoWidget initialized (native_overlay={use_native_overlay})
"
)
logger
.
info
(
"VideoWidget initialized
"
)
def
setup_ui
(
self
):
def
setup_ui
(
self
):
"""Setup video player - ONLY video widget, overlay handled by PlayerWindow"""
"""Setup video player - ONLY video widget, overlay handled by PlayerWindow"""
...
@@ -1989,11 +2020,10 @@ class PlayerWindow(QMainWindow):
...
@@ -1989,11 +2020,10 @@ class PlayerWindow(QMainWindow):
layout
.
setSpacing
(
0
)
layout
.
setSpacing
(
0
)
# SIMPLE VIDEO WIDGET ONLY - overlay as separate top-level window
# SIMPLE VIDEO WIDGET ONLY - overlay as separate top-level window
overlay_type
=
"Native Qt Widgets"
if
self
.
settings
.
use_native_overlay
else
"QWebEngineView"
logger
.
info
(
"PlayerWindow: Using QWebEngineView overlay"
)
logger
.
info
(
f
"PlayerWindow: Overlay configuration - use_native_overlay={self.settings.use_native_overlay}, using {overlay_type}"
)
# Create simple video widget without any overlay (VideoWidget no longer handles overlays)
# Create simple video widget without any overlay (VideoWidget no longer handles overlays)
self
.
video_widget
=
VideoWidget
(
parent
=
central_widget
,
use_native_overlay
=
False
)
self
.
video_widget
=
VideoWidget
(
parent
=
central_widget
)
layout
.
addWidget
(
self
.
video_widget
,
1
)
layout
.
addWidget
(
self
.
video_widget
,
1
)
# THREADING FIXED: Re-enable overlay system with proper Qt main thread architecture
# THREADING FIXED: Re-enable overlay system with proper Qt main thread architecture
...
@@ -2023,18 +2053,13 @@ class PlayerWindow(QMainWindow):
...
@@ -2023,18 +2053,13 @@ class PlayerWindow(QMainWindow):
self
.
overlay_window
.
setStyleSheet
(
"background: transparent;"
)
self
.
overlay_window
.
setStyleSheet
(
"background: transparent;"
)
logger
.
info
(
"Hardware overlay window configured with native transparency (WA_TranslucentBackground=True)"
)
logger
.
info
(
"Hardware overlay window configured with native transparency (WA_TranslucentBackground=True)"
)
# Create overlay based on configuration - matching test_video_debug.py behavior
# Create overlay using QWebEngineView
if
self
.
settings
.
use_native_overlay
:
debug_overlay
=
getattr
(
self
,
'debug_overlay'
,
False
)
self
.
window_overlay
=
NativeOverlayWidget
(
self
.
overlay_window
)
web_server_url
=
self
.
_get_web_server_base_url
()
logger
.
debug
(
"PlayerWindow: Created NativeOverlayWidget overlay as separate window"
)
# Get database manager from message bus
else
:
db_manager
=
self
.
_get_database_manager
()
# Pass debug_overlay setting and web server URL to OverlayWebView
self
.
window_overlay
=
OverlayWebView
(
self
.
overlay_window
,
debug_overlay
=
debug_overlay
,
web_server_url
=
web_server_url
,
db_manager
=
db_manager
)
debug_overlay
=
getattr
(
self
,
'debug_overlay'
,
False
)
logger
.
debug
(
"PlayerWindow: Created QWebEngineView overlay as separate window"
)
web_server_url
=
self
.
_get_web_server_base_url
()
# Get database manager from message bus
db_manager
=
self
.
_get_database_manager
()
self
.
window_overlay
=
OverlayWebView
(
self
.
overlay_window
,
debug_overlay
=
debug_overlay
,
web_server_url
=
web_server_url
,
db_manager
=
db_manager
)
logger
.
debug
(
"PlayerWindow: Created QWebEngineView overlay as separate window"
)
# Layout for overlay window
# Layout for overlay window
overlay_layout
=
QVBoxLayout
(
self
.
overlay_window
)
overlay_layout
=
QVBoxLayout
(
self
.
overlay_window
)
...
@@ -2862,7 +2887,7 @@ class PlayerWindow(QMainWindow):
...
@@ -2862,7 +2887,7 @@ class PlayerWindow(QMainWindow):
def
_is_native_overlay
(
self
,
overlay_view
):
def
_is_native_overlay
(
self
,
overlay_view
):
"""Check if this is a native overlay"""
"""Check if this is a native overlay"""
return
isinstance
(
overlay_view
,
NativeOverlayWidget
)
return
False
# Native overlay removed
def
_clean_overlay_data
(
self
,
data
:
Dict
[
str
,
Any
])
->
Dict
[
str
,
Any
]:
def
_clean_overlay_data
(
self
,
data
:
Dict
[
str
,
Any
])
->
Dict
[
str
,
Any
]:
"""Clean overlay data by removing null/undefined values"""
"""Clean overlay data by removing null/undefined values"""
...
@@ -3682,8 +3707,7 @@ class QtVideoPlayer(QObject):
...
@@ -3682,8 +3707,7 @@ class QtVideoPlayer(QObject):
return
return
# Log current overlay configuration
# Log current overlay configuration
overlay_type
=
"Native Qt Widgets"
if
self
.
settings
.
use_native_overlay
else
"QWebEngineView"
logger
.
info
(
"Qt Player overlay configuration: QWebEngineView"
)
logger
.
info
(
f
"Qt Player overlay configuration: {overlay_type}"
)
# PERFECT: Now running directly on Qt main thread - no threading issues!
# PERFECT: Now running directly on Qt main thread - no threading issues!
logger
.
info
(
f
"Handler thread: {threading.current_thread().name}"
)
logger
.
info
(
f
"Handler thread: {threading.current_thread().name}"
)
...
...
mbetterclient/qt_player/template_watcher.py
View file @
a596debf
...
@@ -169,18 +169,18 @@ class TemplateWatcher(ThreadedComponent):
...
@@ -169,18 +169,18 @@ class TemplateWatcher(ThreadedComponent):
self
.
_scan_existing_templates
()
self
.
_scan_existing_templates
()
# Message processing loop
# Message processing loop
while
self
.
running
:
while
self
.
running
and
not
self
.
shutdown_event
.
is_set
()
:
try
:
try
:
# Process messages
# Process messages
message
=
self
.
message_bus
.
get_message
(
self
.
name
,
timeout
=
1.0
)
message
=
self
.
message_bus
.
get_message
(
self
.
name
,
timeout
=
1.0
)
if
message
:
if
message
:
self
.
_process_message
(
message
)
self
.
_process_message
(
message
)
# Update heartbeat
# Update heartbeat
self
.
heartbeat
()
self
.
heartbeat
()
time
.
sleep
(
0.1
)
time
.
sleep
(
0.1
)
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
error
(
f
"TemplateWatcher run loop error: {e}"
)
logger
.
error
(
f
"TemplateWatcher run loop error: {e}"
)
time
.
sleep
(
1.0
)
time
.
sleep
(
1.0
)
...
@@ -199,14 +199,23 @@ class TemplateWatcher(ThreadedComponent):
...
@@ -199,14 +199,23 @@ class TemplateWatcher(ThreadedComponent):
"""Shutdown template watcher"""
"""Shutdown template watcher"""
try
:
try
:
logger
.
info
(
"Shutting down TemplateWatcher..."
)
logger
.
info
(
"Shutting down TemplateWatcher..."
)
if
self
.
observer
:
if
self
.
observer
:
# Unschedule all handlers
for
handler
in
self
.
event_handlers
:
try
:
self
.
observer
.
unschedule
(
handler
)
except
Exception
:
pass
self
.
event_handlers
.
clear
()
self
.
observer
.
stop
()
self
.
observer
.
stop
()
self
.
observer
.
join
()
try
:
self
.
observer
.
join
(
timeout
=
2.0
)
except
Exception
:
pass
self
.
observer
=
None
self
.
observer
=
None
self
.
event_handler
=
None
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
error
(
f
"TemplateWatcher shutdown error: {e}"
)
logger
.
error
(
f
"TemplateWatcher shutdown error: {e}"
)
...
...
mbetterclient/web_dashboard/app.py
View file @
a596debf
...
@@ -432,25 +432,25 @@ class WebDashboard(ThreadedComponent):
...
@@ -432,25 +432,25 @@ class WebDashboard(ThreadedComponent):
server_thread
.
start
()
server_thread
.
start
()
# Message processing loop
# Message processing loop
while
self
.
running
:
while
self
.
running
and
not
self
.
shutdown_event
.
is_set
()
:
try
:
try
:
# Process messages
# Process messages
message
=
self
.
message_bus
.
get_message
(
self
.
name
,
timeout
=
1.0
)
message
=
self
.
message_bus
.
get_message
(
self
.
name
,
timeout
=
1.0
)
if
message
:
if
message
:
self
.
_process_message
(
message
)
self
.
_process_message
(
message
)
# Update heartbeat
# Update heartbeat
self
.
heartbeat
()
self
.
heartbeat
()
time
.
sleep
(
0.1
)
time
.
sleep
(
0.1
)
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
error
(
f
"WebDashboard run loop error: {e}"
)
logger
.
error
(
f
"WebDashboard run loop error: {e}"
)
time
.
sleep
(
1.0
)
time
.
sleep
(
1.0
)
# Wait for server thread
# Wait for server thread
to finish (with timeout since it's daemon)
if
server_thread
.
is_alive
():
if
server_thread
and
server_thread
.
is_alive
():
server_thread
.
join
(
timeout
=
5
.0
)
server_thread
.
join
(
timeout
=
2
.0
)
except
Exception
as
e
:
except
Exception
as
e
:
logger
.
error
(
f
"WebDashboard run failed: {e}"
)
logger
.
error
(
f
"WebDashboard run failed: {e}"
)
...
@@ -527,7 +527,8 @@ class WebDashboard(ThreadedComponent):
...
@@ -527,7 +527,8 @@ class WebDashboard(ThreadedComponent):
if
self
.
server
:
if
self
.
server
:
logger
.
info
(
"Shutting down HTTP server..."
)
logger
.
info
(
"Shutting down HTTP server..."
)
self
.
server
.
shutdown
()
self
.
server
.
shutdown
()
logger
.
info
(
"HTTP server shutdown initiated"
)
self
.
server
.
server_close
()
logger
.
info
(
"HTTP server shutdown completed"
)
# Note: SocketIO connections will be closed when the server shuts down
# Note: SocketIO connections will be closed when the server shuts down
# No explicit SocketIO shutdown needed as it's handled by the WSGI server
# No explicit SocketIO shutdown needed as it's handled by the WSGI server
...
...
mbetterclient/web_dashboard/static/overlay.js
View file @
a596debf
...
@@ -3,20 +3,106 @@ class OverlayManager {
...
@@ -3,20 +3,106 @@ class OverlayManager {
this
.
overlayData
=
{};
this
.
overlayData
=
{};
this
.
pendingUpdates
=
[];
this
.
pendingUpdates
=
[];
this
.
webChannelReady
=
false
;
this
.
webChannelReady
=
false
;
// Detect VirtualBox environment
this
.
isVirtualBox
=
this
.
detectVirtualBox
();
if
(
this
.
isVirtualBox
)
{
console
.
warn
(
'VIRTUALBOX DETECTED: Overlay may have compatibility issues'
);
console
.
warn
(
'Common VirtualBox overlay problems:'
);
console
.
warn
(
'1. GPU acceleration disabled'
);
console
.
warn
(
'2. WebGL not available'
);
console
.
warn
(
'3. Qt WebEngine software rendering fallback'
);
console
.
warn
(
'4. Transparency effects may fail'
);
console
.
warn
(
'5. WebChannel communication may be unreliable'
);
}
this
.
initWebChannel
();
this
.
initWebChannel
();
this
.
initCanvas
();
this
.
initCanvas
();
}
}
detectVirtualBox
()
{
// Check for VirtualBox-specific indicators
try
{
// Check user agent for VirtualBox
if
(
navigator
.
userAgent
&&
navigator
.
userAgent
.
includes
(
'VirtualBox'
))
{
return
true
;
}
// Check for VirtualBox-specific plugins or extensions
if
(
navigator
.
plugins
)
{
for
(
let
i
=
0
;
i
<
navigator
.
plugins
.
length
;
i
++
)
{
if
(
navigator
.
plugins
[
i
].
name
&&
navigator
.
plugins
[
i
].
name
.
includes
(
'VirtualBox'
))
{
return
true
;
}
}
}
// Check for VirtualBox guest additions in renderer info
const
canvas
=
document
.
createElement
(
'canvas'
);
const
gl
=
canvas
.
getContext
(
'webgl'
)
||
canvas
.
getContext
(
'experimental-webgl'
);
if
(
gl
)
{
const
renderer
=
gl
.
getParameter
(
gl
.
RENDERER
);
if
(
renderer
&&
(
renderer
.
includes
(
'VirtualBox'
)
||
renderer
.
includes
(
'VBox'
)))
{
return
true
;
}
}
// Check for Mesa software rendering (common in VirtualBox)
if
(
gl
)
{
const
vendor
=
gl
.
getParameter
(
gl
.
VENDOR
);
const
renderer
=
gl
.
getParameter
(
gl
.
RENDERER
);
if
((
vendor
&&
vendor
.
includes
(
'Mesa'
))
||
(
renderer
&&
(
renderer
.
includes
(
'Software'
)
||
renderer
.
includes
(
'Mesa'
))))
{
console
.
warn
(
'MESA SOFTWARE RENDERING DETECTED - likely VirtualBox environment'
);
return
true
;
}
}
return
false
;
}
catch
(
e
)
{
console
.
debug
(
'VirtualBox detection failed:'
,
e
);
return
false
;
}
}
initWebChannel
()
{
initWebChannel
()
{
try
{
try
{
console
.
log
(
'OVERLAY: Starting WebChannel initialization'
);
console
.
log
(
'OVERLAY: VirtualBox detected:'
,
this
.
isVirtualBox
);
if
(
this
.
isVirtualBox
)
{
console
.
warn
(
'VIRTUALBOX: WebChannel initialization - potential communication issues'
);
}
// Log browser/environment information
console
.
log
(
'OVERLAY: Browser environment check:'
);
console
.
log
(
' - User agent:'
,
navigator
.
userAgent
);
console
.
log
(
' - Platform:'
,
navigator
.
platform
);
console
.
log
(
' - Cookie enabled:'
,
navigator
.
cookieEnabled
);
console
.
log
(
' - OnLine:'
,
navigator
.
onLine
);
console
.
log
(
' - Qt object available:'
,
typeof
Qt
!==
'undefined'
);
console
.
log
(
' - qt object available:'
,
typeof
qt
!==
'undefined'
);
console
.
log
(
' - QWebChannel available:'
,
typeof
QWebChannel
!==
'undefined'
);
// Wait for DOM to be fully loaded
// Wait for DOM to be fully loaded
this
.
waitForFullInitialization
(()
=>
{
this
.
waitForFullInitialization
(()
=>
{
console
.
log
(
'OVERLAY: DOM fully loaded, checking Qt WebChannel transport'
);
// Check if Qt WebChannel transport is available (set by QWebEnginePage.setWebChannel)
// Check if Qt WebChannel transport is available (set by QWebEnginePage.setWebChannel)
if
(
typeof
qt
!==
'undefined'
&&
qt
.
webChannelTransport
)
{
if
(
typeof
qt
!==
'undefined'
&&
qt
.
webChannelTransport
)
{
console
.
log
(
'OVERLAY: Qt WebChannel transport available, initializing channel'
);
// Use the transport provided by Qt WebEngine
// Use the transport provided by Qt WebEngine
new
QWebChannel
(
qt
.
webChannelTransport
,
(
channel
)
=>
{
new
QWebChannel
(
qt
.
webChannelTransport
,
(
channel
)
=>
{
console
.
log
(
'OVERLAY: QWebChannel created successfully'
);
// Connect to overlay object
// Connect to overlay object
window
.
overlay
=
channel
.
objects
.
overlay
;
window
.
overlay
=
channel
.
objects
.
overlay
;
console
.
log
(
'OVERLAY: Connected to overlay object:'
,
!!
window
.
overlay
);
if
(
this
.
isVirtualBox
)
{
console
.
warn
(
'VIRTUALBOX: WebChannel connected - testing communication reliability'
);
}
// Flush any buffered console messages
// Flush any buffered console messages
if
(
window
.
flushConsoleBuffer
)
{
if
(
window
.
flushConsoleBuffer
)
{
...
@@ -25,94 +111,128 @@ class OverlayManager {
...
@@ -25,94 +111,128 @@ class OverlayManager {
// Connect signals if overlay object exists
// Connect signals if overlay object exists
if
(
window
.
overlay
)
{
if
(
window
.
overlay
)
{
console
.
log
(
'OVERLAY: Setting up signal connections'
);
// Connect positionChanged signal
// Connect positionChanged signal
if
(
window
.
overlay
.
positionChanged
)
{
if
(
window
.
overlay
.
positionChanged
)
{
window
.
overlay
.
positionChanged
.
connect
((
position
,
duration
)
=>
{
window
.
overlay
.
positionChanged
.
connect
((
position
,
duration
)
=>
{
console
.
log
(
'OVERLAY: positionChanged signal received:'
,
position
,
duration
);
if
(
position
!==
null
&&
duration
!==
null
)
{
if
(
position
!==
null
&&
duration
!==
null
)
{
this
.
updateProgress
(
position
,
duration
);
this
.
updateProgress
(
position
,
duration
);
}
else
{
}
else
{
console
.
warn
(
'positionChanged signal received null/undefined parameters, skipping'
);
console
.
warn
(
'positionChanged signal received null/undefined parameters, skipping'
);
}
}
});
});
console
.
log
(
'OVERLAY: positionChanged signal connected'
);
}
else
{
console
.
warn
(
'OVERLAY: positionChanged signal not available'
);
}
}
// Connect videoInfoChanged signal
// Connect videoInfoChanged signal
if
(
window
.
overlay
.
videoInfoChanged
)
{
if
(
window
.
overlay
.
videoInfoChanged
)
{
window
.
overlay
.
videoInfoChanged
.
connect
((
info
)
=>
{
window
.
overlay
.
videoInfoChanged
.
connect
((
info
)
=>
{
console
.
log
(
'OVERLAY: videoInfoChanged signal received:'
,
info
);
if
(
info
&&
typeof
info
===
'object'
)
{
if
(
info
&&
typeof
info
===
'object'
)
{
this
.
updateVideoInfo
(
info
);
this
.
updateVideoInfo
(
info
);
}
else
{
}
else
{
console
.
warn
(
'videoInfoChanged signal received null/undefined parameter, skipping'
);
console
.
warn
(
'videoInfoChanged signal received null/undefined parameter, skipping'
);
}
}
});
});
console
.
log
(
'OVERLAY: videoInfoChanged signal connected'
);
}
else
{
console
.
warn
(
'OVERLAY: videoInfoChanged signal not available'
);
}
}
// Connect dataUpdated signal for templates that need it (like results.html)
// Connect dataUpdated signal for templates that need it (like results.html)
if
(
window
.
overlay
.
dataUpdated
)
{
if
(
window
.
overlay
.
dataUpdated
)
{
window
.
overlay
.
dataUpdated
.
connect
((
data
)
=>
{
window
.
overlay
.
dataUpdated
.
connect
((
data
)
=>
{
console
.
log
(
'OVERLAY: dataUpdated signal received:'
,
data
);
if
(
data
!==
null
&&
data
!==
undefined
)
{
if
(
data
!==
null
&&
data
!==
undefined
)
{
// Call a global callback if it exists (for results.html and other templates)
// Call a global callback if it exists (for results.html and other templates)
if
(
window
.
onDataUpdated
)
{
if
(
window
.
onDataUpdated
)
{
window
.
onDataUpdated
(
data
);
window
.
onDataUpdated
(
data
);
}
}
console
.
log
(
'dataUpdated signal
received:'
,
data
);
console
.
log
(
'dataUpdated signal
processed'
);
}
else
{
}
else
{
console
.
warn
(
'dataUpdated signal received null/undefined data'
);
console
.
warn
(
'dataUpdated signal received null/undefined data'
);
}
}
});
});
console
.
log
(
'OVERLAY: dataUpdated signal connected'
);
}
else
{
console
.
warn
(
'OVERLAY: dataUpdated signal not available'
);
}
}
// Test WebChannel communication
this
.
testWebChannelCommunication
();
// Mark WebChannel as ready
// Mark WebChannel as ready
this
.
webChannelReady
=
true
;
this
.
webChannelReady
=
true
;
console
.
log
(
'OVERLAY: WebChannel fully initialized and ready'
);
// Process pending updates after full initialization
// Process pending updates after full initialization
setTimeout
(()
=>
this
.
processPendingUpdates
(),
100
);
setTimeout
(()
=>
this
.
processPendingUpdates
(),
100
);
console
.
log
(
'WebChannel connected and ready'
);
console
.
log
(
'WebChannel connected and ready'
);
}
else
{
}
else
{
console
.
warn
(
'Overlay object not found in WebChannel'
);
console
.
error
(
'OVERLAY: Overlay object not found in WebChannel'
);
if
(
this
.
isVirtualBox
)
{
console
.
error
(
'VIRTUALBOX: WebChannel object missing - communication setup failed'
);
}
}
}
});
});
}
else
{
}
else
{
console
.
warn
(
'Qt WebChannel transport not available, falling back to QtWebChannel constructor'
);
console
.
warn
(
'OVERLAY: Qt WebChannel transport not available, falling back to QtWebChannel constructor'
);
if
(
this
.
isVirtualBox
)
{
console
.
warn
(
'VIRTUALBOX: Primary WebChannel transport failed, trying fallback'
);
}
// Fallback: try the old method
// Fallback: try the old method
if
(
typeof
Qt
===
'object'
&&
typeof
QtWebChannel
===
'function'
)
{
if
(
typeof
Qt
===
'object'
&&
typeof
QtWebChannel
===
'function'
)
{
console
.
log
(
'OVERLAY: Using QtWebChannel fallback method'
);
const
channel
=
new
QtWebChannel
();
const
channel
=
new
QtWebChannel
();
channel
.
connectTo
(
'overlay'
,
(
overlay
)
=>
{
channel
.
connectTo
(
'overlay'
,
(
overlay
)
=>
{
console
.
log
(
'OVERLAY: Fallback WebChannel connected'
);
window
.
overlay
=
overlay
;
window
.
overlay
=
overlay
;
// Connect positionChanged signal
// Connect signals (same as above)
overlay
.
positionChanged
.
connect
((
position
,
duration
)
=>
{
if
(
overlay
.
positionChanged
)
{
if
(
position
!==
null
&&
duration
!==
null
)
{
overlay
.
positionChanged
.
connect
((
position
,
duration
)
=>
{
this
.
updateProgress
(
position
,
duration
);
console
.
log
(
'OVERLAY: [FALLBACK] positionChanged signal received:'
,
position
,
duration
);
}
else
{
if
(
position
!==
null
&&
duration
!==
null
)
{
console
.
warn
(
'positionChanged signal received null/undefined parameters, skipping'
);
this
.
updateProgress
(
position
,
duration
);
}
}
else
{
});
console
.
warn
(
'positionChanged signal received null/undefined parameters, skipping'
);
}
});
}
// Connect videoInfoChanged signal
if
(
overlay
.
videoInfoChanged
)
{
overlay
.
videoInfoChanged
.
connect
((
info
)
=>
{
overlay
.
videoInfoChanged
.
connect
((
info
)
=>
{
if
(
info
&&
typeof
info
===
'object'
)
{
console
.
log
(
'OVERLAY: [FALLBACK] videoInfoChanged signal received:'
,
info
);
this
.
updateVideoInfo
(
info
);
if
(
info
&&
typeof
info
===
'object'
)
{
}
else
{
this
.
updateVideoInfo
(
info
);
console
.
warn
(
'videoInfoChanged signal received null/undefined parameter, skipping'
);
}
else
{
}
console
.
warn
(
'videoInfoChanged signal received null/undefined parameter, skipping'
);
});
}
});
}
// Connect dataUpdated signal for templates that need it (like results.html)
if
(
overlay
.
dataUpdated
)
{
if
(
overlay
.
dataUpdated
)
{
overlay
.
dataUpdated
.
connect
((
data
)
=>
{
overlay
.
dataUpdated
.
connect
((
data
)
=>
{
console
.
log
(
'OVERLAY: [FALLBACK] dataUpdated signal received:'
,
data
);
if
(
data
!==
null
&&
data
!==
undefined
)
{
if
(
data
!==
null
&&
data
!==
undefined
)
{
// Call a global callback if it exists (for results.html and other templates)
if
(
window
.
onDataUpdated
)
{
if
(
window
.
onDataUpdated
)
{
window
.
onDataUpdated
(
data
);
window
.
onDataUpdated
(
data
);
}
}
console
.
log
(
'dataUpdated signal
received:'
,
data
);
console
.
log
(
'dataUpdated signal
processed'
);
}
else
{
}
else
{
console
.
warn
(
'dataUpdated signal received null/undefined data'
);
console
.
warn
(
'dataUpdated signal received null/undefined data'
);
}
}
});
});
}
}
// Test WebChannel communication
this
.
testWebChannelCommunication
();
// Mark WebChannel as ready
// Mark WebChannel as ready
this
.
webChannelReady
=
true
;
this
.
webChannelReady
=
true
;
...
@@ -121,19 +241,54 @@ class OverlayManager {
...
@@ -121,19 +241,54 @@ class OverlayManager {
console
.
log
(
'WebChannel connected via fallback method'
);
console
.
log
(
'WebChannel connected via fallback method'
);
});
});
}
else
{
}
else
{
console
.
warn
(
'QtWebChannel not available either'
);
console
.
error
(
'OVERLAY: QtWebChannel not available either'
);
if
(
this
.
isVirtualBox
)
{
console
.
error
(
'VIRTUALBOX: Both WebChannel methods failed - overlays will not work'
);
}
// Retry with exponential backoff
// Retry with exponential backoff
setTimeout
(()
=>
this
.
initWebChannel
(),
1000
);
setTimeout
(()
=>
this
.
initWebChannel
(),
1000
);
}
}
}
}
});
});
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
'WebChannel initialization error:'
,
error
);
console
.
error
(
'OVERLAY: WebChannel initialization error:'
,
error
);
if
(
this
.
isVirtualBox
)
{
console
.
error
(
'VIRTUALBOX: WebChannel initialization failed - overlays disabled'
);
}
// Retry with exponential backoff
// Retry with exponential backoff
setTimeout
(()
=>
this
.
initWebChannel
(),
1000
);
setTimeout
(()
=>
this
.
initWebChannel
(),
1000
);
}
}
}
}
testWebChannelCommunication
()
{
try
{
console
.
log
(
'OVERLAY: Testing WebChannel communication'
);
if
(
window
.
overlay
&&
window
.
overlay
.
log
)
{
// Test basic communication
window
.
overlay
.
log
(
'OVERLAY: WebChannel communication test - basic log message'
);
// Test data retrieval if available
if
(
window
.
overlay
.
getCurrentData
)
{
window
.
overlay
.
getCurrentData
().
then
(
result
=>
{
console
.
log
(
'OVERLAY: WebChannel data retrieval test successful:'
,
result
);
}).
catch
(
error
=>
{
console
.
error
(
'OVERLAY: WebChannel data retrieval test failed:'
,
error
);
if
(
this
.
isVirtualBox
)
{
console
.
error
(
'VIRTUALBOX: WebChannel data retrieval failed - communication unreliable'
);
}
});
}
console
.
log
(
'OVERLAY: WebChannel communication test completed'
);
}
else
{
console
.
error
(
'OVERLAY: Cannot test WebChannel - overlay object or log method missing'
);
}
}
catch
(
error
)
{
console
.
error
(
'OVERLAY: WebChannel communication test failed:'
,
error
);
}
}
waitForFullInitialization
(
callback
)
{
waitForFullInitialization
(
callback
)
{
const
checkReady
=
()
=>
{
const
checkReady
=
()
=>
{
if
(
document
.
readyState
===
'complete'
&&
this
.
validateCriticalElements
())
{
if
(
document
.
readyState
===
'complete'
&&
this
.
validateCriticalElements
())
{
...
@@ -442,15 +597,117 @@ class OverlayManager {
...
@@ -442,15 +597,117 @@ class OverlayManager {
}
}
initCanvas
()
{
initCanvas
()
{
console
.
log
(
'OVERLAY: Initializing canvas for diagnostics'
);
this
.
canvas
=
document
.
getElementById
(
'canvasOverlay'
);
this
.
canvas
=
document
.
getElementById
(
'canvasOverlay'
);
this
.
ctx
=
this
.
canvas
?
this
.
canvas
.
getContext
(
'2d'
)
:
null
;
this
.
ctx
=
this
.
canvas
?
this
.
canvas
.
getContext
(
'2d'
)
:
null
;
this
.
animationFrame
=
null
;
this
.
animationFrame
=
null
;
console
.
log
(
'OVERLAY: Canvas element found:'
,
!!
this
.
canvas
);
console
.
log
(
'OVERLAY: Canvas 2D context available:'
,
!!
this
.
ctx
);
if
(
this
.
isVirtualBox
)
{
console
.
warn
(
'VIRTUALBOX: Canvas initialization - checking WebGL support'
);
this
.
checkWebGLSupport
();
}
// Initialize canvas dimensions
// Initialize canvas dimensions
this
.
resizeCanvas
();
this
.
resizeCanvas
();
// Setup resize listener
// Setup resize listener
window
.
addEventListener
(
'resize'
,
()
=>
this
.
resizeCanvas
());
window
.
addEventListener
(
'resize'
,
()
=>
this
.
resizeCanvas
());
// Run rendering diagnostics
this
.
runRenderingDiagnostics
();
}
checkWebGLSupport
()
{
try
{
console
.
log
(
'VIRTUALBOX: Checking WebGL support for overlay rendering'
);
const
canvas
=
document
.
createElement
(
'canvas'
);
const
gl
=
canvas
.
getContext
(
'webgl'
)
||
canvas
.
getContext
(
'experimental-webgl'
);
console
.
log
(
'VIRTUALBOX: WebGL context available:'
,
!!
gl
);
if
(
gl
)
{
const
debugInfo
=
gl
.
getExtension
(
'WEBGL_debug_renderer_info'
);
if
(
debugInfo
)
{
const
renderer
=
gl
.
getParameter
(
debugInfo
.
UNMASKED_RENDERER_WEBGL
);
const
vendor
=
gl
.
getParameter
(
debugInfo
.
UNMASKED_VENDOR_WEBGL
);
console
.
log
(
'VIRTUALBOX: WebGL renderer:'
,
renderer
);
console
.
log
(
'VIRTUALBOX: WebGL vendor:'
,
vendor
);
// Check for software rendering indicators
if
(
renderer
&&
(
renderer
.
includes
(
'Software'
)
||
renderer
.
includes
(
'Mesa'
)
||
renderer
.
includes
(
'LLVM'
)))
{
console
.
warn
(
'VIRTUALBOX: SOFTWARE WEBGL RENDERING DETECTED - overlays may not display properly'
);
console
.
warn
(
'VIRTUALBOX: This is the likely cause of invisible overlays'
);
}
}
else
{
console
.
log
(
'VIRTUALBOX: WebGL debug info not available'
);
}
// Test basic WebGL functionality
const
vertexShader
=
gl
.
createShader
(
gl
.
VERTEX_SHADER
);
const
fragmentShader
=
gl
.
createShader
(
gl
.
FRAGMENT_SHADER
);
console
.
log
(
'VIRTUALBOX: WebGL shader creation works:'
,
!!
(
vertexShader
&&
fragmentShader
));
}
else
{
console
.
error
(
'VIRTUALBOX: WebGL not available - overlays will fail to render'
);
console
.
error
(
'VIRTUALBOX: This is the primary cause of overlay visibility issues'
);
}
}
catch
(
error
)
{
console
.
error
(
'VIRTUALBOX: WebGL check failed:'
,
error
);
}
}
runRenderingDiagnostics
()
{
try
{
console
.
log
(
'OVERLAY: Running rendering diagnostics'
);
// Check CSS rendering capabilities
const
testElement
=
document
.
createElement
(
'div'
);
testElement
.
style
.
position
=
'absolute'
;
testElement
.
style
.
left
=
'-9999px'
;
testElement
.
style
.
background
=
'rgba(255, 0, 0, 0.5)'
;
testElement
.
style
.
width
=
'100px'
;
testElement
.
style
.
height
=
'100px'
;
document
.
body
.
appendChild
(
testElement
);
// Test CSS transparency
const
computedStyle
=
window
.
getComputedStyle
(
testElement
);
console
.
log
(
'OVERLAY: CSS rgba() support:'
,
computedStyle
.
background
.
includes
(
'rgba'
));
// Test CSS transforms
testElement
.
style
.
transform
=
'translate3d(0, 0, 0)'
;
const
hasTransform
=
computedStyle
.
transform
&&
computedStyle
.
transform
!==
'none'
;
console
.
log
(
'OVERLAY: CSS 3D transforms support:'
,
hasTransform
);
if
(
this
.
isVirtualBox
&&
!
hasTransform
)
{
console
.
warn
(
'VIRTUALBOX: CSS 3D transforms not supported - overlay positioning may fail'
);
}
// Clean up
document
.
body
.
removeChild
(
testElement
);
// Check for compositor issues
console
.
log
(
'OVERLAY: Window device pixel ratio:'
,
window
.
devicePixelRatio
);
console
.
log
(
'OVERLAY: Screen color depth:'
,
screen
.
colorDepth
);
console
.
log
(
'OVERLAY: Screen pixel depth:'
,
screen
.
pixelDepth
);
if
(
this
.
isVirtualBox
)
{
if
(
window
.
devicePixelRatio
!==
1
)
{
console
.
warn
(
'VIRTUALBOX: Non-standard device pixel ratio detected - may cause rendering issues'
);
}
}
console
.
log
(
'OVERLAY: Rendering diagnostics completed'
);
}
catch
(
error
)
{
console
.
error
(
'OVERLAY: Rendering diagnostics failed:'
,
error
);
}
}
}
resizeCanvas
()
{
resizeCanvas
()
{
...
...
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