feat: improve daemon resilience, streaming, and usenet downloads

- Add daemon state persistence and stale resume file cleanup
- Add TriggerPoll for WebSocket resume actions
- Improve stream server with graceful shutdown and connection tracking
- Add desktop notifications for download completion
- Add media file organization with Movies/TV Shows detection
- Improve usenet downloader with progress tracking and resume support
- Add self-update package with GitHub release verification
- Downgrade tablewriter to v0.0.5 (v1.x API breaking change)
This commit is contained in:
Deivid Soto 2026-03-28 21:36:12 +01:00
parent e332c0a6e4
commit 197e33956a
24 changed files with 2310 additions and 84 deletions

View file

@ -14,8 +14,29 @@ func desktopNotify(title, body string) {
case "darwin":
script := `display notification "` + escapeAppleScript(body) + `" with title "` + escapeAppleScript(title) + `"`
exec.Command("osascript", "-e", script).Start()
case "windows":
// Use PowerShell toast notification (Windows 10+)
script := `[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null;` +
`$xml = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent(1);` +
`$text = $xml.GetElementsByTagName('text');` +
`$text[0].AppendChild($xml.CreateTextNode('` + escapePowerShell(title) + `')) > $null;` +
`$text[1].AppendChild($xml.CreateTextNode('` + escapePowerShell(body) + `')) > $null;` +
`$toast = [Windows.UI.Notifications.ToastNotification]::new($xml);` +
`[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('unarr').Show($toast)`
exec.Command("powershell", "-NoProfile", "-Command", script).Start()
}
// Windows: no-op for now
}
func escapePowerShell(s string) string {
out := make([]byte, 0, len(s))
for i := 0; i < len(s); i++ {
if s[i] == '\'' {
out = append(out, '\'', '\'') // double single-quote to escape
} else {
out = append(out, s[i])
}
}
return string(out)
}
func escapeAppleScript(s string) string {