feat(clip): add Parts-format preset to Trim-Clip dialog
The Trim-Clip filename-format radio group only offered three presets
(simple, timestamp, custom template). Users who organise their archive
with the global filename_template_parts pattern (e.g.
08.05.2026_Part07.mp4) had to switch to "custom template" and retype
{date}_Part{part_padded}.mp4 every time.
New "parts" preset:
- index.html: 4th radio option, span#formatParts for the live preview
- types.ts + renderer-globals.d.ts: filenameFormat union extended
- main.ts: makeClipFilename branch produces ${dateStr}_Part${padded}.mp4;
sanitizeCustomClip whitelists "parts" so persisted queue items with
the new format survive a restart
- renderer.ts: getSelectedFilenameFormat returns "parts"; live preview
via partNum.padStart(2, "0")
- DE/EN locales: clips.formatParts label
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
173ae61a3f
commit
013e8be1f0
@ -106,6 +106,11 @@
|
||||
style="width: 18px; height: 18px; accent-color: #9146FF;">
|
||||
<span id="formatTimestamp" style="color: #aaa;">01.02.2026_CLIP_00-00-00_1.mp4 (mit Zeitstempel)</span>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; margin-bottom: 8px;">
|
||||
<input type="radio" name="filenameFormat" value="parts" onchange="updateFilenameExamples()"
|
||||
style="width: 18px; height: 18px; accent-color: #9146FF;">
|
||||
<span id="formatParts" style="color: #aaa;">01.02.2026_Part01.mp4 (Parts-Format)</span>
|
||||
</label>
|
||||
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; margin-bottom: 10px;">
|
||||
<input type="radio" name="filenameFormat" value="template" onchange="updateFilenameExamples()"
|
||||
style="width: 18px; height: 18px; accent-color: #9146FF;">
|
||||
|
||||
12
src/main.ts
12
src/main.ts
@ -364,7 +364,7 @@ function sanitizeCustomClip(raw: unknown): CustomClip | undefined {
|
||||
if (!Number.isFinite(startSec) || !Number.isFinite(durationSec) || durationSec <= 0 || !Number.isFinite(startPart)) return undefined;
|
||||
|
||||
const filenameFormat = raw.filenameFormat;
|
||||
if (filenameFormat !== 'simple' && filenameFormat !== 'timestamp' && filenameFormat !== 'template') return undefined;
|
||||
if (filenameFormat !== 'simple' && filenameFormat !== 'timestamp' && filenameFormat !== 'template' && filenameFormat !== 'parts') return undefined;
|
||||
|
||||
return {
|
||||
startSec: Math.max(0, startSec),
|
||||
@ -2666,9 +2666,15 @@ async function downloadVOD(
|
||||
const s = Math.floor(startOffset % 60);
|
||||
const timeStr = `${h.toString().padStart(2, '0')}-${m.toString().padStart(2, '0')}-${s.toString().padStart(2, '0')}`;
|
||||
return path.join(folder, `${dateStr}_CLIP_${timeStr}_${partNum}.mp4`);
|
||||
} else {
|
||||
return path.join(folder, `${dateStr}_${partNum}.mp4`);
|
||||
}
|
||||
|
||||
if (clip.filenameFormat === 'parts') {
|
||||
// Mirrors the global filename_template_parts default:
|
||||
// `{date}_Part{part_padded}.mp4` -> e.g. 08.05.2026_Part07.mp4
|
||||
return path.join(folder, `${dateStr}_Part${partNum.toString().padStart(2, '0')}.mp4`);
|
||||
}
|
||||
|
||||
return path.join(folder, `${dateStr}_${partNum}.mp4`);
|
||||
};
|
||||
|
||||
// If clip is longer than part duration, split into parts
|
||||
|
||||
2
src/renderer-globals.d.ts
vendored
2
src/renderer-globals.d.ts
vendored
@ -34,7 +34,7 @@ interface CustomClip {
|
||||
startSec: number;
|
||||
durationSec: number;
|
||||
startPart: number;
|
||||
filenameFormat: 'simple' | 'timestamp' | 'template';
|
||||
filenameFormat: 'simple' | 'timestamp' | 'template' | 'parts';
|
||||
filenameTemplate?: string;
|
||||
}
|
||||
|
||||
|
||||
@ -189,6 +189,7 @@ const UI_TEXT_DE = {
|
||||
unknownError: 'Unbekannter Fehler',
|
||||
formatSimple: '(Standard)',
|
||||
formatTimestamp: '(mit Zeitstempel)',
|
||||
formatParts: '(Parts-Format)',
|
||||
formatTemplate: '(benutzerdefiniert)',
|
||||
templateEmpty: 'Das Template darf im benutzerdefinierten Modus nicht leer sein.',
|
||||
templatePlaceholder: '{date}_{part}.mp4',
|
||||
|
||||
@ -189,6 +189,7 @@ const UI_TEXT_EN = {
|
||||
unknownError: 'Unknown error',
|
||||
formatSimple: '(default)',
|
||||
formatTimestamp: '(with timestamp)',
|
||||
formatParts: '(parts naming)',
|
||||
formatTemplate: '(custom template)',
|
||||
templateEmpty: 'Template cannot be empty in custom template mode.',
|
||||
templatePlaceholder: '{date}_{part}.mp4',
|
||||
|
||||
@ -535,9 +535,12 @@ function formatSecondsWithPattern(totalSeconds: number, pattern: string): string
|
||||
.replace(/\\(.)/g, '$1');
|
||||
}
|
||||
|
||||
function getSelectedFilenameFormat(): 'simple' | 'timestamp' | 'template' {
|
||||
function getSelectedFilenameFormat(): 'simple' | 'timestamp' | 'template' | 'parts' {
|
||||
const selected = query<HTMLInputElement>('input[name="filenameFormat"]:checked').value;
|
||||
return selected === 'template' ? 'template' : selected === 'timestamp' ? 'timestamp' : 'simple';
|
||||
if (selected === 'template') return 'template';
|
||||
if (selected === 'timestamp') return 'timestamp';
|
||||
if (selected === 'parts') return 'parts';
|
||||
return 'simple';
|
||||
}
|
||||
|
||||
function updateFilenameTemplateVisibility(): void {
|
||||
@ -895,6 +898,7 @@ function updateFilenameExamples(): void {
|
||||
|
||||
byId('formatSimple').textContent = `${dateStr}_${partNum}.mp4 ${UI_TEXT.clips.formatSimple}`;
|
||||
byId('formatTimestamp').textContent = `${dateStr}_CLIP_${timeStr}_${partNum}.mp4 ${UI_TEXT.clips.formatTimestamp}`;
|
||||
byId('formatParts').textContent = `${dateStr}_Part${partNum.padStart(2, '0')}.mp4 ${UI_TEXT.clips.formatParts}`;
|
||||
byId('formatTemplate').textContent = `${buildTemplatePreview(template, {
|
||||
title: clipDialogData.title,
|
||||
date,
|
||||
|
||||
@ -2,7 +2,7 @@ export interface CustomClip {
|
||||
startSec: number;
|
||||
durationSec: number;
|
||||
startPart: number;
|
||||
filenameFormat: 'simple' | 'timestamp' | 'template';
|
||||
filenameFormat: 'simple' | 'timestamp' | 'template' | 'parts';
|
||||
filenameTemplate?: string;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user