feat: Replaced SponsorBlock free-text input with multi-select checkbox…
- "src/frontend/src/components/FormatProfileForm.tsx" GSD-Task: S05/T02
This commit is contained in:
parent
f814e8d261
commit
8cf998a697
1 changed files with 66 additions and 14 deletions
|
|
@ -9,6 +9,17 @@ const CODEC_OPTIONS = ['Any', 'AAC', 'MP3', 'OPUS', 'FLAC'] as const;
|
||||||
const BITRATE_OPTIONS = ['Any', 'Best', '320k', '256k', '192k', '128k'] as const;
|
const BITRATE_OPTIONS = ['Any', 'Best', '320k', '256k', '192k', '128k'] as const;
|
||||||
const CONTAINER_OPTIONS = ['Any', 'MP4', 'MKV', 'WEBM', 'MP3'] as const;
|
const CONTAINER_OPTIONS = ['Any', 'MP4', 'MKV', 'WEBM', 'MP3'] as const;
|
||||||
|
|
||||||
|
const SPONSORBLOCK_CATEGORIES = [
|
||||||
|
{ value: 'sponsor', label: 'Sponsor' },
|
||||||
|
{ value: 'selfpromo', label: 'Self-Promotion' },
|
||||||
|
{ value: 'interaction', label: 'Interaction' },
|
||||||
|
{ value: 'intro', label: 'Intro' },
|
||||||
|
{ value: 'outro', label: 'Outro' },
|
||||||
|
{ value: 'preview', label: 'Preview' },
|
||||||
|
{ value: 'music_offtopic', label: 'Music (Off-Topic)' },
|
||||||
|
{ value: 'filler', label: 'Filler' },
|
||||||
|
] as const;
|
||||||
|
|
||||||
// ── Types ──
|
// ── Types ──
|
||||||
|
|
||||||
export interface FormatProfileFormValues {
|
export interface FormatProfileFormValues {
|
||||||
|
|
@ -98,7 +109,11 @@ export function FormatProfileForm({
|
||||||
const [embedSubtitles, setEmbedSubtitles] = useState(profile?.embedSubtitles ?? false);
|
const [embedSubtitles, setEmbedSubtitles] = useState(profile?.embedSubtitles ?? false);
|
||||||
const [embedChapters, setEmbedChapters] = useState(profile?.embedChapters ?? false);
|
const [embedChapters, setEmbedChapters] = useState(profile?.embedChapters ?? false);
|
||||||
const [embedThumbnail, setEmbedThumbnail] = useState(profile?.embedThumbnail ?? false);
|
const [embedThumbnail, setEmbedThumbnail] = useState(profile?.embedThumbnail ?? false);
|
||||||
const [sponsorBlockRemove, setSponsorBlockRemove] = useState(profile?.sponsorBlockRemove ?? '');
|
const [sponsorBlockCategories, setSponsorBlockCategories] = useState<Set<string>>(() => {
|
||||||
|
const raw = profile?.sponsorBlockRemove ?? '';
|
||||||
|
if (!raw.trim()) return new Set<string>();
|
||||||
|
return new Set(raw.split(',').map((s) => s.trim()).filter(Boolean));
|
||||||
|
});
|
||||||
const [outputTemplate, setOutputTemplate] = useState(profile?.outputTemplate ?? '');
|
const [outputTemplate, setOutputTemplate] = useState(profile?.outputTemplate ?? '');
|
||||||
|
|
||||||
const TEMPLATE_VARIABLES = ['platform', 'channel', 'title', 'date', 'year', 'month', 'contentType', 'id', 'ext'] as const;
|
const TEMPLATE_VARIABLES = ['platform', 'channel', 'title', 'date', 'year', 'month', 'contentType', 'id', 'ext'] as const;
|
||||||
|
|
@ -147,11 +162,11 @@ export function FormatProfileForm({
|
||||||
embedSubtitles,
|
embedSubtitles,
|
||||||
embedChapters,
|
embedChapters,
|
||||||
embedThumbnail,
|
embedThumbnail,
|
||||||
sponsorBlockRemove: sponsorBlockRemove.trim() || null,
|
sponsorBlockRemove: sponsorBlockCategories.size > 0 ? [...sponsorBlockCategories].join(',') : null,
|
||||||
outputTemplate: outputTemplate.trim() || null,
|
outputTemplate: outputTemplate.trim() || null,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[name, videoResolution, audioCodec, audioBitrate, containerFormat, isDefault, subtitleLanguages, embedSubtitles, embedChapters, embedThumbnail, sponsorBlockRemove, outputTemplate, onSubmit],
|
[name, videoResolution, audioCodec, audioBitrate, containerFormat, isDefault, subtitleLanguages, embedSubtitles, embedChapters, embedThumbnail, sponsorBlockCategories, outputTemplate, onSubmit],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -328,21 +343,58 @@ export function FormatProfileForm({
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* SponsorBlock Remove */}
|
{/* SponsorBlock Remove — checkbox group */}
|
||||||
<div style={fieldGroupStyle}>
|
<div style={fieldGroupStyle}>
|
||||||
<label htmlFor="fp-sponsorblock" style={labelStyle}>
|
<label style={labelStyle}>
|
||||||
SponsorBlock — Remove Segments
|
SponsorBlock — Remove Segments
|
||||||
</label>
|
</label>
|
||||||
|
<div style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: '1fr 1fr',
|
||||||
|
gap: 'var(--space-2)',
|
||||||
|
padding: 'var(--space-3)',
|
||||||
|
backgroundColor: 'var(--bg-input)',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
borderRadius: 'var(--radius-md)',
|
||||||
|
}}>
|
||||||
|
{SPONSORBLOCK_CATEGORIES.map(({ value, label }) => (
|
||||||
|
<label
|
||||||
|
key={value}
|
||||||
|
htmlFor={`fp-sb-${value}`}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 'var(--space-2)',
|
||||||
|
fontSize: 'var(--font-size-sm)',
|
||||||
|
color: 'var(--text-secondary)',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
id="fp-sponsorblock"
|
id={`fp-sb-${value}`}
|
||||||
type="text"
|
type="checkbox"
|
||||||
value={sponsorBlockRemove}
|
checked={sponsorBlockCategories.has(value)}
|
||||||
onChange={(e) => setSponsorBlockRemove(e.target.value)}
|
onChange={(e) => {
|
||||||
placeholder="e.g. sponsor,selfpromo,intro,outro"
|
setSponsorBlockCategories((prev) => {
|
||||||
style={inputStyle}
|
const next = new Set(prev);
|
||||||
|
if (e.target.checked) next.add(value);
|
||||||
|
else next.delete(value);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
accentColor: 'var(--accent)',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
<span style={{ fontSize: 'var(--font-size-xs)', color: 'var(--text-muted)', marginTop: 'var(--space-1)', display: 'block' }}>
|
<span style={{ fontSize: 'var(--font-size-xs)', color: 'var(--text-muted)', marginTop: 'var(--space-1)', display: 'block' }}>
|
||||||
Comma-separated categories: sponsor, selfpromo, interaction, intro, outro, preview, music_offtopic, filler
|
Selected segments will be removed from downloaded videos using SponsorBlock data.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue