Skip to content

Commit

Permalink
feat(h2non#8): add CLI support. several refactors and improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
h2non committed Mar 12, 2015
1 parent 01a4459 commit d4a2fcd
Show file tree
Hide file tree
Showing 14 changed files with 372 additions and 45 deletions.
118 changes: 111 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

<img src="https://github.com/h2non/videoshow/blob/master/test/fixtures/norris.gif" width="180" align="right" />

Simple programmatic interface for node/io.js to **create straightforward slide show videos based from a series of images** using [ffmpeg](http://ffmpeg.org)
Simple programmatic interface for node/io.js to **create straightforward slideshow videos based on images** using [ffmpeg](http://ffmpeg.org)

You can easily **create videos** from **images** with optional **audio**, **subtitles** and **fade-in/out effects**.
Take a look to [examples](https://github.com/h2non/videoshow/tree/master/examples) to see some supported features

Still beta
You can easily **create videos** with optional **audio**, **subtitles** and **fade in/out transitions**.
Take a look to [examples](https://github.com/h2non/videoshow/tree/master/examples), [programmatic API](#api) and [command-line](#command-line-interface)

## Requirements

Expand All @@ -19,6 +17,11 @@ Still beta
npm install videoshow
```

For command-line usage, install it as global package:
```bash
npm install -g videoshow
```

## Usage

```js
Expand Down Expand Up @@ -47,6 +50,56 @@ videoshow(images, videoOptions)
.save('video.mp4')
```

## Command-line interface

```bash
$ videoshow --help
```

```bash
Create video slides easily from images
Usage: bin/videoshow [options]

Options:
--help, -h Show help
--config, -c File path to JSON config file [required]
--audio, -a Optional audio file path
--input, -i Add additional input to video
--output, -o Output video file path
--debug, -d Enable debug mode in error case

Examples:
bin/videoshow -c config.json --audio song.mp3
bin/videoshow -c config.json --audio song.mp3 video.mp4
```
Example `config.json` file:
```json
{
"output": "video.mp4",
"options": {
"fps": 25,
"loop": 5,
"transition": true,
"transitionDuration": 1,
"videoBitrate": 1024,
"videoCodec": "libx264",
"size": "640x?",
"audioBitrate": "128k",
"audioChannels": 2,
"format": "mp4"
},
"images": [
"./test/fixtures/step_1.png",
"./test/fixtures/step_2.png",
"./test/fixtures/step_3.png",
"./test/fixtures/step_4.png",
"./test/fixtures/step_5.png"
]
}
```
## API
### videoshow(images, [ options ])
Expand Down Expand Up @@ -79,20 +132,69 @@ videoshow([{
.on('end', function () {})
```
#### Video options
You can define any of the methods allowed by [fluent-ffmpeg][ffmpeg-api]
Default options are:
```js
{
fps: 25,
loop: 5, // seconds
transition: true,
transitionDuration: 1,
videoBitrate: 1024,
videoCodec: 'libx264',
size: '640x?',
audioBitrate: '128k',
audioChannels: 2,
format: 'mp4'
}
```
#### Image options
- **path** `string` - File path to image
- **loop** `number` - Image slide duration in **seconds**. Default to `5`
- **caption** `string` - Caption text as subtitle. It allows a limited set of HTML tags. See [Subrip][subrip]
- **transition** `boolean` - Enable fade in/out transition for the current image
- **transitionDuration** `number` - Fade in/out transition duration in **seconds**. Default to `1`
- **captionStart** `number` - Miliseconds to start the caption. Default to `1000`
- **captionEnd** `number` - Miliseconds to remove the caption. Default to `loop - 1000`
#### videoshow#image(image)
Push an image to the video. You can pass an `string` as path to the image,
or a plain `object` with [image options](#supported-image-options)
#### videoshow#audio(path)
Define the audio file path to use. It supports multiple formats and codecs such as `acc`, `mp3` or `ogg`
#### videoshow#subtitles(path)
Define the [SubRip subtitles](http://en.wikipedia.org/wiki/SubRip#SubRip_text_file_format)
Define the [SubRip subtitles][subrip]
file path to load. It should be a `.srt` file
If you want to use [SubStation Alpha](http://en.wikipedia.org/wiki/SubStation_Alpha) (SSA/ASS) subtitles,
you should pass it as video input:
```js
videoshow(images)
.input('subtitles.ass')
.on('end', end)
.save('video.mp4')
```
#### videoshow#save(path)
Return: `EventEmitter`
Return: `EventEmitter` Alias: `render`
Render and write the final video in the given path
#### videoshow#input(input)
Add input file to video
#### videoshow#filter(filter)
Add custom filter to the video
Expand Down Expand Up @@ -126,3 +228,5 @@ Type: `function`
[travis]: http://travis-ci.org/h2non/videoshow
[gemnasium]: https://gemnasium.com/h2non/videoshow
[npm]: http://npmjs.org/package/videoshow
[subrip]: http://en.wikipedia.org/wiki/SubRip#SubRip_text_file_format
[ffmpeg-api]: https://github.com/fluent-ffmpeg/node-fluent-ffmpeg#creating-an-ffmpeg-command
88 changes: 88 additions & 0 deletions bin/videoshow
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env node

var fs = require('fs')
var videoshow = require('../')
var version = require('../package.json').version

var yargs = require('yargs')
var argv = yargs
.usage('Create video slides easily from images with audio, subtitles and transitions\nUsage: $0 [options]')
.example('$0 -c config.json --audio song.mp3', '')
.help('help').alias('help', 'h')
.version(version)
.alias('v', 'version')
.options({
config: {
alias: 'c',
description: 'File path to JSON config file',
required: true
},
audio: {
alias: 'a',
description: 'Optional audio file path'
},
input: {
alias: 'i',
description: 'Add additional input to video'
},
output: {
alias: 'o',
description: 'Output video file path'
},
debug: {
alias: 'd',
description: 'Enable debug mode in error case'
}
})
.argv

try {
render(options())
} catch (e) {
error(e)
}

function render(opts) {
var output = opts.output || argv.output || getOutput()
var audio = opts.audio || argv.audio

var video = videoshow(opts.images, opts.params)
if (audio) video.audio(audio)

video
.save(output)
.on('error', error)
.on('end', function () {
echo('Video created:', output)
})
}

function getOutput() {
var output = argv._.pop()
if (output && output[0] !== '-') {
return output
}
return 'video.mp4'
}

function options() {
var opts = {}
var config = JSON.parse(fs.readFileSync(argv.config))
if (config && config.options) {
opts = config
}
return opts
}

function error(msg) {
if (argv.debug && msg.stack) {
console.error(msg.stack)
} else {
console.error(msg.message || msg)
}
process.exitCode = 1
}

function echo() {
console.log.apply(console, arguments)
}
2 changes: 1 addition & 1 deletion examples/audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var images = [

videoshow(images)
.audio(audio)
.save('audio.mp4')
.save('video.mp4')
.on('error', function (err) {
console.error('Error:', err)
})
Expand Down
2 changes: 1 addition & 1 deletion examples/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var images = [
]

videoshow(images)
.save('test.mp4')
.save('video.mp4')
.on('error', function (err) {
console.error('Error:', err)
})
Expand Down
37 changes: 37 additions & 0 deletions examples/captions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
var videoshow = require('../')

var audio = __dirname + '/../test/fixtures/song.mp3'

var options = {
transition: true
}

var images = [
{
path: __dirname + '/../test/fixtures/step_1.png',
caption: 'This is a sample subtitle'
}, {
path: __dirname + '/../test/fixtures/step_2.png',
caption: 'Another sample text'
}, {
path: __dirname + '/../test/fixtures/step_3.png',
caption: 'Fast caption',
captionStart: 2,
captionEnd: 3
}, {
path: __dirname + '/../test/fixtures/step_4.png'
}, {
path: __dirname + '/../test/fixtures/step_5.png',
caption: 'Bye bye'
}
]

videoshow(images, options)
.audio(audio)
.save('video.mp4')
.on('error', function (err) {
console.error('Error:', err)
})
.on('end', function (output) {
console.log('Video created in:', output)
})
2 changes: 1 addition & 1 deletion examples/subtitles.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var images = [
videoshow(images)
.subtitles(subtitles)
.audio(audio)
.save('audio.mp4')
.save('video.mp4')
.on('error', function (err) {
console.error('Error:', err)
})
Expand Down
8 changes: 3 additions & 5 deletions examples/fade.js → examples/transition.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ var options = {

var images = [
{
path: __dirname + '/../test/fixtures/step_1.png',
caption: 'This is a sample text'
path: __dirname + '/../test/fixtures/step_1.png'
}, {
path: __dirname + '/../test/fixtures/step_2.png',
caption: 'Another sample text'
path: __dirname + '/../test/fixtures/step_2.png'
}, {
path: __dirname + '/../test/fixtures/step_3.png'
}, {
Expand All @@ -26,7 +24,7 @@ var images = [

videoshow(images, options)
.audio(audio)
.save('audio.mp4')
.save('video.mp4')
.on('error', function (err) {
console.error('Error:', err)
})
Expand Down
Loading

0 comments on commit d4a2fcd

Please sign in to comment.