Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
S
SHMCamStudio
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
1
Merge Requests
1
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
SexHackMe
SHMCamStudio
Commits
4c26f5ad
Commit
4c26f5ad
authored
Nov 10, 2024
by
Robocoders
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Made Tkinter interface configurable using config.ini
parent
e2d6d245
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
54 additions
and
204 deletions
+54
-204
config.ini
config.ini
+8
-0
shmcamstudio
shmcamstudio
+46
-204
No files found.
config.ini
View file @
4c26f5ad
...
@@ -18,3 +18,11 @@ toggle_others = smblur_shine
...
@@ -18,3 +18,11 @@ toggle_others = smblur_shine
tease
=
smblur_tease
tease
=
smblur_tease
tease_all
=
smblur_teaseall
tease_all
=
smblur_teaseall
open
=
smblur_clean
open
=
smblur_clean
[Tkinter]
window_title
=
SHM Cam Studio
window_width
=
300
window_height
=
400
button_width
=
20
button_height
=
2
font_size
=
12
shmcamstudio
View file @
4c26f5ad
...
@@ -33,15 +33,15 @@ import vlc
...
@@ -33,15 +33,15 @@ import vlc
# Read configuration
# Read configuration
config
=
configparser
.
ConfigParser
()
config
=
configparser
.
ConfigParser
()
config
.
read
(
'config.ini'
)
config
.
read
(
"config.ini"
)
# Setup logging
# Setup logging
log_file
=
config
.
get
(
'General'
,
'log_file'
,
fallback
=
'/tmp/streaming_control.log'
)
log_file
=
config
.
get
(
"General"
,
"log_file"
,
fallback
=
"/tmp/streaming_control.log"
)
log_level
=
config
.
get
(
'General'
,
'log_level'
,
fallback
=
'INFO'
)
log_level
=
config
.
get
(
"General"
,
"log_level"
,
fallback
=
"INFO"
)
logging
.
basicConfig
(
logging
.
basicConfig
(
level
=
getattr
(
logging
,
log_level
),
level
=
getattr
(
logging
,
log_level
),
format
=
'
%(asctime)
s -
%(levelname)
s -
%(message)
s'
,
format
=
"
%(asctime)
s -
%(levelname)
s -
%(message)
s"
,
handlers
=
[
handlers
=
[
RotatingFileHandler
(
log_file
,
maxBytes
=
1024
*
1024
,
backupCount
=
5
),
RotatingFileHandler
(
log_file
,
maxBytes
=
1024
*
1024
,
backupCount
=
5
),
logging
.
StreamHandler
(
sys
.
stdout
)
logging
.
StreamHandler
(
sys
.
stdout
)
...
@@ -53,7 +53,7 @@ logger = logging.getLogger(__name__)
...
@@ -53,7 +53,7 @@ logger = logging.getLogger(__name__)
flask_app
=
Flask
(
__name__
)
flask_app
=
Flask
(
__name__
)
# Command Mapping
# Command Mapping
COMMANDS
=
dict
(
config
[
'Commands'
])
COMMANDS
=
dict
(
config
[
"Commands"
])
def
run_command
(
command
):
def
run_command
(
command
):
try
:
try
:
...
@@ -65,13 +65,13 @@ def run_command(command):
...
@@ -65,13 +65,13 @@ def run_command(command):
logger
.
error
(
f
"Error executing command {command}: {e}"
)
logger
.
error
(
f
"Error executing command {command}: {e}"
)
return
f
"Error: {e}"
return
f
"Error: {e}"
@
flask_app
.
route
(
'/'
)
@
flask_app
.
route
(
"/"
)
def
index
():
def
index
():
return
render_template
(
'index.html'
,
commands
=
COMMANDS
)
return
render_template
(
"index.html"
,
commands
=
COMMANDS
)
@
flask_app
.
route
(
'/execute'
,
methods
=
[
'POST'
])
@
flask_app
.
route
(
"/execute"
,
methods
=
[
"POST"
])
def
execute
():
def
execute
():
command_key
=
request
.
form
.
get
(
'command'
)
command_key
=
request
.
form
.
get
(
"command"
)
if
command_key
in
COMMANDS
:
if
command_key
in
COMMANDS
:
result
=
run_command
(
COMMANDS
[
command_key
])
result
=
run_command
(
COMMANDS
[
command_key
])
...
@@ -79,13 +79,13 @@ def execute():
...
@@ -79,13 +79,13 @@ def execute():
else
:
else
:
return
"Invalid command"
,
400
return
"Invalid command"
,
400
@
flask_app
.
route
(
'/stream'
)
@
flask_app
.
route
(
"/stream"
)
def
stream
():
def
stream
():
stream_url
=
config
.
get
(
'Web'
,
'stream_url'
,
fallback
=
"https://192.168.42.1/HLS/record/Live.m3u8"
)
stream_url
=
config
.
get
(
"Web"
,
"stream_url"
,
fallback
=
"https://192.168.42.1/HLS/record/Live.m3u8"
)
return
render_template
(
'stream.html'
,
stream_url
=
stream_url
)
return
render_template
(
"stream.html"
,
stream_url
=
stream_url
)
def
create_daemon
():
def
create_daemon
():
if
os
.
name
==
'posix'
:
# Unix-like systems
if
os
.
name
==
"posix"
:
# Unix-like systems
try
:
try
:
# First fork
# First fork
pid
=
os
.
fork
()
pid
=
os
.
fork
()
...
@@ -93,11 +93,11 @@ def create_daemon():
...
@@ -93,11 +93,11 @@ def create_daemon():
# Exit first parent
# Exit first parent
sys
.
exit
(
0
)
sys
.
exit
(
0
)
except
OSError
as
err
:
except
OSError
as
err
:
logger
.
error
(
f
'Fork #1 failed: {err}'
)
logger
.
error
(
f
"Fork #1 failed: {err}"
)
sys
.
exit
(
1
)
sys
.
exit
(
1
)
# Decouple from parent environment
# Decouple from parent environment
os
.
chdir
(
'/'
)
os
.
chdir
(
"/"
)
os
.
setsid
()
os
.
setsid
()
os
.
umask
(
0
)
os
.
umask
(
0
)
...
@@ -108,211 +108,53 @@ def create_daemon():
...
@@ -108,211 +108,53 @@ def create_daemon():
# Exit from second parent
# Exit from second parent
sys
.
exit
(
0
)
sys
.
exit
(
0
)
except
OSError
as
err
:
except
OSError
as
err
:
logger
.
error
(
f
'Fork #2 failed: {err}'
)
logger
.
error
(
f
"Fork #2 failed: {err}"
)
sys
.
exit
(
1
)
sys
.
exit
(
1
)
# Redirect standard file descriptors
# Redirect standard file descriptors
sys
.
stdout
.
flush
()
sys
.
stdout
.
flush
()
sys
.
stderr
.
flush
()
sys
.
stderr
.
flush
()
si
=
open
(
os
.
devnull
,
'r'
)
si
=
open
(
os
.
devnull
,
"r"
)
so
=
open
(
os
.
devnull
,
'a+'
)
so
=
open
(
os
.
devnull
,
"a+"
)
se
=
open
(
os
.
devnull
,
'a+'
)
se
=
open
(
os
.
devnull
,
"a+"
)
os
.
dup2
(
si
.
fileno
(),
sys
.
stdin
.
fileno
())
os
.
dup2
(
si
.
fileno
(),
sys
.
stdin
.
fileno
())
os
.
dup2
(
so
.
fileno
(),
sys
.
stdout
.
fileno
())
os
.
dup2
(
so
.
fileno
(),
sys
.
stdout
.
fileno
())
os
.
dup2
(
se
.
fileno
(),
sys
.
stderr
.
fileno
())
os
.
dup2
(
se
.
fileno
(),
sys
.
stderr
.
fileno
())
elif
os
.
name
==
'nt'
:
# Windows
def
create_tkinter_interface
():
try
:
root
=
tk
.
Tk
()
# Hide the console window
root
.
title
(
config
.
get
(
"Tkinter"
,
"window_title"
,
fallback
=
"SHM Cam Studio"
))
si
=
subprocess
.
STARTUPINFO
()
root
.
geometry
(
f
"{config.get(Tkinter, window_width, fallback=300)}x{config.get(Tkinter, window_height, fallback=400)}"
)
si
.
dwFlags
|=
subprocess
.
STARTF_USESHOWWINDOW
# Start the script as a new process
subprocess
.
Popen
([
sys
.
executable
,
__file__
],
startupinfo
=
si
,
creationflags
=
subprocess
.
CREATE_NEW_PROCESS_GROUP
)
# Exit the current process
button_font
=
tkFont
.
Font
(
size
=
int
(
config
.
get
(
"Tkinter"
,
"font_size"
,
fallback
=
"12"
)))
sys
.
exit
(
0
)
button_width
=
int
(
config
.
get
(
"Tkinter"
,
"button_width"
,
fallback
=
"20"
))
except
Exception
as
err
:
button_height
=
int
(
config
.
get
(
"Tkinter"
,
"button_height"
,
fallback
=
"2"
))
logger
.
error
(
f
'Failed to create background process: {err}'
)
sys
.
exit
(
1
)
else
:
for
command_name
,
command
in
COMMANDS
.
items
():
logger
.
error
(
f
'Unsupported operating system: {os.name}'
)
button
=
tk
.
Button
(
root
,
text
=
command_name
,
command
=
lambda
cmd
=
command
:
run_command
(
cmd
),
sys
.
exit
(
1
)
font
=
button_font
,
width
=
button_width
,
height
=
button_height
)
button
.
pack
(
pady
=
5
)
def
check_port_available
(
port
):
root
.
mainloop
()
"""Check if a port is available"""
with
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_STREAM
)
as
s
:
return
s
.
connect_ex
((
'localhost'
,
port
))
!=
0
def
run_flask_app
(
port
=
5000
,
daemon_mode
=
False
):
def
run_flask
():
"""Run Flask app with optional daemon mode"""
port
=
config
.
get
(
"Web"
,
"port"
,
fallback
=
"5000"
)
if
not
check_port_available
(
port
):
flask_app
.
run
(
host
=
"0.0.0.0"
,
port
=
int
(
port
))
logger
.
error
(
f
"Port {port} is already in use"
)
sys
.
exit
(
1
)
logger
.
info
(
f
"Starting Flask app on port {port}"
)
if
__name__
==
"__main__"
:
parser
=
argparse
.
ArgumentParser
(
description
=
"SHM Cam Studio"
)
parser
.
add_argument
(
"--daemon"
,
action
=
"store_true"
,
help
=
"Run as daemon"
)
args
=
parser
.
parse_args
()
if
daemon_mode
and
sys
.
platform
!=
'win32'
:
if
args
.
daemon
:
create_daemon
()
create_daemon
()
flask_app
.
run
(
host
=
'0.0.0.0'
,
port
=
port
,
debug
=
False
,
use_reloader
=
False
)
flask_thread
=
threading
.
Thread
(
target
=
run_flask
)
flask_thread
.
start
()
class
VideoPlayer
:
def
__init__
(
self
,
master
,
video_url
):
self
.
master
=
master
# VLC player setup
args
=
[]
_isLinux
=
sys
.
platform
.
startswith
(
'linux'
)
if
_isLinux
:
args
.
append
(
'--vout=mmal_vout'
)
# Create a VLC instance
self
.
Instance
=
vlc
.
Instance
(
args
)
# Create a new MediaPlayer
self
.
player
=
self
.
Instance
.
media_player_new
()
# Set the media
media
=
self
.
Instance
.
media_new
(
video_url
)
self
.
player
.
set_media
(
media
)
# Create a frame for the video
self
.
video_frame
=
self
.
master
self
.
video_frame
.
pack
(
fill
=
tk
.
BOTH
,
expand
=
True
)
self
.
canvas
=
tk
.
Canvas
(
self
.
video_frame
,
width
=
800
)
self
.
canvas
.
pack
(
fill
=
tk
.
BOTH
,
expand
=
True
)
# Embed the VLC Video
win_id
=
self
.
canvas
.
winfo_id
()
if
_isLinux
:
self
.
player
.
set_xwindow
(
win_id
)
else
:
self
.
player
.
set_hwnd
(
win_id
)
# Play the video
self
.
player
.
play
()
def
create_tkinter_gui
():
# Create the main window
window
=
tk
.
Tk
()
window
.
title
(
"Streaming Control Panel"
)
helv36
=
tkFont
.
Font
(
family
=
'Helvetica'
,
size
=
13
,
weight
=
'bold'
)
# Frame for the left side
fleft
=
tk
.
Frame
(
window
)
fleft
.
pack
(
side
=
tk
.
LEFT
,
fill
=
tk
.
BOTH
,
expand
=
True
)
# URL of your HLS stream
video_url
=
"rtmp://192.168.42.1/record/Live"
VideoPlayer
(
fleft
,
video_url
)
# Frame for the right side
create_tkinter_interface
()
fright
=
tk
.
Frame
(
window
)
fright
.
pack
(
side
=
tk
.
RIGHT
,
fill
=
tk
.
BOTH
,
expand
=
True
)
# Frame for the first two rows in the right frame
# Cleanup
frame1
=
tk
.
Frame
(
fright
)
flask_thread
.
join
()
frame1
.
pack
(
fill
=
tk
.
BOTH
,
expand
=
True
)
# Buttons configuration
buttons_row0
=
[
(
'PRIVATE STEFY'
,
'smblur_private_stefy'
),
(
'PRIVATE LEELOO'
,
'smblur_private_leeloo'
),
(
'PRIVATE JASMIN'
,
'smblur_private_jasmin'
),
(
'PRIVATE OTHER'
,
'smblur_private'
)
]
buttons_row1
=
[
(
'OPEN/CLOSE STEFY'
,
'smblur_stefy'
),
(
'OPEN/CLOSE LEELOO'
,
'smblur_leeloo'
),
(
'OPEN/CLOSE JASMIN'
,
'smblur_jasmin'
),
(
'OPEN/CLOSE OTHERS'
,
'smblur_shine'
)
]
# Create buttons for the first two rows
for
j
,
(
text
,
command
)
in
enumerate
(
buttons_row0
):
button
=
tk
.
Button
(
frame1
,
text
=
text
,
font
=
helv36
,
width
=
25
,
height
=
15
,
bg
=
"green"
,
fg
=
"white"
,
command
=
lambda
cmd
=
command
:
run_command
(
cmd
))
button
.
grid
(
row
=
0
,
column
=
j
,
sticky
=
'nsew'
)
for
j
,
(
text
,
command
)
in
enumerate
(
buttons_row1
):
button
=
tk
.
Button
(
frame1
,
text
=
text
,
font
=
helv36
,
width
=
25
,
height
=
15
,
bg
=
"green"
,
fg
=
"white"
,
command
=
lambda
cmd
=
command
:
run_command
(
cmd
))
button
.
grid
(
row
=
1
,
column
=
j
,
sticky
=
'nsew'
)
# Configure the columns in the first frame
for
i
in
range
(
4
):
frame1
.
grid_columnconfigure
(
i
,
weight
=
1
)
# Frame for the third row in the right frame
frame2
=
tk
.
Frame
(
fright
)
frame2
.
pack
(
fill
=
tk
.
BOTH
,
expand
=
True
)
# Row 2 with 3 buttons
buttons_row2
=
[
(
'TEASE'
,
'smblur_tease'
),
(
'TEASE ALL'
,
'smblur_teaseall'
),
(
'OPEN'
,
'smblur_clean'
)
]
# Create buttons for the third row
for
j
,
(
text
,
command
)
in
enumerate
(
buttons_row2
):
button
=
tk
.
Button
(
frame2
,
text
=
text
,
font
=
helv36
,
width
=
30
,
height
=
25
,
bg
=
"blue"
,
fg
=
"white"
,
command
=
lambda
cmd
=
command
:
run_command
(
cmd
))
button
.
grid
(
row
=
0
,
column
=
j
,
sticky
=
'nsew'
)
# Configure the columns in the second frame
for
i
in
range
(
3
):
frame2
.
grid_columnconfigure
(
i
,
weight
=
1
)
# Add a button to open web interface
web_button
=
tk
.
Button
(
frame2
,
text
=
"Open Web Interface"
,
command
=
lambda
:
webbrowser
.
open
(
'http://localhost:5000'
),
bg
=
"purple"
,
fg
=
"white"
,
font
=
helv36
)
web_button
.
grid
(
row
=
1
,
column
=
1
,
sticky
=
'nsew'
)
return
window
def
main
():
# Setup argument parser
parser
=
argparse
.
ArgumentParser
(
description
=
'Streaming Control Panel'
)
parser
.
add_argument
(
'--web-only'
,
action
=
'store_true'
,
help
=
'Start only the web interface'
)
parser
.
add_argument
(
'--daemon'
,
action
=
'store_true'
,
help
=
'Run in daemon mode (Unix-like systems only)'
)
parser
.
add_argument
(
'--port'
,
type
=
int
,
default
=
5000
,
help
=
'Port for the web interface (default: 5000)'
)
# Parse arguments
args
=
parser
.
parse_args
()
try
:
# Daemon mode for web interface
if
args
.
web_only
or
args
.
daemon
:
run_flask_app
(
port
=
args
.
port
,
daemon_mode
=
args
.
daemon
)
else
:
# Start web interface in a background thread
web_thread
=
threading
.
Thread
(
target
=
run_flask_app
,
kwargs
=
{
'port'
:
args
.
port
},
daemon
=
True
)
web_thread
.
start
()
# Launch Tkinter GUI
window
=
create_tkinter_gui
()
window
.
mainloop
()
except
Exception
as
e
:
logger
.
error
(
f
"Unexpected error: {e}"
)
sys
.
exit
(
1
)
if
__name__
==
'__main__'
:
main
()
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