From da48d5bd1b8e84a400c97664f26877179dc5b017 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Mon, 14 Dec 2020 17:29:07 +0100 Subject: [PATCH] implement TSV export #545 Also fix #503 --- public/menu.js | 13 ++++++++++++- src/App.jsx | 29 +++++++++++++++++++---------- src/edlStore.js | 8 +++++++- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/public/menu.js b/public/menu.js index 725b981f..72b0ad27 100644 --- a/public/menu.js +++ b/public/menu.js @@ -38,7 +38,7 @@ module.exports = (app, mainWindow, newVersion) => { { label: 'Save project (CSV)', click() { - mainWindow.webContents.send('exportEdlFile'); + mainWindow.webContents.send('exportEdlFile', 'csv'); }, }, { @@ -70,6 +70,17 @@ module.exports = (app, mainWindow, newVersion) => { }, ], }, + { + label: 'Export project', + submenu: [ + { + label: 'Human readable (TSV)', + click() { + mainWindow.webContents.send('exportEdlFile', 'tsv'); + }, + }, + ], + }, { type: 'separator' }, { label: 'Convert to supported format', diff --git a/src/App.jsx b/src/App.jsx index 3ee424b3..9e91f330 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -45,7 +45,7 @@ import { findNearestKeyFrameTime, html5ify as ffmpegHtml5ify, isStreamThumbnail, isAudioSupported, isIphoneHevc, tryReadChaptersToEdl, fixInvalidDuration, getDuration, getTimecodeFromStreams, createChaptersFromSegments, } from './ffmpeg'; -import { saveCsv, loadCsv, loadXmeml, loadCue, loadPbf } from './edlStore'; +import { saveCsv, saveTsv, loadCsv, loadXmeml, loadCue, loadPbf } from './edlStore'; import { getOutPath, formatDuration, toast, errorToast, showFfmpegFail, setFileNameTitle, getOutDir, withBlur, checkDirWriteAccess, dirExists, openDirToast, isMasBuild, isStoreBuild, dragPreventer, doesPlayerSupportFile, @@ -1648,19 +1648,28 @@ const App = memo(() => { setStartTimeOffset(newStartTimeOffset); } - async function exportEdlFile() { + async function exportEdlFile(e, type) { try { if (!checkFileOpened()) return; - const { canceled, filePath: fp } = await dialog.showSaveDialog({ defaultPath: `${new Date().getTime()}.csv`, filters: [{ name: i18n.t('CSV files'), extensions: ['csv'] }] }); - if (canceled || !fp) return; - if (await exists(fp)) { - errorToast(i18n.t('File exists, bailing')); - return; + + let filters; + let ext; + if (type === 'csv') { + ext = 'csv'; + filters = [{ name: i18n.t('CSV files'), extensions: [ext] }]; + } else if (type === 'tsv') { + ext = 'txt'; + filters = [{ name: i18n.t('TXT files'), extensions: [ext] }]; } - await saveCsv(fp, cutSegments); + + const { canceled, filePath: fp } = await dialog.showSaveDialog({ defaultPath: `${new Date().getTime()}.${ext}`, filters }); + if (canceled || !fp) return; + console.log('Saving', type, fp); + if (type === 'csv') await saveCsv(fp, cutSegments); + else if (type === 'tsv') await saveTsv(fp, cutSegments); } catch (err) { - errorToast(i18n.t('Failed to export CSV')); - console.error('Failed to export CSV', err); + errorToast(i18n.t('Failed to export project')); + console.error('Failed to export project', type, err); } } diff --git a/src/edlStore.js b/src/edlStore.js index 7a861a65..98c91cf3 100644 --- a/src/edlStore.js +++ b/src/edlStore.js @@ -2,6 +2,7 @@ import csvStringify from 'csv-stringify'; import pify from 'pify'; import { parseCuesheet, parseXmeml, parseCsv, parsePbf } from './edlFormats'; +import { formatDuration } from './util'; const fs = window.require('fs-extra'); const cueParser = window.require('cue-parser'); @@ -25,8 +26,13 @@ export async function loadCue(path) { } export async function saveCsv(path, cutSegments) { - console.log('Saving', path); const rows = cutSegments.map(({ start, end, name }) => [start, end, name]); const str = await csvStringifyAsync(rows); await fs.writeFile(path, str); } + +export async function saveTsv(path, cutSegments) { + // TODO escape tab, how? + const rows = cutSegments.map(({ start, end, name }) => `${start != null ? formatDuration({ seconds: start }) : ''}\t${end != null ? formatDuration({ seconds: end }) : ''}\t${name}`); + await fs.writeFile(path, rows.join('\n')); +}