nsfwapp/backend/tray.go
2026-06-23 10:35:56 +02:00

235 lines
4.9 KiB
Go

//go:build windows
// backend\tray.go
package main
import (
"context"
_ "embed"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/getlantern/systray"
)
//go:embed assets/img/tray.ico
var trayIcon []byte
func runTray(ctx context.Context, onQuit func(), statsFn func() TrayStats) {
systray.Run(func() {
if len(trayIcon) > 0 {
systray.SetIcon(trayIcon)
}
systray.SetTitle("Recorder")
systray.SetTooltip("Recorder Backend läuft")
mStatus := systray.AddMenuItem("Status wird geladen…", "Aktueller Recorder-Status")
mStatus.Disable()
mAutostart := systray.AddMenuItem("Autostart: …", "Autostart-Status")
mAutostart.Disable()
mDownloads := systray.AddMenuItem("Downloads: …", "Download-Status")
mDownloads.Disable()
mPostwork := systray.AddMenuItem("Postwork: …", "Postwork-Status")
mPostwork.Disable()
mEnrich := systray.AddMenuItem("Enrich: …", "Enrich-Status")
mEnrich.Disable()
mDisk := systray.AddMenuItem("Freier Speicher: …", "Speicherplatz")
mDisk.Disable()
mUptime := systray.AddMenuItem("Uptime: …", "Laufzeit")
mUptime.Disable()
systray.AddSeparator()
mOpenFrontend := systray.AddMenuItem("Frontend öffnen", "Öffnet das Web-Frontend")
mOpenRecordDir := systray.AddMenuItem("Record-Ordner öffnen", "Öffnet den Aufnahmeordner")
mOpenDoneDir := systray.AddMenuItem("Done-Ordner öffnen", "Öffnet den Done-Ordner")
systray.AddSeparator()
mToggleAutostart := systray.AddMenuItem("Autostart pausieren", "Autostart pausieren oder fortsetzen")
mStopAll := systray.AddMenuItem(
"Alle Downloads stoppen + Autostart pausieren",
"Stoppt alle aktiven Downloads und pausiert den Autostart",
)
systray.AddSeparator()
mQuit := systray.AddMenuItem("Beenden", "Backend sauber beenden")
updateStatus := func() {
if statsFn == nil {
return
}
stats := statsFn()
mStatus.SetTitle(stats.MenuTitle())
mAutostart.SetTitle(stats.AutostartLine())
mDownloads.SetTitle(stats.DownloadsLine())
mPostwork.SetTitle(stats.PostworkLine())
mEnrich.SetTitle(stats.EnrichLine())
mDisk.SetTitle(stats.DiskLine())
mUptime.SetTitle(stats.UptimeLine())
mToggleAutostart.SetTitle(stats.ToggleAutostartTitle())
if stats.AutostartPausedByDisk {
mToggleAutostart.Disable()
} else {
mToggleAutostart.Enable()
}
if stats.ActiveDownloads > 0 || !stats.AutostartPaused {
mStopAll.Enable()
} else {
mStopAll.Disable()
}
systray.SetTooltip(stats.Tooltip())
}
updateStatus()
go func() {
<-ctx.Done()
systray.Quit()
}()
go func() {
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
updateStatus()
case <-mOpenFrontend.ClickedCh:
_ = openBrowser("https://l14pbbk95100006.tegdssd.de:9999")
case <-mOpenRecordDir.ClickedCh:
_ = openFolder(resolveConfiguredDir(getSettings().RecordDir))
case <-mOpenDoneDir.ClickedCh:
_ = openFolder(resolveConfiguredDir(getSettings().DoneDir))
case <-mToggleAutostart.ClickedCh:
state := getAutostartStatePayload()
if state.PausedByDisk {
updateStatus()
continue
}
if state.Paused {
setAutostartPaused(false)
} else {
setAutostartPaused(true)
}
updateStatus()
case <-mStopAll.ClickedCh:
_ = stopAllDownloadsAndPauseAutostart()
updateStatus()
case <-mQuit.ClickedCh:
onQuit()
systray.Quit()
return
}
}
}()
}, func() {
// optional cleanup
})
}
func stopAllDownloadsAndPauseAutostart() int {
// Immer zuerst Autostart pausieren, damit nichts direkt wieder neu startet.
setAutostartPaused(true)
// Danach aktive Downloads stoppen.
return stopAllActiveDownloads()
}
func stopAllActiveDownloads() int {
jobsMu.RLock()
toStop := make([]*RecordJob, 0, len(jobs))
for _, job := range jobs {
if job == nil {
continue
}
if isPostworkJob(job) {
continue
}
if isTerminalJobStatus(job.Status) {
continue
}
if job.EndedAt != nil {
continue
}
toStop = append(toStop, job)
}
jobsMu.RUnlock()
if len(toStop) > 0 {
stopJobsInternal(toStop)
}
return len(toStop)
}
func resolveConfiguredDir(raw string) string {
raw = strings.TrimSpace(raw)
if raw == "" {
return ""
}
if abs, err := resolvePathRelativeToApp(raw); err == nil && strings.TrimSpace(abs) != "" {
return abs
}
return raw
}
func openFolder(dir string) error {
dir = filepath.Clean(strings.TrimSpace(dir))
if dir == "" {
return nil
}
switch runtime.GOOS {
case "windows":
return exec.Command("explorer", dir).Start()
case "darwin":
return exec.Command("open", dir).Start()
default:
return exec.Command("xdg-open", dir).Start()
}
}
func openBrowser(url string) error {
switch runtime.GOOS {
case "windows":
return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
case "darwin":
return exec.Command("open", url).Start()
default:
return exec.Command("xdg-open", url).Start()
}
}