@@ -192,6 +192,7 @@ export interface SimpleCsvRow {
|
||||
seqTimecodeStart: string;
|
||||
seqTimecodeEnd: string;
|
||||
description: string;
|
||||
group: string;
|
||||
action: "create" | "update" | "skip";
|
||||
}
|
||||
|
||||
@@ -222,6 +223,7 @@ export function parseSimpleCsv(
|
||||
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"]);
|
||||
const groupIdx = col(["group", "shot group", "shotgroup", "group name"]);
|
||||
|
||||
if (shotNameIdx === -1) {
|
||||
return { rows: [], errors: ['CSV must contain a "Shot Name" (or "Shot Code") column'] };
|
||||
@@ -255,9 +257,72 @@ export function parseSimpleCsv(
|
||||
seqTimecodeStart: tcIn,
|
||||
seqTimecodeEnd: tcOut,
|
||||
description: get(descIdx),
|
||||
group: get(groupIdx),
|
||||
action: existingCodes.has(shotCode) ? "update" : "create",
|
||||
});
|
||||
}
|
||||
|
||||
return { rows, errors };
|
||||
}
|
||||
|
||||
// ── Group Assignment CSV ──────────────────────────────────────────────────────
|
||||
|
||||
export interface GroupAssignRow {
|
||||
shotCode: string;
|
||||
groupName: string;
|
||||
exists: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a group-assignment CSV: Shot Name, Group.
|
||||
* Only shots that already exist in the project will be updated.
|
||||
*/
|
||||
export function parseGroupAssignCsv(
|
||||
csvText: string,
|
||||
existingCodes: Set<string>
|
||||
): { rows: GroupAssignRow[]; 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 groupIdx = col(["group", "shot group", "shotgroup", "group name"]);
|
||||
|
||||
if (shotNameIdx === -1) {
|
||||
return { rows: [], errors: ['CSV must contain a "Shot Name" (or "Shot Code") column'] };
|
||||
}
|
||||
if (groupIdx === -1) {
|
||||
return { rows: [], errors: ['CSV must contain a "Group" column'] };
|
||||
}
|
||||
|
||||
const rows: GroupAssignRow[] = [];
|
||||
const errors: string[] = [];
|
||||
const seen = new Set<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);
|
||||
const groupName = get(groupIdx);
|
||||
if (!shotCode) { errors.push(`Row ${i + 1}: empty Shot Name — skipped`); continue; }
|
||||
if (!groupName) { errors.push(`Row ${i + 1} (${shotCode}): empty Group — skipped`); continue; }
|
||||
if (seen.has(shotCode)) continue;
|
||||
seen.add(shotCode);
|
||||
|
||||
rows.push({ shotCode, groupName, exists: existingCodes.has(shotCode) });
|
||||
}
|
||||
|
||||
return { rows, errors };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user