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;
|
||||
}
|
||||
|
||||
// Stub exports — will be implemented in subsequent tasks
|
||||
export function parseAssCues(_content: string): SubtitleCue[] {
|
||||
throw new Error('parseAssCues not yet implemented');
|
||||
const ASS_OVERRIDE_TAG_PATTERN = /\{[^}]*\}/g;
|
||||
|
||||
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[] {
|
||||
throw new Error('parseSubtitleCues not yet implemented');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user