-
-
Notifications
You must be signed in to change notification settings - Fork 316
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
1,769 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"extends": "airbnb-base", | ||
"env": { | ||
"node": true | ||
}, | ||
"parserOptions": { | ||
"sourceType": "script" | ||
}, | ||
"rules": { | ||
"max-len": 0, | ||
"no-console": 0, | ||
"object-curly-newline": 0, | ||
"no-await-in-loop": 0, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
#!/usr/bin/env node | ||
const meow = require('meow'); | ||
const fs = require('fs'); | ||
const FileType = require('file-type'); | ||
const pMap = require('p-map'); | ||
const JSON5 = require('json5'); | ||
const assert = require('assert'); | ||
|
||
const editly = require('.'); | ||
|
||
|
||
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 config, all other options will be ignored | ||
--transition-name Name of default transition to use | ||
--transition-duration Default transition duration in milliseconds | ||
--width Width which all videos will be converted to | ||
--height Height which all videos 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 | ||
--fast, -f Fast mode (low resolution and FPS, useful for getting a quick preview) | ||
--verbose | ||
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 --json my-editly.json --out output.gif | ||
`, { | ||
flags: { | ||
fast: { type: 'boolean', alias: 'f' }, | ||
transitionDuration: { type: 'number' }, | ||
width: { type: 'number' }, | ||
height: { type: 'number' }, | ||
fps: { type: 'number' }, | ||
}, | ||
}); | ||
|
||
(async () => { | ||
let params = { | ||
defaults: {}, | ||
}; | ||
if (cli.flags.json) { | ||
params = JSON5.parse(fs.readFileSync(cli.flags.json, 'utf-8')); | ||
} else { | ||
const clipsIn = cli.input; | ||
if (clipsIn.length < 1) cli.showHelp(); | ||
|
||
const clips = await pMap(clipsIn, async (clip) => { | ||
const match = clip.match(/^title:(.+)$/); | ||
if (match) return { type: 'title-background', text: match[1] }; | ||
|
||
const { mime } = await FileType.fromFile(clip); | ||
|
||
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, width, height, fps, audioFilePath, fontPath, fast, out: outPath } = cli.flags; | ||
|
||
if (transitionName || transitionDuration) { | ||
params.defaults.transition = { | ||
name: transitionName, | ||
duration: transitionDuration, | ||
}; | ||
} | ||
|
||
if (fontPath) { | ||
params.defaults.layer = { | ||
fontPath, | ||
}; | ||
} | ||
|
||
if (outPath) params.outPath = outPath; | ||
if (audioFilePath) params.audioFilePath = audioFilePath; | ||
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(console.error); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
// TODO make separate npm module | ||
|
||
// https://stackoverflow.com/a/4382138/6519037 | ||
const allColors = [ | ||
'hsl(42, 100%, 50%)', | ||
'hsl(310, 34%, 37%)', | ||
'hsl(24, 100%, 50%)', | ||
'hsl(211, 38%, 74%)', | ||
'hsl(350, 100%, 37%)', | ||
'hsl(35, 52%, 59%)', | ||
'hsl(22, 11%, 45%)', | ||
'hsl(145, 100%, 24%)', | ||
'hsl(348, 87%, 71%)', | ||
'hsl(203, 100%, 27%)', | ||
'hsl(11, 100%, 68%)', | ||
'hsl(265, 37%, 34%)', | ||
'hsl(33, 100%, 50%)', | ||
'hsl(342, 63%, 42%)', | ||
'hsl(49, 100%, 47%)', | ||
'hsl(5, 81%, 27%)', | ||
'hsl(68, 100%, 33%)', | ||
'hsl(26, 61%, 21%)', | ||
'hsl(10, 88%, 51%)', | ||
'hsl(84, 33%, 12%)', | ||
]; | ||
|
||
// https://digitalsynopsis.com/design/beautiful-color-ui-gradients-backgrounds/ | ||
const gradientColors = [ | ||
[ | ||
'#ff9aac', | ||
'#ffa875', | ||
], | ||
[ | ||
'#cc2b5e', | ||
'#753a88', | ||
], | ||
[ | ||
'#42275a', | ||
'#734b6d', | ||
], | ||
[ | ||
'#bdc3c7', | ||
'#2c3e50', | ||
], | ||
[ | ||
'#de6262', | ||
'#ffb88c', | ||
], | ||
[ | ||
'#eb3349', | ||
'#f45c43', | ||
], | ||
[ | ||
'#dd5e89', | ||
'#f7bb97', | ||
], | ||
[ | ||
'#56ab2f', | ||
'#a8e063', | ||
], | ||
[ | ||
'#614385', | ||
'#516395', | ||
], | ||
[ | ||
'#eecda3', | ||
'#ef629f', | ||
], | ||
[ | ||
'#eacda3', | ||
'#d6ae7b', | ||
], | ||
[ | ||
'#02aab0', | ||
'#00cdac', | ||
], | ||
[ | ||
'#d66d75', | ||
'#e29587', | ||
], | ||
[ | ||
'#000428', | ||
'#004e92', | ||
], | ||
[ | ||
'#ddd6f3', | ||
'#faaca8', | ||
], | ||
[ | ||
'#7b4397', | ||
'#dc2430', | ||
], | ||
[ | ||
'#43cea2', | ||
'#185a9d', | ||
], | ||
[ | ||
'#ba5370', | ||
'#f4e2d8', | ||
], | ||
[ | ||
'#ff512f', | ||
'#dd2476', | ||
], | ||
[ | ||
'#4568dc', | ||
'#b06ab3', | ||
], | ||
[ | ||
'#ec6f66', | ||
'#f3a183', | ||
], | ||
[ | ||
'#ffd89b', | ||
'#19547b', | ||
], | ||
[ | ||
'#3a1c71', | ||
'#d76d77', | ||
], | ||
[ | ||
'#4ca1af', | ||
'#c4e0e5', | ||
], | ||
[ | ||
'#ff5f6d', | ||
'#ffc371', | ||
], | ||
[ | ||
'#36d1dc', | ||
'#5b86e5', | ||
], | ||
[ | ||
'#c33764', | ||
'#1d2671', | ||
], | ||
[ | ||
'#141e30', | ||
'#243b55', | ||
], | ||
[ | ||
'#ff7e5f', | ||
'#feb47b', | ||
], | ||
[ | ||
'#ed4264', | ||
'#ffedbc', | ||
], | ||
[ | ||
'#2b5876', | ||
'#4e4376', | ||
], | ||
[ | ||
'#ff9966', | ||
'#ff5e62', | ||
], | ||
[ | ||
'#aa076b', | ||
'#61045f', | ||
], | ||
]; | ||
|
||
/* const lightGradients = [ | ||
[ | ||
'#ee9ca7', | ||
'#ffdde1', | ||
], | ||
[ | ||
'#2193b0', | ||
'#6dd5ed', | ||
], | ||
]; */ | ||
|
||
function getRandomColor(colors = allColors) { | ||
const index = Math.floor(Math.random() * colors.length); | ||
const remainingColors = [...colors]; | ||
remainingColors.splice(index, 1); | ||
return { remainingColors, color: colors[index] || allColors[0] }; | ||
} | ||
|
||
function getRandomColors(num) { | ||
let colors = allColors; | ||
const out = []; | ||
for (let i = 0; i < Math.min(num, allColors.length); i += 1) { | ||
const { remainingColors, color } = getRandomColor(colors); | ||
out.push(color); | ||
colors = remainingColors; | ||
} | ||
return out; | ||
} | ||
|
||
function getRandomGradient() { | ||
return gradientColors[Math.floor(Math.random() * gradientColors.length)]; | ||
} | ||
|
||
module.exports = { | ||
getRandomColors, | ||
getRandomGradient, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
// fast: true, | ||
// width: 2166, height: 1650, fps: 30, | ||
outPath: './out.mp4', | ||
// outPath: './out.gif', | ||
// verbose: true, | ||
// enableFfmpegLog: true, | ||
audioFilePath: './High [NCS Release] - JPB (No Copyright Music)-R8ZRCXy5vhA.m4a', | ||
defaults: { | ||
transition: { name: 'random' }, | ||
layer: { fontPath: './Patua_One/PatuaOne-Regular.ttf' }, | ||
}, | ||
clips: [ | ||
{ duration: 3, transition: { name: 'directional-left' }, layers: [{ type: 'title-background', text: 'EDITLY\nVideo editing framework', background: { type: 'linear-gradient', colors: ['#02aab0', '#00cdac'] } }] }, | ||
{ duration: 4, transition: { name: 'dreamyzoom' }, layers: [{ type: 'title-background', text: 'Multi-line text with animated linear or radial gradients', background: { type: 'radial-gradient' } }] }, | ||
{ duration: 3, transition: { name: 'directional-right' }, layers: [{ type: 'rainbow-colors' }, { type: 'title', text: 'Colorful backgrounds' }] }, | ||
{ duration: 3, layers: [{ type: 'pause' }, { type: 'title', text: 'and separators' }] }, | ||
|
||
{ duration: 3, transition: { name: 'fadegrayscale' }, layers: [{ type: 'title-background', text: 'Image slideshows with Ken Burns effect', background: { type: 'linear-gradient' } }] }, | ||
{ duration: 2.5, transition: { name: 'directionalWarp' }, layers: [{ type: 'image', path: './vertical.jpg', zoomDirection: 'out' }] }, | ||
{ duration: 3, transition: { name: 'dreamyzoom' }, layers: [{ type: 'image', path: './img1.jpg', duration: 2.5, zoomDirection: 'in' }, { type: 'subtitle', text: 'Indonesia has many spectacular locations. Here is the volcano Kelimutu, which has three lakes in its core, some days with three different colors!' }, { type: 'title', position: 'top', text: 'With text' }] }, | ||
{ duration: 3, transition: { name: 'colorphase' }, layers: [{ type: 'image', path: './img2.jpg', zoomDirection: 'out' }, { type: 'subtitle', text: 'Komodo national park is the only home of the endangered Komodo dragons' }] }, | ||
{ duration: 2.5, transition: { name: 'simplezoom' }, layers: [{ type: 'image', path: './img3.jpg', zoomDirection: 'in' }] }, | ||
|
||
{ duration: 1.5, transition: { name: 'crosszoom', duration: 0.3 }, layers: [{ type: 'video', path: '/Users/mifi/Desktop/photos/drone koh lipe/DJI_0402.MOV', cutTo: 58 }, { type: 'title', text: 'Videos' }] }, | ||
{ duration: 3, transition: { name: 'fade' }, layers: [{ type: 'video', path: '/Users/mifi/Desktop/photos/drone koh lipe/DJI_0402.MOV', cutFrom: 58 }] }, | ||
{ transition: { name: 'fade' }, layers: [{ type: 'video', path: '/Users/mifi/Desktop/photos/drone koh lipe/DJI_0403.MOV', cutTo: 2.5 }] }, | ||
{ duration: 1.5, layers: [{ type: 'video', path: '/Users/mifi/Desktop/photos/drone koh lipe/DJI_0401.MOV', cutFrom: 3, cutTo: 30 }] }, | ||
|
||
{ duration: 3, transition: { name: 'crosszoom' }, layers: [{ type: 'gl', fragmentPath: './shaders/3l23Rh.frag' }, { type: 'title', text: 'OpenGL\nshaders' }] }, | ||
{ duration: 3, layers: [{ type: 'gl', fragmentPath: './shaders/MdXyzX.frag' }] }, | ||
{ duration: 3, layers: [{ type: 'gl', fragmentPath: './shaders/30daysofshade_010.frag' }] }, | ||
{ duration: 3, layers: [{ type: 'gl', fragmentPath: './shaders/wd2yDm.frag', speed: 5 }] }, | ||
{ duration: 3, layers: [{ type: 'editly-banner' }] }, | ||
], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
const editly = require('..'); | ||
|
||
async function func({ width, height, canvas }) { | ||
async function onRender(progress) { | ||
const context = canvas.getContext('2d'); | ||
const centerX = canvas.width / 2; | ||
const centerY = canvas.height / 2; | ||
const radius = 40 * (1 + progress * 0.5); | ||
|
||
context.beginPath(); | ||
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false); | ||
context.fillStyle = 'hsl(350, 100%, 37%)'; | ||
context.fill(); | ||
context.lineWidth = 5; | ||
context.strokeStyle = '#ffffff'; | ||
context.stroke(); | ||
} | ||
|
||
function onClose() { | ||
// Cleanup if you initialized anything | ||
} | ||
|
||
return { onRender, onClose }; | ||
} | ||
|
||
editly({ | ||
fast: true, | ||
outPath: './canvas.mp4', | ||
clips: [ | ||
{ duration: 2, layers: [{ type: 'canvas', func }] }, | ||
], | ||
}).catch(console.error); |
Oops, something went wrong.