From aa46524829e02849aa8b664ee0a74e86e524ef30 Mon Sep 17 00:00:00 2001 From: thot experiment <94414189+thot-experiment@users.noreply.github.com> Date: Fri, 9 May 2025 10:40:50 -0700 Subject: [PATCH] add workflow parsing for mp3 and opus formats (#3832) --- src/scripts/app.ts | 22 +++++++++++++++++++++- src/scripts/metadata/mp3.ts | 29 +++++++++++++++++++++++++++++ src/scripts/metadata/ogg.ts | 30 ++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 src/scripts/metadata/mp3.ts create mode 100644 src/scripts/metadata/ogg.ts diff --git a/src/scripts/app.ts b/src/scripts/app.ts index d63dd08d5..d4f8ec4c6 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -29,6 +29,9 @@ import type { ComfyNodeDef as ComfyNodeDefV1 } from '@/schemas/nodeDefSchema' import { getFromWebmFile } from '@/scripts/metadata/ebml' import { getGltfBinaryMetadata } from '@/scripts/metadata/gltf' import { getFromIsobmffFile } from '@/scripts/metadata/isobmff' +import { getMp3Metadata } from '@/scripts/metadata/mp3' +import { getOggMetadata } from '@/scripts/metadata/ogg' +import { getSvgMetadata } from '@/scripts/metadata/svg' import { useDialogService } from '@/services/dialogService' import { useExtensionService } from '@/services/extensionService' import { useLitegraphService } from '@/services/litegraphService' @@ -64,7 +67,6 @@ import { deserialiseAndCreate } from '@/utils/vintageClipboard' import { type ComfyApi, PromptExecutionError, api } from './api' import { defaultGraph } from './defaultGraph' import { pruneWidgets } from './domWidget' -import { getSvgMetadata } from './metadata/svg' import { getFlacMetadata, getLatentMetadata, @@ -1300,6 +1302,24 @@ export class ComfyApp { } else { this.showErrorOnFileLoad(file) } + } else if (file.type === 'audio/mpeg') { + const { workflow, prompt } = await getMp3Metadata(file) + if (workflow) { + this.loadGraphData(workflow, true, true, fileName) + } else if (prompt) { + this.loadApiJson(prompt, fileName) + } else { + this.showErrorOnFileLoad(file) + } + } else if (file.type === 'audio/ogg') { + const { workflow, prompt } = await getOggMetadata(file) + if (workflow) { + this.loadGraphData(workflow, true, true, fileName) + } else if (prompt) { + this.loadApiJson(prompt, fileName) + } else { + this.showErrorOnFileLoad(file) + } } else if (file.type === 'audio/flac' || file.type === 'audio/x-flac') { const pngInfo = await getFlacMetadata(file) const workflow = pngInfo?.workflow || pngInfo?.Workflow diff --git a/src/scripts/metadata/mp3.ts b/src/scripts/metadata/mp3.ts new file mode 100644 index 000000000..f3e5e23bb --- /dev/null +++ b/src/scripts/metadata/mp3.ts @@ -0,0 +1,29 @@ +export async function getMp3Metadata(file: File) { + const reader = new FileReader() + const read_process = new Promise( + (r) => (reader.onload = (event) => r(event?.target?.result)) + ) + reader.readAsArrayBuffer(file) + const arrayBuffer = (await read_process) as ArrayBuffer + //https://stackoverflow.com/questions/7302439/how-can-i-determine-that-a-particular-file-is-in-fact-an-mp3-file#7302482 + const sig_bytes = new Uint8Array(arrayBuffer, 0, 3) + if ( + (sig_bytes[0] != 0xff && sig_bytes[1] != 0xfb) || + (sig_bytes[0] != 0x49 && sig_bytes[1] != 0x44 && sig_bytes[2] != 0x33) + ) + console.error('Invalid file signature.') + let header = '' + while (header.length < arrayBuffer.byteLength) { + const page = String.fromCharCode( + ...new Uint8Array(arrayBuffer, header.length, header.length + 4096) + ) + header += page + if (page.match('\u00ff\u00fb')) break + } + let workflow, prompt + let prompt_s = header.match(/prompt\u0000(\{.*?\})\u0000/s)?.[1] + if (prompt_s) prompt = JSON.parse(prompt_s) + let workflow_s = header.match(/workflow\u0000(\{.*?\})\u0000/s)?.[1] + if (workflow_s) workflow = JSON.parse(workflow_s) + return { prompt, workflow } +} diff --git a/src/scripts/metadata/ogg.ts b/src/scripts/metadata/ogg.ts new file mode 100644 index 000000000..5dc49c02b --- /dev/null +++ b/src/scripts/metadata/ogg.ts @@ -0,0 +1,30 @@ +export async function getOggMetadata(file: File) { + const reader = new FileReader() + const read_process = new Promise( + (r) => (reader.onload = (event) => r(event?.target?.result)) + ) + reader.readAsArrayBuffer(file) + const arrayBuffer = (await read_process) as ArrayBuffer + const signature = String.fromCharCode(...new Uint8Array(arrayBuffer, 0, 4)) + if (signature !== 'OggS') console.error('Invalid file signature.') + let oggs = 0 + let header = '' + while (header.length < arrayBuffer.byteLength) { + const page = String.fromCharCode( + ...new Uint8Array(arrayBuffer, header.length, header.length + 4096) + ) + if (page.match('OggS\u0000')) oggs++ + header += page + if (oggs > 1) break + } + let workflow, prompt + let prompt_s = header + .match(/prompt=(\{.*?(\}.*?\u0000))/s)?.[1] + ?.match(/\{.*\}/)?.[0] + if (prompt_s) prompt = JSON.parse(prompt_s) + let workflow_s = header + .match(/workflow=(\{.*?(\}.*?\u0000))/s)?.[1] + ?.match(/\{.*\}/)?.[0] + if (workflow_s) workflow = JSON.parse(workflow_s) + return { prompt, workflow } +}