Comfy-Org / Comfy-Org/ComfyUI_frontend
Support Generalized Public API for declaring node supports uploading given filetypes
- Dominant language
- TypeScript
- Stars
- 2k
- Forks
- 699
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 490
Description
## Implementation Strategy for Generalized Upload API
Here's how I would approach the generalization:
### 1. **Create a Universal Upload Schema**
First, extend the node definition schema to support a more flexible upload configuration:
```typescript
// In nodeDefSchema.ts
file_upload: z.object({
target_input: z.string(), // The combo widget to populate
accept: z.array(z.string()), // MIME types and extensions
allow_batch: z.boolean().optional().default(false),
preview: z.boolean().optional().default(false),
subfolder: z.string().optional(),
}).optional()
```
This would replace the current individual flags (`image_upload`, `text_file_upload`, etc.) with a single, configurable system.
### 2. **Create a Generic Upload Widget Factory**
Replace the multiple upload widget composables with a single, configurable one:
```typescript
// useGenericUploadWidget.ts
export const useGenericUploadWidget = () => {
const widgetConstructor = (node, inputSpec: GenericUploadInputSpec) => {
const { target_input, accept, allow_batch, preview } = inputSpec;
// Universal file filter based on accept array
const fileFilter = createFileFilter(accept);
// Common upload logic
const handleFileUpload = async (files: File[]) => {
const supportedFiles = files.filter(fileFilter);
const paths = await uploadFiles(supportedFiles);
updateTargetWidget(node, target_input, paths, allow_batch);
};
// Setup drag/drop, paste, and file input consistently
setupFileInteractions(node, { fileFilter, allow_batch, onFileSelect: handleFileUpload });
return createUploadButton(node, inputSpec);
};
return widgetConstructor;
};
```
### 3. **Universal Upload Detection Extension**
Create a single extension that handles all upload types:
```typescript
// universalUpload.ts
app.registerExtension({
name: 'Comfy.UniversalUpload',
beforeRegisterNodeDef(nodeType, nodeData) {
const uploads = findUploadConfigurations(nodeData);
uploads.forEach(({ inputName, config }) => {
nodeData.input.required[`${inputName}_upload`] = [
'GENERIC_FILE_UPLOAD',
{
target_input: inputName,
accept: config.accept,
allow_batch: config.allow_batch,
preview: config.preview
}
];
});
}
});
```
### 4. **Backward Compatibility Layer**
To ensure existing nodes continue working, create a migration layer:
```typescript
// uploadMigration.ts
const LEGACY_UPLOAD_MAPPINGS = {
image_upload: { accept: ['image/*'], preview: true },
video_upload: { accept: ['video/*'], preview: true },
audio_upload: { accept: ['audio/*'], preview: true },
text_file_upload: { accept: ['text/plain', '.txt', '.pdf'], preview: false }
};
function migrateLegacyUploads(nodeData) {
Object.entries(nodeData.input?.required || {}).forEach(([name, spec]) => {
const uploadType = findLegacyUploadType(spec);
if (uploadType) {
nodeData.input.required[name].file_upload = {
target_input: name,
...LEGACY_UPLOAD_MAPPINGS[uploadType]
};
}
});
}
```
### 5. **Enhanced File Type Support**
Create a robust file filtering system:
```typescript
// fileTypeUtils.ts
export function createFileFilter(accept: string[]): (file: File) => boolean {
return (file: File) => {
return accept.some(pattern => {
if (pattern.startsWith('.')) {
return file.name.toLowerCase().endsWith(pattern.toLowerCase());
}
if (pattern.includes('*')) {
const regex = new RegExp(pattern.replace('*', '.*'));
return regex.test(file.type);
}
return file.type === pattern;
});
};
}
```
### 6. **Benefits of This Approach**
1. **Reduced Boilerplate**: One widget handles all file types
2. **Consistent Behavior**: Drag/drop, paste, and upload work the same across all file types
3. **Easy Extension**: Adding new file types requires no code changes
4. **Bug Fix Propagation**: Fixes apply to all upload types automatically
5. **Better Testing**: Single code path to test instead of multiple implementations
6. **Schema Consistency**: Unified configuration format
### 7. **Migration Path**
1. Implement the generic system alongside existing code
2. Add backward compatibility for current upload flags
3. Gradually migrate existing nodes to use the new schema
4. Remove legacy upload extensions once migration is complete
5. Update documentation and examples
This approach would eliminate the current duplication while maintaining full backward compatibility and providing a much more maintainable foundation for future file upload features.
┆Issue is synchronized with this [Notion page](https://www.notion.so/Issue-4071-Support-Generalized-Public-API-for-declaring-node-supports-uploading-given-filetypes-2086d73d36508133beeded88a8b9f600) by [Unito](https://www.unito.io)
Contributor guide
Assessment
This issue has not been assessed yet.