Background running for downloads/auto-download, library clipboard detect, updater auth + token

Closing the window no longer kills in-progress downloads, the library gains the
same copied-link suggestion the downloads tab has, and the in-app updater can now
authenticate to a sign-in-required Gitea.

Background / tray:
- The window's close handler now hides to the tray (instead of quitting) whenever
  a download is in flight, even if "Keep running in the tray" is off — quitting
  was killing the spawned yt-dlp processes. A one-time notification explains the
  app is still running. (download.ts exposes hasActiveDownloads().)
- tray.ts now falls back to an embedded icon when no build/icon.ico ships, so the
  tray actually appears — previously an empty icon meant the tray was skipped and
  a minimized window could be stranded with no way back.
- New "Start with Windows" setting (launchAtStartup) wired to
  app.setLoginItemSettings, synced at startup and on toggle. Useful with
  auto-download so watched channels stay current in the background.
- The "Keep running in the tray" hint now explains it also enables background
  auto-download of new uploads.

Library clipboard detection:
- Extracted the downloads tab's clipboard watcher into a shared useClipboardLink
  hook and used it in the library's add-source field, so a copied channel/playlist
  link is offered there too.

Updater fix (works on a private / sign-in-required instance):
- Added an optional updateToken setting. When set, the updater sends it as a Gitea
  Authorization header on the release check, the checksum fetch, and the installer
  download — so "Check for updates" works where anonymous access is blocked (the
  previous "could not reach the update server" case). Blank = anonymous, unchanged.
  No token is ever shipped; it's only ever sent to the host-pinned update host.
  Settings gains a masked "Update access token" field.

Typecheck clean; 187 tests pass; electron-vite build clean. New UI verified in the
browser preview (tray/startup toggles, token field, library copied-link banner).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 12:22:19 -04:00
parent e2b33603c6
commit f167c02946
12 changed files with 270 additions and 17 deletions
+27 -1
View File
@@ -50,7 +50,23 @@ const DEFAULTS: Settings = {
notifyOnComplete: true,
autoDownloadNew: true,
hasCompletedOnboarding: false,
minimizeToTray: false
minimizeToTray: false,
launchAtStartup: false,
updateToken: ''
}
/**
* Sync the OS "run at sign-in" entry with the launchAtStartup setting. Called at
* startup and whenever the toggle changes. On Windows this writes/removes a
* per-user registry Run entry — no admin needed, matching the app's no-elevation
* stance. Best-effort: a failure here just means the toggle didn't take effect.
*/
export function applyLaunchAtStartup(enabled: boolean): void {
try {
app.setLoginItemSettings({ openAtLogin: enabled })
} catch {
/* non-fatal — e.g. unsupported platform */
}
}
/** Fixed path for the --download-archive file; not user-configurable. */
@@ -212,6 +228,12 @@ export function setSettings(partial: Partial<Settings>): Settings {
case 'minimizeToTray':
if (typeof value === 'boolean') s.set(key, value)
break
case 'launchAtStartup':
if (typeof value === 'boolean') {
s.set('launchAtStartup', value)
applyLaunchAtStartup(value)
}
break
case 'ytdlpChannel':
// Same allowlist that guards the `--update-to` flag (audit F1).
if (isYtdlpUpdateChannel(value)) s.set('ytdlpChannel', value)
@@ -262,6 +284,10 @@ export function setSettings(partial: Partial<Settings>): Settings {
case 'youtubePoToken':
if (typeof value === 'string') s.set(key, value)
break
case 'updateToken':
// A Gitea token has no spaces; trim and store as-is (like proxy creds).
if (typeof value === 'string') s.set('updateToken', value.trim())
break
}
}
return getSettings()