The base implementation from pngs uses String.fromCharCode, but that causes overflow from having too many arguments when decoding video files, likely since no chunking is done and everything is embedded as a single monolithic json As an additional benefit, data is now properly decoded from utf-8 instead of being interpreted as utf-16
105 lines
4.3 KiB
JavaScript
105 lines
4.3 KiB
JavaScript
import { app } from '../../../scripts/app.js'
|
|
|
|
|
|
function getVideoMetadata(file) {
|
|
return new Promise((r) => {
|
|
const reader = new FileReader();
|
|
reader.onload = (event) => {
|
|
const videoData = new Uint8Array(event.target.result);
|
|
const dataView = new DataView(videoData.buffer);
|
|
|
|
let decoder = new TextDecoder();
|
|
// Check for known valid magic strings
|
|
if (dataView.getUint32(0) == 0x1A45DFA3) {
|
|
//webm
|
|
//see http://wiki.webmproject.org/webm-metadata/global-metadata
|
|
//and https://www.matroska.org/technical/elements.html
|
|
//contrary to specs, tag seems consistently at start
|
|
//COMMENT + 0x4487 + packed length?
|
|
//length 0x8d8 becomes 0x48d8
|
|
//
|
|
//description for variable length ints https://github.com/ietf-wg-cellar/ebml-specification/blob/master/specification.markdown
|
|
let offset = 4 + 8; //COMMENT is 7 chars + 1 to realign
|
|
while(offset < videoData.length-16) {
|
|
//Check for text tags
|
|
if (dataView.getUint16(offset) == 0x4487) {
|
|
//check that name of tag is COMMENT
|
|
const name = String.fromCharCode(...videoData.slice(offset-7,offset));
|
|
if (name === "COMMENT") {
|
|
let vint = dataView.getUint32(offset+2);
|
|
let n_octets = Math.clz32(vint)+1;
|
|
if (n_octets < 4) {//250MB sanity cutoff
|
|
let length = (vint >> (8*(4-n_octets))) & ~(1 << (7*n_octets));
|
|
const content = decoder.decode(videoData.slice(offset+2+n_octets, offset+2+n_octets+length));
|
|
const json = JSON.parse(content);
|
|
r(json);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
offset+=1;
|
|
}
|
|
} else if (dataView.getUint32(4) == 0x66747970 && dataView.getUint32(8) == 0x69736F6D) {
|
|
//mp4
|
|
//see https://developer.apple.com/documentation/quicktime-file-format
|
|
//Seems to make no guarantee for alignment
|
|
let offset = videoData.length-4;
|
|
while (offset > 16) {//rough safe guess
|
|
if (dataView.getUint32(offset) == 0x64617461) {//any data tag
|
|
if (dataView.getUint32(offset - 8) == 0xa9636d74) {//cmt data tag
|
|
let type = dataView.getUint32(offset+4); //seemingly 1
|
|
let locale = dataView.getUint32(offset+8); //seemingly 0
|
|
let size = dataView.getUint32(offset-4) - 4*4;
|
|
const content = decoder.decode(videoData.slice(offset+12, offset+12+size));
|
|
const json = JSON.parse(content);
|
|
r(json);
|
|
return;
|
|
}
|
|
}
|
|
|
|
offset-=1;
|
|
}
|
|
} else {
|
|
console.error("Unknown magic: " + dataView.getUint32(0))
|
|
r();
|
|
return;
|
|
}
|
|
|
|
};
|
|
|
|
reader.readAsArrayBuffer(file);
|
|
});
|
|
}
|
|
function isVideoFile(file) {
|
|
if (file?.name?.endsWith(".webm")) {
|
|
return true;
|
|
}
|
|
if (file?.name?.endsWith(".mp4")) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
async function handleFile(file) {
|
|
if (file?.type?.startsWith("video/") || isVideoFile(file)) {
|
|
const videoInfo = await getVideoMetadata(file);
|
|
if (videoInfo) {
|
|
if (videoInfo.workflow) {
|
|
|
|
app.loadGraphData(videoInfo.workflow);
|
|
}
|
|
//Potentially check for/parse A1111 metadata here.
|
|
}
|
|
} else {
|
|
await app.originalHandleFile(file);
|
|
}
|
|
}
|
|
|
|
//Storing the original function in app is probably a major no-no
|
|
//But it's the only way I've found to keep the 'this' reference
|
|
app.originalHandleFile = app.handleFile;
|
|
app.handleFile = handleFile;
|
|
//hijack comfy-file-input to allow webm/mp4
|
|
document.getElementById("comfy-file-input").accept += ",video/webm,video/mp4";
|