kopia lustrzana https://github.com/mifi/lossless-cut
rodzic
ec1759c965
commit
62189f2294
79
src/App.jsx
79
src/App.jsx
|
@ -853,20 +853,6 @@ const App = memo(() => {
|
|||
});
|
||||
}, [playing, filePath]);
|
||||
|
||||
const onVideoError = useCallback(() => {
|
||||
const { error } = videoRef.current;
|
||||
if (!error) return;
|
||||
if (!fileUri) return; // Probably MEDIA_ELEMENT_ERROR: Empty src attribute
|
||||
|
||||
console.error(error.message);
|
||||
|
||||
const MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
|
||||
if (error.code === MEDIA_ERR_SRC_NOT_SUPPORTED && !dummyVideoPath) {
|
||||
console.log('MEDIA_ERR_SRC_NOT_SUPPORTED - trying to create dummy');
|
||||
tryCreateDummyVideo();
|
||||
}
|
||||
}, [tryCreateDummyVideo, fileUri, dummyVideoPath]);
|
||||
|
||||
const deleteSource = useCallback(async () => {
|
||||
if (!filePath) return;
|
||||
|
||||
|
@ -1094,7 +1080,7 @@ const App = memo(() => {
|
|||
setHtml5FriendlyPath(html5FriendlyPathRequested);
|
||||
showUnsupportedFileMessage();
|
||||
} else if (
|
||||
!(await checkAndSetExistingHtml5FriendlyFile('slow-audio') || await checkAndSetExistingHtml5FriendlyFile('slow') || await checkAndSetExistingHtml5FriendlyFile('fast'))
|
||||
!(await checkAndSetExistingHtml5FriendlyFile('slowest') || await checkAndSetExistingHtml5FriendlyFile('slow-audio') || await checkAndSetExistingHtml5FriendlyFile('slow') || await checkAndSetExistingHtml5FriendlyFile('fast-audio') || await checkAndSetExistingHtml5FriendlyFile('fast'))
|
||||
&& !doesPlayerSupportFile(streams)
|
||||
) {
|
||||
await createDummyVideo(cod, fp);
|
||||
|
@ -1274,6 +1260,40 @@ const App = memo(() => {
|
|||
userOpenFiles(filePaths);
|
||||
}, [userOpenFiles, loadEdlFile]);
|
||||
|
||||
const html5ifyInternal = useCallback(async ({ customOutDir: cod, filePath: fp, speed, hasAudio: ha, hasVideo: hv }) => {
|
||||
const path = getHtml5ifiedPath(cod, fp, speed);
|
||||
const includeVideo = hv;
|
||||
const includeAudio = ['fast-audio', 'slow-audio', 'slowest'].includes(speed) && ha;
|
||||
const encode = ['slow-audio', 'slow', 'slowest'].includes(speed);
|
||||
const highQuality = speed === 'slowest';
|
||||
await ffmpegHtml5ify({ filePath: fp, outPath: path, encode, includeVideo, includeAudio, highQuality });
|
||||
return path;
|
||||
}, [getHtml5ifiedPath]);
|
||||
|
||||
const html5ifyAndLoad = useCallback(async (speed) => {
|
||||
const path = await html5ifyInternal({ customOutDir, filePath, speed, hasAudio, hasVideo });
|
||||
load({ filePath, html5FriendlyPathRequested: path, customOutDir });
|
||||
}, [hasAudio, hasVideo, customOutDir, filePath, html5ifyInternal, load]);
|
||||
|
||||
const onVideoError = useCallback(async () => {
|
||||
const { error } = videoRef.current;
|
||||
if (!error) return;
|
||||
if (!fileUri) return; // Probably MEDIA_ELEMENT_ERROR: Empty src attribute
|
||||
|
||||
console.error(error.message);
|
||||
|
||||
const MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
|
||||
if (error.code === MEDIA_ERR_SRC_NOT_SUPPORTED && !dummyVideoPath) {
|
||||
console.log('MEDIA_ERR_SRC_NOT_SUPPORTED - trying to create dummy');
|
||||
|
||||
if (hasVideo) tryCreateDummyVideo();
|
||||
else {
|
||||
toast.fire({ icon: 'info', text: 'This file does not have any supported. Encoding a preview file...' });
|
||||
await html5ifyAndLoad('slow-audio');
|
||||
}
|
||||
}
|
||||
}, [tryCreateDummyVideo, fileUri, dummyVideoPath, hasVideo, html5ifyAndLoad]);
|
||||
|
||||
useEffect(() => {
|
||||
function fileOpened(event, filePaths) {
|
||||
userOpenFiles(filePaths);
|
||||
|
@ -1290,9 +1310,11 @@ const App = memo(() => {
|
|||
async function askForHtml5ifySpeed(allowedOptions) {
|
||||
const availOptions = {
|
||||
fastest: i18n.t('Fastest: Low playback speed (no audio)'),
|
||||
fast: i18n.t('Fast: Full quality remux, unlikely to work'),
|
||||
slow: i18n.t('Slow: Low video quality encode (no audio)'),
|
||||
'slow-audio': i18n.t('Slowest: Low video/audio quality encode'),
|
||||
fast: i18n.t('Fast: Full quality remux (no audio), likely to fail'),
|
||||
'fast-audio': i18n.t('Fast: Full quality remux, likely to fail'),
|
||||
slow: i18n.t('Slow: Low quality encode (no audio)'),
|
||||
'slow-audio': i18n.t('Slow: Low quality encode'),
|
||||
slowest: i18n.t('Slowest: High quality encode'),
|
||||
};
|
||||
const inputOptions = {};
|
||||
allowedOptions.forEach((allowedOption) => {
|
||||
|
@ -1313,28 +1335,19 @@ const App = memo(() => {
|
|||
return value;
|
||||
}
|
||||
|
||||
async function html5ifyInternal({ customOutDir: cod, filePath: fp, speed, hasAudio: ha, hasVideo: hv }) {
|
||||
const path = getHtml5ifiedPath(cod, fp, speed);
|
||||
const encodeVideo = ['slow', 'slow-audio'].includes(speed) && hv;
|
||||
const encodeAudio = speed === 'slow-audio' && ha;
|
||||
await ffmpegHtml5ify(fp, path, encodeVideo, encodeAudio);
|
||||
return path;
|
||||
}
|
||||
|
||||
async function html5ify() {
|
||||
if (!filePath) return;
|
||||
|
||||
try {
|
||||
setWorking(true);
|
||||
|
||||
const speed = await askForHtml5ifySpeed(['fastest', 'fast', 'slow', 'slow-audio']);
|
||||
const speed = await askForHtml5ifySpeed(['fastest', 'fast-audio', 'fast', 'slow', 'slow-audio', 'slowest']);
|
||||
if (!speed) return;
|
||||
|
||||
if (speed === 'fastest') {
|
||||
await createDummyVideo(customOutDir, filePath);
|
||||
} else if (['fast', 'slow', 'slow-audio'].includes(speed)) {
|
||||
const path = await html5ifyInternal({ customOutDir, filePath, speed, hasAudio, hasVideo });
|
||||
load({ filePath, html5FriendlyPathRequested: path, customOutDir });
|
||||
} else if (['fast-audio', 'fast', 'slow', 'slow-audio', 'slowest'].includes(speed)) {
|
||||
await html5ifyAndLoad(speed);
|
||||
}
|
||||
} catch (err) {
|
||||
errorToast(i18n.t('Failed to html5ify file'));
|
||||
|
@ -1416,7 +1429,7 @@ const App = memo(() => {
|
|||
const failedFiles = [];
|
||||
let i = 0;
|
||||
|
||||
const speed = await askForHtml5ifySpeed(['fast', 'slow', 'slow-audio']);
|
||||
const speed = await askForHtml5ifySpeed(['fast-audio', 'fast', 'slow', 'slow-audio', 'slowest']);
|
||||
if (!speed) return;
|
||||
|
||||
try {
|
||||
|
@ -1486,9 +1499,9 @@ const App = memo(() => {
|
|||
electron.ipcRenderer.removeListener('batchConvertFriendlyFormat', batchConvertFriendlyFormat);
|
||||
};
|
||||
}, [
|
||||
load, mergeFiles, outputDir, filePath, isFileOpened, customOutDir, startTimeOffset, getHtml5ifiedPath,
|
||||
mergeFiles, outputDir, filePath, isFileOpened, customOutDir, startTimeOffset,
|
||||
createDummyVideo, resetState, extractAllStreams, userOpenFiles, cutSegmentsHistory,
|
||||
loadEdlFile, cutSegments, edlFilePath, askBeforeClose, toggleHelp, toggleSettings, assureOutDirAccess, hasAudio, hasVideo,
|
||||
loadEdlFile, cutSegments, edlFilePath, askBeforeClose, toggleHelp, toggleSettings, assureOutDirAccess, html5ifyAndLoad, html5ifyInternal,
|
||||
]);
|
||||
|
||||
async function showAddStreamSourceDialog() {
|
||||
|
|
|
@ -306,18 +306,43 @@ export async function cutMultiple({
|
|||
return outFiles;
|
||||
}
|
||||
|
||||
export async function html5ify(filePath, outPath, encodeVideo, encodeAudio) {
|
||||
console.log('Making HTML5 friendly version', { filePath, outPath, encodeVideo });
|
||||
export async function html5ify({ filePath, outPath, encode, includeVideo, includeAudio, highQuality }) {
|
||||
console.log('Making HTML5 friendly version', { filePath, outPath, encode, includeVideo, includeAudio, highQuality });
|
||||
|
||||
let videoArgs;
|
||||
if (!encodeVideo) videoArgs = ['-vcodec', 'copy'];
|
||||
else if (os.platform() === 'darwin') {
|
||||
videoArgs = ['-vf', 'scale=-2:400,format=yuv420p', '-allow_sw', '1', '-sws_flags', 'lanczos', '-vcodec', 'h264', '-b:v', '1500k'];
|
||||
let audioArgs;
|
||||
|
||||
if (includeVideo) {
|
||||
if (!encode) {
|
||||
videoArgs = ['-vcodec', 'copy'];
|
||||
} else if (os.platform() === 'darwin') {
|
||||
if (highQuality) {
|
||||
videoArgs = ['-vf', 'format=yuv420p', '-allow_sw', '1', '-vcodec', 'h264', '-b:v', '15M'];
|
||||
} else {
|
||||
videoArgs = ['-vf', 'scale=-2:400,format=yuv420p', '-allow_sw', '1', '-sws_flags', 'lanczos', '-vcodec', 'h264', '-b:v', '1500k'];
|
||||
}
|
||||
} else if (highQuality) {
|
||||
videoArgs = ['-vf', 'format=yuv420p', '-vcodec', 'libx264', '-profile:v', 'high', '-preset:v', 'slow', '-crf', '17'];
|
||||
} else {
|
||||
videoArgs = ['-vf', 'scale=-2:400,format=yuv420p', '-sws_flags', 'neighbor', '-vcodec', 'libx264', '-profile:v', 'baseline', '-x264opts', 'level=3.0', '-preset:v', 'ultrafast', '-crf', '28'];
|
||||
}
|
||||
} else {
|
||||
videoArgs = ['-vf', 'scale=-2:400,format=yuv420p', '-sws_flags', 'neighbor', '-vcodec', 'libx264', '-profile:v', 'baseline', '-x264opts', 'level=3.0', '-preset:v', 'ultrafast', '-crf', '28'];
|
||||
videoArgs = ['-vn'];
|
||||
}
|
||||
|
||||
const audioArgs = encodeAudio ? ['-acodec', 'aac', '-ar', '44100', '-ac', '2', '-b:a', '96k'] : ['-an'];
|
||||
if (includeAudio) {
|
||||
if (encode) {
|
||||
if (highQuality) {
|
||||
audioArgs = ['-acodec', 'aac', '-b:a', '192k'];
|
||||
} else {
|
||||
audioArgs = ['-acodec', 'aac', '-ar', '44100', '-ac', '2', '-b:a', '96k'];
|
||||
}
|
||||
} else {
|
||||
audioArgs = ['-acodec', 'copy'];
|
||||
}
|
||||
} else {
|
||||
audioArgs = ['-an'];
|
||||
}
|
||||
|
||||
const ffmpegArgs = [
|
||||
'-hide_banner',
|
||||
|
|
Ładowanie…
Reference in New Issue