mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-24 19:32:29 +01:00
Implement merge function
Also upgrade to react 16, sweetalert2 8 and refactor a bit
This commit is contained in:
parent
8817c2c80b
commit
cfaa11028e
13
package.json
13
package.json
@ -60,17 +60,22 @@
|
||||
"execa": "^0.5.0",
|
||||
"file-type": "^4.1.0",
|
||||
"github-api": "^3.0.0",
|
||||
"hammerjs": "^2.0.8",
|
||||
"jquery": "^3.1.1",
|
||||
"lodash": "^4.16.4",
|
||||
"mime-types": "^2.1.14",
|
||||
"moment": "^2.18.1",
|
||||
"mousetrap": "^1.6.1",
|
||||
"react": "^15.3.2",
|
||||
"react-dom": "^15.3.2",
|
||||
"react-hammerjs": "^0.5.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0",
|
||||
"react-hammerjs": "^1.0.1",
|
||||
"react-sortable-hoc": "^1.5.3",
|
||||
"read-chunk": "^2.0.0",
|
||||
"string-to-stream": "^1.1.1",
|
||||
"strong-data-uri": "^1.0.4",
|
||||
"sweetalert2": "^7.28.6",
|
||||
"sweetalert2": "^8.0.1",
|
||||
"sweetalert2-react-content": "^1.0.1",
|
||||
"trash": "^4.3.0",
|
||||
"which": "^1.2.11"
|
||||
}
|
||||
|
36
src/HelpSheet.jsx
Normal file
36
src/HelpSheet.jsx
Normal file
@ -0,0 +1,36 @@
|
||||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
|
||||
/* eslint-disable react/jsx-one-expression-per-line */
|
||||
const HelpSheet = ({ visible }) => {
|
||||
if (visible) {
|
||||
return (
|
||||
<div className="help-sheet">
|
||||
<h1>Keyboard shortcuts</h1>
|
||||
<ul>
|
||||
<li><kbd>H</kbd> Show/hide help</li>
|
||||
<li><kbd>SPACE</kbd>, <kbd>k</kbd> Play/pause</li>
|
||||
<li><kbd>J</kbd> Slow down video</li>
|
||||
<li><kbd>L</kbd> Speed up video</li>
|
||||
<li><kbd>←</kbd> Seek backward 1 sec</li>
|
||||
<li><kbd>→</kbd> Seek forward 1 sec</li>
|
||||
<li><kbd>.</kbd> (period) Tiny seek forward (1/60 sec)</li>
|
||||
<li><kbd>,</kbd> (comma) Tiny seek backward (1/60 sec)</li>
|
||||
<li><kbd>I</kbd> Mark in / cut start point</li>
|
||||
<li><kbd>O</kbd> Mark out / cut end point</li>
|
||||
<li><kbd>E</kbd> Cut (export selection in the same directory)</li>
|
||||
<li><kbd>C</kbd> Capture snapshot (in the same directory)</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
/* eslint-enable react/jsx-one-expression-per-line */
|
||||
|
||||
HelpSheet.propTypes = {
|
||||
visible: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
module.exports = HelpSheet;
|
@ -3,7 +3,7 @@ const fs = require('fs');
|
||||
const mime = require('mime-types');
|
||||
const strongDataUri = require('strong-data-uri');
|
||||
|
||||
const util = require('./util');
|
||||
const { formatDuration, getOutPath, transferTimestampsWithOffset } = require('./util');
|
||||
|
||||
bluebird.promisifyAll(fs);
|
||||
|
||||
@ -23,12 +23,12 @@ async function captureFrame(customOutDir, filePath, video, currentTime, captureF
|
||||
const buf = getFrameFromVideo(video, captureFormat);
|
||||
|
||||
const ext = mime.extension(buf.mimetype);
|
||||
const time = util.formatDuration(currentTime, true);
|
||||
const time = formatDuration(currentTime, true);
|
||||
|
||||
const outPath = util.getOutPath(customOutDir, filePath, `${time}.${ext}`);
|
||||
const outPath = getOutPath(customOutDir, filePath, `${time}.${ext}`);
|
||||
await fs.writeFileAsync(outPath, buf);
|
||||
const offset = -video.duration + currentTime;
|
||||
return util.transferTimestampsWithOffset(filePath, outPath, offset);
|
||||
return transferTimestampsWithOffset(filePath, outPath, offset);
|
||||
}
|
||||
|
||||
module.exports = captureFrame;
|
||||
|
@ -7,8 +7,9 @@ const readChunk = require('read-chunk');
|
||||
const _ = require('lodash');
|
||||
const readline = require('readline');
|
||||
const moment = require('moment');
|
||||
const stringToStream = require('string-to-stream');
|
||||
|
||||
const util = require('./util');
|
||||
const { formatDuration, getOutPath, transferTimestamps } = require('./util');
|
||||
|
||||
function getWithExt(name) {
|
||||
return process.platform === 'win32' ? `${name}.exe` : name;
|
||||
@ -51,9 +52,9 @@ async function cut({
|
||||
includeAllStreams, onProgress, stripAudio, keyframeCut,
|
||||
}) {
|
||||
const ext = path.extname(filePath) || `.${format}`;
|
||||
const cutSpecification = `${util.formatDuration(cutFrom, true)}-${util.formatDuration(cutToApparent, true)}`;
|
||||
const cutSpecification = `${formatDuration(cutFrom, true)}-${formatDuration(cutToApparent, true)}`;
|
||||
|
||||
const outPath = util.getOutPath(customOutDir, filePath, `${cutSpecification}${ext}`);
|
||||
const outPath = getOutPath(customOutDir, filePath, `${cutSpecification}${ext}`);
|
||||
|
||||
console.log('Cutting from', cutFrom, 'to', cutToApparent);
|
||||
|
||||
@ -101,7 +102,7 @@ async function cut({
|
||||
const result = await process;
|
||||
console.log(result.stdout);
|
||||
|
||||
await util.transferTimestamps(filePath, outPath);
|
||||
await transferTimestamps(filePath, outPath);
|
||||
}
|
||||
|
||||
async function html5ify(filePath, outPath, encodeVideo) {
|
||||
@ -123,7 +124,37 @@ async function html5ify(filePath, outPath, encodeVideo) {
|
||||
const result = await process;
|
||||
console.log(result.stdout);
|
||||
|
||||
await util.transferTimestamps(filePath, outPath);
|
||||
await transferTimestamps(filePath, outPath);
|
||||
}
|
||||
|
||||
async function mergeFiles(paths) {
|
||||
const firstPath = paths[0];
|
||||
const ext = path.extname(firstPath);
|
||||
const outPath = `${firstPath}-merged.${ext}`;
|
||||
console.log('Merging files', { paths }, 'to', outPath);
|
||||
|
||||
// https://blog.yo1.dog/fix-for-ffmpeg-protocol-not-on-whitelist-error-for-urls/
|
||||
const ffmpegArgs = [
|
||||
'-f', 'concat', '-safe', '0', '-protocol_whitelist', 'file,pipe', '-i', '-',
|
||||
'-c', 'copy',
|
||||
'-map_metadata', '0',
|
||||
'-y', outPath,
|
||||
];
|
||||
|
||||
console.log('ffmpeg', ffmpegArgs.join(' '));
|
||||
|
||||
// https://superuser.com/questions/787064/filename-quoting-in-ffmpeg-concat
|
||||
const concatTxt = paths.map(file => `file '${path.join(file).replace(/'/g, "'\\''")}'`).join('\n');
|
||||
|
||||
console.log(concatTxt);
|
||||
|
||||
const ffmpegPath = await getFfmpegPath();
|
||||
const process = execa(ffmpegPath, ffmpegArgs);
|
||||
|
||||
stringToStream(concatTxt).pipe(process.stdin);
|
||||
|
||||
const result = await process;
|
||||
console.log(result.stdout);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,4 +210,5 @@ module.exports = {
|
||||
cut,
|
||||
getFormat,
|
||||
html5ify,
|
||||
mergeFiles,
|
||||
};
|
||||
|
@ -151,3 +151,7 @@ input, button, textarea, :focus {
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 #bbb;
|
||||
}
|
||||
|
||||
.dragging-helper-class {
|
||||
color: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
11
src/menu.js
11
src/menu.js
@ -54,6 +54,17 @@ module.exports = (app, mainWindow, newVersion) => {
|
||||
const helpIndex = menu.findIndex(item => item.role === 'help');
|
||||
if (helpIndex >= 0) {
|
||||
menu.splice(helpIndex, 1, {
|
||||
label: 'Tools',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Merge files',
|
||||
click() {
|
||||
mainWindow.webContents.send('show-merge-dialog', true);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
|
71
src/merge/SortableFiles.jsx
Normal file
71
src/merge/SortableFiles.jsx
Normal file
@ -0,0 +1,71 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
sortableContainer,
|
||||
sortableElement,
|
||||
arrayMove,
|
||||
} from 'react-sortable-hoc';
|
||||
import { basename } from 'path';
|
||||
|
||||
const rowStyle = {
|
||||
padding: 5, fontSize: 14, margin: '7px 0', boxShadow: '0 0 5px 0px rgba(0,0,0,0.3)', overflowY: 'auto', whiteSpace: 'nowrap',
|
||||
};
|
||||
|
||||
const SortableItem = sortableElement(({ value, sortIndex }) => (
|
||||
<div style={rowStyle} title={value}>
|
||||
{sortIndex + 1}
|
||||
{'. '}
|
||||
{basename(value)}
|
||||
</div>));
|
||||
|
||||
const SortableContainer = sortableContainer(({ items }) => (
|
||||
<div>
|
||||
{items.map((value, index) => (
|
||||
<SortableItem key={value} index={index} sortIndex={index} value={value} />
|
||||
))}
|
||||
</div>
|
||||
));
|
||||
|
||||
class SortableFiles extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
items: props.items,
|
||||
};
|
||||
}
|
||||
|
||||
onSortEnd = ({ oldIndex, newIndex }) => {
|
||||
const { items } = this.state;
|
||||
const { onChange } = this.props;
|
||||
const newItems = arrayMove(items, oldIndex, newIndex);
|
||||
this.setState({ items: newItems });
|
||||
onChange(newItems);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { helperContainer } = this.props;
|
||||
const { items } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div><b>Sort your files for merge</b></div>
|
||||
<SortableContainer
|
||||
items={items}
|
||||
onSortEnd={this.onSortEnd}
|
||||
helperContainer={helperContainer}
|
||||
getContainer={() => helperContainer().parentNode}
|
||||
helperClass="dragging-helper-class"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SortableFiles.propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
helperContainer: PropTypes.func.isRequired,
|
||||
items: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
export default SortableFiles;
|
49
src/merge/merge.jsx
Normal file
49
src/merge/merge.jsx
Normal file
@ -0,0 +1,49 @@
|
||||
const React = require('react');
|
||||
const swal = require('sweetalert2');
|
||||
const withReactContent = require('sweetalert2-react-content');
|
||||
|
||||
const SortableFiles = require('./SortableFiles').default;
|
||||
|
||||
const { errorToast } = require('../util');
|
||||
|
||||
const MySwal = withReactContent(swal);
|
||||
|
||||
|
||||
function showMergeDialog({ dialog, defaultPath, onMergeClick }) {
|
||||
const title = 'Please select files to be merged';
|
||||
const message = 'Please select files to be merged. The files need to be of the exact same format and codecs';
|
||||
dialog.showOpenDialog({
|
||||
title,
|
||||
defaultPath,
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
message,
|
||||
}, async (paths) => {
|
||||
if (!paths) return;
|
||||
if (paths.length < 2) {
|
||||
errorToast('More than one file must be selected');
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
let swalElem;
|
||||
let outPaths = paths;
|
||||
const { dismiss } = await MySwal.fire({
|
||||
width: '90%',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Merge!',
|
||||
onBeforeOpen: (el) => { swalElem = el; },
|
||||
html: (<SortableFiles
|
||||
items={outPaths}
|
||||
onChange={(val) => { outPaths = val; }}
|
||||
helperContainer={() => swalElem}
|
||||
/>),
|
||||
});
|
||||
|
||||
if (!dismiss) {
|
||||
onMergeClick(outPaths);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { showMergeDialog };
|
@ -4,29 +4,28 @@ const Mousetrap = require('mousetrap');
|
||||
const round = require('lodash/round');
|
||||
const clamp = require('lodash/clamp');
|
||||
const throttle = require('lodash/throttle');
|
||||
const Hammer = require('react-hammerjs');
|
||||
const Hammer = require('react-hammerjs').default;
|
||||
const path = require('path');
|
||||
const trash = require('trash');
|
||||
const swal = require('sweetalert2');
|
||||
|
||||
const React = require('react');
|
||||
const ReactDOM = require('react-dom');
|
||||
const classnames = require('classnames');
|
||||
|
||||
const HelpSheet = require('./HelpSheet');
|
||||
const { showMergeDialog } = require('./merge/merge');
|
||||
|
||||
const captureFrame = require('./capture-frame');
|
||||
const ffmpeg = require('./ffmpeg');
|
||||
|
||||
|
||||
const {
|
||||
getOutPath, parseDuration, formatDuration, toast, errorToast, showFfmpegFail,
|
||||
getOutPath, parseDuration, formatDuration, toast, errorToast, showFfmpegFail, setFileNameTitle,
|
||||
promptTimeOffset,
|
||||
} = require('./util');
|
||||
|
||||
const { dialog } = electron.remote;
|
||||
|
||||
function setFileNameTitle(filePath) {
|
||||
const appName = 'LosslessCut';
|
||||
document.title = filePath ? `${appName} - ${path.basename(filePath)}` : 'appName';
|
||||
}
|
||||
|
||||
function getVideo() {
|
||||
return $('#player video')[0];
|
||||
}
|
||||
@ -54,35 +53,6 @@ function shortStep(dir) {
|
||||
seekRel((1 / 60) * dir);
|
||||
}
|
||||
|
||||
/* eslint-disable react/jsx-one-expression-per-line */
|
||||
function renderHelpSheet(visible) {
|
||||
if (visible) {
|
||||
return (
|
||||
<div className="help-sheet">
|
||||
<h1>Keyboard shortcuts</h1>
|
||||
<ul>
|
||||
<li><kbd>H</kbd> Show/hide help</li>
|
||||
<li><kbd>SPACE</kbd>, <kbd>k</kbd> Play/pause</li>
|
||||
<li><kbd>J</kbd> Slow down video</li>
|
||||
<li><kbd>L</kbd> Speed up video</li>
|
||||
<li><kbd>←</kbd> Seek backward 1 sec</li>
|
||||
<li><kbd>→</kbd> Seek forward 1 sec</li>
|
||||
<li><kbd>.</kbd> (period) Tiny seek forward (1/60 sec)</li>
|
||||
<li><kbd>,</kbd> (comma) Tiny seek backward (1/60 sec)</li>
|
||||
<li><kbd>I</kbd> Mark in / cut start point</li>
|
||||
<li><kbd>O</kbd> Mark out / cut end point</li>
|
||||
<li><kbd>E</kbd> Cut (export selection in the same directory)</li>
|
||||
<li><kbd>C</kbd> Capture snapshot (in the same directory)</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
/* eslint-enable react/jsx-one-expression-per-line */
|
||||
|
||||
|
||||
function withBlur(cb) {
|
||||
return (e) => {
|
||||
e.target.blur();
|
||||
@ -144,13 +114,13 @@ class App extends React.Component {
|
||||
errorToast('Unsupported file');
|
||||
return;
|
||||
}
|
||||
setFileNameTitle(filePath);
|
||||
setFileNameTitle(filePath);
|
||||
this.setState({ filePath, html5FriendlyPath, fileFormat });
|
||||
} catch (err) {
|
||||
if (err.code === 1 || err.code === 'ENOENT') {
|
||||
if (err.code === 1 || err.code === 'ENOENT') {
|
||||
errorToast('Unsupported file');
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
showFfmpegFail(err);
|
||||
} finally {
|
||||
this.setState({ working: false });
|
||||
@ -179,26 +149,24 @@ class App extends React.Component {
|
||||
}
|
||||
});
|
||||
|
||||
async function promptTimeOffset(inputValue) {
|
||||
const { value } = await swal({
|
||||
title: 'Set custom start time offset',
|
||||
text: 'Instead of video apparently starting at 0, you can offset by a specified value (useful for timecodes)',
|
||||
input: 'text',
|
||||
inputValue: inputValue || '',
|
||||
showCancelButton: true,
|
||||
inputPlaceholder: '00:00:00.000',
|
||||
});
|
||||
electron.ipcRenderer.on('show-merge-dialog', () => showMergeDialog({
|
||||
dialog,
|
||||
defaultPath: this.getOutputDir(),
|
||||
onMergeClick: async (paths) => {
|
||||
try {
|
||||
this.setState({ working: true });
|
||||
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const duration = parseDuration(value);
|
||||
// Invalid, try again
|
||||
if (duration === undefined) return promptTimeOffset(value);
|
||||
|
||||
return duration;
|
||||
}
|
||||
// TODO customOutDir ?
|
||||
// console.log('merge', paths);
|
||||
await ffmpeg.mergeFiles(paths);
|
||||
} catch (err) {
|
||||
errorToast('Failed to merge files. Make sure they are all of the exact same format and codecs');
|
||||
console.error('Failed to merge files', err);
|
||||
} finally {
|
||||
this.setState({ working: false });
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
electron.ipcRenderer.on('set-start-offset', async () => {
|
||||
const { startTimeOffset: startTimeOffsetOld } = this.state;
|
||||
@ -303,7 +271,6 @@ class App extends React.Component {
|
||||
return (this.state.currentTime || 0) + this.state.startTimeOffset;
|
||||
}
|
||||
|
||||
|
||||
increaseRotation = () => {
|
||||
this.setState(({ rotation }) => ({ rotation: (rotation + 90) % 450 }));
|
||||
}
|
||||
@ -349,7 +316,7 @@ class App extends React.Component {
|
||||
return video.play().catch((err) => {
|
||||
console.log(err);
|
||||
if (err.name === 'NotSupportedError') {
|
||||
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 });
|
||||
toast.fire({ 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 });
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -717,7 +684,7 @@ class App extends React.Component {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{renderHelpSheet(this.state.helpVisible)}
|
||||
<HelpSheet visible={!!this.state.helpVisible} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
29
src/util.js
29
src/util.js
@ -65,7 +65,7 @@ const toast = swal.mixin({
|
||||
timer: 3000,
|
||||
});
|
||||
|
||||
const errorToast = title => toast({
|
||||
const errorToast = title => toast.fire({
|
||||
type: 'error',
|
||||
title,
|
||||
});
|
||||
@ -75,6 +75,31 @@ async function showFfmpegFail(err) {
|
||||
return errorToast(`Failed to run ffmpeg: ${err.stack}`);
|
||||
}
|
||||
|
||||
function setFileNameTitle(filePath) {
|
||||
const appName = 'LosslessCut';
|
||||
document.title = filePath ? `${appName} - ${path.basename(filePath)}` : 'appName';
|
||||
}
|
||||
|
||||
async function promptTimeOffset(inputValue) {
|
||||
const { value } = await swal.fire({
|
||||
title: 'Set custom start time offset',
|
||||
text: 'Instead of video apparently starting at 0, you can offset by a specified value (useful for timecodes)',
|
||||
input: 'text',
|
||||
inputValue: inputValue || '',
|
||||
showCancelButton: true,
|
||||
inputPlaceholder: '00:00:00.000',
|
||||
});
|
||||
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const duration = parseDuration(value);
|
||||
// Invalid, try again
|
||||
if (duration === undefined) return promptTimeOffset(value);
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
formatDuration,
|
||||
@ -85,4 +110,6 @@ module.exports = {
|
||||
toast,
|
||||
errorToast,
|
||||
showFfmpegFail,
|
||||
setFileNameTitle,
|
||||
promptTimeOffset,
|
||||
};
|
||||
|
157
yarn.lock
157
yarn.lock
@ -292,11 +292,6 @@ arrify@^1.0.0, arrify@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
||||
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
|
||||
|
||||
asap@~2.0.3:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||
|
||||
asar@^0.13.0:
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/asar/-/asar-0.13.1.tgz#dfc73f574a7db256b09ba62d1f0e95cd4a6cb8d3"
|
||||
@ -1357,11 +1352,6 @@ convert-source-map@^1.5.0:
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
|
||||
integrity sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=
|
||||
|
||||
core-js@^1.0.0:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||
integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
|
||||
|
||||
core-js@^2.4.0, core-js@^2.5.0:
|
||||
version "2.5.3"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
|
||||
@ -1372,15 +1362,6 @@ core-util-is@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
|
||||
create-react-class@^15.6.0:
|
||||
version "15.6.3"
|
||||
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036"
|
||||
integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==
|
||||
dependencies:
|
||||
fbjs "^0.8.9"
|
||||
loose-envify "^1.3.1"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
cross-spawn-async@^2.1.1:
|
||||
version "2.2.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc"
|
||||
@ -1683,13 +1664,6 @@ emoji-regex@^6.5.1:
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2"
|
||||
integrity sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==
|
||||
|
||||
encoding@^0.1.11:
|
||||
version "0.1.12"
|
||||
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
|
||||
integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=
|
||||
dependencies:
|
||||
iconv-lite "~0.4.13"
|
||||
|
||||
env-paths@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0"
|
||||
@ -2042,19 +2016,6 @@ fast-levenshtein@~2.0.4:
|
||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
||||
|
||||
fbjs@^0.8.16, fbjs@^0.8.9:
|
||||
version "0.8.16"
|
||||
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
|
||||
integrity sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=
|
||||
dependencies:
|
||||
core-js "^1.0.0"
|
||||
isomorphic-fetch "^2.1.1"
|
||||
loose-envify "^1.0.0"
|
||||
object-assign "^4.1.0"
|
||||
promise "^7.1.1"
|
||||
setimmediate "^1.0.5"
|
||||
ua-parser-js "^0.7.9"
|
||||
|
||||
fd-slicer@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
|
||||
@ -2655,11 +2616,6 @@ iconv-lite@^0.4.24:
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
iconv-lite@~0.4.13:
|
||||
version "0.4.19"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
|
||||
integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==
|
||||
|
||||
ignore@^3.3.5:
|
||||
version "3.3.7"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021"
|
||||
@ -2946,14 +2902,6 @@ isobject@^2.0.0:
|
||||
dependencies:
|
||||
isarray "1.0.0"
|
||||
|
||||
isomorphic-fetch@^2.1.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
|
||||
integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
|
||||
dependencies:
|
||||
node-fetch "^1.0.1"
|
||||
whatwg-fetch ">=0.10.0"
|
||||
|
||||
isstream@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
@ -3383,14 +3331,6 @@ nice-try@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
node-fetch@^1.0.1:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
|
||||
integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
|
||||
dependencies:
|
||||
encoding "^0.1.11"
|
||||
is-stream "^1.0.1"
|
||||
|
||||
node-pre-gyp@^0.6.39:
|
||||
version "0.6.39"
|
||||
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649"
|
||||
@ -3845,22 +3785,6 @@ progress@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
|
||||
integrity sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=
|
||||
|
||||
promise@^7.1.1:
|
||||
version "7.3.1"
|
||||
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
|
||||
integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
|
||||
dependencies:
|
||||
asap "~2.0.3"
|
||||
|
||||
prop-types@^15.5.10:
|
||||
version "15.6.0"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
|
||||
integrity sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=
|
||||
dependencies:
|
||||
fbjs "^0.8.16"
|
||||
loose-envify "^1.3.1"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
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"
|
||||
@ -3932,42 +3856,41 @@ rcedit@^0.9.0:
|
||||
resolved "https://registry.yarnpkg.com/rcedit/-/rcedit-0.9.0.tgz#3910df57345399e2b0325f4a519007f89e55ef1c"
|
||||
integrity sha1-ORDfVzRTmeKwMl9KUZAH+J5V7xw=
|
||||
|
||||
react-dom@^15.3.2:
|
||||
version "15.6.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.2.tgz#41cfadf693b757faf2708443a1d1fd5a02bef730"
|
||||
integrity sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=
|
||||
react-dom@^16.7.0:
|
||||
version "16.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0.tgz#a17b2a7ca89ee7390bc1ed5eb81783c7461748b8"
|
||||
integrity sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg==
|
||||
dependencies:
|
||||
fbjs "^0.8.9"
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.0"
|
||||
prop-types "^15.5.10"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.12.0"
|
||||
|
||||
react-hammerjs@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/react-hammerjs/-/react-hammerjs-0.5.0.tgz#aa20e5c5f44d660f3e8e87ed11282f12173e77ae"
|
||||
integrity sha1-qiDlxfRNZg8+joftESgvEhc+d64=
|
||||
react-hammerjs@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-hammerjs/-/react-hammerjs-1.0.1.tgz#bc1ed9e9ef7da057163fb169ce12917b6d6ca7d8"
|
||||
integrity sha1-vB7Z6e99oFcWP7FpzhKRe21sp9g=
|
||||
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==
|
||||
react-sortable-hoc@^1.5.3:
|
||||
version "1.5.3"
|
||||
resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-1.5.3.tgz#99482ee6435e898cae3cd4632958bb9c7cc5a948"
|
||||
integrity sha512-suRfXqq3KRzhpHsUoc+srHBp5o7cwx7ZXSNH/PKtKOtKBw18JgXNQ7QbIMTCcH3mdog3espf+A9hybTixNfe3Q==
|
||||
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"
|
||||
integrity sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=
|
||||
react@^16.7.0:
|
||||
version "16.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.7.0.tgz#b674ec396b0a5715873b350446f7ea0802ab6381"
|
||||
integrity sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A==
|
||||
dependencies:
|
||||
create-react-class "^15.6.0"
|
||||
fbjs "^0.8.9"
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.0"
|
||||
prop-types "^15.5.10"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.12.0"
|
||||
|
||||
read-chunk@^2.0.0:
|
||||
version "2.1.0"
|
||||
@ -4377,6 +4300,14 @@ sanitize-filename@^1.6.0:
|
||||
dependencies:
|
||||
truncate-utf8-bytes "^1.0.0"
|
||||
|
||||
scheduler@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0.tgz#8ab17699939c0aedc5a196a657743c496538647b"
|
||||
integrity sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
||||
@ -4397,11 +4328,6 @@ set-immediate-shim@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
|
||||
integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=
|
||||
|
||||
setimmediate@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
|
||||
|
||||
shebang-command@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||
@ -4667,10 +4593,15 @@ svg2png@4.1.1:
|
||||
pn "^1.0.0"
|
||||
yargs "^6.5.0"
|
||||
|
||||
sweetalert2@^7.28.6:
|
||||
version "7.28.6"
|
||||
resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-7.28.6.tgz#6e20519eaf007dc3703c890c8db3797b6b08bc17"
|
||||
integrity sha512-rkMfmOSkg7zWTCg/YOg1VuJDSf2fpniiYhYpBc0MNrOzJnx24bfDOCTj35RulgyDtKrwQIEVR2gVaG3eOnpCXQ==
|
||||
sweetalert2-react-content@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sweetalert2-react-content/-/sweetalert2-react-content-1.0.1.tgz#0403145a1af819504e394df0dfc6194df67068a0"
|
||||
integrity sha512-wZxDGbF24jzNXGsr3hjkSa5wQlhgq4wOPPZShe4RMujGdDuPtniodQGZOW2Tn38dzpgXSZ4/sb7++zxwueberw==
|
||||
|
||||
sweetalert2@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-8.0.1.tgz#d59fa124a56595c1e799dd4c635689afa0a5f811"
|
||||
integrity sha512-N88+meCLt1t/5dQEAvBs31j6qcEVucE/bwMj8JiuPsMRvXhnWKMbvLAFPGNO7a/LECRVjT6o0/mccmIVZqQZSQ==
|
||||
|
||||
table@^4.0.3:
|
||||
version "4.0.3"
|
||||
@ -4867,11 +4798,6 @@ typedarray@^0.0.6:
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
ua-parser-js@^0.7.9:
|
||||
version "0.7.17"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
|
||||
integrity sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==
|
||||
|
||||
uid-number@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
|
||||
@ -4953,11 +4879,6 @@ verror@1.3.6:
|
||||
dependencies:
|
||||
extsprintf "1.0.2"
|
||||
|
||||
whatwg-fetch@>=0.10.0:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
|
||||
integrity sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=
|
||||
|
||||
which-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
|
||||
|
Loading…
Reference in New Issue
Block a user