Skip to content

Commit

Permalink
feat(28): Add write-stream output support (#36)
Browse files Browse the repository at this point in the history
* Update @ScottyFillups as a contributor

* Add @cmd430 as a contributor

* Add write-stream output support

* Add null output support
  • Loading branch information
philipjscott authored Dec 5, 2018
1 parent edd053a commit 26d22d6
Show file tree
Hide file tree
Showing 5 changed files with 696 additions and 546 deletions.
14 changes: 13 additions & 1 deletion .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,19 @@
"avatar_url": "https://avatars2.githubusercontent.com/u/18666879?v=4",
"profile": "http://scottyfillups.io",
"contributions": [
"doc"
"doc",
"code",
"design"
]
},
{
"login": "cmd430",
"name": "cmd430",
"avatar_url": "https://avatars1.githubusercontent.com/u/2668906?v=4",
"profile": "https://github.com/cmd430",
"contributions": [
"code",
"ideas"
]
}
]
Expand Down
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# simple-thumbnail
[![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=rounded)](#contributors)
[![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=rounded)](#contributors)
[![npm version](https://badge.fury.io/js/simple-thumbnail.svg)](https://badge.fury.io/js/simple-thumbnail)
[![Build Status](https://travis-ci.org/ScottyFillups/simple-thumbnail.svg?branch=master)](https://travis-ci.org/ScottyFillups/simple-thumbnail)
[![Coverage Status](https://coveralls.io/repos/github/ScottyFillups/simple-thumbnail/badge.svg?branch=master)](https://coveralls.io/github/ScottyFillups/simple-thumbnail?branch=master)
Expand Down Expand Up @@ -34,6 +34,13 @@ async function run () {
}

run()

// genThumbnail also supports piping to write streams, so you can do this with Express!
app.get('/some/endpoint', (req, res) => {
genThumbnail('path/to/video.webm', res, '150x100')
.then(() => console.log('done!'))
.catch(err => console.error(err))
})
```

## Getting FFmpeg
Expand Down Expand Up @@ -69,9 +76,9 @@ The URL, file path, or read-stream of an image or video.

#### output

Type: `String`
Type: `String | stream.Writable | Null`

The file path of the generated thumbnail, assumes directories exist.
The file path of the generated thumbnail, a write-stream, or null. If null, `genThumbnail` will resolve to a read-stream that you can pipe somewhere. If you're specifying a file path, make sure the directories exist.

#### size

Expand Down Expand Up @@ -106,8 +113,12 @@ Seeks the video to the provided time. The time must be in the following form: `h

<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore -->
| [<img src="https://avatars2.githubusercontent.com/u/18666879?v=4" width="100px;"/><br /><sub><b>Philip Scott</b></sub>](http://scottyfillups.io)<br />[📖](https://github.com/ScottyFillups/simple-thumbnail/commits?author=ScottyFillups "Documentation") |
| :---: |
| [<img src="https://avatars2.githubusercontent.com/u/18666879?v=4" width="100px;"/><br /><sub><b>Philip Scott</b></sub>](http://scottyfillups.io)<br />[📖](https://github.com/ScottyFillups/simple-thumbnail/commits?author=ScottyFillups "Documentation") [💻](https://github.com/ScottyFillups/simple-thumbnail/commits?author=ScottyFillups "Code") [🎨](#design-ScottyFillups "Design") | [<img src="https://avatars1.githubusercontent.com/u/2668906?v=4" width="100px;"/><br /><sub><b>cmd430</b></sub>](https://github.com/cmd430)<br />[💻](https://github.com/ScottyFillups/simple-thumbnail/commits?author=cmd430 "Code") [🤔](#ideas-cmd430 "Ideas, Planning, & Feedback") |
| :---: | :---: |
<!-- ALL-CONTRIBUTORS-LIST:END -->
<!-- ALL-CONTRIBUTORS-LIST: START - Do not remove or modify this section -->
<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification.

Contributions of any kind are welcome!
58 changes: 42 additions & 16 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,24 @@ function buildArgs (input, output, { width, height, percentage }, seek) {
/**
* Spawn an instance of ffmpeg and generate a thumbnail
* @func ffmpegExecute
* @param {String} path The path of the ffmpeg binary
* @param {Array<string>} args An array of arguments for ffmpeg
* @param {stream.Readable} stream A readable stream to pipe data to
* the standard input of ffmpeg
* @param {String} path The path of the ffmpeg binary
* @param {Array<string>} args An array of arguments for ffmpeg
* @param {stream.Readable} [rstream] A readable stream to pipe data to
* the standard input of ffmpeg
* @param {stream.Writable} [wstream] A writable stream to receive data from
* the standard output of ffmpeg
* @returns {Promise} Promise that resolves once thumbnail is generated
*/
function ffmpegExecute (path, args, stream = null) {
function ffmpegExecute (path, args, rstream, wstream) {
const ffmpeg = spawn(path, args, { shell: true })
let stderr = ''

return new Promise((resolve, reject) => {
if (stream) {
stream.pipe(ffmpeg.stdin)
if (rstream) {
rstream.pipe(ffmpeg.stdin)
}
if (wstream) {
ffmpeg.stdout.pipe(wstream)
}

ffmpeg.stderr.on('data', (data) => {
Expand All @@ -84,23 +89,40 @@ function ffmpegExecute (path, args, stream = null) {
ffmpeg.stderr.on('error', (err) => {
reject(err)
})

ffmpeg.on('close', resolve)
ffmpeg.on('exit', (code, signal) => {
if (code !== 0) {
const err = new Error(`ffmpeg exited ${code}\nffmpeg stderr:\n\n${stderr}`)

reject(err)
}
})
ffmpeg.on('close', resolve)
})
}

/**
* Spawn an instance of ffmpeg and generate a thumbnail
* @func ffmpegStreamExecute
* @param {String} path The path of the ffmpeg binary
* @param {Array<string>} args An array of arguments for ffmpeg
* @param {stream.Readable} [rstream] A readable stream to pipe data to
* the standard input of ffmpeg
* @returns {Promise} Promise that resolves to ffmpeg stdout
*/
function ffmpegStreamExecute (path, args, rstream) {
const ffmpeg = spawn(path, args, { shell: true })

if (rstream) {
rstream.pipe(ffmpeg.stdin)
}

return Promise.resolve(ffmpeg.stdout)
}

/**
* Generates a thumbnail from the first frame of a video file
* @func genThumbnail
* @param {String|stream.Readable} input Path to video, or a read stream
* @param {String} output Output path of the thumbnail
* @param {String|stream.Readable} input Path to video, or a read stream
* @param {String|stream.Writeable} output Output path of the thumbnail
* @param {String} size The size of the thumbnail, eg. '240x240'
* @param {Object} [config={}] A configuration object
* @param {String} [config.path='ffmpeg'] Path of the ffmpeg binary
Expand All @@ -110,18 +132,22 @@ function ffmpegExecute (path, args, stream = null) {
function genThumbnail (input, output, size, config = {}) {
const ffmpegPath = config.path || process.env.FFMPEG_PATH || 'ffmpeg'
const seek = config.seek || '00:00:00'
const rstream = typeof input === 'string' ? null : input
const wstream = typeof output === 'string' ? null : output

const parsedSize = parseSize(size)
const args = buildArgs(
typeof input === 'string' ? input : 'pipe:0',
output,
typeof output === 'string' ? output : '-f singlejpeg pipe:1',
parsedSize,
seek
)

return typeof input === 'string'
? ffmpegExecute(ffmpegPath, args)
: ffmpegExecute(ffmpegPath, args, input)
if (output === null) {
return ffmpegStreamExecute(ffmpegPath, args, rstream)
}

return ffmpegExecute(ffmpegPath, args, rstream, wstream)
}

module.exports = genThumbnail
Loading

0 comments on commit 26d22d6

Please sign in to comment.