-
-
Notifications
You must be signed in to change notification settings - Fork 311
/
cli.js
144 lines (117 loc) · 4.8 KB
/
cli.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/env node
import meow from 'meow';
import { readFileSync } from 'fs';
import { fileTypeFromFile } from 'file-type';
import pMap from 'p-map';
import JSON5 from 'json5';
import assert from 'assert';
import Editly from './index.js';
// See also readme
const cli = meow(`
Usage
$ editly CLIP1 [CLIP2 [CLIP3 ...]]
where each CLIP can be one of the following:
- A path to a video file
- A path to an image
- A quoted text to show in a title screen, prefixed by "title:"
Or alternatively:
$ editly --json JSON_PATH
where JSON_PATH is the path to an edit spec JSON file, can be a normal JSON or JSON5
Options
--out Out video path (defaults to ./editly-out.mp4) - can also be a .gif
--json Use JSON edit spec
--transition-name Name of default transition to use (default: random)
--transition-duration Default transition duration
--clip-duration Default clip duration
--width Width which all media will be converted to
--height Height which all media will be converted to
--fps FPS which all videos will be converted to
--font-path Set default font to a .ttf
--audio-file-path Add an audio track
--loop-audio Loop the audio track if it is shorter than video?
--keep-source-audio Keep audio from source files
--output-volume Adjust audio output volume
--allow-remote-requests
--fast, -f Fast mode (low resolution and FPS, useful for getting a quick preview)
--verbose, -v
For more detailed explanation, see:
https://github.com/mifi/editly
Examples
$ editly title:'My video' clip1.mov clip2.mov title:'My slideshow' img1.jpg img2.jpg title:'THE END' --audio-file-path /path/to/music.mp3 --font-path /path/to/my-favorite-font.ttf
$ editly my-editly.json5 --out output.gif
`, {
importMeta: import.meta,
flags: {
verbose: { type: 'boolean', alias: 'v' },
keepSourceAudio: { type: 'boolean' },
allowRemoteRequests: { type: 'boolean' },
fast: { type: 'boolean', alias: 'f' },
transitionDuration: { type: 'number' },
clipDuration: { type: 'number' },
width: { type: 'number' },
height: { type: 'number' },
fps: { type: 'number' },
loopAudio: { type: 'boolean' },
outputVolume: { type: 'string' },
},
});
(async () => {
let { json } = cli.flags;
// eslint-disable-next-line prefer-destructuring
if (cli.input.length === 1 && /\.(json|json5|js)$/.test(cli.input[0].toLowerCase())) json = cli.input[0];
let params = {
defaults: {},
};
if (json) {
params = JSON5.parse(readFileSync(json, 'utf-8'));
} else {
const clipsIn = cli.input;
if (clipsIn.length < 1) cli.showHelp();
const clips = await pMap(clipsIn, async (clip) => {
let match = clip.match(/^title:(.+)$/);
if (match) return { type: 'title-background', text: match[1] };
match = clip.match(/^https?:\/\/.*\.(jpg|jpeg|png|webp|gif|svg)$/); // todo improve
if (match) return { type: 'image', path: clip };
const fileType = await fileTypeFromFile(clip);
if (!fileType) {
console.error('Invalid file for clip', clip);
cli.showHelp();
}
const { mime } = fileType;
if (mime.startsWith('video')) return { type: 'video', path: clip };
if (mime.startsWith('image')) return { type: 'image', path: clip };
throw new Error(`Unrecognized clip or file type "${clip}"`);
}, { concurrency: 1 });
assert(clips.length > 0, 'No clips specified');
params.clips = clips.map((clip) => ({ layers: [clip] }));
}
const { verbose, transitionName, transitionDuration, clipDuration, width, height, fps, audioFilePath, fontPath, fast, out: outPath, keepSourceAudio, loopAudio, outputVolume, allowRemoteRequests } = cli.flags;
if (transitionName || transitionDuration != null) {
params.defaults.transition = {};
if (transitionName) params.defaults.transition.name = transitionName;
if (transitionDuration) params.defaults.transition.duration = transitionDuration;
}
if (clipDuration) params.defaults.duration = clipDuration;
if (fontPath) {
params.defaults.layer = {
fontPath,
};
}
if (outPath) params.outPath = outPath;
if (audioFilePath) params.audioFilePath = audioFilePath;
if (loopAudio) params.loopAudio = loopAudio;
if (outputVolume) params.outputVolume = outputVolume;
if (keepSourceAudio) params.keepSourceAudio = true;
if (allowRemoteRequests) params.allowRemoteRequests = true;
if (width) params.width = width;
if (height) params.height = height;
if (fps) params.fps = fps;
if (fast) params.fast = fast;
if (verbose) params.verbose = verbose;
if (params.verbose) console.log(JSON5.stringify(params, null, 2));
if (!params.outPath) params.outPath = './editly-out.mp4';
await Editly(params);
})().catch((err) => {
console.error('Caught error', err);
process.exitCode = 1;
});