feat: publish av-tools 0.1.0
This commit is contained in:
101
src/project/project.schema.ts
Normal file
101
src/project/project.schema.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import {
|
||||
AV_PROJECT_SCHEMA_VERSION,
|
||||
type AvProjectV1,
|
||||
type BrowserFileIdentity,
|
||||
type CreateMediaAssetOptions,
|
||||
type CreateProjectOptions,
|
||||
type MediaAssetReference,
|
||||
} from './project.types';
|
||||
|
||||
export function createEmptyProject(
|
||||
options: CreateProjectOptions = {}
|
||||
): AvProjectV1 {
|
||||
const timestamp = options.timestamp ?? new Date().toISOString();
|
||||
if (!isIsoDate(timestamp)) {
|
||||
throw new TypeError('Project timestamp must be a valid ISO-8601 date.');
|
||||
}
|
||||
|
||||
return {
|
||||
schemaVersion: AV_PROJECT_SCHEMA_VERSION,
|
||||
id: options.id ?? createProjectId(),
|
||||
name: options.name?.trim() || 'Untitled project',
|
||||
createdAt: timestamp,
|
||||
updatedAt: timestamp,
|
||||
assets: [],
|
||||
timeline: [],
|
||||
output: {
|
||||
presetId: options.output?.presetId ?? 'mp4-h264-balanced',
|
||||
fileName: options.output?.fileName ?? 'output.mp4',
|
||||
container: options.output?.container ?? 'mp4',
|
||||
videoEnabled: options.output?.videoEnabled ?? true,
|
||||
audioEnabled: options.output?.audioEnabled ?? true,
|
||||
...(options.output?.width === undefined
|
||||
? {}
|
||||
: { width: options.output.width }),
|
||||
...(options.output?.height === undefined
|
||||
? {}
|
||||
: { height: options.output.height }),
|
||||
...(options.output?.frameRate === undefined
|
||||
? {}
|
||||
: { frameRate: options.output.frameRate }),
|
||||
sampleRate: options.output?.sampleRate ?? 48_000,
|
||||
channelLayout: options.output?.channelLayout ?? 'stereo',
|
||||
missingAudioPolicy:
|
||||
options.output?.missingAudioPolicy ?? 'insert-silence',
|
||||
},
|
||||
metadata: {
|
||||
custom: {},
|
||||
},
|
||||
chapters: [],
|
||||
subtitles: [],
|
||||
};
|
||||
}
|
||||
|
||||
export function createMediaAssetReference(
|
||||
file: BrowserFileIdentity,
|
||||
options: CreateMediaAssetOptions = {}
|
||||
): MediaAssetReference {
|
||||
if (!file.name.trim()) {
|
||||
throw new TypeError('A source file must have a name.');
|
||||
}
|
||||
if (!Number.isSafeInteger(file.size) || file.size < 0) {
|
||||
throw new TypeError('A source file size must be a non-negative integer.');
|
||||
}
|
||||
if (!Number.isSafeInteger(file.lastModified) || file.lastModified < 0) {
|
||||
throw new TypeError(
|
||||
'A source last-modified time must be a non-negative integer.'
|
||||
);
|
||||
}
|
||||
return {
|
||||
id: options.id ?? createId('asset'),
|
||||
originalName: file.name,
|
||||
size: file.size,
|
||||
lastModified: file.lastModified,
|
||||
mimeType: file.type,
|
||||
sourceStatus: options.sourceStatus ?? 'attached',
|
||||
...(options.probe === undefined ? {} : { probe: options.probe }),
|
||||
...(options.hash === undefined ? {} : { hash: options.hash }),
|
||||
};
|
||||
}
|
||||
|
||||
function createProjectId(): string {
|
||||
return createId('project');
|
||||
}
|
||||
|
||||
function createId(prefix: string): string {
|
||||
if (
|
||||
typeof globalThis.crypto !== 'undefined' &&
|
||||
typeof globalThis.crypto.randomUUID === 'function'
|
||||
) {
|
||||
return globalThis.crypto.randomUUID();
|
||||
}
|
||||
return `${prefix}-${Date.now().toString(36)}-${Math.random()
|
||||
.toString(36)
|
||||
.slice(2, 10)}`;
|
||||
}
|
||||
|
||||
function isIsoDate(value: string): boolean {
|
||||
return (
|
||||
/^\d{4}-\d{2}-\d{2}T/.test(value) && Number.isFinite(Date.parse(value))
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user