Files
vfxreview/lib/batch-upload-parser.ts
twotalesanimation 637b141c87
Deploy / deploy (push) Successful in 2m38s
Batch Video Uploads
2026-07-09 12:46:50 +02:00

96 lines
2.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Utilities for parsing VFX filenames into shot / task metadata.
*
* Expected naming convention (any separator count is fine):
* {SHOW}_{EP}_{SCENE}_{CUT}_{TASK}_{ARTIST}_v{NNN}.{ext}
* e.g. UNG_106_035_010_cmp_TT_v002.mp4
*
* The shot code is matched against the database the parser itself
* does NOT hard-code how many underscore segments belong to the shot.
*/
export interface ParsedFilename {
originalName: string;
/** Filename without the extension */
nameWithoutExt: string;
/** Lowercased extension without the leading dot (e.g. "mp4", "mov") */
ext: string;
/**
* Task name base the full name without the version suffix.
* e.g. "UNG_106_035_010_cmp_TT"
*/
taskNameBase: string;
/**
* Full task title including the version suffix (no extension).
* This is what the task should be named after upload.
* e.g. "UNG_106_035_010_cmp_TT_v002"
*/
taskNameWithVersion: string;
/**
* Version string extracted from the filename, e.g. "v002".
* Empty string if no version pattern was found.
*/
version: string;
type: "mp4" | "mov" | "other";
}
/**
* Parse a VFX filename into its constituent parts.
*/
export function parseFilename(filename: string): ParsedFilename {
const lastDot = filename.lastIndexOf(".");
const ext = lastDot >= 0 ? filename.slice(lastDot + 1).toLowerCase() : "";
const nameWithoutExt = lastDot >= 0 ? filename.slice(0, lastDot) : filename;
// Version pattern: _v followed by one or more digits at the end (case insensitive)
const versionMatch = nameWithoutExt.match(/_v(\d+)$/i);
const version = versionMatch ? `v${versionMatch[1]}` : "";
const taskNameBase = versionMatch
? nameWithoutExt.slice(0, nameWithoutExt.length - versionMatch[0].length)
: nameWithoutExt;
const type: "mp4" | "mov" | "other" =
ext === "mp4" ? "mp4" : ext === "mov" ? "mov" : "other";
return {
originalName: filename,
nameWithoutExt,
ext,
taskNameBase,
taskNameWithVersion: nameWithoutExt,
version,
type,
};
}
/**
* Strip a trailing version suffix (_v###) from a task title.
* e.g. "UNG_106_035_010_cmp_TT_v001" → "UNG_106_035_010_cmp_TT"
*/
export function stripVersionFromTitle(title: string): string {
return title.replace(/_v\d+$/i, "");
}
/**
* Given a taskNameBase and a list of shot codes, return the shot code that
* is a prefix of the task name base (shot code + "_" separator).
*
* e.g. taskNameBase "UNG_106_035_010_cmp_TT" matches shot "UNG_106_035_010"
*/
export function findMatchingShotCode(
taskNameBase: string,
shotCodes: string[]
): string | null {
// Sort by length descending so longer (more specific) codes are matched first
const sorted = [...shotCodes].sort((a, b) => b.length - a.length);
for (const code of sorted) {
if (
taskNameBase === code ||
taskNameBase.toLowerCase().startsWith(code.toLowerCase() + "_")
) {
return code;
}
}
return null;
}