mirror of
https://github.com/alexta69/metube.git
synced 2026-06-13 16:40:05 +00:00
change option presets to be multi-select
This commit is contained in:
+11
-11
@@ -482,18 +482,18 @@
|
||||
<div class="row g-3">
|
||||
<div class="col-12" [class.col-md-6]="allowYtdlOptionsOverrides()">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Option Preset</span>
|
||||
<select class="form-select"
|
||||
name="ytdlOptionsPreset"
|
||||
[(ngModel)]="ytdlOptionsPreset"
|
||||
(change)="ytdlOptionsPresetChanged()"
|
||||
<span class="input-group-text">Option Presets</span>
|
||||
<ng-select
|
||||
class="flex-grow-1"
|
||||
name="ytdlOptionsPresets"
|
||||
[items]="ytdlOptionPresetNames"
|
||||
[multiple]="true"
|
||||
[closeOnSelect]="false"
|
||||
placeholder="Default"
|
||||
[(ngModel)]="ytdlOptionsPresets"
|
||||
(ngModelChange)="ytdlOptionsPresetsChanged()"
|
||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
||||
ngbTooltip="Choose a named yt-dlp option preset configured on the server">
|
||||
<option value="">Default</option>
|
||||
@for (preset of ytdlOptionPresetNames; track preset) {
|
||||
<option [value]="preset">{{ preset }}</option>
|
||||
}
|
||||
</select>
|
||||
ngbTooltip="Choose one or more yt-dlp option presets configured on the server (applied in order)" />
|
||||
</div>
|
||||
</div>
|
||||
@if (allowYtdlOptionsOverrides()) {
|
||||
|
||||
@@ -140,10 +140,10 @@ describe('App', () => {
|
||||
const root = fixture.nativeElement as HTMLElement;
|
||||
expect(root.querySelector('input[name="ytdlOptionsOverrides"]')).toBeNull();
|
||||
|
||||
const presetWrapper = root.querySelector('select[name="ytdlOptionsPreset"]')?.closest('.col-12');
|
||||
const presetWrapper = root.querySelector('ng-select[name="ytdlOptionsPresets"]')?.closest('.col-12');
|
||||
expect(presetWrapper?.classList.contains('col-md-6')).toBe(false);
|
||||
|
||||
const presetRow = root.querySelector('select[name="ytdlOptionsPreset"]')?.closest('.row');
|
||||
const presetRow = root.querySelector('ng-select[name="ytdlOptionsPresets"]')?.closest('.row');
|
||||
expect(presetRow?.querySelector('input[name="checkIntervalMinutes"]')).toBeNull();
|
||||
});
|
||||
|
||||
@@ -157,10 +157,10 @@ describe('App', () => {
|
||||
const root = fixture.nativeElement as HTMLElement;
|
||||
expect(root.querySelector('input[name="ytdlOptionsOverrides"]')).not.toBeNull();
|
||||
|
||||
const presetWrapper = root.querySelector('select[name="ytdlOptionsPreset"]')?.closest('.col-12');
|
||||
const presetWrapper = root.querySelector('ng-select[name="ytdlOptionsPresets"]')?.closest('.col-12');
|
||||
expect(presetWrapper?.classList.contains('col-md-6')).toBe(true);
|
||||
|
||||
const presetRow = root.querySelector('select[name="ytdlOptionsPreset"]')?.closest('.row');
|
||||
const presetRow = root.querySelector('ng-select[name="ytdlOptionsPresets"]')?.closest('.row');
|
||||
expect(presetRow?.querySelector('input[name="checkIntervalMinutes"]')).toBeNull();
|
||||
expect(presetRow?.querySelector('input[name="ytdlOptionsOverrides"]')).not.toBeNull();
|
||||
});
|
||||
|
||||
+35
-9
@@ -83,7 +83,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
|
||||
chapterTemplate: string;
|
||||
subtitleLanguage: string;
|
||||
subtitleMode: string;
|
||||
ytdlOptionsPreset: string;
|
||||
ytdlOptionsPresets: string[] = [];
|
||||
ytdlOptionsOverrides: string;
|
||||
ytdlOptionPresetNames: string[] = [];
|
||||
addInProgress = false;
|
||||
@@ -234,7 +234,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
|
||||
this.chapterTemplate = this.cookieService.get('metube_chapter_template') || '';
|
||||
this.subtitleLanguage = this.cookieService.get('metube_subtitle_language') || 'en';
|
||||
this.subtitleMode = this.cookieService.get('metube_subtitle_mode') || 'prefer_manual';
|
||||
this.ytdlOptionsPreset = this.cookieService.get('metube_ytdl_options_preset') || '';
|
||||
this.ytdlOptionsPresets = this.loadYtdlOptionsPresetsFromCookie();
|
||||
this.ytdlOptionsOverrides = this.cookieService.get('metube_ytdl_options_overrides') || '';
|
||||
const allowedDownloadTypes = new Set(this.downloadTypes.map(t => t.id));
|
||||
const allowedVideoCodecs = new Set(this.videoCodecs.map(c => c.id));
|
||||
@@ -431,15 +431,35 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
|
||||
this.ytdlOptionPresetNames = Array.isArray(data?.presets)
|
||||
? data.presets.filter((preset): preset is string => typeof preset === 'string')
|
||||
: [];
|
||||
if (this.ytdlOptionsPreset && !this.ytdlOptionPresetNames.includes(this.ytdlOptionsPreset)) {
|
||||
this.ytdlOptionsPreset = '';
|
||||
this.ytdlOptionsPresetChanged();
|
||||
if (this.ytdlOptionsPresets?.length) {
|
||||
const valid = new Set(this.ytdlOptionPresetNames);
|
||||
const filtered = this.ytdlOptionsPresets.filter((p) => valid.has(p));
|
||||
if (filtered.length !== this.ytdlOptionsPresets.length) {
|
||||
this.ytdlOptionsPresets = filtered;
|
||||
this.ytdlOptionsPresetsChanged();
|
||||
}
|
||||
}
|
||||
this.cdr.markForCheck();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private loadYtdlOptionsPresetsFromCookie(): string[] {
|
||||
const jsonCookie = this.cookieService.get('metube_ytdl_options_presets');
|
||||
if (jsonCookie) {
|
||||
try {
|
||||
const parsed = JSON.parse(jsonCookie) as unknown;
|
||||
if (Array.isArray(parsed)) {
|
||||
return parsed.filter((p): p is string => typeof p === 'string' && p.length > 0);
|
||||
}
|
||||
} catch {
|
||||
// fall through to legacy cookie
|
||||
}
|
||||
}
|
||||
const legacy = this.cookieService.get('metube_ytdl_options_preset')?.trim();
|
||||
return legacy ? [legacy] : [];
|
||||
}
|
||||
|
||||
private validateYtdlOptionsOverrides(value: string): boolean {
|
||||
if (!this.allowYtdlOptionsOverrides()) {
|
||||
return true;
|
||||
@@ -744,8 +764,12 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
|
||||
this.saveSelection(this.downloadType);
|
||||
}
|
||||
|
||||
ytdlOptionsPresetChanged() {
|
||||
this.cookieService.set('metube_ytdl_options_preset', this.ytdlOptionsPreset, { expires: this.settingsCookieExpiryDays });
|
||||
ytdlOptionsPresetsChanged() {
|
||||
this.cookieService.set(
|
||||
'metube_ytdl_options_presets',
|
||||
JSON.stringify(this.ytdlOptionsPresets ?? []),
|
||||
{ expires: this.settingsCookieExpiryDays },
|
||||
);
|
||||
}
|
||||
|
||||
ytdlOptionsOverridesChanged() {
|
||||
@@ -952,7 +976,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
|
||||
chapterTemplate: overrides.chapterTemplate ?? this.chapterTemplate,
|
||||
subtitleLanguage: overrides.subtitleLanguage ?? this.subtitleLanguage,
|
||||
subtitleMode: overrides.subtitleMode ?? this.subtitleMode,
|
||||
ytdlOptionsPreset: overrides.ytdlOptionsPreset ?? this.ytdlOptionsPreset,
|
||||
ytdlOptionsPresets: overrides.ytdlOptionsPresets ?? [...this.ytdlOptionsPresets],
|
||||
ytdlOptionsOverrides: allowYtdlOptionsOverrides
|
||||
? (overrides.ytdlOptionsOverrides ?? this.ytdlOptionsOverrides)
|
||||
: '',
|
||||
@@ -1025,7 +1049,9 @@ export class App implements AfterViewInit, OnInit, OnDestroy {
|
||||
chapterTemplate: download.chapter_template,
|
||||
subtitleLanguage: download.subtitle_language,
|
||||
subtitleMode: download.subtitle_mode,
|
||||
ytdlOptionsPreset: download.ytdl_options_preset || '',
|
||||
ytdlOptionsPresets: download.ytdl_options_presets?.length
|
||||
? [...download.ytdl_options_presets]
|
||||
: [],
|
||||
ytdlOptionsOverrides: download.ytdl_options_overrides ? JSON.stringify(download.ytdl_options_overrides) : '',
|
||||
});
|
||||
this.downloads.delById('done', [key]).subscribe();
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface Download {
|
||||
chapter_template?: string;
|
||||
subtitle_language?: string;
|
||||
subtitle_mode?: string;
|
||||
ytdl_options_preset?: string;
|
||||
ytdl_options_presets?: string[];
|
||||
ytdl_options_overrides?: Record<string, unknown>;
|
||||
status: string;
|
||||
msg: string;
|
||||
|
||||
@@ -39,7 +39,7 @@ function basePayload(): AddDownloadPayload {
|
||||
chapterTemplate: '',
|
||||
subtitleLanguage: 'en',
|
||||
subtitleMode: 'prefer_manual',
|
||||
ytdlOptionsPreset: '',
|
||||
ytdlOptionsPresets: [],
|
||||
ytdlOptionsOverrides: '',
|
||||
};
|
||||
}
|
||||
@@ -81,7 +81,7 @@ describe('DownloadsService', () => {
|
||||
chapter_template: '',
|
||||
subtitle_language: 'en',
|
||||
subtitle_mode: 'prefer_manual',
|
||||
ytdl_options_preset: '',
|
||||
ytdl_options_presets: [],
|
||||
ytdl_options_overrides: '',
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface AddDownloadPayload {
|
||||
chapterTemplate: string;
|
||||
subtitleLanguage: string;
|
||||
subtitleMode: string;
|
||||
ytdlOptionsPreset: string;
|
||||
ytdlOptionsPresets: string[];
|
||||
ytdlOptionsOverrides: string;
|
||||
}
|
||||
@Injectable({
|
||||
@@ -143,7 +143,7 @@ export class DownloadsService {
|
||||
chapter_template: payload.chapterTemplate,
|
||||
subtitle_language: payload.subtitleLanguage,
|
||||
subtitle_mode: payload.subtitleMode,
|
||||
ytdl_options_preset: payload.ytdlOptionsPreset,
|
||||
ytdl_options_presets: payload.ytdlOptionsPresets,
|
||||
ytdl_options_overrides: payload.ytdlOptionsOverrides,
|
||||
}).pipe(
|
||||
catchError(this.handleHTTPError)
|
||||
|
||||
@@ -94,7 +94,7 @@ export class SubscriptionsService {
|
||||
chapter_template: payload.chapterTemplate,
|
||||
subtitle_language: payload.subtitleLanguage,
|
||||
subtitle_mode: payload.subtitleMode,
|
||||
ytdl_options_preset: payload.ytdlOptionsPreset,
|
||||
ytdl_options_presets: payload.ytdlOptionsPresets,
|
||||
ytdl_options_overrides: payload.ytdlOptionsOverrides,
|
||||
check_interval_minutes: payload.checkIntervalMinutes,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user