csv take 3
Deploy / deploy (push) Successful in 3m2s

This commit is contained in:
twotalesanimation
2026-06-12 17:08:10 +02:00
parent 2402d5414d
commit 6abfa8d0b5
3 changed files with 77 additions and 74 deletions
+72
View File
@@ -0,0 +1,72 @@
export interface EdlImportRow {
outputName: string;
sourceClip: string;
timecodeStart: string;
timecodeEnd: string;
clipDuration: string;
// Derived
shotCode: string;
exrOutput: string;
// Action decided during preview
action: "create" | "update" | "skip";
existingShotId?: string;
}
/**
* Parse a VFX pull CSV into import rows.
* Output Name format: {showId}_{episode}_{scene}_{shot}_BG01_TT_v001
* shotCode = first 4 underscore-separated segments
* exrOutput = {shotCode}_{sourceClip}
*/
export function parseEdlCsv(csvText: string): { rows: EdlImportRow[]; errors: string[] } {
const lines = csvText.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
if (lines.length < 2) return { rows: [], errors: ["CSV must have a header row and at least one data row"] };
const unquote = (s: string) => s.replace(/^"|"$/g, "").trim();
const rawHeaders = lines[0].split(",").map(unquote).map((h) => h.toLowerCase());
const col = (name: string) => rawHeaders.indexOf(name);
const outNameIdx = col("output name");
const srcClipIdx = col("source clip");
const tcStartIdx = col("timecode start");
const tcEndIdx = col("timecode end");
const durIdx = col("duration");
if (outNameIdx === -1 || srcClipIdx === -1) {
return { rows: [], errors: ['CSV must contain "Output Name" and "Source Clip" columns'] };
}
const rows: EdlImportRow[] = [];
const errors: string[] = [];
for (let i = 1; i < lines.length; i++) {
const cells = lines[i].match(/("(?:[^"]|"")*"|[^,]*)/g)?.map(unquote) ?? [];
const get = (idx: number) => (idx !== -1 ? cells[idx] ?? "" : "");
const outputName = get(outNameIdx);
const sourceClip = get(srcClipIdx);
if (!outputName) { errors.push(`Row ${i + 1}: empty Output Name — skipped`); continue; }
const parts = outputName.split("_");
if (parts.length < 4) {
errors.push(`Row ${i + 1}: "${outputName}" — cannot parse shot code (need at least 4 segments)`);
continue;
}
const shotCode = parts.slice(0, 4).join("_");
const exrOutput = sourceClip ? `${shotCode}_${sourceClip}` : shotCode;
rows.push({
outputName,
sourceClip,
timecodeStart: get(tcStartIdx),
timecodeEnd: get(tcEndIdx),
clipDuration: get(durIdx),
shotCode,
exrOutput,
action: "create",
});
}
return { rows, errors };
}