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
d4927b64
Commit
d4927b64
authored
Dec 04, 2025
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixed crashes
parent
89aa1260
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
697 additions
and
12 deletions
+697
-12
player.py
mbetterclient/qt_player/player.py
+16
-0
web_player.html
mbetterclient/qt_player/web_player_assets/web_player.html
+1
-1
web_player.js
mbetterclient/qt_player/web_player_assets/web_player.js
+98
-1
routes.py
mbetterclient/web_dashboard/routes.py
+582
-10
No files found.
mbetterclient/qt_player/player.py
View file @
d4927b64
...
...
@@ -1998,6 +1998,22 @@ class WebPlayerWindow(QMainWindow):
def
setup_web_player
(
self
):
"""Setup web player with QWebEngineView"""
# Configure web engine settings to allow autoplay
from
PyQt6.QtWebEngineCore
import
QWebEngineSettings
settings
=
self
.
web_player_view
.
settings
()
settings
.
setAttribute
(
QWebEngineSettings
.
WebAttribute
.
PlaybackRequiresUserGesture
,
False
)
settings
.
setAttribute
(
QWebEngineSettings
.
WebAttribute
.
AllowRunningInsecureContent
,
True
)
settings
.
setAttribute
(
QWebEngineSettings
.
WebAttribute
.
AllowGeolocationOnInsecureOrigins
,
True
)
settings
.
setAttribute
(
QWebEngineSettings
.
WebAttribute
.
LocalContentCanAccessRemoteUrls
,
True
)
settings
.
setAttribute
(
QWebEngineSettings
.
WebAttribute
.
LocalContentCanAccessFileUrls
,
True
)
logger
.
info
(
"Configured web engine settings to allow autoplay and local content"
)
# Configure web engine profile for local file access
profile
=
self
.
web_player_view
.
page
()
.
profile
()
profile
.
setHttpCacheType
(
QWebEngineProfile
.
HttpCacheType
.
NoCache
)
profile
.
setPersistentCookiesPolicy
(
QWebEngineProfile
.
PersistentCookiesPolicy
.
NoPersistentCookies
)
logger
.
info
(
"Configured web engine profile for local file access"
)
# Get web player HTML path
web_player_html_path
=
self
.
_get_web_player_html_path
()
...
...
mbetterclient/qt_player/web_player_assets/web_player.html
View file @
d4927b64
...
...
@@ -266,7 +266,7 @@
<body>
<!-- Video container -->
<div
class=
"video-container"
>
<video
id=
"webVideoPlayer"
controls
autoplay
playsinline
>
<video
id=
"webVideoPlayer"
controls
playsinline
muted
>
<source
src=
""
type=
"video/mp4"
>
Your browser does not support the video tag.
</video>
...
...
mbetterclient/qt_player/web_player_assets/web_player.js
View file @
d4927b64
...
...
@@ -191,6 +191,26 @@ class WebPlayerAPI {
console
.
log
(
'Video readyState:'
,
this
.
videoElement
.
readyState
);
console
.
log
(
'Video networkState:'
,
this
.
videoElement
.
networkState
);
console
.
log
(
'Video error:'
,
this
.
videoElement
.
error
);
// For debug mode, ensure video is visible and properly sized
if
(
window
.
location
.
href
.
includes
(
'debug'
)
||
window
.
location
.
href
.
includes
(
'debug-player'
))
{
console
.
log
(
'DEBUG MODE: Ensuring video visibility'
);
this
.
videoElement
.
style
.
display
=
'block'
;
this
.
videoElement
.
style
.
visibility
=
'visible'
;
this
.
videoElement
.
style
.
opacity
=
'1'
;
this
.
videoElement
.
style
.
width
=
'100%'
;
this
.
videoElement
.
style
.
height
=
'100%'
;
this
.
videoElement
.
style
.
objectFit
=
'contain'
;
// Force video element to be visible in debug mode
const
videoContainer
=
document
.
querySelector
(
'.video-container'
);
if
(
videoContainer
)
{
videoContainer
.
style
.
display
=
'block'
;
videoContainer
.
style
.
visibility
=
'visible'
;
videoContainer
.
style
.
opacity
=
'1'
;
videoContainer
.
style
.
zIndex
=
'100'
;
}
}
}
// Attempt playback with autoplay policy handling
...
...
@@ -201,9 +221,21 @@ class WebPlayerAPI {
if
(
this
.
videoElement
.
readyState
>=
HTMLMediaElement
.
HAVE_FUTURE_DATA
)
{
console
.
log
(
'Video ready, attempting playback...'
);
// For debug mode, ensure video is visible before attempting playback
if
(
window
.
location
.
href
.
includes
(
'debug'
)
||
window
.
location
.
href
.
includes
(
'debug-player'
))
{
console
.
log
(
'DEBUG MODE: Ensuring video is visible before playback'
);
this
.
videoElement
.
style
.
display
=
'block'
;
this
.
videoElement
.
style
.
visibility
=
'visible'
;
this
.
videoElement
.
style
.
opacity
=
'1'
;
}
// Try to play with autoplay policy handling
this
.
videoElement
.
play
().
then
(()
=>
{
console
.
log
(
'Playback started successfully'
);
// Ensure video is visible after successful playback
this
.
videoElement
.
style
.
display
=
'block'
;
this
.
videoElement
.
style
.
visibility
=
'visible'
;
this
.
videoElement
.
style
.
opacity
=
'1'
;
}).
catch
(
e
=>
{
console
.
error
(
'Playback failed (likely due to autoplay policy):'
,
e
);
...
...
@@ -211,6 +243,14 @@ class WebPlayerAPI {
this
.
videoElement
.
controls
=
true
;
this
.
videoElement
.
muted
=
true
;
// Muted videos can often autoplay
// For debug mode, ensure video is visible even if autoplay fails
if
(
window
.
location
.
href
.
includes
(
'debug'
)
||
window
.
location
.
href
.
includes
(
'debug-player'
))
{
console
.
log
(
'DEBUG MODE: Forcing video visibility despite autoplay block'
);
this
.
videoElement
.
style
.
display
=
'block'
;
this
.
videoElement
.
style
.
visibility
=
'visible'
;
this
.
videoElement
.
style
.
opacity
=
'1'
;
}
// Try again with muted
this
.
videoElement
.
play
().
catch
(
e2
=>
{
console
.
error
(
'Muted playback also failed:'
,
e2
);
...
...
@@ -219,6 +259,13 @@ class WebPlayerAPI {
});
}
else
{
console
.
log
(
'Video not ready yet, waiting for more data...'
);
// For debug mode, ensure video is visible even while waiting
if
(
window
.
location
.
href
.
includes
(
'debug'
)
||
window
.
location
.
href
.
includes
(
'debug-player'
))
{
console
.
log
(
'DEBUG MODE: Ensuring video is visible while waiting for data'
);
this
.
videoElement
.
style
.
display
=
'block'
;
this
.
videoElement
.
style
.
visibility
=
'visible'
;
this
.
videoElement
.
style
.
opacity
=
'1'
;
}
}
}
...
...
@@ -325,4 +372,54 @@ document.addEventListener('DOMContentLoaded', function() {
console
.
log
(
'Web Player API initialized and exposed to window.webPlayer'
);
console
.
log
(
'Global functions playVideo and updateOverlayData exposed for WebChannel'
);
// Initialize debug mode if detected
if
(
window
.
location
.
href
.
includes
(
'debug'
)
||
window
.
location
.
href
.
includes
(
'debug-player'
))
{
console
.
log
(
'DEBUG MODE DETECTED: Applying debug-specific video fixes'
);
webPlayer
.
_initDebugMode
();
}
});
// Add debug mode initialization method to WebPlayerAPI prototype
WebPlayerAPI
.
prototype
.
_initDebugMode
=
function
()
{
console
.
log
(
'Initializing debug mode for web player'
);
// Ensure video element is visible in debug mode
if
(
this
.
videoElement
)
{
this
.
videoElement
.
style
.
display
=
'block'
;
this
.
videoElement
.
style
.
visibility
=
'visible'
;
this
.
videoElement
.
style
.
opacity
=
'1'
;
this
.
videoElement
.
style
.
width
=
'100%'
;
this
.
videoElement
.
style
.
height
=
'100%'
;
this
.
videoElement
.
style
.
objectFit
=
'contain'
;
this
.
videoElement
.
muted
=
true
;
// Ensure muted for autoplay in debug mode
// Force video container to be visible
const
videoContainer
=
document
.
querySelector
(
'.video-container'
);
if
(
videoContainer
)
{
videoContainer
.
style
.
display
=
'block'
;
videoContainer
.
style
.
visibility
=
'visible'
;
videoContainer
.
style
.
opacity
=
'1'
;
videoContainer
.
style
.
zIndex
=
'100'
;
}
// Add debug overlay to indicate debug mode
const
debugOverlay
=
document
.
createElement
(
'div'
);
debugOverlay
.
style
.
position
=
'absolute'
;
debugOverlay
.
style
.
top
=
'10px'
;
debugOverlay
.
style
.
right
=
'10px'
;
debugOverlay
.
style
.
backgroundColor
=
'rgba(255, 0, 0, 0.7)'
;
debugOverlay
.
style
.
color
=
'white'
;
debugOverlay
.
style
.
padding
=
'5px 10px'
;
debugOverlay
.
style
.
borderRadius
=
'5px'
;
debugOverlay
.
style
.
fontFamily
=
'Arial, sans-serif'
;
debugOverlay
.
style
.
fontSize
=
'14px'
;
debugOverlay
.
style
.
zIndex
=
'1000'
;
debugOverlay
.
textContent
=
'DEBUG MODE - Video Player'
;
document
.
body
.
appendChild
(
debugOverlay
);
console
.
log
(
'Debug mode initialization completed'
);
}
else
{
console
.
error
(
'DEBUG MODE: Video element not available for debug initialization'
);
}
};
\ No newline at end of file
mbetterclient/web_dashboard/routes.py
View file @
d4927b64
...
...
@@ -1412,6 +1412,148 @@ def send_test_message():
return
jsonify
({
"error"
:
str
(
e
)}),
500
# Video serving route for web player
@
api_bp
.
route
(
'/video/serve'
)
def
serve_video_general
():
"""Serve video files for web player access"""
try
:
from
flask
import
send_file
import
os
from
pathlib
import
Path
# Get video path from query parameter
video_path
=
request
.
args
.
get
(
'path'
,
''
)
.
strip
()
if
not
video_path
:
return
jsonify
({
"error"
:
"Video path parameter required"
}),
400
# Security: Only allow access to specific directories
allowed_base_paths
=
[
str
(
Path
.
home
()
/
"mbetterclient"
/
"videos"
),
# User videos
str
(
Path
.
home
()
/
"mbetterclient"
/
"temp"
),
# Temp videos
"/tmp"
,
# System temp
str
(
Path
(
__file__
)
.
parent
.
parent
.
parent
/
"assets"
),
# Built-in assets
]
# Check if the requested path is within allowed directories
video_path_obj
=
Path
(
video_path
)
.
resolve
()
is_allowed
=
False
for
allowed_path
in
allowed_base_paths
:
allowed_path_obj
=
Path
(
allowed_path
)
.
resolve
()
try
:
video_path_obj
.
relative_to
(
allowed_path_obj
)
is_allowed
=
True
break
except
ValueError
:
continue
if
not
is_allowed
:
logger
.
warning
(
f
"Access denied to video path: {video_path}"
)
return
jsonify
({
"error"
:
"Access denied"
}),
403
# Check if file exists
if
not
video_path_obj
.
exists
():
logger
.
warning
(
f
"Video file not found: {video_path}"
)
return
jsonify
({
"error"
:
"Video file not found"
}),
404
# Check if it's actually a file
if
not
video_path_obj
.
is_file
():
logger
.
warning
(
f
"Path is not a file: {video_path}"
)
return
jsonify
({
"error"
:
"Invalid file path"
}),
400
# Get file extension to determine MIME type
file_ext
=
video_path_obj
.
suffix
.
lower
()
mime_types
=
{
'.mp4'
:
'video/mp4'
,
'.avi'
:
'video/x-msvideo'
,
'.mov'
:
'video/quicktime'
,
'.mkv'
:
'video/x-matroska'
,
'.webm'
:
'video/webm'
,
'.m4v'
:
'video/x-m4v'
}
mime_type
=
mime_types
.
get
(
file_ext
,
'video/mp4'
)
# Default to mp4
logger
.
info
(
f
"Serving video file: {video_path} (MIME: {mime_type})"
)
# Serve the file with appropriate headers for video streaming
return
send_file
(
str
(
video_path_obj
),
mimetype
=
mime_type
,
as_attachment
=
False
,
conditional
=
True
# Support range requests for video seeking
)
except
Exception
as
e
:
logger
.
error
(
f
"Error serving video file: {e}"
)
return
jsonify
({
"error"
:
str
(
e
)}),
500
# Video serving route for web player
@
api_bp
.
route
(
'/video/play/<path:filename>'
)
def
serve_video_play
(
filename
):
"""Serve video files for web player playback"""
try
:
from
pathlib
import
Path
from
flask
import
send_file
logger
.
info
(
f
"Serving video file: {filename}"
)
# Handle different video file locations
possible_paths
=
[]
# 1. Check uploaded videos directory
from
..config.settings
import
get_user_data_dir
user_data_dir
=
get_user_data_dir
()
uploaded_video_path
=
user_data_dir
/
"videos"
/
filename
possible_paths
.
append
(
uploaded_video_path
)
# 2. Check assets directory
assets_video_path
=
Path
(
__file__
)
.
parent
.
parent
.
parent
/
"assets"
/
filename
possible_paths
.
append
(
assets_video_path
)
# 3. Check temp directories for extracted match videos
import
tempfile
temp_base
=
Path
(
tempfile
.
gettempdir
())
for
temp_dir
in
temp_base
.
glob
(
"match_*"
):
if
temp_dir
.
is_dir
():
temp_video_path
=
temp_dir
/
filename
possible_paths
.
append
(
temp_video_path
)
# Find the first existing file
video_path
=
None
for
path
in
possible_paths
:
if
path
.
exists
():
video_path
=
path
break
if
not
video_path
:
logger
.
error
(
f
"Video file not found: {filename}"
)
return
jsonify
({
"error"
:
"Video file not found"
}),
404
logger
.
info
(
f
"Serving video from: {video_path}"
)
# Serve the video file with appropriate headers for streaming
response
=
send_file
(
str
(
video_path
),
mimetype
=
'video/mp4'
,
as_attachment
=
False
,
conditional
=
True
)
# Add CORS headers for web player
response
.
headers
[
'Access-Control-Allow-Origin'
]
=
'*'
response
.
headers
[
'Access-Control-Allow-Headers'
]
=
'Range'
response
.
headers
[
'Accept-Ranges'
]
=
'bytes'
return
response
except
Exception
as
e
:
logger
.
error
(
f
"Error serving video {filename}: {e}"
)
return
jsonify
({
"error"
:
str
(
e
)}),
500
# Video upload and delete routes
@
api_bp
.
route
(
'/video/upload'
,
methods
=
[
'POST'
])
@
api_bp
.
auth_manager
.
require_auth
if
hasattr
(
api_bp
,
'auth_manager'
)
and
api_bp
.
auth_manager
else
login_required
...
...
@@ -1470,6 +1612,162 @@ def delete_video():
return
jsonify
({
"error"
:
str
(
e
)}),
500
@
api_bp
.
route
(
'/video/serve/<path:filename>'
)
def
serve_video_by_filename
(
filename
):
"""Serve video files via HTTP for web player"""
try
:
from
pathlib
import
Path
from
flask
import
send_file
logger
.
info
(
f
"Serving video file: {filename}"
)
# Get user data directory
from
..config.settings
import
get_user_data_dir
user_data_dir
=
Path
(
get_user_data_dir
())
# Check multiple possible locations for the video file
possible_paths
=
[
user_data_dir
/
"videos"
/
filename
,
# Uploaded videos
user_data_dir
/
"zip_files"
/
filename
,
# ZIP files (not videos, but check anyway)
Path
(
__file__
)
.
parent
.
parent
.
parent
/
"assets"
/
filename
,
# Built-in assets
]
# Also check for match videos in temp directories
import
tempfile
temp_base
=
Path
(
tempfile
.
gettempdir
())
for
temp_dir
in
temp_base
.
glob
(
"match_*"
):
if
temp_dir
.
is_dir
():
temp_video_path
=
temp_dir
/
filename
if
temp_video_path
.
exists
():
possible_paths
.
append
(
temp_video_path
)
# Find the first existing file
video_path
=
None
for
path
in
possible_paths
:
if
path
.
exists
()
and
path
.
is_file
():
video_path
=
path
break
if
not
video_path
:
logger
.
warning
(
f
"Video file not found: {filename}"
)
return
jsonify
({
"error"
:
"Video file not found"
}),
404
logger
.
info
(
f
"Serving video from: {video_path}"
)
# Determine MIME type based on file extension
mime_type
=
"video/mp4"
# Default
if
filename
.
lower
()
.
endswith
(
'.avi'
):
mime_type
=
"video/x-msvideo"
elif
filename
.
lower
()
.
endswith
(
'.mov'
):
mime_type
=
"video/quicktime"
elif
filename
.
lower
()
.
endswith
(
'.mkv'
):
mime_type
=
"video/x-matroska"
elif
filename
.
lower
()
.
endswith
(
'.webm'
):
mime_type
=
"video/webm"
# Serve the file with appropriate headers for video streaming
response
=
send_file
(
str
(
video_path
),
mimetype
=
mime_type
,
as_attachment
=
False
,
conditional
=
True
# Enable conditional requests for better performance
)
# Add cache control headers
response
.
headers
[
'Cache-Control'
]
=
'public, max-age=3600'
# Cache for 1 hour
response
.
headers
[
'Accept-Ranges'
]
=
'bytes'
# Enable range requests for seeking
return
response
except
Exception
as
e
:
logger
.
error
(
f
"Error serving video file {filename}: {e}"
)
return
jsonify
({
"error"
:
str
(
e
)}),
500
@
api_bp
.
route
(
'/video/serve/<path:filename>'
)
def
serve_video_by_filename_2
(
filename
):
"""Serve video files for web player playback"""
try
:
from
flask
import
send_from_directory
import
os
from
pathlib
import
Path
logger
.
info
(
f
"Serving video file: {filename}"
)
# Get project root directory
project_root
=
Path
(
__file__
)
.
parent
.
parent
.
parent
upload_dir
=
project_root
/
'uploads'
# Security check: ensure the file is within the uploads directory
requested_path
=
(
upload_dir
/
filename
)
.
resolve
()
upload_dir_resolved
=
upload_dir
.
resolve
()
if
not
str
(
requested_path
)
.
startswith
(
str
(
upload_dir_resolved
)):
logger
.
warning
(
f
"Security violation: attempted to access file outside uploads directory: {filename}"
)
return
jsonify
({
"error"
:
"Access denied"
}),
403
# Check if file exists
if
not
requested_path
.
exists
():
logger
.
warning
(
f
"Video file not found: {requested_path}"
)
return
jsonify
({
"error"
:
"File not found"
}),
404
# Check if it's actually a file
if
not
requested_path
.
is_file
():
logger
.
warning
(
f
"Requested path is not a file: {requested_path}"
)
return
jsonify
({
"error"
:
"Not a file"
}),
400
# Get relative path from uploads directory
relative_path
=
requested_path
.
relative_to
(
upload_dir_resolved
)
relative_dir
=
str
(
relative_path
.
parent
)
if
relative_path
.
parent
!=
Path
(
'.'
)
else
''
logger
.
info
(
f
"Serving video: {relative_path} from directory: {upload_dir}"
)
# Serve the file with appropriate headers for video streaming
response
=
send_from_directory
(
str
(
upload_dir
),
str
(
relative_path
),
mimetype
=
'video/mp4'
,
# Default to mp4, but Flask will detect based on file extension
as_attachment
=
False
,
conditional
=
True
# Support range requests for video seeking
)
# Add CORS headers for web player access
response
.
headers
[
'Access-Control-Allow-Origin'
]
=
'*'
response
.
headers
[
'Access-Control-Allow-Headers'
]
=
'Range'
response
.
headers
[
'Accept-Ranges'
]
=
'bytes'
logger
.
info
(
f
"Video served successfully: {filename}"
)
return
response
except
Exception
as
e
:
logger
.
error
(
f
"Error serving video file {filename}: {e}"
)
return
jsonify
({
"error"
:
str
(
e
)}),
500
@
api_bp
.
route
(
'/video/<filename>'
)
def
serve_video_by_filename_3
(
filename
):
"""Serve video files via HTTP - no authentication required for video playback"""
try
:
from
..config.settings
import
get_user_data_dir
from
flask
import
send_from_directory
import
os
# Get persistent storage directory
user_data_dir
=
get_user_data_dir
()
videos_dir
=
user_data_dir
/
"videos"
# Check if file exists
if
not
(
videos_dir
/
filename
)
.
exists
():
return
jsonify
({
"error"
:
"Video file not found"
}),
404
# Serve the file
return
send_from_directory
(
str
(
videos_dir
),
filename
,
mimetype
=
'video/mp4'
)
except
Exception
as
e
:
logger
.
error
(
f
"API serve video error: {e}"
)
return
jsonify
({
"error"
:
str
(
e
)}),
500
# Auth token endpoint for JWT creation
@
auth_bp
.
route
(
'/token'
,
methods
=
[
'POST'
])
def
create_auth_token
():
...
...
@@ -5066,6 +5364,232 @@ def get_template_preview(template_name):
return
f
"Error loading template preview: {str(e)}"
,
500
@
api_bp
.
route
(
'/video/serve/<path:video_path>'
)
def
serve_video_by_path
(
video_path
):
"""Serve video files by path for web player"""
try
:
from
pathlib
import
Path
from
flask
import
send_file
import
mimetypes
# Security: Only allow serving video files
allowed_extensions
=
{
'.mp4'
,
'.avi'
,
'.mov'
,
'.mkv'
,
'.webm'
,
'.mp3'
,
'.wav'
,
'.flv'
}
video_path_obj
=
Path
(
video_path
)
if
video_path_obj
.
suffix
.
lower
()
not
in
allowed_extensions
:
return
jsonify
({
"error"
:
"Invalid file type"
}),
400
# Security: Prevent directory traversal
if
'..'
in
video_path
or
video_path
.
startswith
(
'/'
):
return
jsonify
({
"error"
:
"Invalid path"
}),
400
# Check if file exists
if
not
video_path_obj
.
exists
():
return
jsonify
({
"error"
:
"File not found"
}),
404
# Check if it's actually a file
if
not
video_path_obj
.
is_file
():
return
jsonify
({
"error"
:
"Not a file"
}),
400
# Get MIME type
mime_type
,
_
=
mimetypes
.
guess_type
(
str
(
video_path_obj
))
if
not
mime_type
:
mime_type
=
'video/mp4'
# Default fallback
logger
.
info
(
f
"Serving video file: {video_path} (MIME: {mime_type})"
)
# Serve the file with appropriate headers for video streaming
return
send_file
(
str
(
video_path_obj
),
mimetype
=
mime_type
,
as_attachment
=
False
,
conditional
=
True
)
except
Exception
as
e
:
logger
.
error
(
f
"Error serving video file {video_path}: {e}"
)
return
jsonify
({
"error"
:
"Internal server error"
}),
500
@
api_bp
.
route
(
'/video/<path:filepath>'
)
def
serve_video_by_filepath
(
filepath
):
"""Serve video files for web player playback"""
try
:
from
pathlib
import
Path
from
flask
import
send_file
# Convert URL path to filesystem path
video_path
=
Path
(
filepath
)
# Security check: only allow access to video files in specific directories
allowed_dirs
=
[
Path
(
__file__
)
.
parent
.
parent
.
parent
/
"assets"
,
# Built-in assets
Path
(
__file__
)
.
parent
.
parent
.
parent
/
"mbetterclient"
/
"assets"
,
# Alternative assets
]
# Get user data directory for additional video locations
try
:
from
..config.settings
import
get_user_data_dir
user_data_dir
=
get_user_data_dir
()
allowed_dirs
.
extend
([
user_data_dir
/
"videos"
,
# Uploaded videos
user_data_dir
/
"zip_files"
,
# Match ZIP files (temporary extraction)
])
except
Exception
as
e
:
logger
.
warning
(
f
"Could not get user data directory: {e}"
)
# Check if the requested file is in an allowed directory
file_allowed
=
False
for
allowed_dir
in
allowed_dirs
:
try
:
# Resolve both paths to handle symlinks and relative paths
resolved_video_path
=
video_path
.
resolve
()
resolved_allowed_dir
=
allowed_dir
.
resolve
()
# Check if the video path is within the allowed directory
if
resolved_video_path
.
is_relative_to
(
resolved_allowed_dir
):
file_allowed
=
True
break
except
Exception
as
e
:
logger
.
debug
(
f
"Path resolution error for {allowed_dir}: {e}"
)
continue
if
not
file_allowed
:
logger
.
warning
(
f
"Access denied to video file: {filepath}"
)
return
jsonify
({
"error"
:
"Access denied"
}),
403
# Check if file exists
if
not
video_path
.
exists
():
logger
.
warning
(
f
"Video file not found: {filepath}"
)
return
jsonify
({
"error"
:
"File not found"
}),
404
# Check if it's actually a file
if
not
video_path
.
is_file
():
logger
.
warning
(
f
"Path is not a file: {filepath}"
)
return
jsonify
({
"error"
:
"Not a file"
}),
400
# Check file extension (basic security)
allowed_extensions
=
{
'.mp4'
,
'.avi'
,
'.mov'
,
'.mkv'
,
'.webm'
,
'.m4v'
,
'.flv'
}
if
video_path
.
suffix
.
lower
()
not
in
allowed_extensions
:
logger
.
warning
(
f
"Invalid video file extension: {video_path.suffix}"
)
return
jsonify
({
"error"
:
"Invalid file type"
}),
400
logger
.
info
(
f
"Serving video file: {filepath}"
)
# Serve the file with appropriate headers for video streaming
return
send_file
(
str
(
video_path
),
mimetype
=
f
'video/{video_path.suffix[1:].lower()}'
,
as_attachment
=
False
,
conditional
=
True
# Support range requests for video seeking
)
except
Exception
as
e
:
logger
.
error
(
f
"Error serving video file {filepath}: {e}"
)
return
jsonify
({
"error"
:
"Internal server error"
}),
500
@
api_bp
.
route
(
'/video/<path:filename>'
)
def
serve_video_by_filename_4
(
filename
):
"""Serve video files for web player playback"""
try
:
from
pathlib
import
Path
import
tempfile
import
os
logger
.
info
(
f
"Serving video file: {filename}"
)
# Define possible video locations in order of priority
video_paths
=
[]
# 1. Check temp directories for match videos (highest priority)
temp_base
=
Path
(
tempfile
.
gettempdir
())
for
temp_dir
in
temp_base
.
glob
(
"match_*"
):
if
temp_dir
.
is_dir
():
video_file
=
temp_dir
/
filename
if
video_file
.
exists
():
logger
.
info
(
f
"Found video in temp directory: {video_file}"
)
return
send_file
(
str
(
video_file
),
mimetype
=
'video/mp4'
)
# 2. Check assets directory for INTRO.mp4 and other built-in videos
assets_dir
=
Path
(
__file__
)
.
parent
.
parent
.
parent
/
"assets"
assets_video
=
assets_dir
/
filename
if
assets_video
.
exists
():
logger
.
info
(
f
"Found video in assets directory: {assets_video}"
)
return
send_file
(
str
(
assets_video
),
mimetype
=
'video/mp4'
)
# 3. Check user data videos directory for uploaded videos
from
..config.settings
import
get_user_data_dir
user_data_dir
=
get_user_data_dir
()
videos_dir
=
user_data_dir
/
"videos"
user_video
=
videos_dir
/
filename
if
user_video
.
exists
():
logger
.
info
(
f
"Found video in user data directory: {user_video}"
)
return
send_file
(
str
(
user_video
),
mimetype
=
'video/mp4'
)
# 4. Check zip_files directory for any video files
zip_files_dir
=
user_data_dir
/
"zip_files"
zip_video
=
zip_files_dir
/
filename
if
zip_video
.
exists
():
logger
.
info
(
f
"Found video in zip files directory: {zip_video}"
)
return
send_file
(
str
(
zip_video
),
mimetype
=
'video/mp4'
)
# Video not found
logger
.
warning
(
f
"Video file not found: {filename}"
)
return
jsonify
({
"error"
:
"Video file not found"
}),
404
except
Exception
as
e
:
logger
.
error
(
f
"Error serving video file {filename}: {e}"
)
return
jsonify
({
"error"
:
str
(
e
)}),
500
@
api_bp
.
route
(
'/video/serve/<path:filename>'
)
def
serve_video_by_filename_5
(
filename
):
"""Serve video files for web player playback"""
try
:
from
flask
import
send_file
,
Response
from
pathlib
import
Path
import
tempfile
import
os
logger
.
info
(
f
"Serving video file: {filename}"
)
# First, try to find the video in temp directories (for match videos)
temp_base
=
Path
(
tempfile
.
gettempdir
())
temp_dir_pattern
=
f
"match_*"
for
temp_dir
in
temp_base
.
glob
(
temp_dir_pattern
):
if
temp_dir
.
is_dir
():
video_path
=
temp_dir
/
filename
if
video_path
.
exists
():
logger
.
info
(
f
"Found video in temp directory: {video_path}"
)
return
send_file
(
str
(
video_path
),
mimetype
=
'video/mp4'
)
# If not found in temp directories, try assets directory (for intro videos)
assets_dir
=
Path
(
__file__
)
.
parent
.
parent
.
parent
/
"assets"
assets_video
=
assets_dir
/
filename
if
assets_video
.
exists
():
logger
.
info
(
f
"Found video in assets directory: {assets_video}"
)
return
send_file
(
str
(
assets_video
),
mimetype
=
'video/mp4'
)
# If not found in assets, try user data videos directory
from
..config.settings
import
get_user_data_dir
user_data_dir
=
get_user_data_dir
()
videos_dir
=
user_data_dir
/
"videos"
user_video
=
videos_dir
/
filename
if
user_video
.
exists
():
logger
.
info
(
f
"Found video in user videos directory: {user_video}"
)
return
send_file
(
str
(
user_video
),
mimetype
=
'video/mp4'
)
# Video not found
logger
.
warning
(
f
"Video file not found: {filename}"
)
return
jsonify
({
"error"
:
"Video file not found"
}),
404
except
Exception
as
e
:
logger
.
error
(
f
"Error serving video file {filename}: {e}"
)
return
jsonify
({
"error"
:
str
(
e
)}),
500
@
api_bp
.
route
(
'/upload-intro-video'
,
methods
=
[
'POST'
])
@
api_bp
.
auth_manager
.
require_auth
if
hasattr
(
api_bp
,
'auth_manager'
)
and
api_bp
.
auth_manager
else
login_required
@
api_bp
.
auth_manager
.
require_admin
if
hasattr
(
api_bp
,
'auth_manager'
)
and
api_bp
.
auth_manager
else
login_required
...
...
@@ -5125,3 +5649,51 @@ def upload_intro_video():
logger
.
error
(
f
"API upload intro video error: {e}"
)
return
jsonify
({
"error"
:
str
(
e
)}),
500
@
api_bp
.
route
(
'/video/<path:filename>'
)
def
serve_video_by_filename_6
(
filename
):
"""Serve video files via HTTP for web player"""
try
:
from
..config.settings
import
get_user_data_dir
from
flask
import
send_from_directory
import
tempfile
from
pathlib
import
Path
logger
.
info
(
f
"Serving video file: {filename}"
)
# First, try to serve from persistent videos directory
user_data_dir
=
get_user_data_dir
()
videos_dir
=
user_data_dir
/
"videos"
if
videos_dir
.
exists
():
video_path
=
videos_dir
/
filename
if
video_path
.
exists
():
logger
.
info
(
f
"Serving video from persistent directory: {video_path}"
)
return
send_from_directory
(
str
(
videos_dir
),
filename
,
mimetype
=
'video/mp4'
)
# Second, try to serve from temp directories (for extracted match videos)
temp_base
=
Path
(
tempfile
.
gettempdir
())
temp_dir_pattern
=
"match_*"
for
temp_dir
in
temp_base
.
glob
(
temp_dir_pattern
):
if
temp_dir
.
is_dir
():
video_path
=
temp_dir
/
filename
if
video_path
.
exists
():
logger
.
info
(
f
"Serving video from temp directory: {video_path}"
)
return
send_from_directory
(
str
(
temp_dir
),
filename
,
mimetype
=
'video/mp4'
)
# Third, try to serve from assets directory (for INTRO.mp4)
assets_dir
=
Path
(
__file__
)
.
parent
.
parent
.
parent
/
"assets"
video_path
=
assets_dir
/
filename
if
video_path
.
exists
():
logger
.
info
(
f
"Serving video from assets directory: {video_path}"
)
return
send_from_directory
(
str
(
assets_dir
),
filename
,
mimetype
=
'video/mp4'
)
# If not found in any location, return 404
logger
.
warning
(
f
"Video file not found: {filename}"
)
return
jsonify
({
"error"
:
"Video file not found"
}),
404
except
Exception
as
e
:
logger
.
error
(
f
"Error serving video file {filename}: {e}"
)
return
jsonify
({
"error"
:
str
(
e
)}),
500
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