nsfwapp/backend/tray_stats.go
2026-04-27 14:40:16 +02:00

233 lines
4.6 KiB
Go

// backend\tray_stats.go
package main
import (
"fmt"
"strconv"
"strings"
"time"
godisk "github.com/shirou/gopsutil/v3/disk"
)
type TrayStats struct {
ActiveDownloads int
DownloadLimitEnabled bool
DownloadLimitMax int
DownloadLimitReached bool
PostworkWaiting int
PostworkRunning int
EnrichWaiting int
EnrichRunning int
AutostartPaused bool
AutostartPausedByUser bool
AutostartPausedByDisk bool
DiskFreeBytes uint64
HasDiskInfo bool
DiskEmergency bool
Uptime time.Duration
}
func buildTrayStats() TrayStats {
active := activeRecordingJobsCount()
limitEnabled, limitMax, _, limitReached := concurrentDownloadsLimitState()
if limitMax < 1 {
limitMax = 1
}
pwWaiting, pwRunning, _ := postWorkQ.Stats()
enWaiting, enRunning, _ := enrichQ.Stats()
auto := getAutostartStatePayload()
diskFree, hasDisk := getTrayDiskFreeBytes()
return TrayStats{
ActiveDownloads: active,
DownloadLimitEnabled: limitEnabled,
DownloadLimitMax: limitMax,
DownloadLimitReached: limitReached,
PostworkWaiting: pwWaiting,
PostworkRunning: pwRunning,
EnrichWaiting: enWaiting,
EnrichRunning: enRunning,
AutostartPaused: auto.Paused,
AutostartPausedByUser: auto.PausedByUser,
AutostartPausedByDisk: auto.PausedByDisk,
DiskFreeBytes: diskFree,
HasDiskInfo: hasDisk,
DiskEmergency: diskEmergencyActive(),
Uptime: time.Since(serverStartedAt).Round(time.Second),
}
}
func getTrayDiskFreeBytes() (uint64, bool) {
s := getSettings()
recordDir, err := resolvePathRelativeToApp(s.RecordDir)
if err != nil || strings.TrimSpace(recordDir) == "" {
recordDir = strings.TrimSpace(s.RecordDir)
}
if strings.TrimSpace(recordDir) == "" {
return 0, false
}
u, err := godisk.Usage(recordDir)
if err != nil || u == nil {
return 0, false
}
return u.Free, true
}
func diskEmergencyActive() bool {
return isAutostartPaused() && getAutostartStatePayload().PausedByDisk
}
func (s TrayStats) MenuTitle() string {
return fmt.Sprintf(
"Aktiv: %d • PW: %d/%d • Enrich: %d/%d",
s.ActiveDownloads,
s.PostworkRunning,
s.PostworkWaiting,
s.EnrichRunning,
s.EnrichWaiting,
)
}
func (s TrayStats) Tooltip() string {
return fmt.Sprintf(
"Recorder | Downloads %s | PW %d/%d | Enrich %d/%d",
s.downloadLimitText(),
s.PostworkRunning,
s.PostworkWaiting,
s.EnrichRunning,
s.EnrichWaiting,
)
}
func (s TrayStats) AutostartLine() string {
switch {
case s.AutostartPausedByDisk:
return "Autostart: Pausiert (Disk-Notbremse)"
case s.AutostartPausedByUser:
return "Autostart: Pausiert"
default:
return "Autostart: Aktiv"
}
}
func (s TrayStats) DownloadsLine() string {
if s.DownloadLimitEnabled {
if s.DownloadLimitReached {
return fmt.Sprintf(
"Downloads: %d/%d aktiv (Limit erreicht)",
s.ActiveDownloads,
s.DownloadLimitMax,
)
}
return fmt.Sprintf(
"Downloads: %d/%d aktiv",
s.ActiveDownloads,
s.DownloadLimitMax,
)
}
return fmt.Sprintf("Downloads: %d aktiv (kein Limit)", s.ActiveDownloads)
}
func (s TrayStats) PostworkLine() string {
return fmt.Sprintf(
"Postwork: %d läuft • %d wartend",
s.PostworkRunning,
s.PostworkWaiting,
)
}
func (s TrayStats) EnrichLine() string {
return fmt.Sprintf(
"Enrich: %d läuft • %d wartend",
s.EnrichRunning,
s.EnrichWaiting,
)
}
func (s TrayStats) DiskLine() string {
if !s.HasDiskInfo {
return "Freier Speicher: unbekannt"
}
if s.DiskEmergency {
return fmt.Sprintf(
"Freier Speicher: %s (kritisch)",
formatTrayBytes(s.DiskFreeBytes),
)
}
return fmt.Sprintf(
"Freier Speicher: %s",
formatTrayBytes(s.DiskFreeBytes),
)
}
func (s TrayStats) UptimeLine() string {
return "Uptime: " + formatTrayDuration(s.Uptime)
}
func (s TrayStats) ToggleAutostartTitle() string {
if s.AutostartPausedByDisk {
return "Autostart fortsetzen (gesperrt: Disk)"
}
if s.AutostartPaused {
return "Autostart fortsetzen"
}
return "Autostart pausieren"
}
func (s TrayStats) downloadLimitText() string {
if s.DownloadLimitEnabled {
return fmt.Sprintf("%d/%d", s.ActiveDownloads, s.DownloadLimitMax)
}
return strconv.Itoa(s.ActiveDownloads)
}
func formatTrayDuration(d time.Duration) string {
if d < 0 {
d = 0
}
total := int(d.Seconds())
h := total / 3600
m := (total % 3600) / 60
s := total % 60
return fmt.Sprintf("%02d:%02d:%02d", h, m, s)
}
func formatTrayBytes(b uint64) string {
const unit = 1000
if b < unit {
return fmt.Sprintf("%d B", b)
}
div, exp := uint64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp])
}