1
0
mirror of https://github.com/mifi/lossless-cut.git synced 2024-11-25 03:33:14 +01:00

Load timecode offset from file #506

This commit is contained in:
Mikael Finstad 2020-11-27 22:43:06 +01:00
parent 49a0d7db17
commit a3bce63650
6 changed files with 60 additions and 4 deletions

View File

@ -73,6 +73,7 @@
"react-scripts": "^3.4.0",
"react-sortable-hoc": "^1.5.3",
"react-use": "^13.26.1",
"smpte-timecode": "^1.2.3",
"strong-data-uri": "^1.0.5",
"svg2png": "^4.1.1",
"sweetalert2": "^9.10.10",

View File

@ -20,6 +20,7 @@ const defaults = {
preserveMovData: false,
avoidNegativeTs: 'make_zero',
hideNotifications: undefined,
autoLoadTimecode: false,
},
};

View File

@ -42,7 +42,7 @@ import {
getDefaultOutFormat, getFormatData, mergeFiles as ffmpegMergeFiles, renderThumbnails as ffmpegRenderThumbnails,
readFrames, renderWaveformPng, html5ifyDummy, cutMultiple, extractStreams, autoMergeSegments, getAllStreams,
findNearestKeyFrameTime, html5ify as ffmpegHtml5ify, isStreamThumbnail, isAudioSupported, isIphoneHevc, tryReadChaptersToEdl,
fixInvalidDuration, getDuration,
fixInvalidDuration, getDuration, getTimecodeFromStreams,
} from './ffmpeg';
import { saveCsv, loadCsv, loadXmeml, loadCue } from './edlStore';
import {
@ -194,6 +194,8 @@ const App = memo(() => {
useEffect(() => safeSetConfig('ffmpegExperimental', ffmpegExperimental), [ffmpegExperimental]);
const [hideNotifications, setHideNotifications] = useState(configStore.get('hideNotifications'));
useEffect(() => safeSetConfig('hideNotifications', hideNotifications), [hideNotifications]);
const [autoLoadTimecode, setAutoLoadTimecode] = useState(configStore.get('autoLoadTimecode'));
useEffect(() => safeSetConfig('autoLoadTimecode', autoLoadTimecode), [autoLoadTimecode]);
useEffect(() => {
i18n.changeLanguage(language || fallbackLng).catch(console.error);
@ -1193,6 +1195,11 @@ const App = memo(() => {
const { streams } = await getAllStreams(fp);
// console.log('streams', streamsNew);
if (autoLoadTimecode) {
const timecode = getTimecodeFromStreams(streams);
if (timecode) setStartTimeOffset(timecode);
}
const videoStream = streams.find(stream => stream.codec_type === 'video' && !isStreamThumbnail(stream));
const audioStream = streams.find(stream => stream.codec_type === 'audio');
setMainVideoStream(videoStream);
@ -1270,7 +1277,7 @@ const App = memo(() => {
} finally {
setWorking();
}
}, [resetState, working, createDummyVideo, loadEdlFile, getEdlFilePath, getHtml5ifiedPath, loadCutSegments, enableAskForImportChapters, showUnsupportedFileMessage]);
}, [resetState, working, createDummyVideo, loadEdlFile, getEdlFilePath, getHtml5ifiedPath, loadCutSegments, enableAskForImportChapters, showUnsupportedFileMessage, autoLoadTimecode]);
const toggleHelp = useCallback(() => setHelpVisible(val => !val), []);
const toggleSettings = useCallback(() => setSettingsVisible(val => !val), []);
@ -1883,12 +1890,14 @@ const App = memo(() => {
setLanguage={setLanguage}
hideNotifications={hideNotifications}
setHideNotifications={setHideNotifications}
autoLoadTimecode={autoLoadTimecode}
setAutoLoadTimecode={setAutoLoadTimecode}
AutoExportToggler={AutoExportToggler}
renderCaptureFormatButton={renderCaptureFormatButton}
onWheelTunerRequested={onWheelTunerRequested}
/>
), [AutoExportToggler, askBeforeClose, autoMerge, autoSaveProjectFile, customOutDir, invertCutSegments, keyframeCut, renderCaptureFormatButton, timecodeShowFrames, changeOutDir, onWheelTunerRequested, language, invertTimelineScroll, ffmpegExperimental, setFfmpegExperimental, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, hideNotifications, setHideNotifications]);
), [AutoExportToggler, askBeforeClose, autoMerge, autoSaveProjectFile, customOutDir, invertCutSegments, keyframeCut, renderCaptureFormatButton, timecodeShowFrames, changeOutDir, onWheelTunerRequested, language, invertTimelineScroll, ffmpegExperimental, setFfmpegExperimental, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode]);
useEffect(() => {
if (!isStoreBuild) loadMifiLink().then(setMifiLink);

View File

@ -9,7 +9,7 @@ const Settings = memo(({
AutoExportToggler, renderCaptureFormatButton, onWheelTunerRequested, language, setLanguage,
invertTimelineScroll, setInvertTimelineScroll, ffmpegExperimental, setFfmpegExperimental,
enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction,
hideNotifications, setHideNotifications,
hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode
}) => {
const { t } = useTranslation();
@ -197,6 +197,17 @@ const Settings = memo(({
</Table.TextCell>
</Row>
<Row>
<KeyCell>{t('Auto load timecode from file as an offset in the timeline?')}</KeyCell>
<Table.TextCell>
<Checkbox
label={t('Auto load timecode')}
checked={autoLoadTimecode}
onChange={e => setAutoLoadTimecode(e.target.checked)}
/>
</Table.TextCell>
</Row>
<Row>
<KeyCell>{t('Hide informational notifications?')}</KeyCell>
<Table.TextCell>

View File

@ -5,6 +5,7 @@ import sum from 'lodash/sum';
import sortBy from 'lodash/sortBy';
import moment from 'moment';
import i18n from 'i18next';
import Timecode from 'smpte-timecode';
import { formatDuration, getOutPath, transferTimestamps, filenamify, isDurationValid } from './util';
@ -897,3 +898,31 @@ export async function fixInvalidDuration({ filePath, fileFormat, customOutDir })
return outPath;
}
function parseTimecode(str, frameRate) {
// console.log(str, frameRate);
const t = Timecode(str, frameRate ? parseFloat(frameRate.toFixed(3)) : undefined);
if (!t) return undefined;
const seconds = ((t.hours * 60) + t.minutes) * 60 + t.seconds + (t.frames / t.frameRate);
return Number.isFinite(seconds) ? seconds : undefined;
}
export function getTimecodeFromStreams(streams) {
console.log('Trying to load timecode');
let foundTimecode;
streams.find((stream) => {
try {
if (stream.tags && stream.tags.timecode) {
const fps = getStreamFps(stream);
foundTimecode = parseTimecode(stream.tags.timecode, fps);
console.log('Loaded timecode', stream.tags.timecode, 'from stream', stream.index);
return true;
}
return undefined;
} catch (err) {
// console.warn('Failed to parse timecode from file streams', err);
return undefined;
}
});
return foundTimecode;
}

View File

@ -12067,6 +12067,11 @@ slice-ansi@^2.1.0:
astral-regex "^1.0.0"
is-fullwidth-code-point "^2.0.0"
smpte-timecode@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/smpte-timecode/-/smpte-timecode-1.2.3.tgz#272ac8826724ce793d956109cfd3982b2f6b1893"
integrity sha512-6U+vdStmprzmpzEWS+9pw8GfiChBcMmJOdJxBjaXlGJhB9U/jcQhvh0buxJzEfhuX8E0OfYJy4hayBgAO92dBg==
snapdragon-node@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"