chore: reorder and expand script
This commit is contained in:
39
install.sh
39
install.sh
@@ -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
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
124
src/draw.go
124
src/draw.go
@@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
15
src/main.go
15
src/main.go
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user