From 7da209b253ec4d12bbe224bae6a36eb4d63ae227 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Sat, 21 Nov 2020 00:04:08 +0100 Subject: [PATCH] Add function to create number of segments #469 --- public/menu.js | 12 ++++++++++++ src/App.jsx | 28 ++++++++++++++++++++-------- src/util.js | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/public/menu.js b/public/menu.js index af98be41..28a34772 100644 --- a/public/menu.js +++ b/public/menu.js @@ -117,6 +117,18 @@ module.exports = (app, mainWindow, newVersion) => { { role: 'copy' }, { role: 'paste' }, { role: 'selectall' }, + { type: 'separator' }, + { + label: 'Segments', + submenu: [ + { + label: 'Create num segments', + click() { + mainWindow.webContents.send('createNumSegments'); + }, + }, + ], + }, ], }, diff --git a/src/App.jsx b/src/App.jsx index f7b6a4c8..d7d47f04 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -48,7 +48,7 @@ import { getOutPath, formatDuration, toast, errorToast, showFfmpegFail, setFileNameTitle, promptTimeOffset, generateColor, getOutDir, withBlur, checkDirWriteAccess, dirExists, askForOutDir, openDirToast, askForHtml5ifySpeed, askForYouTubeInput, isMasBuild, isStoreBuild, askForFileOpenAction, - askForImportChapters, + askForImportChapters, createNumSegments, } from './util'; import { openSendReportDialog } from './reporting'; import { fallbackLng } from './i18n'; @@ -1386,6 +1386,12 @@ const App = memo(() => { } }, [addStreamSourceFile, isFileOpened, load, mergeFiles, assureOutDirAccess, enableAskForFileOpenAction]); + const checkFileOpened = useCallback(() => { + if (isFileOpened) return true; + toast.fire({ icon: 'info', title: i18n.t('You need to open a media file first') }); + return false; + }, [isFileOpened]); + const onDrop = useCallback(async (ev) => { ev.preventDefault(); const { files } = ev.dataTransfer; @@ -1394,11 +1400,12 @@ const App = memo(() => { focusWindow(); if (filePaths.length === 1 && filePaths[0].toLowerCase().endsWith('.csv')) { + if (!checkFileOpened()) return; loadEdlFile(filePaths[0]); return; } userOpenFiles(filePaths); - }, [userOpenFiles, loadEdlFile]); + }, [userOpenFiles, loadEdlFile, checkFileOpened]); const html5ifyInternal = useCallback(async ({ customOutDir: cod, filePath: fp, speed, hasAudio: ha, hasVideo: hv }) => { const path = getHtml5ifiedPath(cod, fp, speed); @@ -1519,7 +1526,7 @@ const App = memo(() => { async function exportEdlFile() { try { - if (!isFileOpened) return; + 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)) { @@ -1534,10 +1541,7 @@ const App = memo(() => { } async function importEdlFile(e, type) { - if (!isFileOpened) { - toast.fire({ icon: 'info', title: i18n.t('You need to open a media file first') }); - return; - } + if (!checkFileOpened()) return; if (type === 'youtube') { const edl = await askForYouTubeInput(); @@ -1621,6 +1625,12 @@ const App = memo(() => { openSendReportDialogWithState(); } + async function createNumSegments2() { + if (!checkFileOpened()) return; + const segments = await createNumSegments(duration); + if (segments) loadCutSegments(segments); + } + electron.ipcRenderer.on('file-opened', fileOpened); electron.ipcRenderer.on('close-file', closeFile); electron.ipcRenderer.on('html5ify', html5ifyCurrentFile); @@ -1636,6 +1646,7 @@ const App = memo(() => { electron.ipcRenderer.on('openAbout', openAbout); electron.ipcRenderer.on('batchConvertFriendlyFormat', batchConvertFriendlyFormat); electron.ipcRenderer.on('openSendReportDialog', openSendReportDialog2); + electron.ipcRenderer.on('createNumSegments', createNumSegments2); return () => { electron.ipcRenderer.removeListener('file-opened', fileOpened); @@ -1653,12 +1664,13 @@ const App = memo(() => { electron.ipcRenderer.removeListener('openAbout', openAbout); electron.ipcRenderer.removeListener('batchConvertFriendlyFormat', batchConvertFriendlyFormat); electron.ipcRenderer.removeListener('openSendReportDialog', openSendReportDialog2); + electron.ipcRenderer.removeListener('createNumSegments', createNumSegments2); }; }, [ mergeFiles, outputDir, filePath, isFileOpened, customOutDir, startTimeOffset, html5ifyCurrentFile, createDummyVideo, resetState, extractAllStreams, userOpenFiles, cutSegmentsHistory, openSendReportDialogWithState, loadEdlFile, cutSegments, edlFilePath, askBeforeClose, toggleHelp, toggleSettings, assureOutDirAccess, html5ifyAndLoad, html5ifyInternal, - loadCutSegments, + loadCutSegments, duration, checkFileOpened, ]); async function showAddStreamSourceDialog() { diff --git a/src/util.js b/src/util.js index b5c95d5d..d69f7b7d 100644 --- a/src/util.js +++ b/src/util.js @@ -262,3 +262,40 @@ export async function askForImportChapters() { return value; } + +async function askForNumSegments() { + const maxSegments = 1000; + + const { value } = await Swal.fire({ + input: 'number', + inputAttributes: { + min: 0, + max: maxSegments, + }, + showCancelButton: true, + inputValue: '2', + text: i18n.t('Divide timeline into a number of equal length segments'), + inputValidator: (v) => { + const parsed = parseInt(v, 10); + if (!Number.isNaN(parsed) && parsed >= 2 && parsed <= maxSegments) { + return undefined; + } + return i18n.t('Please input a valid number of segments'); + }, + }); + + if (value == null) return undefined; + + return parseInt(value, 10); +} + +export async function createNumSegments(fileDuration) { + const numSegments = await askForNumSegments(); + if (numSegments == null) return undefined; + const edl = []; + const segDuration = fileDuration / numSegments; + for (let i = 0; i < numSegments; i += 1) { + edl.push({ start: i * segDuration, end: i === numSegments - 1 ? undefined : (i + 1) * segDuration }); + } + return edl; +}