chore: reorder and expand script
Some checks failed
Cross-Compile Binaries / compile-linux (push) Failing after 3m4s
Cross-Compile Binaries / compile-windows (push) Has been cancelled

This commit is contained in:
2026-01-13 10:49:20 +01:00
parent 621822ab5b
commit ab1b46d71c
7 changed files with 139 additions and 64 deletions

View File

@@ -23,3 +23,42 @@ if [[ ! -d /opt/${program_name} ]]; then
mkdir -p /opt/${program_name} -m 755 mkdir -p /opt/${program_name} -m 755
chown -R ${userland_name}:${userland_name} /opt/${program_name} chown -R ${userland_name}:${userland_name} /opt/${program_name}
fi fi
cat > /home/${userland_name}/.config/systemd/user/presentation.service << EOF
[Unit]
Description="Systemec Akarton Raspberry Pi Uploader Program (SARPUS)"
After=graphical.target
[Service]
Type=simple
Environment=DISPLAY=:0
WorkingDirectory=/opt/akartontv
ExecStartPre=/usr/bin/sleep 5
ExecStart=/usr/bin/libreoffice --impress --show /opt/akartontv/presentation.pptx --norestore
ExecStop=/usr/bin/killall libreoffice
Restart=always
Restart=on-failure
RestartSec=2
[Install]
WantedBy=default.target
EOF
cat > /home/${userland_name}/.config/systemd/user/video.service << EOF
[Unit]
Description="Systemec Akarton Raspberry Pi Uploader Program (SARPUS)"
After=graphical.target
[Service]
Type=simple
Environment=DISPLAY=:0
WorkingDirectory=/opt/akartontv
ExecStart=/usr/bin/vlc --loop /opt/akartontv/video.mp4 -I dummy --no-video-title
ExecStop=/usr/bin/killall vlc
Restart=always
Restart=on-failure
RestartSec=2
[Install]
WantedBy=default.target
EOF

View File

@@ -94,7 +94,11 @@ func sftpUploadFile(targetName, localPath, remotePath string, cfg RaspiConfig) b
return true return true
} }
func restartShow(targetName string, cfg RaspiConfig) bool { func restartShow(targetName string, targetMode int, cfg RaspiConfig) bool {
const restartPresentation string = "systemctl --user restart presentation"
const restartVideo string = "systemctl --user restart video"
var err error
sshClient, err := createSSHClient(targetName, cfg) sshClient, err := createSSHClient(targetName, cfg)
if err != nil { if err != nil {
@@ -106,14 +110,23 @@ func restartShow(targetName string, cfg RaspiConfig) bool {
session, err := sshClient.NewSession() session, err := sshClient.NewSession()
if err != nil { if err != nil {
log.Printf("Failed to create session over the connection: %v", err) log.Printf("Failed to create session over the connection: %v", err)
return false
} }
defer session.Close() defer session.Close()
output, err := session.CombinedOutput("systemctl --user restart pres") var output []byte
if err != nil { switch targetMode {
log.Printf("Failed to restart the show over SSH: %v", err) case 1:
output, err = session.CombinedOutput(restartPresentation)
case 2:
output, err = session.CombinedOutput(restartVideo)
} }
log.Println(output) if err != nil {
log.Printf("Failed to restart the show over SSH: %v", err)
return false
}
log.Println(string(output))
return true return true
} }

View File

@@ -3,6 +3,7 @@ package main
import ( import (
"image/color" "image/color"
"log" "log"
"path/filepath"
"time" "time"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
@@ -14,36 +15,49 @@ import (
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
) )
type FlashKind int
const (
FlashError FlashKind = iota
FlashSuccess
)
var ( var (
presFileFilter []string = []string{".pptx", ".odp"} presFileFilter []string = []string{".pptx", ".odp"}
videoFileFilter []string = []string{".mp4", ".mkv", ".mov", ".webm"} videoFileFilter []string = []string{".mp4", ".mkv", ".mov", ".webm"}
flashTimers = map[*widget.Button]*time.Timer{}
) )
func flashColor(btnPtr *widget.Button, color string) { func flashColor(btn *widget.Button, kind FlashKind) {
var sleepAmount int = 1 if t, ok := flashTimers[btn]; ok {
// "red" or "green" t.Stop() // cancel previous flash
// Set red on UI thread }
duration := 1 * time.Second // default flash duration
// Determine button style based on kind
var importance widget.Importance
switch kind {
case FlashError:
importance = widget.DangerImportance
case FlashSuccess:
importance = widget.SuccessImportance
duration = 3 * time.Second
}
// Apply the flash immediately on UI thread
fyne.CurrentApp().Driver().DoFromGoroutine(func() { fyne.CurrentApp().Driver().DoFromGoroutine(func() {
switch color { btn.Importance = importance
case "red": btn.Refresh()
btnPtr.Importance = widget.DangerImportance
case "green":
btnPtr.Importance = widget.SuccessImportance
sleepAmount = 3
}
refreshButtons(btnPtr)
}, false) }, false)
log.Println("Waiting to change button") // Schedule reset after duration (non-blocking)
time.Sleep(time.Duration(sleepAmount) * time.Second) flashTimers[btn] = time.AfterFunc(duration, func() {
log.Println("Done waiting") fyne.CurrentApp().Driver().DoFromGoroutine(func() {
btn.Importance = widget.LowImportance
// Reset on UI thread btn.Refresh()
fyne.CurrentApp().Driver().DoFromGoroutine(func() { }, false)
btnPtr.Importance = widget.LowImportance })
refreshButtons(btnPtr)
}, false)
} }
func drawSeparator(trailNewLine bool) *fyne.Container { func drawSeparator(trailNewLine bool) *fyne.Container {
@@ -116,20 +130,17 @@ func drawModeRow(targetMode *int) *fyne.Container {
func drawTargetSection(raspiNames []string, raspiTarget *string, uploadBtn, reloadBtn *widget.Button, cfg RaspiConfig) *fyne.Container { func drawTargetSection(raspiNames []string, raspiTarget *string, uploadBtn, reloadBtn *widget.Button, cfg RaspiConfig) *fyne.Container {
actionText := widget.NewLabel("Select Target") actionText := widget.NewLabel("Select Target")
var previousTarget string
var verifyBtn *widget.Button var verifyBtn *widget.Button
// Left side for selection of target // Left side for selection of target
piSelection := widget.NewRadioGroup(raspiNames, func(selected string) { piSelection := widget.NewRadioGroup(raspiNames, func(selected string) {
if len(selected) != 0 { if selected == previousTarget {
*raspiTarget = selected return // nothing changed
log.Println("Selected target:", selected)
} else {
log.Println("Deselected Target")
*raspiTarget = ""
} }
// Reload all buttons! previousTarget = selected
verifyBtn.Importance = widget.LowImportance *raspiTarget = selected
uploadBtn.Importance = widget.LowImportance
reloadBtn.Importance = widget.LowImportance
refreshButtons(verifyBtn, uploadBtn, reloadBtn) refreshButtons(verifyBtn, uploadBtn, reloadBtn)
}) })
@@ -154,13 +165,13 @@ func drawTargetSection(raspiNames []string, raspiTarget *string, uploadBtn, relo
if credOK { if credOK {
// Must update UI on main thread // Must update UI on main thread
fyne.CurrentApp().Driver().DoFromGoroutine(func() { fyne.CurrentApp().Driver().DoFromGoroutine(func() {
go flashColor(verifyBtn, "green") // flashcolor should handle RunOnMain internally flashColor(verifyBtn, FlashSuccess) // flashcolor should handle RunOnMain internally
uploadBtn.Enable() uploadBtn.Enable()
reloadBtn.Enable() reloadBtn.Enable()
}, false) }, false)
} else { } else {
go flashColor(verifyBtn, "red") // flashcolor should handle RunOnMain internally flashColor(verifyBtn, FlashError) // flashcolor should handle RunOnMain internally
} }
}() }()
}) })
@@ -178,11 +189,12 @@ func drawTargetSection(raspiNames []string, raspiTarget *string, uploadBtn, relo
return wholeCol return wholeCol
} }
func drawFileSelection(localPath *string, targetMode *int, parentWindow *fyne.Window) *fyne.Container { func drawFileSelection(localPath *string, targetMode *int, parentWindow fyne.Window) *fyne.Container {
actionText := widget.NewLabel("Select File") actionText := widget.NewLabel("Select File")
pathLabel := widget.NewLabel("No File Selected Yet...") pathLabel := widget.NewLabel("No File Selected Yet...")
uploadBtn := widget.NewButton("Click to Select File", func() { var uploadBtn *widget.Button
uploadBtn = widget.NewButton("Click to Select File", func() {
// Stupid dialog error on 'headless' servers... // Stupid dialog error on 'headless' servers...
log.Println("Ignore the next error. ~Refer: https://github.com/fyne-io/fyne/issues/4110") log.Println("Ignore the next error. ~Refer: https://github.com/fyne-io/fyne/issues/4110")
@@ -194,9 +206,11 @@ func drawFileSelection(localPath *string, targetMode *int, parentWindow *fyne.Wi
pathLabel.SetText(r.URI().Name()) pathLabel.SetText(r.URI().Name())
*localPath = r.URI().Path() *localPath = r.URI().Path()
uploadBtn.Importance = widget.HighImportance
refreshButtons(uploadBtn)
log.Println("Local path of selected file:", *localPath) log.Println("Local path of selected file:", *localPath)
} }
}, *parentWindow) }, parentWindow)
var fileFilter storage.FileFilter var fileFilter storage.FileFilter
switch *targetMode { switch *targetMode {
@@ -231,9 +245,9 @@ func drawFileSelection(localPath *string, targetMode *int, parentWindow *fyne.Wi
} }
// targetMode *int // targetMode *int
func drawFooter(app fyne.App, raspiTarget, localUploadPath *string, cfg RaspiConfig) (*fyne.Container, *widget.Button, *widget.Button) { func drawFooter(app fyne.App, raspiTarget, localUploadPath *string, targetMode *int, cfg RaspiConfig) (*fyne.Container, *widget.Button, *widget.Button) {
remotePresPath := "/opt/akartontv/presentation.pptx" fExt := filepath.Ext(*localUploadPath)
//remoteVideoPath := "/opt/akartontv/video.mp4" remotePresPath := "/opt/akartontv/media" + fExt
// Configuration of the bottom of the application // Configuration of the bottom of the application
var uploadBtn *widget.Button var uploadBtn *widget.Button
@@ -241,14 +255,18 @@ func drawFooter(app fyne.App, raspiTarget, localUploadPath *string, cfg RaspiCon
uploadBtn.Importance = widget.HighImportance uploadBtn.Importance = widget.HighImportance
refreshButtons(uploadBtn) refreshButtons(uploadBtn)
if sftpUploadFile(*raspiTarget, *localUploadPath, remotePresPath, cfg) { go func() {
go flashColor(uploadBtn, "green") ok := sftpUploadFile(*raspiTarget, *localUploadPath, remotePresPath, cfg)
} else {
go flashColor(uploadBtn, "red") if ok {
} flashColor(uploadBtn, FlashSuccess)
} else {
flashColor(uploadBtn, FlashError)
}
}()
}) })
uploadWide := container.NewGridWrap( uploadWide := container.NewGridWrap(
buttonSize, dButtonSize,
uploadBtn, uploadBtn,
) )
@@ -257,14 +275,18 @@ func drawFooter(app fyne.App, raspiTarget, localUploadPath *string, cfg RaspiCon
reloadBtn.Importance = widget.HighImportance reloadBtn.Importance = widget.HighImportance
refreshButtons(uploadBtn) refreshButtons(uploadBtn)
if restartShow(*raspiTarget, cfg) { go func() {
go flashColor(reloadBtn, "green") // flashcolor should handle RunOnMain internally ok := restartShow(*raspiTarget, *targetMode, cfg)
} else {
go flashColor(reloadBtn, "red") if ok {
} flashColor(reloadBtn, FlashSuccess)
} else {
flashColor(reloadBtn, FlashError)
}
}()
}) })
reloadWide := container.NewGridWrap( reloadWide := container.NewGridWrap(
buttonSize, dButtonSize,
reloadBtn, reloadBtn,
) )

View File

@@ -22,14 +22,14 @@ const (
) )
var ( var (
windowSize fyne.Size = fyne.NewSize(750, 700) // Default Window size windowSize fyne.Size = fyne.NewSize(750, 700) // Default Window size
buttonSize fyne.Size = fyne.NewSize(200, 50) // Default button size buttonSize fyne.Size = fyne.NewSize(200, 50) // Default button size
entrySize fyne.Size = fyne.NewSize(200, 40) dButtonSize fyne.Size = fyne.NewSize(150, 50) // Button size for upload & reload
entrySize fyne.Size = fyne.NewSize(200, 40)
) )
func main() { func main() {
cfg := readConfig() cfg := readConfig()
log.Println(cfg)
app := app.NewWithID("nl.systemec.rpi-charon") app := app.NewWithID("nl.systemec.rpi-charon")
app.Settings().SetTheme(theme.DefaultTheme()) app.Settings().SetTheme(theme.DefaultTheme())
@@ -54,10 +54,10 @@ func main() {
} }
// Call the draw functions -> ./src/draw.go // Call the draw functions -> ./src/draw.go
footerRow, uploadBtn, reloadBtn := drawFooter(app, &raspiTarget, &localUploadPath, cfg) footerRow, uploadBtn, reloadBtn := drawFooter(app, &raspiTarget, &localUploadPath, &targetMode, cfg)
modeBtnRow := drawModeRow(&targetMode) modeBtnRow := drawModeRow(&targetMode)
selectionRow := drawTargetSection(raspiNames, &raspiTarget, uploadBtn, reloadBtn, cfg) selectionRow := drawTargetSection(raspiNames, &raspiTarget, uploadBtn, reloadBtn, cfg)
fileSelectRow := drawFileSelection(&localUploadPath, &targetMode, &w) fileSelectRow := drawFileSelection(&localUploadPath, &targetMode, w)
center := container.NewVBox( center := container.NewVBox(
modeBtnRow, modeBtnRow,
@@ -80,7 +80,8 @@ func main() {
} }
func refreshButtons(givenButtons ...*widget.Button) { func refreshButtons(givenButtons ...*widget.Button) {
for _, btn := range givenButtons { for _, b := range givenButtons {
btn := b
fyne.CurrentApp().Driver().DoFromGoroutine(func() { fyne.CurrentApp().Driver().DoFromGoroutine(func() {
btn.Refresh() btn.Refresh()
}, false) }, false)