diff --git a/src/renderer/src/__snapshots__/edlFormats.test.ts.snap b/src/renderer/src/__snapshots__/edlFormats.test.ts.snap index 4817ec9f..9c2cd3ab 100644 --- a/src/renderer/src/__snapshots__/edlFormats.test.ts.snap +++ b/src/renderer/src/__snapshots__/edlFormats.test.ts.snap @@ -12,6 +12,65 @@ Subtitle 2 line 2 " `; +exports[`otio 1`] = ` +[ + { + "end": 4.5, + "name": "2004-2-汐洛定制女包专营店.mp4", + "start": 0, + "tags": { + "otioClip": "2", + "otioTrack": "1", + }, + }, + { + "end": 482.23333333333335, + "name": "2004-2-汐洛定制女包专营店.mp4", + "start": 66.36666666666666, + "tags": { + "otioClip": "2", + "otioTrack": "1", + }, + }, + { + "end": 66.36666666666667, + "name": "2004-2-汐洛定制女包专营店.mp4", + "start": 4.5, + "tags": { + "otioClip": "2", + "otioTrack": "1", + }, + }, + { + "end": 4.5, + "name": "2004-2-汐洛定制女包专营店.mp4", + "start": 0, + "tags": { + "otioClip": "2", + "otioTrack": "1", + }, + }, + { + "end": 482.23333333333335, + "name": "2004-2-汐洛定制女包专营店.mp4", + "start": 66.36666666666666, + "tags": { + "otioClip": "2", + "otioTrack": "1", + }, + }, + { + "end": 66.36666666666667, + "name": "2004-2-汐洛定制女包专营店.mp4", + "start": 4.5, + "tags": { + "otioClip": "2", + "otioTrack": "1", + }, + }, +] +`; + exports[`parseGpsLine 1`] = ` { "alt": 19, diff --git a/src/renderer/src/edlFormats.test.ts b/src/renderer/src/edlFormats.test.ts index 3fdb2b2b..c813d482 100644 --- a/src/renderer/src/edlFormats.test.ts +++ b/src/renderer/src/edlFormats.test.ts @@ -1,7 +1,8 @@ import { it, describe, expect, test } from 'vitest'; -import { parseSrtToSegments, formatSrt, parseYouTube, formatYouTube, parseMplayerEdl, parseXmeml, parseFcpXml, parseCsv, parseCsvTime, getFrameValParser, formatCsvFrames, getFrameCountRaw, parsePbf, parseDvAnalyzerSummaryTxt, parseCutlist, parseGpsLine } from './edlFormats'; +import { parseSrtToSegments, formatSrt, parseYouTube, formatYouTube, parseMplayerEdl, parseXmeml, parseFcpXml, parseCsv, parseCsvTime, getFrameValParser, formatCsvFrames, getFrameCountRaw, parsePbf, parseDvAnalyzerSummaryTxt, parseCutlist, parseGpsLine, Otio, parseOtio } from './edlFormats'; import { readFixture, readFixtureBinary } from './test/util'; +import otioFixture from './test/fixtures/otio'; const expectYouTube1 = [ @@ -334,3 +335,7 @@ test('parseGpsLine', () => { // https://github.com/mifi/lossless-cut/issues/2072#issuecomment-2325755148 expect(parseGpsLine('F/2.8, SS 678.52, ISO 100, EV 0, DZOOM 1.000, GPS (-3.9130, 56.5019, 26), D 0.57m, H 102.40m, H.S 0.00m/s, V.S -0.00m/s')).toMatchSnapshot(); }); + +test('otio', () => { + expect(parseOtio(otioFixture satisfies Otio)).toMatchSnapshot(); +}); diff --git a/src/renderer/src/edlFormats.ts b/src/renderer/src/edlFormats.ts index 2c4613aa..ebc8e300 100644 --- a/src/renderer/src/edlFormats.ts +++ b/src/renderer/src/edlFormats.ts @@ -7,10 +7,11 @@ import { parse as csvParse } from 'csv-parse/browser/esm/sync'; import { stringify as csvStringify } from 'csv-stringify/browser/esm/sync'; import sortBy from 'lodash/sortBy'; import type { ICueSheet, ITrack } from 'cue-parser/lib/types'; +import { z } from 'zod'; import { formatDuration } from './util/duration'; import { invertSegments, sortSegments } from './segments'; -import { GetFrameCount, SegmentBase } from './types'; +import { GetFrameCount, SegmentBase, SegmentTags } from './types'; import parseCmx3600 from './cmx3600'; export const getTimeFromFrameNum = (detectedFps: number, frameNum: number) => frameNum / detectedFps; @@ -451,3 +452,61 @@ export function parseGpsLine(line: string) { verticalSpeed: parseFloat(gpsMatch[12]!), }; } + +const otioSchema = z.object({ + OTIO_SCHEMA: z.string().refine((val) => val.startsWith('Timeline.'), { message: 'Invalid OTIO schema' }), + name: z.string(), + tracks: z.object({ + OTIO_SCHEMA: z.string().refine((val) => val.startsWith('Stack.'), { message: 'Invalid OTIO schema for tracks' }), + children: z.array( + z.object({ + OTIO_SCHEMA: z.string().refine((val) => val.startsWith('Track.'), { message: 'Invalid OTIO schema for track' }), + children: z.array( + z.object({ + OTIO_SCHEMA: z.string().refine((val) => val.startsWith('Clip.'), { message: 'Invalid OTIO schema for clip' }), + name: z.string(), + source_range: z.object({ + start_time: z.object({ + value: z.number(), + rate: z.number(), + }), + duration: z.object({ + value: z.number(), + rate: z.number(), + }), + }), + }), + ), + }), + ), + }), +}); + +export type Otio = z.infer; + +// implemented by OpenAI: +export function parseOtio(data: unknown): SegmentBase[] { + const schema = otioSchema.parse(data); + + const segments: (SegmentBase & { tags: SegmentTags })[] = []; + + schema.tracks.children.forEach((track) => { + track.children.forEach((clip) => { + const start = clip.source_range.start_time.value / clip.source_range.start_time.rate; + const duration = clip.source_range.duration.value / clip.source_range.duration.rate; + const end = start + duration; + + segments.push({ + start, + end, + name: clip.name, + tags: { + otioTrack: track.OTIO_SCHEMA.replace(/^Track\./, ''), + otioClip: clip.OTIO_SCHEMA.replace(/^Clip\./, ''), + }, + }); + }); + }); + + return segments; +} \ No newline at end of file diff --git a/src/renderer/src/edlStore.ts b/src/renderer/src/edlStore.ts index 6e56b004..77454940 100644 --- a/src/renderer/src/edlStore.ts +++ b/src/renderer/src/edlStore.ts @@ -3,7 +3,7 @@ import i18n from 'i18next'; import invariant from 'tiny-invariant'; import { ZodError } from 'zod'; -import { parseSrtToSegments, formatSrt, parseCuesheet, parseXmeml, parseFcpXml, parseCsv, parseCutlist, parsePbf, parseEdl, formatCsvHuman, formatTsv, formatCsvFrames, formatCsvSeconds, parseCsvTime, getFrameValParser, parseDvAnalyzerSummaryTxt } from './edlFormats'; +import { parseSrtToSegments, formatSrt, parseCuesheet, parseXmeml, parseFcpXml, parseCsv, parseCutlist, parsePbf, parseEdl, formatCsvHuman, formatTsv, formatCsvFrames, formatCsvSeconds, parseCsvTime, getFrameValParser, parseDvAnalyzerSummaryTxt, parseOtio } from './edlFormats'; import { askForYouTubeInput, showOpenDialog } from './dialogs'; import { getOutPath } from './util'; import { EdlExportType, EdlFileType, EdlImportType, GetFrameCount, LlcProject, llcProjectV1Schema, llcProjectV2Schema, SegmentBase, StateSegment } from './types'; @@ -125,6 +125,10 @@ export async function loadLlcProject(path: string) { return project; } +export async function loadOtio(path: string) { + return parseOtio(JSON.parse(await readFile(path, 'utf8'))); +} + export async function readEdlFile({ type, path, fps }: { type: EdlFileType, path: string, @@ -143,6 +147,7 @@ export async function readEdlFile({ type, path, fps }: { if (type === 'cue') return loadCue(path); if (type === 'pbf') return loadPbf(path); if (type === 'srt') return loadSrt(path); + if (type === 'otio') return loadOtio(path); if (type === 'llc') { const project = await loadLlcProject(path); return project.cutSegments; diff --git a/src/renderer/src/test/fixtures/otio.otio b/src/renderer/src/test/fixtures/otio.otio new file mode 100644 index 00000000..c7f6e08d --- /dev/null +++ b/src/renderer/src/test/fixtures/otio.otio @@ -0,0 +1,375 @@ +{ + "OTIO_SCHEMA": "Timeline.1", + "metadata": {}, + "name": "", + "global_start_time": null, + "tracks": { + "OTIO_SCHEMA": "Stack.1", + "metadata": {}, + "name": "tracks", + "source_range": null, + "effects": [], + "markers": [], + "enabled": true, + "children": [ + { + "OTIO_SCHEMA": "Track.1", + "metadata": {}, + "name": "", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "children": [], + "kind": "Video" + }, + { + "OTIO_SCHEMA": "Track.1", + "metadata": {}, + "name": "", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 28934.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "children": [ + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "2004-2-汐洛定制女包专营店.mp4", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 270.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": {}, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 34023.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url": "G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "2004-2-汐洛定制女包专营店.mp4", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 24952.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 3982.0 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": {}, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 34023.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url": "G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "2004-2-汐洛定制女包专营店.mp4", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 3712.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 270.0 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": {}, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 34023.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url": "G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + } + ], + "kind": "Video" + }, + { + "OTIO_SCHEMA": "Track.1", + "metadata": {}, + "name": "", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 28934.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "children": [ + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "2004-2-汐洛定制女包专营店.mp4", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 270.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": {}, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 34023.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url": "G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "2004-2-汐洛定制女包专营店.mp4", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 24952.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 3982.0 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": {}, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 34023.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url": "G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "2004-2-汐洛定制女包专营店.mp4", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 3712.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 270.0 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": {}, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 34023.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url": "G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + } + ], + "kind": "Audio" + }, + { + "OTIO_SCHEMA": "Track.1", + "metadata": {}, + "name": "", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 60.0, + "value": 0.0 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "children": [], + "kind": "Audio" + } + ] + } +} diff --git a/src/renderer/src/test/fixtures/otio.ts b/src/renderer/src/test/fixtures/otio.ts new file mode 100644 index 00000000..a5847b85 --- /dev/null +++ b/src/renderer/src/test/fixtures/otio.ts @@ -0,0 +1,377 @@ +// https://github.com/mifi/lossless-cut/issues/2441#issuecomment-2898207795 +// see also otio.json +export default { + OTIO_SCHEMA: 'Timeline.1', + metadata: {}, + name: '', + global_start_time: null, + tracks: { + OTIO_SCHEMA: 'Stack.1', + metadata: {}, + name: 'tracks', + source_range: null, + effects: [], + markers: [], + enabled: true, + children: [ + { + OTIO_SCHEMA: 'Track.1', + metadata: {}, + name: '', + source_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + effects: [], + markers: [], + enabled: true, + children: [], + kind: 'Video', + }, + { + OTIO_SCHEMA: 'Track.1', + metadata: {}, + name: '', + source_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 28934, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + effects: [], + markers: [], + enabled: true, + children: [ + { + OTIO_SCHEMA: 'Clip.2', + metadata: {}, + name: '2004-2-汐洛定制女包专营店.mp4', + source_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 270, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + effects: [], + markers: [], + enabled: true, + media_references: { + DEFAULT_MEDIA: { + OTIO_SCHEMA: 'ExternalReference.1', + metadata: {}, + name: '', + available_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 34023, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + available_image_bounds: null, + target_url: 'G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4', + }, + }, + active_media_reference_key: 'DEFAULT_MEDIA', + }, + { + OTIO_SCHEMA: 'Clip.2', + metadata: {}, + name: '2004-2-汐洛定制女包专营店.mp4', + source_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 24952, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 3982, + }, + }, + effects: [], + markers: [], + enabled: true, + media_references: { + DEFAULT_MEDIA: { + OTIO_SCHEMA: 'ExternalReference.1', + metadata: {}, + name: '', + available_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 34023, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + available_image_bounds: null, + target_url: 'G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4', + }, + }, + active_media_reference_key: 'DEFAULT_MEDIA', + }, + { + OTIO_SCHEMA: 'Clip.2', + metadata: {}, + name: '2004-2-汐洛定制女包专营店.mp4', + source_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 3712, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 270, + }, + }, + effects: [], + markers: [], + enabled: true, + media_references: { + DEFAULT_MEDIA: { + OTIO_SCHEMA: 'ExternalReference.1', + metadata: {}, + name: '', + available_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 34023, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + available_image_bounds: null, + target_url: 'G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4', + }, + }, + active_media_reference_key: 'DEFAULT_MEDIA', + }, + ], + kind: 'Video', + }, + { + OTIO_SCHEMA: 'Track.1', + metadata: {}, + name: '', + source_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 28934, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + effects: [], + markers: [], + enabled: true, + children: [ + { + OTIO_SCHEMA: 'Clip.2', + metadata: {}, + name: '2004-2-汐洛定制女包专营店.mp4', + source_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 270, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + effects: [], + markers: [], + enabled: true, + media_references: { + DEFAULT_MEDIA: { + OTIO_SCHEMA: 'ExternalReference.1', + metadata: {}, + name: '', + available_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 34023, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + available_image_bounds: null, + target_url: 'G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4', + }, + }, + active_media_reference_key: 'DEFAULT_MEDIA', + }, + { + OTIO_SCHEMA: 'Clip.2', + metadata: {}, + name: '2004-2-汐洛定制女包专营店.mp4', + source_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 24952, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 3982, + }, + }, + effects: [], + markers: [], + enabled: true, + media_references: { + DEFAULT_MEDIA: { + OTIO_SCHEMA: 'ExternalReference.1', + metadata: {}, + name: '', + available_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 34023, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + available_image_bounds: null, + target_url: 'G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4', + }, + }, + active_media_reference_key: 'DEFAULT_MEDIA', + }, + { + OTIO_SCHEMA: 'Clip.2', + metadata: {}, + name: '2004-2-汐洛定制女包专营店.mp4', + source_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 3712, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 270, + }, + }, + effects: [], + markers: [], + enabled: true, + media_references: { + DEFAULT_MEDIA: { + OTIO_SCHEMA: 'ExternalReference.1', + metadata: {}, + name: '', + available_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 34023, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + available_image_bounds: null, + target_url: 'G:/汐洛定制女包/2004-2-汐洛定制女包专营店/2004-2-汐洛定制女包专营店.mp4', + }, + }, + active_media_reference_key: 'DEFAULT_MEDIA', + }, + ], + kind: 'Audio', + }, + { + OTIO_SCHEMA: 'Track.1', + metadata: {}, + name: '', + source_range: { + OTIO_SCHEMA: 'TimeRange.1', + duration: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + start_time: { + OTIO_SCHEMA: 'RationalTime.1', + rate: 60, + value: 0, + }, + }, + effects: [], + markers: [], + enabled: true, + children: [], + kind: 'Audio', + }, + ], + }, +}; diff --git a/src/renderer/src/types.ts b/src/renderer/src/types.ts index 944d86f4..f711b76b 100644 --- a/src/renderer/src/types.ts +++ b/src/renderer/src/types.ts @@ -84,7 +84,7 @@ export interface InverseCutSegment extends DefiniteSegmentBase { export type PlaybackMode = 'loop-segment-start-end' | 'loop-segment' | 'play-segment-once' | 'loop-selected-segments'; -export type EdlFileType = 'csv' | 'csv-frames' | 'cutlist' | 'xmeml' | 'fcpxml' | 'dv-analyzer-summary-txt' | 'cue' | 'pbf' | 'edl' | 'srt' | 'llc'; +export type EdlFileType = 'llc' | 'csv' | 'csv-frames' | 'cutlist' | 'xmeml' | 'fcpxml' | 'dv-analyzer-summary-txt' | 'cue' | 'pbf' | 'edl' | 'srt' | 'otio'; export type EdlImportType = 'youtube' | EdlFileType; diff --git a/src/renderer/src/util.ts b/src/renderer/src/util.ts index 417e569e..8405a93d 100644 --- a/src/renderer/src/util.ts +++ b/src/renderer/src/util.ts @@ -516,7 +516,7 @@ export async function readDirRecursively(dirPath: string) { export function getImportProjectType(filePath: string) { if (filePath.endsWith('Summary.txt')) return 'dv-analyzer-summary-txt'; - const edlFormatForExtension = { csv: 'csv', pbf: 'pbf', edl: 'edl', cue: 'cue', xml: 'xmeml', fcpxml: 'fcpxml' } as const; + const edlFormatForExtension = { csv: 'csv', pbf: 'pbf', edl: 'edl', cue: 'cue', xml: 'xmeml', fcpxml: 'fcpxml', otio: 'otio' } as const; const matchingExt = Object.keys(edlFormatForExtension).find((ext) => filePath.toLowerCase().endsWith(`.${ext}`)) as keyof typeof edlFormatForExtension | undefined; if (!matchingExt) return undefined; return edlFormatForExtension[matchingExt];