mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-22 02:12:30 +01:00
implement DV Analyzer import #1664
This commit is contained in:
parent
5ee1df97fa
commit
2c2fa88dff
@ -100,6 +100,12 @@ module.exports = ({ app, mainWindow, newVersion, isStoreBuild }) => {
|
||||
mainWindow.webContents.send('importEdlFile', 'pbf');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: esc(t('DV Analyzer Summary.txt')),
|
||||
click() {
|
||||
mainWindow.webContents.send('importEdlFile', 'dv-analyzer-summary-txt');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
15
src/App.jsx
15
src/App.jsx
@ -103,6 +103,14 @@ let lastOpenedPath;
|
||||
const hevcPlaybackSupportedPromise = doesPlayerSupportHevcPlayback();
|
||||
hevcPlaybackSupportedPromise.catch((err) => console.error(err));
|
||||
|
||||
function getImportProjectType(filePath) {
|
||||
if (filePath.endsWith('Summary.txt')) return 'dv-analyzer-summary-txt';
|
||||
const edlFormatForExtension = { csv: 'csv', pbf: 'pbf', edl: 'mplayer', cue: 'cue', xml: 'xmeml', fcpxml: 'fcpxml' };
|
||||
const matchingExt = Object.keys(edlFormatForExtension).find((ext) => filePath.toLowerCase().endsWith(`.${ext}`));
|
||||
if (!matchingExt) return undefined;
|
||||
return edlFormatForExtension[matchingExt];
|
||||
}
|
||||
|
||||
const App = memo(() => {
|
||||
// Per project state
|
||||
const [commandedTime, setCommandedTime] = useState(0);
|
||||
@ -1712,11 +1720,10 @@ const App = memo(() => {
|
||||
setWorking(i18n.t('Loading file'));
|
||||
|
||||
// Import segments for for already opened file
|
||||
const edlFormats = { csv: 'csv', pbf: 'pbf', edl: 'mplayer', cue: 'cue', xml: 'xmeml', fcpxml: 'fcpxml' };
|
||||
const matchingExt = Object.keys(edlFormats).find((ext) => filePathLowerCase.endsWith(`.${ext}`));
|
||||
if (matchingExt) {
|
||||
const matchingImportProjectType = getImportProjectType(firstFilePath);
|
||||
if (matchingImportProjectType) {
|
||||
if (!checkFileOpened()) return;
|
||||
await loadEdlFile({ path: firstFilePath, type: edlFormats[matchingExt], append: true });
|
||||
await loadEdlFile({ path: firstFilePath, type: matchingImportProjectType, append: true });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,130 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`parses DV Analyzer Summary.txt 1`] = `
|
||||
[
|
||||
{
|
||||
"end": 60.4,
|
||||
"name": "XXXX-XX-XX 00:00:00.000 - XXXX-XX-XX XX:XX:XX:XX",
|
||||
"start": 0,
|
||||
},
|
||||
{
|
||||
"end": 485.08,
|
||||
"name": "XXXX-XX-XX XX:XX:XX:XX - 2001-12-31 23:22:09",
|
||||
"start": 60.4,
|
||||
},
|
||||
{
|
||||
"end": 1010.68,
|
||||
"name": "2001-12-31 23:28:13 - 2002-01-01 19:34:38",
|
||||
"start": 485.08,
|
||||
},
|
||||
{
|
||||
"end": 1235.32,
|
||||
"name": "2002-01-01 13:31:24 - 2002-01-01 22:03:01",
|
||||
"start": 1010.68,
|
||||
},
|
||||
{
|
||||
"end": 1575.04,
|
||||
"name": "2002-01-02 14:27:10 - 2002-01-02 15:48:55",
|
||||
"start": 1235.32,
|
||||
},
|
||||
{
|
||||
"end": 1575.08,
|
||||
"name": "2002-01-02 22:30:22 - 2002-01-02 22:30:22",
|
||||
"start": 1575.04,
|
||||
},
|
||||
{
|
||||
"end": 1575.16,
|
||||
"name": "2002-01-02 22:30:22 - 2002-01-02 22:30:22",
|
||||
"start": 1575.08,
|
||||
},
|
||||
{
|
||||
"end": 1575.24,
|
||||
"name": "2002-01-02 22:30:22 - 2002-01-05 10:57:51",
|
||||
"start": 1575.16,
|
||||
},
|
||||
{
|
||||
"end": 1919.44,
|
||||
"name": "2002-01-05 10:57:51 - 2002-01-05 11:36:20",
|
||||
"start": 1575.24,
|
||||
},
|
||||
{
|
||||
"end": 2075.88,
|
||||
"name": "2002-01-05 13:18:43 - 2002-01-05 14:04:19",
|
||||
"start": 1919.44,
|
||||
},
|
||||
{
|
||||
"end": 2138.2,
|
||||
"name": "2002-01-05 16:39:22 - 2002-01-05 22:51:40",
|
||||
"start": 2075.88,
|
||||
},
|
||||
{
|
||||
"end": 2138.76,
|
||||
"name": "2002-01-05 16:40:24 - 2002-01-05 16:40:25",
|
||||
"start": 2138.2,
|
||||
},
|
||||
{
|
||||
"end": 2217.32,
|
||||
"name": "2002-01-05 22:53:17 - 2002-01-05 22:55:08",
|
||||
"start": 2138.76,
|
||||
},
|
||||
{
|
||||
"end": 2269.96,
|
||||
"name": "2002-01-16 21:17:04 - 2002-01-16 21:18:01",
|
||||
"start": 2217.32,
|
||||
},
|
||||
{
|
||||
"end": 2332.64,
|
||||
"name": "2002-01-20 20:06:37 - 2002-01-20 20:07:48",
|
||||
"start": 2269.96,
|
||||
},
|
||||
{
|
||||
"end": 3009.12,
|
||||
"name": "2002-01-30 18:34:52 - 2002-03-12 00:46:51",
|
||||
"start": 2332.64,
|
||||
},
|
||||
{
|
||||
"end": 3596.48,
|
||||
"name": "2002-03-14 20:27:57 - 2002-04-12 21:06:54",
|
||||
"start": 3009.12,
|
||||
},
|
||||
{
|
||||
"end": 3622.08,
|
||||
"name": "2002-04-12 21:06:56 - 2002-04-12 21:07:22",
|
||||
"start": 3596.48,
|
||||
},
|
||||
{
|
||||
"end": 4305,
|
||||
"name": "2002-04-12 21:11:47 - 2002-04-27 00:05:36",
|
||||
"start": 3622.08,
|
||||
},
|
||||
{
|
||||
"end": 4307.12,
|
||||
"name": "2002-04-25 22:59:57 - 2002-04-25 22:59:59",
|
||||
"start": 4305,
|
||||
},
|
||||
{
|
||||
"end": 4357.68,
|
||||
"name": "2002-04-25 23:00:00 - 2002-05-02 13:33:33",
|
||||
"start": 4307.12,
|
||||
},
|
||||
{
|
||||
"end": 4359.72,
|
||||
"name": "2002-04-25 23:00:50 - 2002-04-25 23:00:52",
|
||||
"start": 4357.68,
|
||||
},
|
||||
{
|
||||
"end": 4660.44,
|
||||
"name": "2002-05-02 13:40:27 - 2002-05-02 13:53:34",
|
||||
"start": 4359.72,
|
||||
},
|
||||
{
|
||||
"end": undefined,
|
||||
"name": "2002-05-02 13:54:14 - 2002-05-02 13:59:29",
|
||||
"start": 4660.44,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`parses fcpxml 1.9 1`] = `
|
||||
[
|
||||
{
|
||||
|
@ -244,3 +244,32 @@ export async function formatCsvHuman(cutSegments) {
|
||||
export async function formatTsv(cutSegments) {
|
||||
return csvStringifyAsync(formatSegmentsTimes(cutSegments), { delimiter: '\t' });
|
||||
}
|
||||
|
||||
export function parseDvAnalyzerSummaryTxt(txt) {
|
||||
const lines = txt.split(/\r?\n/);
|
||||
|
||||
let headerFound = false;
|
||||
|
||||
const times = [];
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const line of lines) {
|
||||
if (headerFound) {
|
||||
const match = line.match(/^(\d{2}):(\d{2}):(\d{2}).(\d{3})\s+([^\s]+)\s+-\s+([^\s]+)\s+([^\s]+\s+[^\s]+)\s+-\s+([^\s]+\s+[^\s]+)/);
|
||||
if (!match) break;
|
||||
const h = parseInt(match[1], 10);
|
||||
const m = parseInt(match[2], 10);
|
||||
const s = parseInt(match[3], 10);
|
||||
const ms = parseInt(match[4], 10);
|
||||
const total = s + ((m + (h * 60)) * 60) + (ms / 1000);
|
||||
times.push({ time: total, name: `${match[7]} - ${match[8]}` });
|
||||
}
|
||||
if (/^Absolute time\s+DV timecode range\s+Recorded date\/time range\s+Frame range\s*$/.test(line)) headerFound = true;
|
||||
}
|
||||
|
||||
const edl = times.map(({ time, name }, i) => {
|
||||
const nextTime = times[i + 1];
|
||||
return { start: time, end: nextTime?.time, name };
|
||||
});
|
||||
|
||||
return edl;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { fileURLToPath } from 'url';
|
||||
import { it, describe, expect } from 'vitest';
|
||||
|
||||
|
||||
import { parseYouTube, formatYouTube, parseMplayerEdl, parseXmeml, parseFcpXml, parseCsv, getTimeFromFrameNum, formatCsvFrames, getFrameCountRaw, parsePbf } from './edlFormats';
|
||||
import { parseYouTube, formatYouTube, parseMplayerEdl, parseXmeml, parseFcpXml, parseCsv, getTimeFromFrameNum, formatCsvFrames, getFrameCountRaw, parsePbf, parseDvAnalyzerSummaryTxt } from './edlFormats';
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
@ -217,3 +217,8 @@ it('parses pbf', async () => {
|
||||
expect(parsePbf(await readFixture('test3.pbf', null))).toMatchSnapshot();
|
||||
expect(parsePbf(await readFixture('potplayer bookmark format utf16le issue 867.pbf', null))).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// https://github.com/mifi/lossless-cut/issues/1664
|
||||
it('parses DV Analyzer Summary.txt', async () => {
|
||||
expect(parseDvAnalyzerSummaryTxt(await readFixture('DV Analyzer Summary.txt', 'utf-8'))).toMatchSnapshot();
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import JSON5 from 'json5';
|
||||
import i18n from 'i18next';
|
||||
|
||||
import { parseCuesheet, parseXmeml, parseFcpXml, parseCsv, parsePbf, parseMplayerEdl, formatCsvHuman, formatTsv, formatCsvFrames, formatCsvSeconds, getTimeFromFrameNum } from './edlFormats';
|
||||
import { parseCuesheet, parseXmeml, parseFcpXml, parseCsv, parsePbf, parseMplayerEdl, formatCsvHuman, formatTsv, formatCsvFrames, formatCsvSeconds, getTimeFromFrameNum, parseDvAnalyzerSummaryTxt } from './edlFormats';
|
||||
import { askForYouTubeInput, showOpenDialog } from './dialogs';
|
||||
import { getOutPath } from './util';
|
||||
|
||||
@ -28,6 +28,10 @@ export async function loadFcpXml(path) {
|
||||
return parseFcpXml(await fs.readFile(path, 'utf-8'));
|
||||
}
|
||||
|
||||
export async function loadDvAnalyzerSummaryTxt(path) {
|
||||
return parseDvAnalyzerSummaryTxt(await fs.readFile(path, 'utf-8'));
|
||||
}
|
||||
|
||||
export async function loadPbf(path) {
|
||||
return parsePbf(await fs.readFile(path));
|
||||
}
|
||||
@ -75,6 +79,7 @@ export async function readEdlFile({ type, path, fps }) {
|
||||
if (type === 'csv-frames') return loadCsvFrames(path, fps);
|
||||
if (type === 'xmeml') return loadXmeml(path);
|
||||
if (type === 'fcpxml') return loadFcpXml(path);
|
||||
if (type === 'dv-analyzer-summary-txt') return loadDvAnalyzerSummaryTxt(path);
|
||||
if (type === 'cue') return loadCue(path);
|
||||
if (type === 'pbf') return loadPbf(path);
|
||||
if (type === 'mplayer') return loadMplayerEdl(path);
|
||||
@ -95,6 +100,7 @@ export async function askForEdlImport({ type, fps }) {
|
||||
else if (type === 'cue') filters = [{ name: i18n.t('CUE files'), extensions: ['cue'] }];
|
||||
else if (type === 'pbf') filters = [{ name: i18n.t('PBF files'), extensions: ['pbf'] }];
|
||||
else if (type === 'mplayer') filters = [{ name: i18n.t('MPlayer EDL'), extensions: ['*'] }];
|
||||
else if (type === 'dv-analyzer-summary-txt') filters = [{ name: i18n.t('DV Analyzer Summary.txt'), extensions: ['txt'] }];
|
||||
else if (type === 'llc') filters = [{ name: i18n.t('LosslessCut project'), extensions: ['llc'] }];
|
||||
|
||||
const { canceled, filePaths } = await showOpenDialog({ properties: ['openFile'], filters });
|
||||
|
47
src/fixtures/DV Analyzer Summary.txt
Normal file
47
src/fixtures/DV Analyzer Summary.txt
Normal file
@ -0,0 +1,47 @@
|
||||
DV Analyzer v.1.4.2 by AudioVisual Preservation Solutions, Inc. http://www.avpreserve.com
|
||||
|
||||
L:\To-Re-Encode\31.12.2001 Cats Test Tape (TDK Tape).avi
|
||||
|
||||
Frame Count: 116883
|
||||
|
||||
Frame count with video error concealment: 2776 frames
|
||||
Total video error concealment: 319120 errors ( 317500 "A" errors, 1620 "F" errors)
|
||||
Frame count with CH1 audio error code: 783 frames
|
||||
Total audio error code for CH1: 16263 errors ( 4005 Dseq=0, 1575 Dseq=1, 3780 Dseq=2, 1611 Dseq=3, 3672 Dseq=4, 1620 Dseq=5)
|
||||
Frame count with DV timecode incoherency: 2 frames
|
||||
Frame count with Arbitrary bit inconsistency: 6 frames
|
||||
|
||||
Absolute time DV timecode range Recorded date/time range Frame range
|
||||
00:00:00.000 00:00:00:15 - 00:01:00:16 XXXX-XX-XX 00:00:00.000 - XXXX-XX-XX XX:XX:XX:XX 0 - 1509
|
||||
00:01:00.400 00:00:00:00 - 00:07:04:08 XXXX-XX-XX XX:XX:XX:XX - 2001-12-31 23:22:09 1510 - 12126
|
||||
00:08:05.080 00:00:00:00 - 00:08:45:14 2001-12-31 23:28:13 - 2002-01-01 19:34:38 12127 - 25266
|
||||
00:16:50.680 00:08:45:15 - 00:12:29:22 2002-01-01 13:31:24 - 2002-01-01 22:03:01 25267 - 30882
|
||||
00:20:35.320 00:00:00:00 - 00:05:39:09 2002-01-02 14:27:10 - 2002-01-02 15:48:55 30883 - 39375
|
||||
00:26:15.040 00:00:00:00 - 00:00:00:00 2002-01-02 22:30:22 - 2002-01-02 22:30:22 39376 - 39376
|
||||
00:26:15.080 00:00:00:02 - 00:00:00:03 2002-01-02 22:30:22 - 2002-01-02 22:30:22 39377 - 39378
|
||||
00:26:15.160 00:00:00:05 - 00:00:00:06 2002-01-02 22:30:22 - 2002-01-05 10:57:51 39379 - 39380
|
||||
00:26:15.240 00:00:00:08 - 00:05:44:04 2002-01-05 10:57:51 - 2002-01-05 11:36:20 39381 - 47985
|
||||
00:31:59.440 00:00:00:00 - 00:02:36:02 2002-01-05 13:18:43 - 2002-01-05 14:04:19 47986 - 51896
|
||||
00:34:35.880 00:00:00:00 - 00:01:02:07 2002-01-05 16:39:22 - 2002-01-05 22:51:40 51897 - 53454
|
||||
00:35:38.200 00:01:02:08 - 00:01:02:14 2002-01-05 16:40:24 - 2002-01-05 16:40:25 53455 - 53468
|
||||
00:35:38.760 00:00:00:00 - 00:01:18:05 2002-01-05 22:53:17 - 2002-01-05 22:55:08 53469 - 55432
|
||||
00:36:57.320 00:00:00:00 - 00:00:52:07 2002-01-16 21:17:04 - 2002-01-16 21:18:01 55433 - 56748
|
||||
00:37:49.960 00:00:00:00 - 00:01:02:08 2002-01-20 20:06:37 - 2002-01-20 20:07:48 56749 - 58315
|
||||
00:38:52.640 00:00:00:00 - 00:11:16:02 2002-01-30 18:34:52 - 2002-03-12 00:46:51 58316 - 75227
|
||||
00:50:09.120 00:00:00:00 - 00:09:47:08 2002-03-14 20:27:57 - 2002-04-12 21:06:54 75228 - 89911
|
||||
00:59:56.480 00:09:49:07 - 00:10:14:14 2002-04-12 21:06:56 - 2002-04-12 21:07:22 89912 - 90551
|
||||
01:00:22.080 00:00:00:00 - 00:11:22:21 2002-04-12 21:11:47 - 2002-04-27 00:05:36 90552 - 107624
|
||||
01:11:45.000 00:11:22:22 - 00:11:25:00 2002-04-25 22:59:57 - 2002-04-25 22:59:59 107625 - 107677
|
||||
01:11:47.120 00:11:25:01 - 00:12:15:14 2002-04-25 23:00:00 - 2002-05-02 13:33:33 107678 - 108941
|
||||
01:12:37.680 00:12:15:15 - 00:12:17:07 2002-04-25 23:00:50 - 2002-04-25 23:00:52 108942 - 108992
|
||||
01:12:39.720 00:00:00:00 - 00:05:00:09 2002-05-02 13:40:27 - 2002-05-02 13:53:34 108993 - 116510
|
||||
01:17:40.440 00:00:00:00 - 00:00:14:21 2002-05-02 13:54:14 - 2002-05-02 13:59:29 116511 - 116882
|
||||
|
||||
Percent of frames with Error: 2.69%
|
||||
Percent of frames with Error (including Arbitrary bit inconsistency): 2.69%
|
||||
Percent of frames with Video Error Concealment: 2.38%
|
||||
Percent of frames with Audio Errors: 0.67%
|
||||
Percent of frames with Timecode Incoherency: 0.00%
|
||||
Percent of frames with Arbitrary bit inconsistency: 0.01%
|
||||
|
||||
Warning, frame count is maybe incoherant (reported by MediaInfo: 116882)
|
Loading…
Reference in New Issue
Block a user