Improvements

- Add a warning for iphone vids #329
- Lenghten toasts (some people say it's too short)
- Remove output dir from messages because now we have button
pull/333/head
Mikael Finstad 6 years ago
parent 67c25e0fc7
commit 0dba7e1faf

@ -41,7 +41,7 @@ import {
defaultProcessedCodecTypes, getStreamFps, isCuttingStart, isCuttingEnd, defaultProcessedCodecTypes, getStreamFps, isCuttingStart, isCuttingEnd,
getDefaultOutFormat, getFormatData, mergeFiles as ffmpegMergeFiles, renderThumbnails as ffmpegRenderThumbnails, getDefaultOutFormat, getFormatData, mergeFiles as ffmpegMergeFiles, renderThumbnails as ffmpegRenderThumbnails,
readFrames, renderWaveformPng, html5ifyDummy, cutMultiple, extractStreams, autoMergeSegments, getAllStreams, readFrames, renderWaveformPng, html5ifyDummy, cutMultiple, extractStreams, autoMergeSegments, getAllStreams,
findNearestKeyFrameTime, html5ify as ffmpegHtml5ify, isStreamThumbnail, isAudioSupported, findNearestKeyFrameTime, html5ify as ffmpegHtml5ify, isStreamThumbnail, isAudioSupported, isIphoneHevc,
} from './ffmpeg'; } from './ffmpeg';
import { save as edlStoreSave, load as edlStoreLoad } from './edlStore'; import { save as edlStoreSave, load as edlStoreLoad } from './edlStore';
import { import {
@ -258,7 +258,7 @@ const App = memo(() => {
setKeyframesEnabled((old) => { setKeyframesEnabled((old) => {
const enabled = !old; const enabled = !old;
if (enabled && !calcShouldShowKeyframes(zoomedDuration)) { if (enabled && !calcShouldShowKeyframes(zoomedDuration)) {
toast.fire({ text: i18n.t('Key frames will now show on the timeline. You need to zoom in to view them') }); toast.fire({ text: i18n.t('Key frames will show on the timeline. You need to zoom in to view them') });
} }
return enabled; return enabled;
}); });
@ -820,7 +820,7 @@ const App = memo(() => {
useEffect(() => () => waveform && URL.revokeObjectURL(waveform.url), [waveform]); useEffect(() => () => waveform && URL.revokeObjectURL(waveform.url), [waveform]);
function showUnsupportedFileMessage() { function showUnsupportedFileMessage() {
toast.fire({ timer: 10000, text: i18n.t('File not natively supported. Preview may have no audio or low quality. The final export will however be lossless with audio. You may convert it from the menu for a better preview.') }); toast.fire({ timer: 13000, text: i18n.t('File not natively supported. Preview may have no audio or low quality. The final export will however be lossless with audio. You may convert it from the menu for a better preview.') });
} }
const createDummyVideo = useCallback(async (cod, fp) => { const createDummyVideo = useCallback(async (cod, fp) => {
@ -836,6 +836,8 @@ const App = memo(() => {
showUnsupportedFileMessage(); showUnsupportedFileMessage();
}, []); }, []);
const showPlaybackFailedMessage = () => errorToast(i18n.t('Unable to playback this file. Try to convert to supported format from the menu'));
const tryCreateDummyVideo = useCallback(async () => { const tryCreateDummyVideo = useCallback(async () => {
try { try {
if (working) return; if (working) return;
@ -843,7 +845,7 @@ const App = memo(() => {
await createDummyVideo(customOutDir, filePath); await createDummyVideo(customOutDir, filePath);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
errorToast(i18n.t('Failed to playback this file. Try to convert to supported format from the menu')); showPlaybackFailedMessage();
} finally { } finally {
setWorking(); setWorking();
} }
@ -861,7 +863,7 @@ const App = memo(() => {
if (resetPlaybackRate) video.playbackRate = 1; if (resetPlaybackRate) video.playbackRate = 1;
video.play().catch((err) => { video.play().catch((err) => {
toast.fire({ icon: 'error', text: 'Unable to play this file. Try to convert to supported format first' }); showPlaybackFailedMessage();
console.error(err); console.error(err);
}); });
}, [playing, filePath]); }, [playing, filePath]);
@ -1016,8 +1018,11 @@ const App = memo(() => {
} }
} }
// https://github.com/mifi/lossless-cut/issues/329
const extraIphoneMsg = isIphoneHevc(fileFormatData, mainStreams) ? ` ${i18n.t('There is a known issue with cutting iPhone HEVC videos. The output file may not work in all players.')}` : '';
const extraStreamsMsg = exportExtraStreams ? ` ${i18n.t('Unprocessable streams were exported as separate files.')}` : ''; const extraStreamsMsg = exportExtraStreams ? ` ${i18n.t('Unprocessable streams were exported as separate files.')}` : '';
openDirToast({ dirPath: outputDir, text: `${i18n.t('Done! Start-cutpoints may not be accurate. Make sure you test the output files in your desired player/editor before you delete the source files. If output does not look right, try to toggle "Keyframe cut" or try a different format. Output file(s) can be found at:')} ${outputDir}.${extraStreamsMsg}` });
openDirToast({ dirPath: outputDir, text: `${i18n.t('Done! Note: cutpoints may be inaccurate. Make sure you test the output files in your desired player/editor before you delete the source. If output does not look right, try to toggle "Keyframe cut" or try a different format.')}${extraIphoneMsg}${extraStreamsMsg}`, timer: 15000 });
} catch (err) { } catch (err) {
console.error('stdout:', err.stdout); console.error('stdout:', err.stdout);
console.error('stderr:', err.stderr); console.error('stderr:', err.stderr);
@ -1036,6 +1041,7 @@ const App = memo(() => {
working, duration, filePath, keyframeCut, working, duration, filePath, keyframeCut,
autoMerge, customOutDir, fileFormat, haveInvalidSegs, copyFileStreams, numStreamsToCopy, autoMerge, customOutDir, fileFormat, haveInvalidSegs, copyFileStreams, numStreamsToCopy,
exportExtraStreams, nonCopiedExtraStreams, outputDir, shortestFlag, isCustomFormatSelected, exportExtraStreams, nonCopiedExtraStreams, outputDir, shortestFlag, isCustomFormatSelected,
fileFormatData, mainStreams,
]); ]);
const capture = useCallback(async () => { const capture = useCallback(async () => {
@ -1156,7 +1162,6 @@ const App = memo(() => {
stream.index, shouldDefaultCopyStream(stream), stream.index, shouldDefaultCopyStream(stream),
]))); ])));
setFileNameTitle(fp); setFileNameTitle(fp);
setFilePath(fp); setFilePath(fp);
setFileFormat(ff); setFileFormat(ff);
@ -1164,7 +1169,7 @@ const App = memo(() => {
setFileFormatData(fd); setFileFormatData(fd);
if (!isAudioSupported(streams)) { if (!isAudioSupported(streams)) {
toast.fire({ icon: 'info', text: 'The audio track is not supported. You can convert to a supported format from the menu' }); toast.fire({ icon: 'info', text: i18n.t('The audio track is not supported. You can convert to a supported format from the menu') });
} }
if (html5FriendlyPathRequested) { if (html5FriendlyPathRequested) {
@ -1282,7 +1287,7 @@ const App = memo(() => {
try { try {
setWorking(i18n.t('Extracting all streams')); setWorking(i18n.t('Extracting all streams'));
await extractStreams({ customOutDir, filePath, streams: mainStreams }); await extractStreams({ customOutDir, filePath, streams: mainStreams });
openDirToast({ dirPath: outputDir, text: `${i18n.t('All streams can be found as separate files at:')} ${outputDir}` }); openDirToast({ dirPath: outputDir, text: i18n.t('All streams have been extracted as separate files') });
} catch (err) { } catch (err) {
errorToast(i18n.t('Failed to extract all streams')); errorToast(i18n.t('Failed to extract all streams'));
console.error('Failed to extract all streams', err); console.error('Failed to extract all streams', err);

@ -733,6 +733,13 @@ export function isAudioSupported(streams) {
return audioStreams.some(stream => !['ac3'].includes(stream.codec_name)); return audioStreams.some(stream => !['ac3'].includes(stream.codec_name));
} }
export function isIphoneHevc(format, streams) {
if (!streams.some((s) => s.codec_name === 'hevc')) return false;
const makeTag = format.tags && format.tags['com.apple.quicktime.make'];
const modelTag = format.tags && format.tags['com.apple.quicktime.model'];
return (makeTag === 'Apple' && modelTag.startsWith('iPhone'));
}
export function getStreamFps(stream) { export function getStreamFps(stream) {
const match = typeof stream.avg_frame_rate === 'string' && stream.avg_frame_rate.match(/^([0-9]+)\/([0-9]+)$/); const match = typeof stream.avg_frame_rate === 'string' && stream.avg_frame_rate.match(/^([0-9]+)\/([0-9]+)$/);
if (stream.codec_type === 'video' && match) { if (stream.codec_type === 'video' && match) {

@ -103,7 +103,7 @@ export const errorToast = (title) => toast.fire({
}); });
export const openDirToast = async ({ dirPath, ...props }) => { export const openDirToast = async ({ dirPath, ...props }) => {
const { value } = await toast.fire({ icon: 'success', ...props, timer: 10000, showConfirmButton: true, confirmButtonText: 'Show', showCancelButton: true, cancelButtonText: 'Close' }); const { value } = await toast.fire({ icon: 'success', ...props, timer: 13000, showConfirmButton: true, confirmButtonText: i18n.t('Show'), showCancelButton: true, cancelButtonText: i18n.t('Close') });
if (value) open(dirPath); if (value) open(dirPath);
}; };

Loading…
Cancel
Save