UNPKG

5.28 kBJavaScriptView Raw
1'use strict'
2
3const EventEmitter = require('events')
4const lib = require('./lib')
5const beginReady = require('./begin-ready')
6
7const EXIFTOOL_PATH = 'exiftool'
8
9const events = {
10 OPEN: 'exiftool_opened',
11 EXIT: 'exiftool_exit',
12}
13
14class ExiftoolProcess extends EventEmitter {
15
16 /**
17 * Create an instance of ExoftoolProcess class.
18 * @param {string} [exiftool] - path to executable
19 */
20 constructor(bin) {
21 super()
22 this._bin = lib.isString(bin) ? bin : EXIFTOOL_PATH
23 this._process = undefined
24 this._open = false
25 }
26
27 /**
28 * Close the exiftool process by passing -stay_open false.
29 * @returns {Promise} a promise to stop the process.
30 */
31 close() {
32 if (!this._open) {
33 return Promise.reject(new Error('Exiftool process is not open'))
34 }
35 return lib.close(this._process)
36 .then(() => {
37 this._stdoutResolveWs.end()
38 this._stderrResolveWs.end()
39 this._open = false
40 })
41 }
42
43 _assignEncoding(encoding) {
44 let _encoding
45 if (encoding === null) {
46 _encoding = undefined
47 } else if (lib.isString(encoding)) {
48 _encoding = encoding
49 } else {
50 _encoding = 'utf8'
51 }
52 this._encoding = _encoding
53 }
54 /**
55 * Spawn exfitool process with -stay_open True -@ - arguments.
56 * @returns {Promise} a promise to spawn exiftool in stay_open mode.
57 * @param {string} [encoding=utf8] - encoding with which to read from and write to streams.
58 * pass null to not use encoding, utf8 otherwise
59 */
60 open(encoding) {
61 this._assignEncoding(encoding)
62 if (this._open) {
63 return Promise.reject(new Error('Exiftool process is already open'))
64 }
65 return lib.spawn(this._bin)
66 .then((exiftoolProcess) => {
67 //console.log(`Started exiftool process %s`, process.pid);
68 this.emit(events.OPEN, exiftoolProcess.pid)
69 this._process = exiftoolProcess
70
71 exiftoolProcess.on('exit', this._exitListener.bind(this))
72
73 // resolve write streams
74 if (this._encoding) {
75 exiftoolProcess.stdout.setEncoding(this._encoding)
76 exiftoolProcess.stderr.setEncoding(this._encoding)
77 }
78 this._stdoutResolveWs = beginReady.setupResolveWriteStreamPipe(exiftoolProcess.stdout)
79 this._stderrResolveWs = beginReady.setupResolveWriteStreamPipe(exiftoolProcess.stderr)
80
81 // handle erros so that Node does not crash
82 this._stdoutResolveWs.on('error', console.error)
83 this._stderrResolveWs.on('error', console.error)
84
85 // debug
86 // exiftoolProcess.stdout.pipe(process.stdout)
87 // exiftoolProcess.stderr.pipe(process.stderr)
88
89 this._open = true
90
91 return exiftoolProcess.pid
92 })
93 }
94
95 _exitListener() {
96 //console.log('exfitool process exit');
97 this.emit(events.EXIT)
98 this._open = false // try to respawn?
99 }
100
101 /**
102 * Checks if process is opens.
103 * @returns {boolean} true if open and false otherwise.
104 */
105 get isOpen() {
106 return this._open
107 }
108
109 _executeCommand(command, args, argsNoSplit, debug) {
110 //test this!
111 if (!this._open) {
112 return Promise.reject(new Error('exiftool is not open'))
113 }
114 if (this._process.signalCode === 'SIGTERM') {
115 return Promise.reject(new Error('Could not connect to the exiftool process'))
116 }
117
118 const proc = debug === true ? process : this._process
119 return lib.executeCommand(proc, this._stdoutResolveWs,
120 this._stderrResolveWs, command, args, argsNoSplit, this._encoding)
121 }
122
123 /**
124 * Read metadata of a file or directory.
125 * @param {string} file - path to the file or directory
126 * @param {Array} args - any additional arguments, e.g.,
127 * ['Orientation#'] to report Orientation only, or ['-FileSize'] to exclude FileSize
128 * @returns {Promise} a promise resolved with data (array or null) and error
129 * (string or null) properties from stdout and stderr of exiftool.
130 */
131 readMetadata(file, args) {
132 return this._executeCommand(file, args)
133 }
134
135 /**
136 * Write metadata to a file or directory.
137 * @param {string} file - path to the file or directory
138 * @param {object} data - data to write, with keys as tags.
139 * @param {object}
140 * @param {string} destination where to write
141 * @param {boolean} debug whether to print to stdout
142 * @returns {Promise} a promise to write metadata
143 */
144 writeMetadata(file, data, args, debug) {
145 if (!lib.isString(file)) {
146 throw new Error('File must be a string')
147 }
148 if (!lib.checkDataObject(data)) {
149 return Promise.reject(new Error('Data argument is not an object'))
150 }
151
152 const writeArgs = lib.mapDataToTagArray(data)
153 return this._executeCommand(file, args, writeArgs, debug)
154 }
155}
156
157module.exports = {
158 ExiftoolProcess,
159 EXIFTOOL_PATH,
160 events,
161}