Add Phase B: cookies, aria2c, proxy/rate-limit, restrict-filenames & archive

Implements the remaining Phase B (Access & networking) items from
ROADMAP.md: cookie sources (browser cookie-store import, or a built-in
sign-in window that exports a Netscape cookie file via a persisted Electron
session partition), the aria2c external downloader, proxy/rate-limit,
restrict-filenames, and a download-archive to skip repeats.

Wires download.ts to the buildArgs() module added in the previous commit
(it wasn't hooked up yet) and renames its options param
NetworkOptions -> AccessOptions to reflect the wider scope now that cookies
and filename/archive flags joined proxy/rate-limit/aria2c.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 20:53:18 -04:00
parent 26cd7fc7b0
commit 67133f13b5
15 changed files with 683 additions and 196 deletions
+29 -2
View File
@@ -1,10 +1,12 @@
import { app } from 'electron'
import { join } from 'path'
import Store from 'electron-store'
import {
AUDIO_FORMATS,
VIDEO_CONTAINERS,
VIDEO_CODECS,
SPONSORBLOCK_CATEGORIES,
COOKIE_BROWSERS,
DEFAULT_DOWNLOAD_OPTIONS,
type Settings,
type DownloadOptions,
@@ -20,7 +22,19 @@ const DEFAULTS: Settings = {
filenameTemplate: '%(title)s.%(ext)s',
theme: 'light',
clipboardWatch: true,
downloadOptions: DEFAULT_DOWNLOAD_OPTIONS
downloadOptions: DEFAULT_DOWNLOAD_OPTIONS,
proxy: '',
rateLimit: '',
useAria2c: false,
cookieSource: 'none',
cookiesBrowser: 'chrome',
restrictFilenames: false,
downloadArchive: false
}
/** Fixed path for the --download-archive file; not user-configurable. */
export function getDownloadArchivePath(): string {
return join(app.getPath('userData'), 'download-archive.txt')
}
// Coerce an untrusted partial into a valid DownloadOptions, falling back to the
@@ -98,7 +112,18 @@ export function setSettings(partial: Partial<Settings>): Settings {
break
}
case 'clipboardWatch':
if (typeof value === 'boolean') s.set('clipboardWatch', value)
case 'useAria2c':
case 'restrictFilenames':
case 'downloadArchive':
if (typeof value === 'boolean') s.set(key, value)
break
case 'cookieSource':
if (value === 'none' || value === 'browser' || value === 'login') s.set(key, value)
break
case 'cookiesBrowser':
if ((COOKIE_BROWSERS as readonly string[]).includes(value as string)) {
s.set(key, value as Settings['cookiesBrowser'])
}
break
case 'downloadOptions':
// Always store a fully-validated object; the renderer sends the whole
@@ -109,6 +134,8 @@ export function setSettings(partial: Partial<Settings>): Settings {
case 'defaultVideoQuality':
case 'defaultAudioQuality':
case 'filenameTemplate':
case 'proxy':
case 'rateLimit':
if (typeof value === 'string') s.set(key, value)
break
}