Skip to content

Commit

Permalink
Enable support for -fast exit
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey committed Jul 28, 2016
1 parent 17dcd78 commit d7e7674
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 55 deletions.
29 changes: 26 additions & 3 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,40 @@ test('exiftool2', t => {
})
})

t.test('pipe jpeg', t => {
t.test('pipe jpeg with trailers', t => {
const exif = exec(['-'])

createReadStream(join(FIXTURE_DIR, 'subway.jpeg')).pipe(exif)
const read = createReadStream(join(FIXTURE_DIR, 'subway.jpeg'))
let ended = false

exif.on('exif', (exif) => {
console.log('exif')

t.equal(ended, true)
t.equal(exif.length, 1)
t.equal(exif[0].FileType, 'JPEG')
t.end()
})

read.on('end', () => ended = true)

read.pipe(exif)
})

t.test('pipe jpeg fast', t => {
const exif = exec(['-fast', '-'])
const read = createReadStream(join(FIXTURE_DIR, 'subway.jpeg'))
let ended = false

exif.on('exif', (exif) => {
t.equal(ended, false)
t.equal(exif.length, 1)
t.equal(exif[0].FileType, 'JPEG')
t.end()
})

read.on('end', () => ended = true)

read.pipe(exif)
})

t.test('filename', t => {
Expand Down
121 changes: 69 additions & 52 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PassThrough } from 'stream'
import { Writable } from 'stream'
import { spawn, spawnSync, ChildProcess } from 'child_process'
import { join } from 'path'

Expand All @@ -16,9 +16,9 @@ const SPAWN_OPTIONS = {
}

/**
* Exec interface extends `PassThrough`.
* Exec interface extends `Writable`.
*/
export interface Exec extends PassThrough {
export interface Exec extends Writable {
on (event: 'exif', listener: (exif: any[]) => void): this
on (event: 'error', listener: (error: Error) => void): this
on (event: string, listener: Function): this
Expand All @@ -29,91 +29,108 @@ export interface Exec extends PassThrough {
/**
* Exec interface for `exiftool`.
*/
export class Exec extends PassThrough implements Exec {
export class Exec extends Writable implements Exec {

send (args: string[]) {
return this.write(`-q\n-json\n${args.join('\n')}\n-execute\n`)
}
process: ChildProcess

close () {
return this.send(['-stay_open', 'False'])
}
constructor (args: string[]) {
super()

}
this.process = spawn(BIN_PATH, args)

/**
* Wrap the stream handler.
*/
function wrap (process: ChildProcess): Exec {
const stream = new Exec()
let stdout = ''
let stderr = ''

stream.pipe(process.stdin)
let stdout = ''
let stderr = ''
let parsed = false

process.stdout.on('data', (chunk: Buffer) => {
let offset: number
this.process.stdout.on('data', (chunk: Buffer) => {
let offset: number

stdout += chunk
stdout += chunk.toString('utf8')

// tslint:disable-next-line
while ((offset = stdout.indexOf(DELIMITER)) > -1) {
const len = offset + DELIMITER.length
const data = stdout.substr(0, len)
// tslint:disable-next-line
while ((offset = stdout.indexOf(DELIMITER)) > -1) {
const len = offset + DELIMITER.length
const data = stdout.substr(0, len)

stdout = stdout.substr(len)
parsed = true
stdout = stdout.substr(len)

try {
stream.emit('exif', JSON.parse(data))
} catch (err) {
stream.emit('error', err)
try {
this.emit('exif', JSON.parse(data))
} catch (err) {
this.emit('error', err)
}
}
}
})
})

process.stderr.on('data', (chunk: Buffer) => {
let offset: number
this.process.stderr.on('data', (chunk: Buffer) => {
let offset: number

stderr += chunk
stderr += chunk.toString('utf8')

// tslint:disable-next-line
while ((offset = stderr.indexOf('\n')) > -1) {
const data = stderr.substr(0, offset)
// tslint:disable-next-line
while ((offset = stderr.indexOf('\n')) > -1) {
const data = stderr.substr(0, offset)

stderr = stderr.substr(offset + 1)
parsed = true
stderr = stderr.substr(offset + 1)

if (data.length) {
stream.emit('error', new Error(data))
if (data.length) {
this.emit('error', new Error(data))
}
}
}
})
})

process.on('exit', () => {
stdout = stderr = ''
})
this.process.stdout.on('end', () => {
this.end() // Mark the stream as done.
stdout = stderr = ''
})

this.process.stdout.on('error', this.emit.bind(this, 'error'))
this.process.stderr.on('error', this.emit.bind(this, 'error'))

this.process.stdin.on('error', (err: Error) => {
if (!parsed || (err as any).code !== 'EPIPE') {
this.emit('error', err)
}
})

this.on('finish', () => this.process.stdin.end())
}

_write (chunk: Buffer, encoding: string, cb: Function) {
return this.process.stdin.write(chunk, encoding, () => cb())
}

send (args: string[]) {
return this.write(`-q\n-json\n${args.join('\n')}\n-execute\n`)
}

close () {
return this.send(['-stay_open', 'False'])
}

return stream
}

/**
* Handle `-stay_open` arguments.
*/
export function open () {
return wrap(spawn(BIN_PATH, ['-stay_open', 'True', '-@', '-'], SPAWN_OPTIONS))
return new Exec(['-stay_open', 'True', '-@', '-'])
}

/**
* Execute a command, returning on data.
*/
export function exec (args: string[]) {
return wrap(spawn(BIN_PATH, ['-q', '-json'].concat(args), SPAWN_OPTIONS))
return new Exec(['-q', '-json', ...args])
}

/**
* Synchronous execution of `exiftool`.
*/
export function execSync (args: string[]) {
const { stdout, stderr } = spawnSync(BIN_PATH, ['-q', '-json'].concat(args), SPAWN_OPTIONS)
const { stdout, stderr } = spawnSync(BIN_PATH, ['-q', '-json', ...args], SPAWN_OPTIONS)
const stdoutOffset = stdout.indexOf(DELIMITER)
const stderrOffset = stderr.indexOf('\n')

Expand Down

0 comments on commit d7e7674

Please sign in to comment.