mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-19 13:59:28 +00:00
Compare commits
1 Commits
v1.18.2
...
upstream-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bde964aed |
14
README.md
14
README.md
@@ -67,20 +67,6 @@ The development of successive minor versions overlaps. For example, while versio
|
||||
| 3 | Mar 15-21 | Released | Feature Freeze | Development | 1.1.7 through 1.1.13 (daily)<br>1.2.0 through 1.2.6 (daily) |
|
||||
| 4 | Mar 22-28 | - | Released | Feature Freeze | 1.2.7 through 1.2.13 (daily)<br>1.3.0 through 1.3.6 (daily) |
|
||||
|
||||
## Contributing
|
||||
|
||||
We're building this frontend together and would love your help — no matter how you'd like to pitch in! You don't need to write code to make a difference.
|
||||
|
||||
Here are some ways to get involved:
|
||||
|
||||
- **Pull Requests:** Add features, fix bugs, or improve code health. Browse [issues](https://github.com/Comfy-Org/ComfyUI_frontend/issues) for inspiration.
|
||||
- **Vote on Features:** Give a 👍 to the feature requests you care about to help us prioritize.
|
||||
- **Verify Bugs:** Try reproducing reported issues and share your results (even if the bug doesn't occur!).
|
||||
- **Community Support:** Hop into our [Discord](https://www.comfy.org/discord) to answer questions or get help.
|
||||
- **Share & Advocate:** Tell your friends, tweet about us, or share tips to support the project.
|
||||
|
||||
Have another idea? Drop into Discord or open an issue, and let's chat!
|
||||
|
||||
## Development
|
||||
|
||||
### Tech Stack
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -48,7 +48,6 @@ const customColorPalettes: Record<string, Palette> = {
|
||||
WIDGET_OUTLINE_COLOR: '#333',
|
||||
WIDGET_TEXT_COLOR: '#a3a3a8',
|
||||
WIDGET_SECONDARY_TEXT_COLOR: '#97979c',
|
||||
WIDGET_DISABLED_TEXT_COLOR: '#646464',
|
||||
LINK_COLOR: '#9A9',
|
||||
EVENT_LINK_COLOR: '#A86',
|
||||
CONNECTING_LINK_COLOR: '#AFA'
|
||||
@@ -112,7 +111,6 @@ const customColorPalettes: Record<string, Palette> = {
|
||||
WIDGET_OUTLINE_COLOR: '#333',
|
||||
WIDGET_TEXT_COLOR: '#a3a3a8',
|
||||
WIDGET_SECONDARY_TEXT_COLOR: '#97979c',
|
||||
WIDGET_DISABLED_TEXT_COLOR: '#646464',
|
||||
LINK_COLOR: '#9A9',
|
||||
EVENT_LINK_COLOR: '#A86',
|
||||
CONNECTING_LINK_COLOR: '#AFA'
|
||||
|
||||
@@ -9,10 +9,7 @@ test.describe('Load Workflow in Media', () => {
|
||||
'no_workflow.webp',
|
||||
'large_workflow.webp',
|
||||
'workflow.webm',
|
||||
'workflow.glb',
|
||||
'workflow.mp4',
|
||||
'workflow.mov',
|
||||
'workflow.m4v'
|
||||
'workflow.glb'
|
||||
]
|
||||
fileNames.forEach(async (fileName) => {
|
||||
test(`Load workflow in ${fileName} (drop from filesystem)`, async ({
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB |
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"version": "1.18.2",
|
||||
"version": "1.18.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"version": "1.18.2",
|
||||
"version": "1.18.1",
|
||||
"license": "GPL-3.0-only",
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
|
||||
"@comfyorg/comfyui-electron-types": "^0.4.42",
|
||||
"@comfyorg/litegraph": "^0.14.2",
|
||||
"@comfyorg/litegraph": "^0.14.1",
|
||||
"@primevue/forms": "^4.2.5",
|
||||
"@primevue/themes": "^4.2.5",
|
||||
"@sentry/vue": "^8.48.0",
|
||||
@@ -482,9 +482,9 @@
|
||||
"license": "GPL-3.0-only"
|
||||
},
|
||||
"node_modules/@comfyorg/litegraph": {
|
||||
"version": "0.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.14.2.tgz",
|
||||
"integrity": "sha512-IGi1EyWPSRXkmNaFm4WWx0SnfLPc2DDJNVbiTfubDBwGWDRZqsA2vlCYN/1gFgw7evy02XrvcSYYuSUC4+gc+g==",
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.14.1.tgz",
|
||||
"integrity": "sha512-P/OlMsFHgkubvXrLhEJohQO+1e+xx6X/8yTXYFNj5Uz44GaXObaCDv+5guMSHASzuwiKMv4MPJbWSbdjeg5qVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"private": true,
|
||||
"version": "1.18.2",
|
||||
"version": "1.18.1",
|
||||
"type": "module",
|
||||
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
|
||||
"homepage": "https://comfy.org",
|
||||
@@ -72,7 +72,7 @@
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
|
||||
"@comfyorg/comfyui-electron-types": "^0.4.42",
|
||||
"@comfyorg/litegraph": "^0.14.2",
|
||||
"@comfyorg/litegraph": "^0.14.1",
|
||||
"@primevue/forms": "^4.2.5",
|
||||
"@primevue/themes": "^4.2.5",
|
||||
"@sentry/vue": "^8.48.0",
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"WIDGET_OUTLINE_COLOR": "#6e7581",
|
||||
"WIDGET_TEXT_COLOR": "#DDD",
|
||||
"WIDGET_SECONDARY_TEXT_COLOR": "#b2b7bd",
|
||||
"WIDGET_DISABLED_TEXT_COLOR": "#5c626d",
|
||||
"LINK_COLOR": "#9A9",
|
||||
"EVENT_LINK_COLOR": "#A86",
|
||||
"CONNECTING_LINK_COLOR": "#AFA"
|
||||
@@ -75,4 +74,4 @@
|
||||
"bar-shadow": "rgba(8, 8, 8, 0.75) 0 0 0.5rem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,6 @@
|
||||
"WIDGET_OUTLINE_COLOR": "#666",
|
||||
"WIDGET_TEXT_COLOR": "#DDD",
|
||||
"WIDGET_SECONDARY_TEXT_COLOR": "#999",
|
||||
"WIDGET_DISABLED_TEXT_COLOR": "#666",
|
||||
"LINK_COLOR": "#9A9",
|
||||
"EVENT_LINK_COLOR": "#A86",
|
||||
"CONNECTING_LINK_COLOR": "#AFA",
|
||||
@@ -69,4 +68,4 @@
|
||||
"bar-shadow": "rgba(16, 16, 16, 0.5) 0 0 0.5rem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,6 @@
|
||||
"WIDGET_OUTLINE_COLOR": "#30363d",
|
||||
"WIDGET_TEXT_COLOR": "#bcc2c8",
|
||||
"WIDGET_SECONDARY_TEXT_COLOR": "#999",
|
||||
"WIDGET_DISABLED_TEXT_COLOR": "#30363d",
|
||||
"LINK_COLOR": "#9A9",
|
||||
"EVENT_LINK_COLOR": "#A86",
|
||||
"CONNECTING_LINK_COLOR": "#AFA"
|
||||
@@ -75,4 +74,4 @@
|
||||
"bar-shadow": "rgba(0, 0, 0, 1) 0 0 0.5rem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,6 @@
|
||||
"WIDGET_OUTLINE_COLOR": "#999",
|
||||
"WIDGET_TEXT_COLOR": "#222",
|
||||
"WIDGET_SECONDARY_TEXT_COLOR": "#555",
|
||||
"WIDGET_DISABLED_TEXT_COLOR": "#999",
|
||||
"LINK_COLOR": "#4CAF50",
|
||||
"EVENT_LINK_COLOR": "#FF9800",
|
||||
"CONNECTING_LINK_COLOR": "#2196F3",
|
||||
@@ -71,4 +70,4 @@
|
||||
"bar-shadow": "rgba(16, 16, 16, 0.25) 0 0 0.5rem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,6 @@
|
||||
"WIDGET_OUTLINE_COLOR": "#545d70",
|
||||
"WIDGET_TEXT_COLOR": "#bcc2c8",
|
||||
"WIDGET_SECONDARY_TEXT_COLOR": "#999",
|
||||
"WIDGET_DISABLED_TEXT_COLOR": "#545d70",
|
||||
"LINK_COLOR": "#9A9",
|
||||
"EVENT_LINK_COLOR": "#A86",
|
||||
"CONNECTING_LINK_COLOR": "#AFA"
|
||||
@@ -75,4 +74,4 @@
|
||||
"bar-shadow": "rgba(0, 0, 0, 0.75) 0 0 0.5rem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,6 @@
|
||||
"WIDGET_OUTLINE_COLOR": "#839496",
|
||||
"WIDGET_TEXT_COLOR": "#fdf6e3",
|
||||
"WIDGET_SECONDARY_TEXT_COLOR": "#93a1a1",
|
||||
"WIDGET_DISABLED_TEXT_COLOR": "#657b83",
|
||||
"LINK_COLOR": "#2aa198",
|
||||
"EVENT_LINK_COLOR": "#268bd2",
|
||||
"CONNECTING_LINK_COLOR": "#859900"
|
||||
@@ -60,4 +59,4 @@
|
||||
"bar-shadow": "rgba(16, 16, 16, 0.5) 0 0 0.5rem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import './load3d'
|
||||
import './maskeditor'
|
||||
import './nodeTemplates'
|
||||
import './noteNode'
|
||||
import './previewAny'
|
||||
import './rerouteNode'
|
||||
import './saveImageExtraOutput'
|
||||
import './saveMesh'
|
||||
|
||||
50
src/extensions/core/previewAny.ts
Normal file
50
src/extensions/core/previewAny.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Preview Any - original implement from
|
||||
https://github.com/rgthree/rgthree-comfy/blob/main/py/display_any.py
|
||||
upstream requested in https://github.com/Kosinkadink/rfcs/blob/main/rfcs/0000-corenodes.md#preview-nodes
|
||||
*/
|
||||
import { IWidget } from '@comfyorg/litegraph'
|
||||
|
||||
import { DOMWidget } from '@/scripts/domWidget'
|
||||
import { ComfyWidgets } from '@/scripts/widgets'
|
||||
import { useExtensionService } from '@/services/extensionService'
|
||||
|
||||
useExtensionService().registerExtension({
|
||||
name: 'Comfy.PreviewAny',
|
||||
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||||
if (nodeData.name === 'PreviewAny') {
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated
|
||||
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
onNodeCreated ? onNodeCreated.apply(this, []) : undefined
|
||||
|
||||
const showValueWidget = ComfyWidgets['STRING'](
|
||||
this,
|
||||
'preview',
|
||||
['STRING', { multiline: true }],
|
||||
app
|
||||
).widget as DOMWidget<any, any>
|
||||
|
||||
showValueWidget.element.readOnly = true
|
||||
|
||||
console.log(showValueWidget)
|
||||
}
|
||||
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted === null || onExecuted === void 0
|
||||
? void 0
|
||||
: onExecuted.apply(this, [message])
|
||||
|
||||
const previewWidget = this.widgets?.find(
|
||||
(w: IWidget) => w.name === 'preview'
|
||||
)
|
||||
|
||||
if (previewWidget) {
|
||||
previewWidget.value = message.text[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -48,7 +48,6 @@ const litegraphBaseSchema = z.object({
|
||||
WIDGET_OUTLINE_COLOR: z.string(),
|
||||
WIDGET_TEXT_COLOR: z.string(),
|
||||
WIDGET_SECONDARY_TEXT_COLOR: z.string(),
|
||||
WIDGET_DISABLED_TEXT_COLOR: z.string(),
|
||||
LINK_COLOR: z.string(),
|
||||
EVENT_LINK_COLOR: z.string(),
|
||||
CONNECTING_LINK_COLOR: z.string(),
|
||||
|
||||
@@ -27,7 +27,6 @@ import {
|
||||
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 { useDialogService } from '@/services/dialogService'
|
||||
import { useExtensionService } from '@/services/extensionService'
|
||||
import { useLitegraphService } from '@/services/litegraphService'
|
||||
@@ -1334,20 +1333,6 @@ export class ComfyApp {
|
||||
} else {
|
||||
this.showErrorOnFileLoad(file)
|
||||
}
|
||||
} else if (
|
||||
file.type === 'video/mp4' ||
|
||||
file.name?.endsWith('.mp4') ||
|
||||
file.name?.endsWith('.mov') ||
|
||||
file.name?.endsWith('.m4v') ||
|
||||
file.type === 'video/quicktime' ||
|
||||
file.type === 'video/x-m4v'
|
||||
) {
|
||||
const mp4Info = await getFromIsobmffFile(file)
|
||||
if (mp4Info.workflow) {
|
||||
this.loadGraphData(mp4Info.workflow, true, true, fileName)
|
||||
} else if (mp4Info.prompt) {
|
||||
this.loadApiJson(mp4Info.prompt, fileName)
|
||||
}
|
||||
} else if (
|
||||
file.type === 'model/gltf-binary' ||
|
||||
file.name?.endsWith('.glb')
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
import {
|
||||
ComfyApiWorkflow,
|
||||
ComfyWorkflowJSON
|
||||
} from '@/schemas/comfyWorkflowSchema'
|
||||
import {
|
||||
ASCII,
|
||||
ComfyMetadata,
|
||||
ComfyMetadataTags,
|
||||
IsobmffBoxContentRange
|
||||
} from '@/types/metadataTypes'
|
||||
|
||||
const MAX_READ_BYTES = 2 * 1024 * 1024
|
||||
const BOX_TYPES = {
|
||||
USER_DATA: [0x75, 0x64, 0x74, 0x61],
|
||||
META_DATA: [0x6d, 0x65, 0x74, 0x61],
|
||||
ITEM_LIST: [0x69, 0x6c, 0x73, 0x74],
|
||||
KEYS: [0x6b, 0x65, 0x79, 0x73],
|
||||
DATA: [0x64, 0x61, 0x74, 0x61],
|
||||
MOVIE: [0x6d, 0x6f, 0x6f, 0x76]
|
||||
}
|
||||
const SIZES = {
|
||||
HEADER: 8,
|
||||
VERSION: 4,
|
||||
LOCALE: 4,
|
||||
ITEM_MIN: 8
|
||||
}
|
||||
|
||||
const bufferMatchesBoxType = (
|
||||
data: Uint8Array,
|
||||
pos: number,
|
||||
boxType: number[]
|
||||
): boolean => {
|
||||
if (pos + 4 > data.length) return false
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (data[pos + i] !== boxType[i]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const readUint32 = (data: Uint8Array, pos: number): number => {
|
||||
if (pos + 4 > data.length) return 0
|
||||
return (
|
||||
(data[pos] << 24) |
|
||||
(data[pos + 1] << 16) |
|
||||
(data[pos + 2] << 8) |
|
||||
data[pos + 3]
|
||||
)
|
||||
}
|
||||
|
||||
const findIsobmffBoxByType = (
|
||||
data: Uint8Array,
|
||||
startPos: number,
|
||||
endPos: number,
|
||||
boxType: number[]
|
||||
): IsobmffBoxContentRange => {
|
||||
for (let pos = startPos; pos < endPos - 8; pos++) {
|
||||
const size = readUint32(data, pos)
|
||||
if (size < SIZES.ITEM_MIN) continue // Minimum size is 8 bytes
|
||||
|
||||
if (bufferMatchesBoxType(data, pos + 4, boxType))
|
||||
return { start: pos + SIZES.HEADER, end: pos + size } // Skip header
|
||||
|
||||
// If type doesn't match, ensure size is valid before skipping
|
||||
if (pos + size > endPos) return null
|
||||
|
||||
pos += size - 1 // Skip to the next potential box start
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const extractJson = (data: Uint8Array, start: number, end: number): any => {
|
||||
let jsonStart = start
|
||||
while (jsonStart < end && data[jsonStart] !== ASCII.OPEN_BRACE) {
|
||||
jsonStart++
|
||||
}
|
||||
if (jsonStart >= end) return null
|
||||
|
||||
try {
|
||||
const jsonText = new TextDecoder().decode(data.slice(jsonStart, end))
|
||||
return JSON.parse(jsonText)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const readUtf8String = (data: Uint8Array, start: number, end: number): string =>
|
||||
new TextDecoder().decode(data.slice(start, end))
|
||||
|
||||
const parseKeysBox = (
|
||||
data: Uint8Array,
|
||||
keysBoxStart: number,
|
||||
keysBoxEnd: number
|
||||
): Map<number, string> => {
|
||||
const keysMap = new Map<number, string>()
|
||||
let pos = keysBoxStart + 4 // Skip version/flags
|
||||
if (pos + 4 > keysBoxEnd) return keysMap
|
||||
|
||||
const entryCount = readUint32(data, pos)
|
||||
pos += 4
|
||||
|
||||
for (let i = 1; i <= entryCount; i++) {
|
||||
// Keys are 1-indexed
|
||||
if (pos + SIZES.HEADER > keysBoxEnd) break
|
||||
|
||||
const keySize = readUint32(data, pos)
|
||||
pos += SIZES.HEADER
|
||||
|
||||
const keyNameEnd = pos + keySize - SIZES.HEADER
|
||||
if (keySize < SIZES.ITEM_MIN || keyNameEnd > keysBoxEnd) break
|
||||
|
||||
const keyName = readUtf8String(data, pos, keyNameEnd)
|
||||
keysMap.set(i, keyName)
|
||||
pos = keyNameEnd
|
||||
}
|
||||
return keysMap
|
||||
}
|
||||
|
||||
const extractMetadataValueFromDataBox = (
|
||||
data: Uint8Array,
|
||||
dataBoxStart: number,
|
||||
dataBoxEnd: number,
|
||||
keyName: string
|
||||
): ComfyWorkflowJSON | ComfyApiWorkflow | null => {
|
||||
const valueStart = dataBoxStart + SIZES.VERSION + SIZES.LOCALE
|
||||
if (valueStart >= dataBoxEnd) return null
|
||||
|
||||
const lowerKeyName = keyName.toLowerCase()
|
||||
if (
|
||||
lowerKeyName === ComfyMetadataTags.PROMPT.toLowerCase() ||
|
||||
lowerKeyName === ComfyMetadataTags.WORKFLOW.toLowerCase()
|
||||
) {
|
||||
return extractJson(data, valueStart, dataBoxEnd) || null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const parseIlstItem = (
|
||||
data: Uint8Array,
|
||||
itemStart: number,
|
||||
itemEnd: number,
|
||||
keysMap: Map<number, string>,
|
||||
metadata: ComfyMetadata
|
||||
) => {
|
||||
if (itemStart + SIZES.HEADER > itemEnd) return
|
||||
|
||||
const itemIndex = readUint32(data, itemStart + 4)
|
||||
const keyName = keysMap.get(itemIndex)
|
||||
if (!keyName) return
|
||||
|
||||
const dataBox = findIsobmffBoxByType(
|
||||
data,
|
||||
itemStart + SIZES.HEADER,
|
||||
itemEnd,
|
||||
BOX_TYPES.DATA
|
||||
)
|
||||
if (dataBox) {
|
||||
const value = extractMetadataValueFromDataBox(
|
||||
data,
|
||||
dataBox.start,
|
||||
dataBox.end,
|
||||
keyName
|
||||
)
|
||||
if (value !== null) {
|
||||
metadata[keyName.toLowerCase() as keyof ComfyMetadata] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const parseIlstBox = (
|
||||
data: Uint8Array,
|
||||
ilstStart: number,
|
||||
ilstEnd: number,
|
||||
keysMap: Map<number, string>,
|
||||
metadata: ComfyMetadata
|
||||
) => {
|
||||
let pos = ilstStart
|
||||
while (pos < ilstEnd - SIZES.HEADER) {
|
||||
const itemSize = readUint32(data, pos)
|
||||
if (itemSize <= SIZES.HEADER || pos + itemSize > ilstEnd) break // Invalid item size
|
||||
parseIlstItem(data, pos, pos + itemSize, keysMap, metadata)
|
||||
pos += itemSize
|
||||
}
|
||||
}
|
||||
|
||||
const findUserDataBox = (data: Uint8Array): IsobmffBoxContentRange => {
|
||||
let userDataBox: IsobmffBoxContentRange = null
|
||||
|
||||
// Metadata can be in 'udta' at top level or inside 'moov'
|
||||
userDataBox = findIsobmffBoxByType(data, 0, data.length, BOX_TYPES.USER_DATA)
|
||||
|
||||
if (!userDataBox) {
|
||||
const moovBox = findIsobmffBoxByType(data, 0, data.length, BOX_TYPES.MOVIE)
|
||||
if (moovBox) {
|
||||
userDataBox = findIsobmffBoxByType(
|
||||
data,
|
||||
moovBox.start,
|
||||
moovBox.end,
|
||||
BOX_TYPES.USER_DATA
|
||||
)
|
||||
}
|
||||
}
|
||||
return userDataBox
|
||||
}
|
||||
|
||||
const parseIsobmffMetadata = (data: Uint8Array): ComfyMetadata => {
|
||||
const metadata: ComfyMetadata = {}
|
||||
const userDataBox = findUserDataBox(data)
|
||||
if (!userDataBox) return metadata
|
||||
|
||||
const metaBox = findIsobmffBoxByType(
|
||||
data,
|
||||
userDataBox.start,
|
||||
userDataBox.end,
|
||||
BOX_TYPES.META_DATA
|
||||
)
|
||||
if (!metaBox) return metadata
|
||||
|
||||
const metaContentStart = metaBox.start + SIZES.VERSION
|
||||
const keysBox = findIsobmffBoxByType(
|
||||
data,
|
||||
metaContentStart,
|
||||
metaBox.end,
|
||||
BOX_TYPES.KEYS
|
||||
)
|
||||
if (!keysBox) return metadata
|
||||
|
||||
const keysMap = parseKeysBox(data, keysBox.start, keysBox.end)
|
||||
if (keysMap.size === 0) return metadata // keys box is empty or failed to parse
|
||||
|
||||
const ilstBox = findIsobmffBoxByType(
|
||||
data,
|
||||
metaContentStart,
|
||||
metaBox.end,
|
||||
BOX_TYPES.ITEM_LIST
|
||||
)
|
||||
if (!ilstBox) return metadata
|
||||
|
||||
parseIlstBox(data, ilstBox.start, ilstBox.end, keysMap, metadata)
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts ComfyUI Workflow metadata from an ISO Base Media File Format (ISOBMFF) file
|
||||
* (e.g., MP4, MOV) by parsing the `udta.meta.keys` and `udta.meta.ilst` boxes.
|
||||
* @param file - The file to extract metadata from.
|
||||
*/
|
||||
export function getFromIsobmffFile(file: File): Promise<ComfyMetadata> {
|
||||
return new Promise<ComfyMetadata>((resolve) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = (event: ProgressEvent<FileReader>) => {
|
||||
if (!event.target?.result) {
|
||||
resolve({})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const data = new Uint8Array(event.target.result as ArrayBuffer)
|
||||
resolve(parseIsobmffMetadata(data))
|
||||
} catch (e) {
|
||||
console.error('Parser: Error parsing ISOBMFF metadata:', e)
|
||||
resolve({})
|
||||
}
|
||||
}
|
||||
reader.onerror = (err) => {
|
||||
console.error('FileReader: Error reading ISOBMFF file:', err)
|
||||
resolve({})
|
||||
}
|
||||
reader.readAsArrayBuffer(file.slice(0, MAX_READ_BYTES))
|
||||
})
|
||||
}
|
||||
@@ -46,8 +46,7 @@ export type TextRange = {
|
||||
|
||||
export enum ASCII {
|
||||
GLTF = 0x46546c67,
|
||||
JSON = 0x4e4f534a,
|
||||
OPEN_BRACE = 0x7b
|
||||
JSON = 0x4e4f534a
|
||||
}
|
||||
|
||||
export enum GltfSizeBytes {
|
||||
@@ -79,9 +78,3 @@ export type GltfJsonData = {
|
||||
}
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the content range [start, end) of an ISOBMFF box, excluding its header.
|
||||
* Null if the box was not found.
|
||||
*/
|
||||
export type IsobmffBoxContentRange = { start: number; end: number } | null
|
||||
|
||||
Reference in New Issue
Block a user