Photoshop publish as png

Hey all,

Getting up and going with shotgun - and having a lot of fun doing it! I’ve got what seems like it should be a relatively simple thing to do, but can’t figure out quite how to do it.

I’d like to be able to change my publishes from photoshop to publish flattened png files instead of psd. I was hoping that I could do something similar to what I’ve done with Maya to publish cameras/etc as a second step - but haven’t quite figured out how to get it to work correctly.

Does anyone have a quick set of instructions I could follow? Thanks!
-Jason

2 Likes

Hey @Shhlife,

Great question, I’ve had a poke around and have some thoughts, but I want to get someone in the tk team to discuss this with before I post it in public :slight_smile:

-David

1 Like

Hi @Shhlife,

You can do this by setting up your own publish plugin. The publisher api documentation has a lot of information on how to set up custom publishers, but here’s a quick proof of concept as well. I was able to publish PNGs by doing the following:

  1. Updating the template to use the “.png” extension
    photoshop_asset_publish:
        definition: '@asset_root/publish/photoshop/{name}.v{version}.png'
  1. Subclassing tk-photoshopcc’s publish_document.py hook and overwriting the _copy_work_to_publish method to do the save correctly. The hook is saved in <config_dir>/hooks/tk-multi-publish2/basic/publish_document.py
import os
import traceback
import sgtk

from sgtk.util.filesystem import ensure_folder_exists

HookBaseClass = sgtk.get_hook_baseclass()

class PhotoshopCCPNGDocumentPublishPlugin(HookBaseClass):
    def _copy_work_to_publish(self, settings, item):
        '''
            Overwrites the method from the publish_document hook in the PS CC engine to
            save the file with a new extension instead of just copying the work file
        '''

        # ---- ensure templates are available
        work_template = item.properties.get("work_template")
        if not work_template:
            self.logger.debug(
                "No work template set on the item. "
                "Skipping file save to publish location."
            )
            return

        publish_template = self.get_publish_template(settings, item)
        if not publish_template:
            self.logger.debug(
                "No publish template set on the item. "
                "Skipping file save to publish location."
            )
            return

        work_file = item.properties.path
        work_fields = work_template.get_fields(work_file)
        missing_keys = publish_template.missing_keys(work_fields)

        if missing_keys:
            self.logger.warning(
                "Work file '%s' missing keys required for the publish "
                "template: %s" % (work_file, missing_keys)
            )
            return

        publish_file = publish_template.apply_fields(work_fields)

        try:
            publish_folder = os.path.dirname(publish_file)
            ensure_folder_exists(publish_folder)

            engine = self.parent.engine
            document = item.properties["document"]

            # If you want to be able to save to other formats by updating the extension in the template
            # you will need more robust logic here to figure out the correct SaveOptions class
            save_options = engine.adobe.PNGSaveOptions()

            # You may want to add these as settings for the publisher plugin instead of hard-coding them here
            save_options.compression = 2
            save_options.interlaced = False

            document.saveAs(engine.adobe.File(publish_file), save_options, True)

        except Exception:
            raise Exception(
                "Failed to save work file '%s' to '%s'.\n%s"
                % (work_file, publish_file, traceback.format_exc())
            )

        self.logger.debug(
            "Saved work file '%s' to publish file '%s'."
            % (work_file, publish_file)
        )
  1. Setting up a publisher plugin to point to my new hook
settings.tk-multi-publish2.photoshop.asset_step:
  ...
  - name: Publish to Shotgun
    hook: "{self}/publish_file.py:{engine}/tk-multi-publish2/basic/publish_document.py:{config}/tk-multi-publish2/basic/publish_document.py"
    settings:
        Publish Template: photoshop_asset_publish

NOTE: I overwrite an internal method _copy_work_to_publish in order to keep the amount of code posted here to a minimum. You cannot rely on the method being called with the same arguments or it might not even be called at all in future releases. The best way to do this would be to define the publish method instead. You would need to combine the publish methods from tk-photoshopcc and tk-multi-publish2 and update it to use the save logic from above.

4 Likes

Hey @Ehsan,

I implemented the hook above to be an additional Publish Plugin.
But there’s a behaviour I don’t understand:

When i only select the .psd publish, the .psd ist published correctly.
When i select the .psd and the new .png publish, only the .png is published to its path, but the .psd is not anymore. In Shotgun both publishes appear, but the psd publish is pointing to the png file as well.

I think understanding this will be a big benefit in understanding the whole publish process.
Could you please help me with fixing this to be an additional publish instead or could you explain why it behaves that way?

Thanks in advance!
Jonas

2 Likes

Hi @jonase,

If you’re trying to have multiple publishes of the same file, there is a little more work to be done. The behaviour you’re seeing is because the two publish plugins are overwriting each other’s properties.

When you click “publish” there are 3 phases that the publisher goes through. Validate, Publish, and Finalize. All plugins go through one phase, then the next, then the next. I’m assumming you’re using different templates for the two publish plugins. If so, the issue you’re running into works out to something like this.

Validation Phase

  1. PSD plugin validates and runs the following:
    item.properties["publish_template"] = publish_template   # This sets the value to the PSD template
    
  2. PNG plugin validates and runs the same code:
    item.properties["publish_template"] = publish_template   # This sets the value to the PNG template
    

Publish Phase

  1. PSD plugin needs a template to publish. It gets it from the item by running:
    item.get_property("publish_template")
    
    There is only one publish_template property on the item and it was most recently set to the PNG publish template. The path for this publish is now pointing to a PNG file which is incorrect. The PSD file will actually be saved with a PNG extension. But you won’t see this because of the next step.
  2. PNG plugin gets the publish_template, which is pointing to the PNG one and this plugin works as expected.

This setup works fine when you only have one plugin publish a specific file. But when you are going to have multiple plugins, you can’t set a single value on the item because as we saw above, it will get overwritten.

You can solve this in multiple ways, but I would just stop relying on the setting from the validation phase and have each of your plugins find the correct template. You can just copy that portion of the the code from the validate method and put it in your publish methods.

    publish_template_setting = settings.get("Publish Template")
    publish_template = publisher.engine.get_template_by_name(
        publish_template_setting.value
    )
    if publish_template:
        item.properties["publish_template"] = publish_template

After you’ve done this, you should get the correct behaviour in that you get a PSD and a PNG publish (if both are checked). But there are other things you need to modify as well.

The publish phase sets another property item that is overwitten when you have multiple publishers. This is what the code looks like:

item.properties.sg_publish_data = sgtk.util.register_publish(**publish_data)

The content of sg_publish_data is later used to show the log messages in the “publish compelted” UI. You’ll need to do something similar here to have each plugin store its own value in the properties and have its finalize method display the correct result.

One last thing that you might want to fix is that both your plugins will try to up-version the work file, which is not necessary. This doesn’t break because the up-version code issues a warning and moves on if the next version already exists. But it’s good to explicitly handle this and make sure only one of the plugins does the up-version process (remember that one or the other plugin can be disabled, so they both need to have the logic and maybe communicate using a different item.property to signal that the up-version is already done)

I hope that helps you understand the mechanism better and solves your problem.

Ehsan

4 Likes

@jonase One more thing you could look at that is probably the best way to solve all the above problems is using local properties instead of global ones. That way you don’t have to worry about the various plugins overwriting each other’s properties.

1 Like

@Ehsan
Thank you very much, that helped a lot to understand what is going on and to build myself a working solution. What was confusing to me was the overwriting of the variables.
I went to specialize the global properties, which is a bit messy, so I guess the local properties will be the go-to for future plugins.

Thanks again! Jonas

1 Like