feat(pinchflat): Media Profiles — named download presets per Library source
A MediaProfile bundles { kind, quality, options, outputTemplate, extraArgs }
that a Source points at via Source.profileId, overriding the global defaults
field-by-field for that source's downloads.
Backend mirrors CommandTemplate: profiles.ts (createJsonStore, PROFILES_MAX,
isProfileLike + sanitize), CRUD IPC (profiles:list/save/remove) + preload +
useProfiles store; Source.profileId + sources:set-profile. Pure resolution
in @shared/profileResolve.ts (profile → global, per field; unit-tested).
Applied in the sources-store enqueueItems; a per-download collectionOutput-
Template override threads to buildArgs so a profile's outputTemplate wins.
UI: ProfileManager (mirrors TemplateManager) in a new Settings → Advanced →
Media profiles card, plus a per-source Profile picker in the Library source
detail. Both live-verified in the preview; 354 tests pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+11
-1
@@ -19,6 +19,7 @@ import {
|
||||
type Settings,
|
||||
type HistoryEntry,
|
||||
type CommandTemplate,
|
||||
type MediaProfile,
|
||||
type YtdlpUpdateChannel,
|
||||
type SystemThemeInfo,
|
||||
type TaskbarProgress,
|
||||
@@ -35,6 +36,7 @@ import { runTerminal, cancelTerminal } from './terminal'
|
||||
import { getSettings, setSettings } from './settings'
|
||||
import { listHistory, addHistory, removeHistory, removeManyHistory, clearHistory } from './history'
|
||||
import { listTemplates, saveTemplate, removeTemplate } from './templates'
|
||||
import { listProfiles, saveProfile, removeProfile } from './profiles'
|
||||
import { safeOpenPath, safeShowInFolder } from './reveal'
|
||||
import { openCookieLoginWindow, getCookiesStatus, clearCookies } from './cookies'
|
||||
import { listErrorLog, addErrorLog, clearErrorLog } from './errorlog'
|
||||
@@ -46,7 +48,8 @@ import {
|
||||
removeSource,
|
||||
listMediaItems,
|
||||
setMediaItemDownloaded,
|
||||
setSourceWatched
|
||||
setSourceWatched,
|
||||
setSourceProfile
|
||||
} from './sources'
|
||||
import { indexSourceCancelable, cancelIndexing } from './indexer'
|
||||
import { syncWatchedSources } from './sync'
|
||||
@@ -213,6 +216,10 @@ export function registerIpcHandlers(getMainWindow: () => BrowserWindow | null):
|
||||
)
|
||||
ipcMain.handle(IpcChannels.templatesRemove, (_e, id: string) => removeTemplate(id))
|
||||
|
||||
ipcMain.handle(IpcChannels.profilesList, () => listProfiles())
|
||||
ipcMain.handle(IpcChannels.profilesSave, (_e, profile: MediaProfile) => saveProfile(profile))
|
||||
ipcMain.handle(IpcChannels.profilesRemove, (_e, id: string) => removeProfile(id))
|
||||
|
||||
ipcMain.handle(IpcChannels.commandPreview, (_e, opts: StartDownloadOptions) =>
|
||||
previewCommand(opts)
|
||||
)
|
||||
@@ -269,6 +276,9 @@ export function registerIpcHandlers(getMainWindow: () => BrowserWindow | null):
|
||||
ipcMain.handle(IpcChannels.sourceSetWatched, (_e, id: string, watched: boolean) =>
|
||||
setSourceWatched(id, watched)
|
||||
)
|
||||
ipcMain.handle(IpcChannels.sourceSetProfile, (_e, id: string, profileId: string | null) =>
|
||||
setSourceProfile(id, profileId)
|
||||
)
|
||||
ipcMain.handle(IpcChannels.sourcesSync, (e) =>
|
||||
syncWatchedSources((p) => {
|
||||
if (!e.sender.isDestroyed()) e.sender.send(IpcChannels.indexProgress, p)
|
||||
|
||||
Reference in New Issue
Block a user