Skip to content
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

Extend Support for custom SaveImage nodes within SaveImageExtraOutput #2218

Open
HaydenReeve opened this issue Jan 10, 2025 · 8 comments · May be fixed by #2219
Open

Extend Support for custom SaveImage nodes within SaveImageExtraOutput #2218

HaydenReeve opened this issue Jan 10, 2025 · 8 comments · May be fixed by #2219

Comments

@HaydenReeve
Copy link

HaydenReeve commented Jan 10, 2025

Overview

I would like to see a small change to the SaveImageExtraOutput class to allow for easier extension of the SaveImage functionality, such as Node S&R, as well as DateTime formatting per run.

app.registerExtension({
name: 'Comfy.SaveImageExtraOutput',
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === 'SaveImage' || nodeData.name === 'SaveAnimatedWEBP') {
const onNodeCreated = nodeType.prototype.onNodeCreated
// When the SaveImage node is created we want to override the serialization of the output name widget to run our S&R

Proposal

There might be a few better ways to increase the versatility and useability than my suggestion here, but I think it acts as an improvement in a stop-gap term until something more sustainable might be thought up.

Simply instead of

nodeData.name === 'SaveImage'

We use

nodeData.name.includes('SaveImage')

This allows for custom nodes to extend using the SaveImage terminology, such as SaveImageAsWebp or SaveImageAnimated, or CustomSaveImage.

I find this approach to be simple without adding too much overhead burdon to the system.

Testing

I've done some initial testing myself and it seems to work quite well. I now have a SaveImageToGrid node properly working as intended and substituting my DateTime string as expected.

I'm in the process of putting up a PR with this tiny change, but I thought before I did that I should put through a general issue and prompt some discussion first.

┆Issue is synchronized with this Notion page by Unito

@HaydenReeve HaydenReeve changed the title Extend Support for Additional Save nodes within the SaveImageExtraOutput Extend Support for custom SaveImage nodes within SaveImageExtraOutput Jan 10, 2025
@HaydenReeve
Copy link
Author

I've put up a PR with my suggestions. Hopefully the change is agreeable.

Please let me know if anyone has any feedback and I'll try to respond quickly.

@webfiltered
Copy link
Contributor

Using string includes on the name is not reliable. Something like a boolean that flags a node as being a custom image save node would be appropriate. But before all that, may I ask what the actual use-case is, for this?

@huchenlei
Copy link
Member

Hmmm, the Node name for S&R is a legacy feature that we have not touched since the inception of this project. We might want to reconsider how to implement it. For now to unblock your node, can you just add your node's name(id) to the matching list?

Maybe something like:

const SAVE_NODES_SUPPORTED = [
  'SaveImage',
  'SaveAnimatedWEBP',
  // Custom save nodes
  'CustomSaveImage',
  ...
]

if (SAVE_NODES_SUPPORTED.includes(nodeData.name) {...}

@HaydenReeve
Copy link
Author

HaydenReeve commented Jan 10, 2025

Using string includes on the name is not reliable. Something like a boolean that flags a node as being a custom image save node would be appropriate. But before all that, may I ask what the actual use-case is, for this?

Originally posted by @webfiltered in #2218 (comment)


Being unreliable is a consideration I did have, however I feel it is reasonable to simply check that the extension we're working with is available before we do anything should be sufficient until this feature is relooked at in the future.

It's not a shining brilliance in terms of a code change, but it is small and relatively unintrusive.

As for the use case; To me it is twofold. I'll provide some example images here as well.

To have an external node input to a SaveImage node, like so;

January-11 (Saturday), 00;32;52

This is useful because with long file names, and subdirectories, it can be challenging to edit them...

January-11 (Saturday), 00;32;25 January-11 (Saturday), 00;34;28

The second use case is for external nodes, such as this very nice node; which exports all images in a batch to a gridded output.

January-11 (Saturday), 00;33;25

Under normal circumstances, without a custom implementation using a dirty flag for DateTime.Now, a custom node cannot search and replace the DateTime string.

@HaydenReeve
Copy link
Author

Hmmm, the Node name for S&R is a legacy feature that we have not touched since the inception of this project. We might want to reconsider how to implement it. For now to unblock your node, can you just add your node's name(id) to the matching list?

Originally posted by @huchenlei in #2218 (comment)


I did very much figure this was a legacy feature, and as such I didn't want to go overboard with suggesting some total overhauls. I absolutely get that there are better ways to do this, but I figure a light touch here might be the best approach until something more comprehensive and maintainable can be implemented in the future.

Your solution is basically what I've set up already. I just have a stash with some private changes and this is one of them.

I thought I would share my current override since I have found it useful in my workflows so far.

@webfiltered
Copy link
Contributor

webfiltered commented Jan 10, 2025

For a custom node, all you should need to do is add the relevant part of saveImageExtraOutput.ts into a js file in your extension's registered WEB dir.

app.registerExtension({
  name: 'Custom.SaveImageExtraOutput',
  async beforeRegisterNodeDef(nodeType, nodeData, app) {
    if (nodeData.name === 'CustomSaveImage') {
      const { onNodeCreated } = nodeType.prototype
      nodeType.prototype.onNodeCreated = function () {
        const r = onNodeCreated
          ? onNodeCreated.apply(this, arguments)
          : undefined

        const widget = this.widgets.find((w) => w.name === 'filename_prefix')
        if (widget) widget.serializeValue = () => applyTextReplacements(app, widget.value)

        return r
      }
    }
  }
})

Disclaimer: written in GH comment box.

@HaydenReeve
Copy link
Author

That is quite useful to know, actually. I appreciate that.

Although that would only apply to nodes that I write or contribute to. Perhaps that could still work as an alternative stop-gap though if you registered a custom input field to this and just loaded any widgets you want the data with using the new custom node.

That said, as I suspect this area might see some revamping soon enough, I'm loath to go too overboard here with something that might last all of a few days in the grand scheme of things.

@webfiltered
Copy link
Contributor

It will apply to any node name you specify, provided it is loaded before the node definition is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants