-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmod.ts
153 lines (129 loc) · 4.52 KB
/
mod.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2021-Eternity Channelping. All rights reserved. MIT License.
// This is a wrapper for the CLI utility mediainfo.
const MEDIA_INFO_CMD = 'mediainfo';
// HTML and XML are other available formats. Although mediainfo documentation
// does not include JSON as one of the available output formats, it's available.
const DEFAULT_OUTPUT_FORMAT = '--Output=JSON';
const OUTPUT_FORMAT_RX = /^--Output=\w+$/;
interface Info {
[key: string]: any
}
interface ResultObject {
exitCode: number,
info: Info
}
class MediaInfo {
// Unlike mediainfo behavior, our wrapper has JSON output as the default.
static setOutputFormat(params: Array<string>) {
let outputOptionFound = false;
for (const item of params) {
if (OUTPUT_FORMAT_RX.test(item)) {
outputOptionFound = true;
break;
}
}
if (!outputOptionFound) {
params.push(DEFAULT_OUTPUT_FORMAT);
}
return params;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// params represents a list of strings.
//
// Order is not important.
//
// get() returns the following JSON message shape, passed through from
// getRaw(). The raw nested data structure is simplified to look like:
//
// {
// "exitCode": number,
// "info": Array<object> | string
// }
//
// If binary command mediainfo is successful, exitCode = 0 and info is an
// array of objects or a string.
//
// If binary command mediainfo is not successful, exitCode = 1 and info is a
// string.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static async get(...params: Array<string>): Promise<ResultObject> {
const resultTrack: ResultObject = { exitCode: 0, info: [] };
const resultRaw: ResultObject = await MediaInfo.getRaw(...params);
resultTrack.exitCode = resultRaw.exitCode;
if (typeof resultRaw.info === 'object' && 'media' in resultRaw.info) {
resultTrack.info = resultRaw.info.media.track;
} else if (typeof resultRaw.info === 'string') {
resultTrack.info = resultRaw.info;
} else {
console.error('unknown data structure returned from getRaw()');
Deno.exit(1);
}
return resultTrack;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// params represents a list of strings.
//
// Order is not important.
//
// getRaw() returns the following data structure, derived from mediainfo
// binary return value:
//
// {
// "exitCode": number,
// "info": object {
// "media": object {
// "@ref": string,
// "track": Array<object>
// }
// }
// }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static async getRaw(...params: Array<string>): Promise<ResultObject> {
const result = { exitCode: 0, info: {} };
const options = MediaInfo.setOutputFormat(params) ;
const cmd = Deno.run({
cmd:[MEDIA_INFO_CMD, ...options],
stdin: 'piped', stdout: 'piped'
});
const rawOutput = await cmd.output();
const outputString = new TextDecoder().decode(rawOutput);
// JSON output may have been a specified option, but if there were other
// options included as well, e.g., --Version, then the mediainfo command
// returns non-JSON version information instead of getting specified media
// file's info in JSON format. That's an expected case and is not an error.
// It is handled as such in the catch block.
try {
result.info = JSON.parse(outputString);
} catch (requiredArgButNotNecessary) {
result.info = outputString.replace(/\n$/, '');
}
const { code } = await cmd.status();
result.exitCode = code;
cmd.stdin.close();
cmd.close();
return result;
}
static async isMediaInfoCommandExist() {
const cmd = Deno.run({
cmd:['bash', 'which', MEDIA_INFO_CMD],
stdout: 'piped'
});
try {
const { code } = await cmd.status();
cmd.stdout.close();
cmd.close();
return code === 0;
} catch (e) {
console.info('cmd.status() error:', e);
Deno.exit(1);
}
}
} // end class
// Make sure mediainfo is available on the system
MediaInfo.isMediaInfoCommandExist().then((cmdExists) => {
if (!cmdExists) {
console.error(`"${MEDIA_INFO_CMD}" command not found.`);
Deno.exit(1);
}
});
export { MediaInfo as mediainfo };