diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 069ea7a6..62b9ab73 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -3,7 +3,7 @@ import { ComfyWidgetConstructor, ComfyWidgets, initWidgets } from "./widgets"; import { ComfyUI, $el } from "./ui"; import { api } from "./api"; import { defaultGraph } from "./defaultGraph"; -import { getPngMetadata, getWebpMetadata, importA1111, getLatentMetadata } from "./pnginfo"; +import { getPngMetadata, getWebpMetadata, getFlacMetadata, importA1111, getLatentMetadata } from "./pnginfo"; import { addDomClippingSetting } from "./domWidget"; import { createImageHost, calculateImageGrid } from "./ui/imagePreview" import { DraggableList } from "./ui/draggableList"; @@ -2304,6 +2304,18 @@ export class ComfyApp { const workflow = pngInfo?.workflow || pngInfo?.Workflow; const prompt = pngInfo?.prompt || pngInfo?.Prompt; + if (workflow) { + this.loadGraphData(await parseComfyWorkflow(workflow)); + } else if (prompt) { + this.loadApiJson(JSON.parse(prompt)); + } else { + this.showErrorOnFileLoad(file); + } + } else if (file.type === "audio/flac") { + const pngInfo = await getFlacMetadata(file); + const workflow = pngInfo?.workflow || pngInfo?.Workflow; + const prompt = pngInfo?.prompt || pngInfo?.Prompt; + if (workflow) { this.loadGraphData(await parseComfyWorkflow(workflow)); } else if (prompt) { diff --git a/src/scripts/pnginfo.ts b/src/scripts/pnginfo.ts index d5a1b52d..3dd24f8f 100644 --- a/src/scripts/pnginfo.ts +++ b/src/scripts/pnginfo.ts @@ -163,6 +163,77 @@ export function getLatentMetadata(file) { }); } +function getString(dataView: DataView, offset: number, length: number): string { + let string = ''; + for (let i = 0; i < length; i++) { + string += String.fromCharCode(dataView.getUint8(offset + i)); + } + return string; +} + +// Function to parse the Vorbis Comment block +function parseVorbisComment(dataView: DataView): Record { + let offset = 0; + const vendorLength = dataView.getUint32(offset, true); + offset += 4; + const vendorString = getString(dataView, offset, vendorLength); + offset += vendorLength; + + const userCommentListLength = dataView.getUint32(offset, true); + offset += 4; + const comments = {}; + for (let i = 0; i < userCommentListLength; i++) { + const commentLength = dataView.getUint32(offset, true); + offset += 4; + const comment = getString(dataView, offset, commentLength); + offset += commentLength; + + const [key, value] = comment.split('='); + + comments[key] = value; + } + + return comments; +} + +// Function to read a FLAC file and parse Vorbis comments +export function getFlacMetadata(file: Blob): Promise> { + return new Promise((r) => { + const reader = new FileReader(); + reader.onload = function(event) { + const arrayBuffer = event.target.result as ArrayBuffer; + const dataView = new DataView(arrayBuffer); + + // Verify the FLAC signature + const signature = String.fromCharCode(...new Uint8Array(arrayBuffer, 0, 4)); + if (signature !== 'fLaC') { + console.error('Not a valid FLAC file'); + return; + } + + // Parse metadata blocks + let offset = 4; + let vorbisComment = null; + while (offset < dataView.byteLength) { + const isLastBlock = dataView.getUint8(offset) & 0x80; + const blockType = dataView.getUint8(offset) & 0x7F; + const blockSize = dataView.getUint32(offset, false) & 0xFFFFFF; + offset += 4; + + if (blockType === 4) { // Vorbis Comment block type + vorbisComment = parseVorbisComment(new DataView(arrayBuffer, offset, blockSize)); + } + + offset += blockSize; + if (isLastBlock) break; + } + + r(vorbisComment); + }; + reader.readAsArrayBuffer(file); + }); +} + export async function importA1111(graph, parameters) { const p = parameters.lastIndexOf("\nSteps:"); if (p > -1) { diff --git a/src/scripts/ui.ts b/src/scripts/ui.ts index 238a0bc4..337ffa01 100644 --- a/src/scripts/ui.ts +++ b/src/scripts/ui.ts @@ -382,7 +382,7 @@ export class ComfyUI { const fileInput = $el("input", { id: "comfy-file-input", type: "file", - accept: ".json,image/png,.latent,.safetensors,image/webp", + accept: ".json,image/png,.latent,.safetensors,image/webp,audio/flac", style: { display: "none" }, parent: document.body, onchange: () => {