Skip to content

Commit 580d61b

Browse files
committed
feat: add mediainfo.js and background worker for supporting all video formats and codecs
1 parent 466467b commit 580d61b

5 files changed

Lines changed: 109 additions & 6 deletions

File tree

web/app.js

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,28 @@ const els = {
1717
resetBtn: document.getElementById("reset-btn"),
1818
};
1919

20+
const mediainfoWorker = new Worker("./mediainfo-worker.js");
21+
22+
function getWasmDuration(file) {
23+
return new Promise((resolve, reject) => {
24+
const listener = (e) => {
25+
const { success, fileName, duration, error } = e.data;
26+
27+
if (fileName === file.name) {
28+
mediainfoWorker.removeEventListener("message", listener);
29+
if (success) {
30+
resolve(duration);
31+
} else {
32+
reject(new Error(error));
33+
}
34+
}
35+
};
36+
37+
mediainfoWorker.addEventListener("message", listener);
38+
mediainfoWorker.postMessage(file);
39+
});
40+
}
41+
2042
["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
2143
els.dropZone.addEventListener(
2244
eventName,
@@ -122,7 +144,7 @@ function handleFiles(rawFiles) {
122144
}
123145

124146
function isVideo(file) {
125-
return file.name.match(/\.(mp4|webm|mov)$/i);
147+
return file.name.match(/\.(mp4|webm|mov|mkv|avi|flv|wmv|m4v)$/i);
126148
}
127149

128150
function getFolderName(file) {
@@ -174,20 +196,34 @@ async function processFiles(files) {
174196
els.status.innerText = `> Processing video ${i + 1} of ${files.length}...`;
175197

176198
const folderName = getFolderName(file);
199+
let duration = 0;
200+
201+
try {
202+
if (file.name.match(/\.(mp4|webm|mov|m4v)$/i)) {
203+
try {
204+
duration = await getDuration(file);
205+
} catch (err) {
206+
console.warn(
207+
`Native scan failed for ${file.name}. Falling back to WASM...`,
208+
);
209+
duration = await getWasmDuration(file);
210+
}
211+
} else {
212+
console.log(`Sending ${file.name} to WASM worker...`);
213+
duration = await getWasmDuration(file);
214+
}
177215

178-
if (file.name.match(/\.(mp4|webm|mov)$/i)) {
179-
try {
180-
const duration = await getDuration(file);
216+
if (duration > 0) {
181217
const folder = state.folderMap.get(folderName);
182218
if (folder) {
183219
folder.duration += duration;
184220
state.total.duration += duration;
185221
updateFolderRow(folderName);
186222
updateTotalStats();
187223
}
188-
} catch (e) {
189-
console.warn(`Could not read duration for: ${file.name}`);
190224
}
225+
} catch (e) {
226+
console.error(`Could not read duration for: ${file.name}`, e);
191227
}
192228
}
193229

web/mediainfo-worker.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
importScripts("./mediainfo.js/index.min.js");
2+
3+
let mediainfoInstance = null;
4+
5+
self.onmessage = async (e) => {
6+
const file = e.data;
7+
8+
try {
9+
if (!mediainfoInstance) {
10+
mediainfoInstance = await MediaInfo.mediaInfoFactory({
11+
format: "object",
12+
locateFile: () => "./mediainfo.js/MediaInfoModule.wasm",
13+
});
14+
}
15+
16+
const readChunk = async (chunkSize, offset) => {
17+
const buffer = await file.slice(offset, offset + chunkSize).arrayBuffer();
18+
return new Uint8Array(buffer);
19+
};
20+
21+
const result = await mediainfoInstance.analyzeData(file.size, readChunk);
22+
23+
const generalTrack = result.media.track.find(
24+
(t) => t["@type"] === "General",
25+
);
26+
const durationSec =
27+
generalTrack && generalTrack.Duration
28+
? parseFloat(generalTrack.Duration)
29+
: 0;
30+
31+
self.postMessage({
32+
success: true,
33+
fileName: file.name,
34+
duration: durationSec,
35+
});
36+
} catch (error) {
37+
self.postMessage({
38+
success: false,
39+
fileName: file.name,
40+
error: error.message || error,
41+
});
42+
}
43+
};

web/mediainfo.js/LICENSE.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2002-2016 MediaArea.net SARL, All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are met:
5+
6+
* Redistributions of source code must retain the above copyright notice, this
7+
list of conditions and the following disclaimer.
8+
9+
* Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation
11+
and/or other materials provided with the distribution.
12+
13+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
17+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2.45 MB
Binary file not shown.

web/mediainfo.js/index.min.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)