refactor: extract duration helpers to src/main/infra/duration + 18 tests
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
995e4b62dd
commit
aee2914397
33
src/main.ts
33
src/main.ts
@ -7,6 +7,7 @@ import axios from 'axios';
|
|||||||
import { autoUpdater } from 'electron-updater';
|
import { autoUpdater } from 'electron-updater';
|
||||||
import { compareUpdateVersions, isNewerUpdateVersion, normalizeUpdateVersion } from './main/domain/update-version-utils';
|
import { compareUpdateVersions, isNewerUpdateVersion, normalizeUpdateVersion } from './main/domain/update-version-utils';
|
||||||
import { writeFileAtomicSync } from './main/infra/fs-atomic';
|
import { writeFileAtomicSync } from './main/infra/fs-atomic';
|
||||||
|
import { parseDuration, formatDuration, formatDurationDashed } from './main/infra/duration';
|
||||||
import { CustomClip, MergeGroupItem, MergeGroup, QueueItem, DownloadProgress, DownloadResult } from './types';
|
import { CustomClip, MergeGroupItem, MergeGroup, QueueItem, DownloadProgress, DownloadResult } from './types';
|
||||||
import {
|
import {
|
||||||
setDebugLogFn, initToolDirs,
|
setDebugLogFn, initToolDirs,
|
||||||
@ -1073,38 +1074,6 @@ function appendDebugLog(message: string, details?: unknown): void {
|
|||||||
setDebugLogFn(appendDebugLog);
|
setDebugLogFn(appendDebugLog);
|
||||||
initToolDirs(TOOLS_STREAMLINK_DIR, TOOLS_FFMPEG_DIR, () => app.getPath('temp'));
|
initToolDirs(TOOLS_STREAMLINK_DIR, TOOLS_FFMPEG_DIR, () => app.getPath('temp'));
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// DURATION HELPERS
|
|
||||||
// ==========================================
|
|
||||||
function parseDuration(duration: string): number {
|
|
||||||
let seconds = 0;
|
|
||||||
const hours = duration.match(/(\d+)h/);
|
|
||||||
const minutes = duration.match(/(\d+)m/);
|
|
||||||
const secs = duration.match(/(\d+)s/);
|
|
||||||
|
|
||||||
if (hours) seconds += parseInt(hours[1]) * 3600;
|
|
||||||
if (minutes) seconds += parseInt(minutes[1]) * 60;
|
|
||||||
if (secs) seconds += parseInt(secs[1]);
|
|
||||||
|
|
||||||
return seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDuration(seconds: number): string {
|
|
||||||
if (!isFinite(seconds) || seconds < 0) return '00:00:00';
|
|
||||||
const h = Math.floor(seconds / 3600);
|
|
||||||
const m = Math.floor((seconds % 3600) / 60);
|
|
||||||
const s = Math.floor(seconds % 60);
|
|
||||||
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDurationDashed(seconds: number): string {
|
|
||||||
if (!isFinite(seconds) || seconds < 0) return '00-00-00';
|
|
||||||
const h = Math.floor(seconds / 3600);
|
|
||||||
const m = Math.floor((seconds % 3600) / 60);
|
|
||||||
const s = Math.floor(seconds % 60);
|
|
||||||
return `${h.toString().padStart(2, '0')}-${m.toString().padStart(2, '0')}-${s.toString().padStart(2, '0')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const claimedFilenames = new Set<string>();
|
const claimedFilenames = new Set<string>();
|
||||||
const itemClaimedFilenames = new Map<string, Set<string>>();
|
const itemClaimedFilenames = new Map<string, Set<string>>();
|
||||||
|
|
||||||
|
|||||||
65
src/main/infra/duration.test.ts
Normal file
65
src/main/infra/duration.test.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { test, expect, describe } from 'vitest';
|
||||||
|
import { parseDuration, formatDuration, formatDurationDashed } from './duration';
|
||||||
|
|
||||||
|
describe('parseDuration', () => {
|
||||||
|
test('1h2m3s = 3723', () => {
|
||||||
|
expect(parseDuration('1h2m3s')).toBe(3723);
|
||||||
|
});
|
||||||
|
test('45m = 2700', () => {
|
||||||
|
expect(parseDuration('45m')).toBe(2700);
|
||||||
|
});
|
||||||
|
test('10s = 10', () => {
|
||||||
|
expect(parseDuration('10s')).toBe(10);
|
||||||
|
});
|
||||||
|
test('empty string = 0', () => {
|
||||||
|
expect(parseDuration('')).toBe(0);
|
||||||
|
});
|
||||||
|
test('unknown format = 0', () => {
|
||||||
|
expect(parseDuration('abcdef')).toBe(0);
|
||||||
|
});
|
||||||
|
test('partial 2h = 7200', () => {
|
||||||
|
expect(parseDuration('2h')).toBe(7200);
|
||||||
|
});
|
||||||
|
test('h and s without m = 3601', () => {
|
||||||
|
expect(parseDuration('1h1s')).toBe(3601);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('formatDuration', () => {
|
||||||
|
test('3723 = 01:02:03', () => {
|
||||||
|
expect(formatDuration(3723)).toBe('01:02:03');
|
||||||
|
});
|
||||||
|
test('0 = 00:00:00', () => {
|
||||||
|
expect(formatDuration(0)).toBe('00:00:00');
|
||||||
|
});
|
||||||
|
test('negative = 00:00:00', () => {
|
||||||
|
expect(formatDuration(-1)).toBe('00:00:00');
|
||||||
|
});
|
||||||
|
test('Infinity = 00:00:00', () => {
|
||||||
|
expect(formatDuration(Infinity)).toBe('00:00:00');
|
||||||
|
});
|
||||||
|
test('NaN = 00:00:00', () => {
|
||||||
|
expect(formatDuration(NaN)).toBe('00:00:00');
|
||||||
|
});
|
||||||
|
test('3600 = 01:00:00', () => {
|
||||||
|
expect(formatDuration(3600)).toBe('01:00:00');
|
||||||
|
});
|
||||||
|
test('86399 = 23:59:59', () => {
|
||||||
|
expect(formatDuration(86399)).toBe('23:59:59');
|
||||||
|
});
|
||||||
|
test('fractional seconds floored', () => {
|
||||||
|
expect(formatDuration(3723.9)).toBe('01:02:03');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('formatDurationDashed', () => {
|
||||||
|
test('3723 = 01-02-03', () => {
|
||||||
|
expect(formatDurationDashed(3723)).toBe('01-02-03');
|
||||||
|
});
|
||||||
|
test('negative = 00-00-00', () => {
|
||||||
|
expect(formatDurationDashed(-1)).toBe('00-00-00');
|
||||||
|
});
|
||||||
|
test('NaN = 00-00-00', () => {
|
||||||
|
expect(formatDurationDashed(NaN)).toBe('00-00-00');
|
||||||
|
});
|
||||||
|
});
|
||||||
28
src/main/infra/duration.ts
Normal file
28
src/main/infra/duration.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
export function parseDuration(duration: string): number {
|
||||||
|
let seconds = 0;
|
||||||
|
const hours = duration.match(/(\d+)h/);
|
||||||
|
const minutes = duration.match(/(\d+)m/);
|
||||||
|
const secs = duration.match(/(\d+)s/);
|
||||||
|
|
||||||
|
if (hours) seconds += parseInt(hours[1]) * 3600;
|
||||||
|
if (minutes) seconds += parseInt(minutes[1]) * 60;
|
||||||
|
if (secs) seconds += parseInt(secs[1]);
|
||||||
|
|
||||||
|
return seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDuration(seconds: number): string {
|
||||||
|
if (!isFinite(seconds) || seconds < 0) return '00:00:00';
|
||||||
|
const h = Math.floor(seconds / 3600);
|
||||||
|
const m = Math.floor((seconds % 3600) / 60);
|
||||||
|
const s = Math.floor(seconds % 60);
|
||||||
|
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDurationDashed(seconds: number): string {
|
||||||
|
if (!isFinite(seconds) || seconds < 0) return '00-00-00';
|
||||||
|
const h = Math.floor(seconds / 3600);
|
||||||
|
const m = Math.floor((seconds % 3600) / 60);
|
||||||
|
const s = Math.floor(seconds % 60);
|
||||||
|
return `${h.toString().padStart(2, '0')}-${m.toString().padStart(2, '0')}-${s.toString().padStart(2, '0')}`;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user