Further CSV Import method added
Deploy / deploy (push) Successful in 2m42s

This commit is contained in:
twotalesanimation
2026-08-06 08:36:26 +02:00
parent c727795a78
commit 5f3c89119a
4 changed files with 415 additions and 3 deletions
+77
View File
@@ -184,3 +184,80 @@ export function parsePictureTrackerCsv(
return { rows, errors };
}
// ── Simple Shot CSV ──────────────────────────────────────────────────────────
export interface SimpleCsvRow {
shotCode: string;
seqTimecodeStart: string;
seqTimecodeEnd: string;
description: string;
action: "create" | "update" | "skip";
}
/**
* Parse a simple shot CSV: Shot Name, Time Code In, Time Code Out, Description.
* Column names are matched case-insensitively with common aliases.
* Timecodes are validated but not required.
*/
export function parseSimpleCsv(
csvText: string,
existingCodes: Set<string>
): { rows: SimpleCsvRow[]; 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 rawHeaders = parseCsvLine(lines[0]).map((h) => h.toLowerCase().trim());
const col = (names: string[]) => {
for (const name of names) {
const idx = rawHeaders.indexOf(name);
if (idx !== -1) return idx;
}
return -1;
};
const shotNameIdx = col(["shot name", "shot code", "shotcode", "shot", "name"]);
const tcInIdx = col(["time code in", "timecode in", "tc in", "tcin", "timecode start", "tc start"]);
const tcOutIdx = col(["time code out", "timecode out", "tc out", "tcout", "timecode end", "tc end"]);
const descIdx = col(["description", "desc", "notes", "note"]);
if (shotNameIdx === -1) {
return { rows: [], errors: ['CSV must contain a "Shot Name" (or "Shot Code") column'] };
}
const rows: SimpleCsvRow[] = [];
const errors: string[] = [];
for (let i = 1; i < lines.length; i++) {
const cells = parseCsvLine(lines[i]);
const get = (idx: number) =>
idx !== -1 && idx < cells.length ? (cells[idx] ?? "").trim() : "";
const shotCode = get(shotNameIdx);
if (!shotCode) { errors.push(`Row ${i + 1}: empty Shot Name — skipped`); continue; }
const tcIn = get(tcInIdx);
const tcOut = get(tcOutIdx);
if (tcIn && !isValidTimecode(tcIn)) {
errors.push(`Row ${i + 1} (${shotCode}): invalid TC In "${tcIn}" — skipped`);
continue;
}
if (tcOut && !isValidTimecode(tcOut)) {
errors.push(`Row ${i + 1} (${shotCode}): invalid TC Out "${tcOut}" — skipped`);
continue;
}
rows.push({
shotCode,
seqTimecodeStart: tcIn,
seqTimecodeEnd: tcOut,
description: get(descIdx),
action: existingCodes.has(shotCode) ? "update" : "create",
});
}
return { rows, errors };
}