mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-20 12:11:28 -07:00
feat: add ASS subtitle cue parser
This commit is contained in:
@@ -53,11 +53,76 @@ export function parseSrtCues(content: string): SubtitleCue[] {
|
|||||||
return cues;
|
return cues;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stub exports — will be implemented in subsequent tasks
|
const ASS_OVERRIDE_TAG_PATTERN = /\{[^}]*\}/g;
|
||||||
export function parseAssCues(_content: string): SubtitleCue[] {
|
|
||||||
throw new Error('parseAssCues not yet implemented');
|
const ASS_TIMING_PATTERN = /^(\d+):(\d{2}):(\d{2})\.(\d{1,2})$/;
|
||||||
|
|
||||||
|
function parseAssTimestamp(raw: string): number | null {
|
||||||
|
const match = ASS_TIMING_PATTERN.exec(raw.trim());
|
||||||
|
if (!match) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const hours = Number(match[1]);
|
||||||
|
const minutes = Number(match[2]);
|
||||||
|
const seconds = Number(match[3]);
|
||||||
|
const centiseconds = Number(match[4]!.padEnd(2, '0'));
|
||||||
|
return hours * 3600 + minutes * 60 + seconds + centiseconds / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseAssCues(content: string): SubtitleCue[] {
|
||||||
|
const cues: SubtitleCue[] = [];
|
||||||
|
const lines = content.split(/\r?\n/);
|
||||||
|
let inEventsSection = false;
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
|
||||||
|
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
||||||
|
inEventsSection = trimmed.toLowerCase() === '[events]';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inEventsSection) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!trimmed.startsWith('Dialogue:')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split on first 9 commas (ASS v4+ has 10 fields; last is Text which can contain commas)
|
||||||
|
const afterPrefix = trimmed.slice('Dialogue:'.length);
|
||||||
|
const fields: string[] = [];
|
||||||
|
let remaining = afterPrefix;
|
||||||
|
for (let fieldIndex = 0; fieldIndex < 9; fieldIndex += 1) {
|
||||||
|
const commaIndex = remaining.indexOf(',');
|
||||||
|
if (commaIndex < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fields.push(remaining.slice(0, commaIndex));
|
||||||
|
remaining = remaining.slice(commaIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fields.length < 9) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startTime = parseAssTimestamp(fields[1]!);
|
||||||
|
const endTime = parseAssTimestamp(fields[2]!);
|
||||||
|
if (startTime === null || endTime === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawText = remaining.replace(ASS_OVERRIDE_TAG_PATTERN, '').trim();
|
||||||
|
if (rawText) {
|
||||||
|
cues.push({ startTime, endTime, text: rawText });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cues;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stub export — will be implemented in Task 6
|
||||||
export function parseSubtitleCues(_content: string, _filename: string): SubtitleCue[] {
|
export function parseSubtitleCues(_content: string, _filename: string): SubtitleCue[] {
|
||||||
throw new Error('parseSubtitleCues not yet implemented');
|
throw new Error('parseSubtitleCues not yet implemented');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user