-
Notifications
You must be signed in to change notification settings - Fork 490
feat: add bulk actions for workflow operations in media assets #7992
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -365,6 +365,208 @@ export function useMediaAssetActions() { | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Add multiple assets to the current workflow | ||
| * Creates loader nodes for each asset | ||
| */ | ||
| const addMultipleToWorkflow = async (assets: AssetItem[]) => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we sure that the "Add to workflow action" for multiple media assets is meant to add a loader node with the asset? For some reason I was under the impression that it was supposed to add the workflow that generated that asset into the current workflow (similar to the behavior when clicking right-clicking a workflow and doing "insert workflow")? |
||
| if (!assets || assets.length === 0) return | ||
|
|
||
| const NODE_OFFSET = 50 | ||
| let nodeIndex = 0 | ||
| let succeeded = 0 | ||
| let failed = 0 | ||
|
|
||
| for (const asset of assets) { | ||
| const { nodeType, widgetName } = detectNodeTypeFromFilename(asset.name) | ||
|
|
||
| if (!nodeType || !widgetName) { | ||
| failed++ | ||
| continue | ||
| } | ||
|
|
||
| const nodeDef = nodeDefStore.nodeDefsByName[nodeType] | ||
| if (!nodeDef) { | ||
| failed++ | ||
| continue | ||
| } | ||
|
|
||
| const center = litegraphService.getCanvasCenter() | ||
| const node = litegraphService.addNodeOnGraph(nodeDef, { | ||
| pos: [ | ||
| center[0] + nodeIndex * NODE_OFFSET, | ||
| center[1] + nodeIndex * NODE_OFFSET | ||
| ] | ||
| }) | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
viva-jinyi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if (!node) { | ||
| failed++ | ||
| continue | ||
| } | ||
|
|
||
| const metadata = getOutputAssetMetadata(asset.user_metadata) | ||
| const assetType = getAssetType(asset, 'input') | ||
|
|
||
| const annotated = createAnnotatedPath( | ||
| { | ||
| filename: asset.name, | ||
| subfolder: metadata?.subfolder || '', | ||
| type: isResultItemType(assetType) ? assetType : undefined | ||
| }, | ||
| { | ||
| rootFolder: isResultItemType(assetType) ? assetType : undefined | ||
| } | ||
| ) | ||
|
|
||
| const widget = node.widgets?.find((w) => w.name === widgetName) | ||
| if (widget) { | ||
| widget.value = annotated | ||
| widget.callback?.(annotated) | ||
| } | ||
| node.graph?.setDirtyCanvas(true, true) | ||
| succeeded++ | ||
| nodeIndex++ | ||
viva-jinyi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| if (failed === 0) { | ||
| toast.add({ | ||
| severity: 'success', | ||
| summary: t('g.success'), | ||
| detail: t('mediaAsset.selection.nodesAddedToWorkflow', { | ||
| count: succeeded | ||
| }), | ||
| life: 2000 | ||
| }) | ||
| } else if (succeeded === 0) { | ||
| toast.add({ | ||
| severity: 'error', | ||
| summary: t('g.error'), | ||
| detail: t('mediaAsset.selection.failedToAddNodes'), | ||
| life: 3000 | ||
| }) | ||
| } else { | ||
| toast.add({ | ||
| severity: 'warn', | ||
| summary: t('g.warning'), | ||
| detail: t('mediaAsset.selection.partialAddNodesSuccess', { | ||
| succeeded, | ||
| failed | ||
| }), | ||
| life: 3000 | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Open workflows from multiple assets in new tabs | ||
| */ | ||
| const openMultipleWorkflows = async (assets: AssetItem[]) => { | ||
| if (!assets || assets.length === 0) return | ||
|
|
||
| let succeeded = 0 | ||
| let failed = 0 | ||
|
|
||
| for (const asset of assets) { | ||
| try { | ||
| const { workflow, filename } = await extractWorkflowFromAsset(asset) | ||
| const result = await workflowActions.openWorkflowAction( | ||
| workflow, | ||
| filename | ||
| ) | ||
|
|
||
| if (result.success) { | ||
| succeeded++ | ||
| } else { | ||
| failed++ | ||
| } | ||
| } catch { | ||
| failed++ | ||
| } | ||
| } | ||
|
|
||
| if (failed === 0) { | ||
| toast.add({ | ||
| severity: 'success', | ||
| summary: t('g.success'), | ||
| detail: t('mediaAsset.selection.workflowsOpened', { count: succeeded }), | ||
| life: 2000 | ||
| }) | ||
| } else if (succeeded === 0) { | ||
| toast.add({ | ||
| severity: 'warn', | ||
| summary: t('g.warning'), | ||
| detail: t('mediaAsset.selection.noWorkflowsFound'), | ||
| life: 3000 | ||
| }) | ||
| } else { | ||
| toast.add({ | ||
| severity: 'warn', | ||
| summary: t('g.warning'), | ||
| detail: t('mediaAsset.selection.partialWorkflowsOpened', { | ||
| succeeded, | ||
| failed | ||
| }), | ||
| life: 3000 | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Export workflows from multiple assets as JSON files | ||
| */ | ||
| const exportMultipleWorkflows = async (assets: AssetItem[]) => { | ||
| if (!assets || assets.length === 0) return | ||
|
|
||
| let succeeded = 0 | ||
| let failed = 0 | ||
|
|
||
| for (const asset of assets) { | ||
| try { | ||
| const { workflow, filename } = await extractWorkflowFromAsset(asset) | ||
| const result = await workflowActions.exportWorkflowAction( | ||
| workflow, | ||
| filename | ||
| ) | ||
|
|
||
| if (result.success) { | ||
| succeeded++ | ||
| } else { | ||
| failed++ | ||
| } | ||
| } catch { | ||
| failed++ | ||
| } | ||
| } | ||
|
|
||
| if (failed === 0) { | ||
| toast.add({ | ||
| severity: 'success', | ||
| summary: t('g.success'), | ||
| detail: t('mediaAsset.selection.workflowsExported', { | ||
| count: succeeded | ||
| }), | ||
| life: 2000 | ||
| }) | ||
| } else if (succeeded === 0) { | ||
| toast.add({ | ||
| severity: 'warn', | ||
| summary: t('g.warning'), | ||
| detail: t('mediaAsset.selection.noWorkflowsToExport'), | ||
| life: 3000 | ||
| }) | ||
| } else { | ||
| toast.add({ | ||
| severity: 'warn', | ||
| summary: t('g.warning'), | ||
| detail: t('mediaAsset.selection.partialWorkflowsExported', { | ||
| succeeded, | ||
| failed | ||
| }), | ||
| life: 3000 | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Delete multiple assets with confirmation dialog | ||
| * @param assets Array of assets to delete | ||
|
|
@@ -482,7 +684,10 @@ export function useMediaAssetActions() { | |
| deleteMultipleAssets, | ||
| copyJobId, | ||
| addWorkflow, | ||
| addMultipleToWorkflow, | ||
| openWorkflow, | ||
| exportWorkflow | ||
| openMultipleWorkflows, | ||
| exportWorkflow, | ||
| exportMultipleWorkflows | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.