mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-21 18:02:35 +01:00
modernize code and use sweetalert2
This commit is contained in:
parent
328a2cd6f8
commit
8817c2c80b
@ -6,9 +6,9 @@
|
||||
"browser": true,
|
||||
},
|
||||
"rules": {
|
||||
"no-alert": 0,
|
||||
"no-console": 0,
|
||||
"react/destructuring-assignment": 0,
|
||||
"react/forbid-prop-types": [1, { "forbid": ["any"] }],
|
||||
},
|
||||
"plugins": [
|
||||
"react"
|
||||
|
@ -2,7 +2,6 @@ const execa = require('execa');
|
||||
const bluebird = require('bluebird');
|
||||
const which = bluebird.promisify(require('which'));
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const fileType = require('file-type');
|
||||
const readChunk = require('read-chunk');
|
||||
const _ = require('lodash');
|
||||
@ -11,14 +10,6 @@ const moment = require('moment');
|
||||
|
||||
const util = require('./util');
|
||||
|
||||
bluebird.promisifyAll(fs);
|
||||
|
||||
|
||||
function showFfmpegFail(err) {
|
||||
alert(`Failed to run ffmpeg:\n${err.stack}`);
|
||||
console.error(err.stack);
|
||||
}
|
||||
|
||||
function getWithExt(name) {
|
||||
return process.platform === 'win32' ? `${name}.exe` : name;
|
||||
}
|
||||
@ -187,6 +178,5 @@ function getFormat(filePath) {
|
||||
module.exports = {
|
||||
cut,
|
||||
getFormat,
|
||||
showFfmpegFail,
|
||||
html5ify,
|
||||
};
|
||||
|
@ -15,7 +15,10 @@ const classnames = require('classnames');
|
||||
|
||||
const captureFrame = require('./capture-frame');
|
||||
const ffmpeg = require('./ffmpeg');
|
||||
const util = require('./util');
|
||||
|
||||
const {
|
||||
getOutPath, parseDuration, formatDuration, toast, errorToast, showFfmpegFail,
|
||||
} = require('./util');
|
||||
|
||||
const { dialog } = electron.remote;
|
||||
|
||||
@ -122,30 +125,36 @@ class App extends React.Component {
|
||||
...globalState,
|
||||
};
|
||||
|
||||
const load = (filePath, html5FriendlyPath) => {
|
||||
const load = async (filePath, html5FriendlyPath) => {
|
||||
const { working } = this.state;
|
||||
|
||||
console.log('Load', { filePath, html5FriendlyPath });
|
||||
if (working) return alert('I\'m busy');
|
||||
if (working) {
|
||||
errorToast('I\'m busy');
|
||||
return;
|
||||
}
|
||||
|
||||
this.resetState();
|
||||
|
||||
this.setState({ working: true });
|
||||
|
||||
return ffmpeg.getFormat(filePath)
|
||||
.then((fileFormat) => {
|
||||
if (!fileFormat) return alert('Unsupported file');
|
||||
try {
|
||||
const fileFormat = await ffmpeg.getFormat(filePath);
|
||||
if (!fileFormat) {
|
||||
errorToast('Unsupported file');
|
||||
return;
|
||||
}
|
||||
setFileNameTitle(filePath);
|
||||
return this.setState({ filePath, html5FriendlyPath, fileFormat });
|
||||
})
|
||||
.catch((err) => {
|
||||
this.setState({ filePath, html5FriendlyPath, fileFormat });
|
||||
} catch (err) {
|
||||
if (err.code === 1 || err.code === 'ENOENT') {
|
||||
alert('Unsupported file');
|
||||
errorToast('Unsupported file');
|
||||
return;
|
||||
}
|
||||
ffmpeg.showFfmpegFail(err);
|
||||
})
|
||||
.finally(() => this.setState({ working: false }));
|
||||
showFfmpegFail(err);
|
||||
} finally {
|
||||
this.setState({ working: false });
|
||||
}
|
||||
};
|
||||
|
||||
electron.ipcRenderer.on('file-opened', (event, filePaths) => {
|
||||
@ -159,12 +168,12 @@ class App extends React.Component {
|
||||
|
||||
try {
|
||||
this.setState({ working: true });
|
||||
const html5ifiedPath = util.getOutPath(customOutDir, filePath, 'html5ified.mp4');
|
||||
const html5ifiedPath = getOutPath(customOutDir, filePath, 'html5ified.mp4');
|
||||
await ffmpeg.html5ify(filePath, html5ifiedPath, encodeVideo);
|
||||
this.setState({ working: false });
|
||||
load(filePath, html5ifiedPath);
|
||||
} catch (err) {
|
||||
alert('Failed to html5ify file');
|
||||
errorToast('Failed to html5ify file');
|
||||
console.error('Failed to html5ify file', err);
|
||||
this.setState({ working: false });
|
||||
}
|
||||
@ -184,7 +193,7 @@ class App extends React.Component {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const duration = util.parseDuration(value);
|
||||
const duration = parseDuration(value);
|
||||
// Invalid, try again
|
||||
if (duration === undefined) return promptTimeOffset(value);
|
||||
|
||||
@ -194,7 +203,7 @@ class App extends React.Component {
|
||||
electron.ipcRenderer.on('set-start-offset', async () => {
|
||||
const { startTimeOffset: startTimeOffsetOld } = this.state;
|
||||
const startTimeOffset = await promptTimeOffset(
|
||||
startTimeOffsetOld !== undefined ? util.formatDuration(startTimeOffsetOld) : undefined,
|
||||
startTimeOffsetOld !== undefined ? formatDuration(startTimeOffsetOld) : undefined,
|
||||
);
|
||||
|
||||
if (startTimeOffset === undefined) {
|
||||
@ -210,7 +219,10 @@ class App extends React.Component {
|
||||
|
||||
document.body.ondrop = (ev) => {
|
||||
ev.preventDefault();
|
||||
if (ev.dataTransfer.files.length !== 1) return;
|
||||
if (ev.dataTransfer.files.length !== 1) {
|
||||
errorToast('Please drop only one file');
|
||||
return;
|
||||
}
|
||||
load(ev.dataTransfer.files[0].path);
|
||||
};
|
||||
|
||||
@ -337,12 +349,13 @@ class App extends React.Component {
|
||||
return video.play().catch((err) => {
|
||||
console.log(err);
|
||||
if (err.name === 'NotSupportedError') {
|
||||
alert('This video format or codec is not supported. Try to convert it to a friendly format/codec in the player from the "File" menu. Note that this will only create a temporary, low quality encoded file used for previewing your cuts, and will not affect the final cut. The final cut will still be lossless. Audio is also removed to make it faster, but only in the preview.');
|
||||
toast({ type: 'error', title: 'This format/codec is not supported. Try to convert it to a friendly format/codec in the player from the "File" menu. Note that this will only create a temporary, low quality encoded file used for previewing your cuts, and will not affect the final cut. The final cut will still be lossless. Audio is also removed to make it faster, but only in the preview.', timer: 10000 });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteSourceClick = async () => {
|
||||
// eslint-disable-next-line no-alert
|
||||
if (this.state.working || !window.confirm('Are you sure you want to move the source file to trash?')) return;
|
||||
const { filePath } = this.state;
|
||||
|
||||
@ -352,7 +365,10 @@ class App extends React.Component {
|
||||
}
|
||||
|
||||
cutClick = async () => {
|
||||
if (this.state.working) return alert('I\'m busy');
|
||||
if (this.state.working) {
|
||||
errorToast('I\'m busy');
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
cutStartTime, cutEndTime, filePath, customOutDir, fileFormat, duration, includeAllStreams,
|
||||
@ -362,12 +378,13 @@ class App extends React.Component {
|
||||
const rotation = this.isRotationSet() ? this.getRotation() : undefined;
|
||||
|
||||
if (!this.isCutRangeValid()) {
|
||||
return alert('Start time must be before end time');
|
||||
errorToast('Start time must be before end time');
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ working: true });
|
||||
try {
|
||||
return await ffmpeg.cut({
|
||||
await ffmpeg.cut({
|
||||
customOutDir,
|
||||
filePath,
|
||||
format: fileFormat,
|
||||
@ -386,21 +403,26 @@ class App extends React.Component {
|
||||
console.error('stderr:', err.stderr);
|
||||
|
||||
if (err.code === 1 || err.code === 'ENOENT') {
|
||||
return alert('Whoops! ffmpeg was unable to cut this video. It may be of an unknown format or codec combination');
|
||||
errorToast('Whoops! ffmpeg was unable to cut this video. It may be of an unknown format or codec combination');
|
||||
return;
|
||||
}
|
||||
return ffmpeg.showFfmpegFail(err);
|
||||
showFfmpegFail(err);
|
||||
} finally {
|
||||
this.setState({ working: false });
|
||||
}
|
||||
}
|
||||
|
||||
capture = () => {
|
||||
capture = async () => {
|
||||
const {
|
||||
filePath, customOutDir: outputDir, currentTime, captureFormat,
|
||||
} = this.state;
|
||||
if (!filePath) return;
|
||||
captureFrame(outputDir, filePath, getVideo(), currentTime, captureFormat)
|
||||
.catch(err => alert(err));
|
||||
try {
|
||||
await captureFrame(outputDir, filePath, getVideo(), currentTime, captureFormat);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
errorToast('Failed to capture frame');
|
||||
}
|
||||
}
|
||||
|
||||
changePlaybackRate(dir) {
|
||||
@ -448,7 +470,7 @@ class App extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const time = util.parseDuration(text);
|
||||
const time = parseDuration(text);
|
||||
if (time === undefined) {
|
||||
this.setState({ [cutTimeManualKey]: text });
|
||||
return;
|
||||
@ -470,7 +492,7 @@ class App extends React.Component {
|
||||
onChange={e => handleCutTimeInput(e.target.value)}
|
||||
value={isCutTimeManualSet()
|
||||
? this.state[cutTimeManualKey]
|
||||
: util.formatDuration(cutTime + this.state.startTimeOffset)
|
||||
: formatDuration(cutTime + this.state.startTimeOffset)
|
||||
}
|
||||
/>
|
||||
);
|
||||
@ -539,7 +561,7 @@ class App extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
<div id="current-time-display">{util.formatDuration(this.getOffsetCurrentTime())}</div>
|
||||
<div id="current-time-display">{formatDuration(this.getOffsetCurrentTime())}</div>
|
||||
</div>
|
||||
</Hammer>
|
||||
|
||||
|
22
src/util.js
22
src/util.js
@ -1,6 +1,7 @@
|
||||
const _ = require('lodash');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const swal = require('sweetalert2');
|
||||
|
||||
function formatDuration(_seconds, fileNameFriendly) {
|
||||
const seconds = _seconds || 0;
|
||||
@ -57,10 +58,31 @@ async function transferTimestampsWithOffset(inPath, outPath, offset) {
|
||||
}
|
||||
}
|
||||
|
||||
const toast = swal.mixin({
|
||||
toast: true,
|
||||
position: 'top',
|
||||
showConfirmButton: false,
|
||||
timer: 3000,
|
||||
});
|
||||
|
||||
const errorToast = title => toast({
|
||||
type: 'error',
|
||||
title,
|
||||
});
|
||||
|
||||
async function showFfmpegFail(err) {
|
||||
console.error(err);
|
||||
return errorToast(`Failed to run ffmpeg: ${err.stack}`);
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
formatDuration,
|
||||
parseDuration,
|
||||
getOutPath,
|
||||
transferTimestamps,
|
||||
transferTimestampsWithOffset,
|
||||
toast,
|
||||
errorToast,
|
||||
showFfmpegFail,
|
||||
};
|
||||
|
40
yarn.lock
40
yarn.lock
@ -57,6 +57,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.1.2.tgz#85c5c47af6d244fab77bce6b9bd830e38c978409"
|
||||
integrity sha512-x5HFsW+E/nQalGMw7hu+fvPqnBeBaIr0lWJ2SG0PPL2j+Pm9lYvCrsZJGIgauPIENx0v10INIyFjmSNUD/gSqQ==
|
||||
|
||||
"@babel/runtime@^7.2.0":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.2.0.tgz#b03e42eeddf5898e00646e4c840fa07ba8dcad7f"
|
||||
integrity sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.12.0"
|
||||
|
||||
"@babel/template@^7.1.0":
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.1.2.tgz#090484a574fef5a2d2d7726a674eceda5c5b5644"
|
||||
@ -2733,6 +2740,13 @@ invariant@^2.2.2:
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
invert-kv@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
|
||||
@ -3847,7 +3861,7 @@ prop-types@^15.5.10:
|
||||
loose-envify "^1.3.1"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
prop-types@^15.6.2:
|
||||
prop-types@^15.5.7, prop-types@^15.6.2:
|
||||
version "15.6.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
|
||||
integrity sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==
|
||||
@ -3935,6 +3949,15 @@ react-hammerjs@^0.5.0:
|
||||
dependencies:
|
||||
hammerjs "^2.0.8"
|
||||
|
||||
react-sortable-hoc@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-1.4.0.tgz#b477ce700ba755754200a1dabd36e588e2f5608d"
|
||||
integrity sha512-4++hdwMTrzpOHcqndi2M2gEsqgoGMGmmYzs3wp/xZdap/d8oT2yUR3m6STNi1d1trRyl9Ud0C54agbYH7XdQAQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.2.0"
|
||||
invariant "^2.2.4"
|
||||
prop-types "^15.5.7"
|
||||
|
||||
react@^15.3.2:
|
||||
version "15.6.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"
|
||||
@ -4041,7 +4064,7 @@ readable-stream@^2.0.6, readable-stream@^2.1.4:
|
||||
string_decoder "~1.0.0"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@^2.2.2:
|
||||
readable-stream@^2.1.0, readable-stream@^2.2.2:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
|
||||
@ -4107,6 +4130,11 @@ regenerator-runtime@^0.11.0:
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
||||
|
||||
regenerator-runtime@^0.12.0:
|
||||
version "0.12.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
|
||||
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
|
||||
|
||||
regenerator-transform@^0.10.0:
|
||||
version "0.10.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
|
||||
@ -4489,6 +4517,14 @@ sshpk@^1.7.0:
|
||||
jsbn "~0.1.0"
|
||||
tweetnacl "~0.14.0"
|
||||
|
||||
string-to-stream@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string-to-stream/-/string-to-stream-1.1.1.tgz#aba78f73e70661b130ee3e1c0192be4fef6cb599"
|
||||
integrity sha512-QySF2+3Rwq0SdO3s7BAp4x+c3qsClpPQ6abAmb0DGViiSBAkT5kL6JT2iyzEVP+T1SmzHrQD1TwlP9QAHCc+Sw==
|
||||
dependencies:
|
||||
inherits "^2.0.1"
|
||||
readable-stream "^2.1.0"
|
||||
|
||||
string-width@^1.0.1, string-width@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
|
||||
|
Loading…
Reference in New Issue
Block a user