feat: rework with a wrapper (v1.0) (#3)
All checks were successful
Cross-Compile Binaries / compile-linux (push) Successful in 4m4s
Cross-Compile Binaries / compile-windows (push) Successful in 9m8s

Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
2026-02-09 13:39:32 +00:00
parent 3afad859ec
commit 65f9210874
8 changed files with 360 additions and 44 deletions

0
build.sh Normal file → Executable file
View File

69
dyn-com-install.sh Executable file
View File

@@ -0,0 +1,69 @@
#!/bin/bash
#
# Development builder
#
install_file="install.sh"
cat > ./$install_file << EOF
#!/bin/bash
program_name="raspscreen"
runtime_id=\$(id -u)
userland_name="\${1-systemec}"
# Check if the user is right (root) for the program
if [[ "\$runtime_id" -ne 0 ]]; then
echo "Not running as root, please run as root."
exit 1
fi
apt-get -y install \\
default-jre libreoffice-impress libreoffice-java-common psmisc ssh vlc
systemctl enable --now ssh
# Create the systemd userland folder
if [[ ! -d /home/\${userland_name}/.config/systemd/user ]]; then
mkdir -p /home/\${userland_name}/.config/systemd/user -m 755
fi
# Create the program place onto /opt
if [[ ! -d /opt/\${program_name} ]]; then
mkdir -p /opt/\${program_name}/media/current -m 755
mkdir -p /opt/\${program_name}/bin -m 755
fi
# THIS MUST BE THE SAME AS \$REPO/service-files/presentation.service
cat > /home/\${userland_name}/.config/systemd/user/presentation.service << EOF
EOF
cat ./service-files/presentation.service >> ./$install_file
echo -e "\nEOF\n" >> ./$install_file
cat >> ./$install_file << EOF
# THIS MUST BE THE SAME AS \$REPO/service-files/video.service
cat > /home/\${userland_name}/.config/systemd/user/video.service << EOF
EOF
cat ./service-files/video.service >> ./$install_file
echo -e "\nEOF\n" >> ./$install_file
cat >> ./$install_file << EOF
# THIS MUST BE THE AS \$REPO/manage.sh
cat > /opt/raspscreen/bin/manage.sh << EOF
EOF
# Make sure to prefix all $ with a \
sed 's/\$/\\$/g' ./manage.sh >> "$install_file"
echo -e "\nEOF\n" >> ./$install_file
cat >> ./$install_file << EOF
echo "alias userjournal='journalctl --user-unit'" /home/\${userland_name}/.bashrc
chmod +x /opt/raspscreen/bin/manage.sh
chown -Rv \${userland_name}:\${userland_name} /home/\${userland_name}/.config/systemd
chown -Rv \${userland_name}:\${userland_name} /opt/\${program_name}
su \${userland_name} -c "systemctl --user daemon-reload"
echo "Self-destruction NOW!"
rm \$(pwd)/\$0
EOF

View File

@@ -9,7 +9,9 @@ if [[ "$runtime_id" -ne 0 ]]; then
echo "Not running as root, please run as root." echo "Not running as root, please run as root."
exit 1 exit 1
fi fi
apt-get -y install libreoffice-impress psmisc ssh vlc apt-get -y install \
default-jre libreoffice-impress libreoffice-java-common psmisc ssh vlc
systemctl enable --now ssh systemctl enable --now ssh
# Create the systemd userland folder # Create the systemd userland folder
@@ -19,23 +21,23 @@ fi
# Create the program place onto /opt # Create the program place onto /opt
if [[ ! -d /opt/${program_name} ]]; then if [[ ! -d /opt/${program_name} ]]; then
mkdir -p /opt/${program_name}/media -m 755 mkdir -p /opt/${program_name}/media/current -m 755
mkdir -p /opt/${program_name}/bin -m 755
fi fi
# THIS MUST BE THE SAME AS $REPO/service-files/presentation.service
cat > /home/${userland_name}/.config/systemd/user/presentation.service << EOF cat > /home/${userland_name}/.config/systemd/user/presentation.service << EOF
[Unit] [Unit]
Description="Systemec RaspScreen" Description="Systemec RaspScreen Presentation"
Conflicts=video.service Conflicts=video.service
After=graphical.target After=graphical-session.target
[Service] [Service]
Type=simple Type=simple
Environment=DISPLAY=:0 Environment=DISPLAY=:0
WorkingDirectory=/opt/raspscreen WorkingDirectory=/opt/raspscreen
ExecStartPre=/usr/bin/sleep 5 ExecStart=/opt/raspscreen/bin/manage.sh start presentation --foreground
ExecStart=/bin/sh -c 'exec /usr/bin/libreoffice --impress --show --norestore \$(find /opt/raspscreen/media \( -name "*.pptx" -o -name "*.odp" \))' KillSignal=SIGKILL
ExecStop=/usr/bin/killall libreoffice
Restart=always
Restart=on-failure Restart=on-failure
RestartSec=2 RestartSec=2
@@ -43,20 +45,19 @@ RestartSec=2
WantedBy=default.target WantedBy=default.target
EOF EOF
# THIS MUST BE THE SAME AS $REPO/service-files/video.service
cat > /home/${userland_name}/.config/systemd/user/video.service << EOF cat > /home/${userland_name}/.config/systemd/user/video.service << EOF
[Unit] [Unit]
Description="Systemec RaspScreen" Description="Systemec RaspScreen Video"
Conflicts=presentation.service Conflicts=presentation.service
After=graphical.target After=graphical-session.target
[Service] [Service]
Type=simple Type=simple
Environment=DISPLAY=:0 Environment=DISPLAY=:0
Environment=QT_QPA_PLATFORM=xcb
WorkingDirectory=/opt/raspscreen WorkingDirectory=/opt/raspscreen
ExecStartPre=/usr/bin/sleep 5 ExecStart=/opt/raspscreen/bin/manage.sh start video --foreground
ExecStart=/bin/sh -c 'exec /usr/bin/vlc --fullscreen --loop --no-video-title --video-on-top --no-qt-privacy-ask --no-qt-fs-controller --qt-continue=0 \$(find /opt/raspscreen/media \( -name "*.mp4" -o -name "*.mkv" -o -name "*.mov" -o -name "*.webm" \))'
ExecStop=/usr/bin/killall vlc
Restart=always
Restart=on-failure Restart=on-failure
RestartSec=2 RestartSec=2
@@ -64,8 +65,145 @@ RestartSec=2
WantedBy=default.target WantedBy=default.target
EOF EOF
# THIS MUST BE THE AS $REPO/manage.sh
cat > /opt/raspscreen/bin/manage.sh << EOF
#!/bin/bash
#
# Utility to manage the userland program called RaspScreen by Systemec B.V.
#
# Where it should live: /opt/raspscreen/bin/manage.sh
#
# Examples:
# ./manage.sh (restart|stop|start) (presentation|video)
#
# systemctl must be reachable from the path of the executing user.
app_name="raspscreen"
runtime_id=\$(id -u)
if [[ "\$runtime_id" -eq 0 ]]; then
echo "Do not run as root. Run as the graphical user."
exit 1
fi
printf "Checking input..."
# Action performed
case "\$1" in
restart|stop|start|switch-state)
printf "Valid command...";;
*)
echo "Invalid command!"
exit 1;;
esac
# Type to select
case "\$2" in
presentation|video)
echo "Valid service...";;
*)
echo "Invalid service!"
exit 1;;
esac
# Foreground or operator mode
case "\$3" in
--foreground)
echo "Gathering uptime data..."
sys_uptime=\$(cat /proc/uptime | awk '{print int(\$1)}')
if [[ "\$sys_uptime" -lt 60 ]]; then
echo "Detected a recent restart - giving the operating system some time..."
sleep 10s
fi
if [[ "\$2" == "presentation" ]]; then
echo "Finding and moving media..."
media_file=\$(find /opt/raspscreen/media -path /opt/raspscreen/media/current -prune -o \( -name "*.pptx" -o -name "*.odp" \) -type f -print)
echo "New media file: \$media_file"
old_file=\$(find /opt/raspscreen/media/current \( -name "*.pptx" -o -name "*.odp" \))
echo "Old file: \$old_file"
echo "Copying new media into staging area..."
cp "\$media_file" /opt/raspscreen/media/current
current_media=\$(find /opt/raspscreen/media/current \( -name "*.pptx" -o -name "*.odp" \))
echo "Prepared media: \$current_media"
if [[ -z "\$current_media" ]]; then
echo "No compatible media file found. Wrong mode?"
echo "Exiting in 20 seconds..."
sleep 20s
exit 1
else
/usr/bin/libreoffice --impress --show --norestore "\$current_media"
fi
elif [[ "\$2" == "video" ]]; then
echo "Finding and moving media..."
media_file=\$(find /opt/raspscreen/media -path /opt/raspscreen/media/current -prune -o \( -name "*.mp4" -o -name "*.mkv" -o -name "*.mov" -o -name "*.webm" \) -print)
echo "New media file: \$media_file"
old_file=\$(find /opt/raspscreen/media/current \( -name "*.mp4" -o -name "*.mkv" -o -name "*.mov" -o -name "*.webm" \))
echo "Old file: \$old_file"
echo "Copying new media into staging area..."
cp "\$media_file" /opt/raspscreen/media/current
current_media=\$(find /opt/raspscreen/media/current \( -name "*.mp4" -o -name "*.mkv" -o -name "*.mov" -o -name "*.webm" \))
echo "Prepared media: \$current_media"
if [[ -z "\$current_media" ]]; then
echo "No compatible media file found. Wrong mode?"
echo "Exiting in 20 seconds..."
sleep 20s
exit 1
else
/usr/bin/vlc --fullscreen --loop --no-video-title --video-on-top --no-qt-fs-controller --qt-continue=0 "\$current_media"
fi
fi
echo "Process exited..."
exit 0
;;
*)
echo "Running in task mode...";;
esac
echo "Executing task..."
if [[ "\$1" != "switch-state" ]]; then
cmd_result=\$(systemctl --user "\$1" "\$2" 2>&1)
cmd_exit=\$?
elif [[ "\$1" == "switch-state" ]]; then
last_state=\$(cat /opt/raspscreen/currentstate.txt)
if [[ "\$2" == "video" ]]; then
alt_serv="presentation"
elif [[ "\$2" == "presentation" ]]; then
alt_serv="video"
fi
echo "alt_serv: \$alt_serv"
if [ "\$last_state" != "\$2" ] || [ -z "\$last_state" ]; then
systemctl --user disable "\$alt_serv"
systemctl --user enable "\$2"
fi
systemctl --user restart "\$2"
echo "\$2" > /opt/raspscreen/currentstate.txt
fi
if [[ "\$cmd_exit" -eq 0 ]]; then
echo "Succesfully exited!"
else
echo "Something went wrong, see..."
echo " -> \$cmd_result"
fi
exit 0
EOF
echo "alias userjournal='journalctl --user-unit'" /home/${userland_name}/.bashrc
chmod +x /opt/raspscreen/bin/manage.sh
chown -Rv ${userland_name}:${userland_name} /home/${userland_name}/.config/systemd chown -Rv ${userland_name}:${userland_name} /home/${userland_name}/.config/systemd
chown -Rv ${userland_name}:${userland_name} /opt/${program_name} chown -Rv ${userland_name}:${userland_name} /opt/${program_name}
su ${userland_name} -c "systemctl --user daemon-reload"
echo "Self-destruction NOW!" echo "Self-destruction NOW!"
rm $(pwd)/$0 rm $(pwd)/$0

117
manage.sh Normal file → Executable file
View File

@@ -6,8 +6,123 @@
# #
# Examples: # Examples:
# ./manage.sh (restart|stop|start) (presentation|video) # ./manage.sh (restart|stop|start) (presentation|video)
#
# systemctl must be reachable from the path of the executing user.
app_name="raspscreen"
runtime_id=$(id -u) runtime_id=$(id -u)
if [[ "$runtime_id" -eq 0 ]]; then if [[ "$runtime_id" -eq 0 ]]; then
echo "Do not run as root. Run as the graphical user." echo "Do not run as root. Run as the graphical user."
fi exit 1
fi
printf "Checking input..."
# Action performed
case "$1" in
restart|stop|start|switch-state)
printf "Valid command...";;
*)
echo "Invalid command!"
exit 1;;
esac
# Type to select
case "$2" in
presentation|video)
echo "Valid service...";;
*)
echo "Invalid service!"
exit 1;;
esac
# Foreground or operator mode
case "$3" in
--foreground)
echo "Gathering uptime data..."
sys_uptime=$(cat /proc/uptime | awk '{print int($1)}')
if [[ "$sys_uptime" -lt 60 ]]; then
echo "Detected a recent restart - giving the operating system some time..."
sleep 10s
fi
if [[ "$2" == "presentation" ]]; then
echo "Finding and moving media..."
media_file=$(find /opt/raspscreen/media -path /opt/raspscreen/media/current -prune -o \( -name "*.pptx" -o -name "*.odp" \) -type f -print)
echo "New media file: $media_file"
old_file=$(find /opt/raspscreen/media/current \( -name "*.pptx" -o -name "*.odp" \))
echo "Old file: $old_file"
echo "Copying new media into staging area..."
cp "$media_file" /opt/raspscreen/media/current
current_media=$(find /opt/raspscreen/media/current \( -name "*.pptx" -o -name "*.odp" \))
echo "Prepared media: $current_media"
if [[ -z "$current_media" ]]; then
echo "No compatible media file found. Wrong mode?"
echo "Exiting in 20 seconds..."
sleep 20s
exit 1
else
/usr/bin/libreoffice --impress --show --norestore "$current_media"
fi
elif [[ "$2" == "video" ]]; then
echo "Finding and moving media..."
media_file=$(find /opt/raspscreen/media -path /opt/raspscreen/media/current -prune -o \( -name "*.mp4" -o -name "*.mkv" -o -name "*.mov" -o -name "*.webm" \) -print)
echo "New media file: $media_file"
old_file=$(find /opt/raspscreen/media/current \( -name "*.mp4" -o -name "*.mkv" -o -name "*.mov" -o -name "*.webm" \))
echo "Old file: $old_file"
echo "Copying new media into staging area..."
cp "$media_file" /opt/raspscreen/media/current
current_media=$(find /opt/raspscreen/media/current \( -name "*.mp4" -o -name "*.mkv" -o -name "*.mov" -o -name "*.webm" \))
echo "Prepared media: $current_media"
if [[ -z "$current_media" ]]; then
echo "No compatible media file found. Wrong mode?"
echo "Exiting in 20 seconds..."
sleep 20s
exit 1
else
/usr/bin/vlc --fullscreen --loop --no-video-title --video-on-top --no-qt-fs-controller --qt-continue=0 "$current_media"
fi
fi
echo "Process exited..."
exit 0
;;
*)
echo "Running in task mode...";;
esac
echo "Executing task..."
if [[ "$1" != "switch-state" ]]; then
cmd_result=$(systemctl --user "$1" "$2" 2>&1)
cmd_exit=$?
elif [[ "$1" == "switch-state" ]]; then
last_state=$(cat /opt/raspscreen/currentstate.txt)
if [[ "$2" == "video" ]]; then
alt_serv="presentation"
elif [[ "$2" == "presentation" ]]; then
alt_serv="video"
fi
echo "alt_serv: $alt_serv"
if [ "$last_state" != "$2" ] || [ -z "$last_state" ]; then
systemctl --user disable "$alt_serv"
systemctl --user enable "$2"
fi
systemctl --user restart "$2"
echo "$2" > /opt/raspscreen/currentstate.txt
fi
if [[ "$cmd_exit" -eq 0 ]]; then
echo "Succesfully exited!"
else
echo "Something went wrong, see..."
echo " -> $cmd_result"
fi
exit 0

View File

@@ -1,16 +1,14 @@
[Unit] [Unit]
Description="Systemec RaspScreen" Description="Systemec RaspScreen Presentation"
Conflicts=video.service Conflicts=video.service
After=graphical.target After=graphical-session.target
[Service] [Service]
Type=simple Type=simple
Environment=DISPLAY=:0 Environment=DISPLAY=:0
WorkingDirectory=/opt/raspscreen WorkingDirectory=/opt/raspscreen
ExecStartPre=/usr/bin/sleep 5 ExecStart=/opt/raspscreen/bin/manage.sh start presentation --foreground
ExecStart=/bin/sh -c 'exec /usr/bin/libreoffice --impress --show --norestore $(find /opt/raspscreen/media \( -name "*.pptx" -o -name "*.odp" \))' KillSignal=SIGKILL
ExecStop=/usr/bin/killall libreoffice
Restart=always
Restart=on-failure Restart=on-failure
RestartSec=2 RestartSec=2

View File

@@ -1,16 +1,14 @@
[Unit] [Unit]
Description="Systemec RaspScreen" Description="Systemec RaspScreen Video"
Conflicts=presentation.service Conflicts=presentation.service
After=graphical.target After=graphical-session.target
[Service] [Service]
Type=simple Type=simple
Environment=DISPLAY=:0 Environment=DISPLAY=:0
Environment=QT_QPA_PLATFORM=xcb
WorkingDirectory=/opt/raspscreen WorkingDirectory=/opt/raspscreen
ExecStartPre=/usr/bin/sleep 5 ExecStart=/opt/raspscreen/bin/manage.sh start video --foreground
ExecStart=/bin/sh -c 'exec /usr/bin/vlc --fullscreen --loop --no-video-title --video-on-top --no-qt-fs-controller --qt-continue=0 $(find /opt/raspscreen/media \( -name "*.mp4" -o -name "*.mkv" -o -name "*.mov" -o -name "*.webm" \))'
ExecStop=/usr/bin/killall vlc
Restart=always
Restart=on-failure Restart=on-failure
RestartSec=2 RestartSec=2

View File

@@ -114,13 +114,7 @@ func sftpUploadFile(targetName, localPath, remotePath string, cfg RaspiConfig) b
} }
func restartShow(targetName string, targetMode int, cfg RaspiConfig) bool { func restartShow(targetName string, targetMode int, cfg RaspiConfig) bool {
const restartPresentation string = "systemctl --user restart presentation" const switchCmd string = "/opt/raspscreen/bin/manage.sh switch-state " // add a blank/whitespace at the end to make the formatting valid for manage.sh
const enablePresentation string = "systemctl --user enable presentation"
const disablePresentation string = "systemctl --user disable presentation"
const restartVideo string = "systemctl --user restart video"
const enableVideo string = "systemctl --user enable video"
const disableVideo string = "systemctl --user disable video"
var err error var err error
sshClient, err := createSSHClient(targetName, cfg) sshClient, err := createSSHClient(targetName, cfg)
@@ -138,18 +132,17 @@ func restartShow(targetName string, targetMode int, cfg RaspiConfig) bool {
} }
defer session.Close() defer session.Close()
var servName string
switch targetMode { switch targetMode {
case 1: case 1:
log.Println("Configuring application workflow: Presentation") servName = "presentation"
_, err = runSSHCommand(sshClient, disableVideo)
_, err = runSSHCommand(sshClient, restartPresentation)
_, err = runSSHCommand(sshClient, enablePresentation)
case 2: case 2:
log.Println("Configuring application workflow: Video") servName = "video"
_, err = runSSHCommand(sshClient, disablePresentation)
_, err = runSSHCommand(sshClient, restartVideo)
_, err = runSSHCommand(sshClient, enableVideo)
} }
log.Println("Configuring application workflow:", servName)
stitchCmd := switchCmd + servName
_, err = runSSHCommand(sshClient, stitchCmd)
if err != nil { if err != nil {
log.Printf("Failed to restart the show over SSH: %v", err) log.Printf("Failed to restart the show over SSH: %v", err)

View File

@@ -137,6 +137,8 @@ func drawTargetSection(raspiNames []string, raspiTarget *string, uploadBtn, relo
previousTarget = selected previousTarget = selected
*raspiTarget = selected *raspiTarget = selected
uploadBtn.Disable()
reloadBtn.Disable()
refreshButtons(verifyBtn, uploadBtn, reloadBtn) refreshButtons(verifyBtn, uploadBtn, reloadBtn)
}) })
@@ -246,8 +248,11 @@ func drawFooter(app fyne.App, raspiTarget, localUploadPath *string, targetMode *
// Configuration of the bottom of the application // Configuration of the bottom of the application
var uploadBtn *widget.Button var uploadBtn *widget.Button
var reloadBtn *widget.Button
uploadBtn = widget.NewButton("Upload File", func() { uploadBtn = widget.NewButton("Upload File", func() {
uploadBtn.Importance = widget.HighImportance uploadBtn.Importance = widget.HighImportance
reloadBtn.Disable()
refreshButtons(uploadBtn) refreshButtons(uploadBtn)
go func() { go func() {
@@ -255,6 +260,7 @@ func drawFooter(app fyne.App, raspiTarget, localUploadPath *string, targetMode *
if ok { if ok {
flashColor(uploadBtn, FlashSuccess) flashColor(uploadBtn, FlashSuccess)
reloadBtn.Enable()
} else { } else {
flashColor(uploadBtn, FlashError) flashColor(uploadBtn, FlashError)
} }
@@ -265,7 +271,6 @@ func drawFooter(app fyne.App, raspiTarget, localUploadPath *string, targetMode *
uploadBtn, uploadBtn,
) )
var reloadBtn *widget.Button
reloadBtn = widget.NewButton("Restart Program", func() { reloadBtn = widget.NewButton("Restart Program", func() {
reloadBtn.Importance = widget.HighImportance reloadBtn.Importance = widget.HighImportance
refreshButtons(uploadBtn) refreshButtons(uploadBtn)