When I try to save as JPEG, it saves as .psd instead. Why?

Here’s the code I use to try to save the current Photoshop document as a JPEG:

async function saveAsJPEG(quality = 6) {
try {
// Prompt the user to select a save location
const entry = await fs.getFileForSaving(“output.jpeg”);

    if (!entry) {
        console.error("No location selected for saving.");
        return;
    }
    
    const outputFileToken = await fs.createSessionToken(entry);
    
    // Use batchPlay to save the document as JPEG
    const result = await app.batchPlay([
        {
            "_obj": "save",
            "as": {
                "_obj": "JPEG",
                "options": {
                    "quality": quality,
                    "formatOptions": "optimized"
                }
            },
            "in": {
                "_path": outputFileToken,
                "_kind": "local"
            },
            "documentID": app.activeDocument._id,
            "copy": false,
            "overwrite": true,
            "_isCommand": true,
            "_options": {
                "dialogOptions": "dontDisplay"
            }
        }
    ], {
        "synchronousExecution": true,
        "modalBehavior": "fail"
    });

    if (result && result[0] && result[0].status === "OK") {
        console.log("JPEG saved successfully");
    } else {
        console.error("Error while saving JPEG", result);
    }
} catch (error) {
    console.error("Error:", error);
}

}

When I run this, it instead saves as a .psd file. Why?

What’s weird is, it throws an error in the console log, but then ALSO circles back around to save as psd after that:

main.js:97 Error while saving JPEG

  1. [{…}]

  2. 0:

1. as:

  1. maximizeCompatibility: true
  2. _obj: "photoshop35Format"

2. copy: true
3. documentID: 318
4. in:

  1. _kind: "local"
  2. _path: "C:\\Users\\anton\\Downloads\\TEST OUTPUT FOLDER\\output.psd"

5. lowerCase: true
6. saveStage:

  1. _enum: "saveStageType"
  2. _value: "saveSucceeded"
  1. length: 1
  • You need to specify copy: true when saving JPEGs
  • Use of the jpg extension instead of jpeg, which is automatically converted to jpg by Photoshop
  • If you use batchPlay, must provide exactly the properties and steps required by Photoshop. Probably use extendedQuality instead of quality, and two actions, saveBegin and saveSucceeded, need to be triggered
  • JPEG export can be replaced by Document.saveAs.jpg
const { localFileSystem } = require('uxp').storage ;
const photoshop = require('photoshop') ;
const { app, core } = photoshop ;

const saveAsJPEG = async (jpgQuality = 6) => {
  // Prompt the user to select a save location
  const entry = await localFileSystem.getFileForSaving('output.jpg') ;
  if(!entry) {
    console.error('No location selected for saving.') ;
    return ;
  }

  const doc = app.activeDocument ;
  if(!doc) {
    console.error('There is no target document.') ;
    return ;
  }

  await doc.saveAs.jpg(entry, {quality: jpgQuality}, true) ;
}

const main = async () => {
  try {
    await core.executeAsModal(
      async (control) => {
        await saveAsJPEG(7) ;
      }, 

      {
        'commandName': 'Save as JPEG',
        'interactive': true
      }
    ) ;
  } catch(e) {
    await app.showAlert(e) ;
  }
} ;

Interesting. I ended up writing it like this to make it work:

async function saveDocumentAsJPEGToFolder(filename, quality, folder) {
    try {
        // Prompt the user to select a folder only once

        const entry = await folder.createFile(`${app.activeDocument.title}_${filename.name}.jpeg`, { overwrite: true });
        const outputFileToken = await fs.createSessionToken(entry);

        // Use batchPlay to save the document as JPEG
        const result = await app.batchPlay([
            {
                "_obj": "save",
                "as": {
                    "_obj": "JPEG",
                    "options": {
                        "quality": quality,
                        "formatOptions": "optimized",
                        "matte": {
                            "_enum": "matteColor",
                            "_value": "none"
                        }
                    }
                },
                "in": {
                    "_path": outputFileToken,
                    "_kind": "local"
                },
                "documentID": app.activeDocument._id,
                "copy": true,  // Make sure to save a copy to prevent overwriting the original
                "overwrite": true,
                "_isCommand": true,
                "_options": {
                    "dialogOptions": "dontDisplay"
                }
            }
        ], {
            "synchronousExecution": true,
            "modalBehavior": "fail"
        });

        if (result && result[0] && result[0].status === "OK") {
            console.log(`JPEG (${filename}.jpeg) saved successfully`);
        } else {
            console.error("Error while saving JPEG", result);
        }
    } catch (error) {
        console.error("Error:", error);
    }
}

copy: true → appears to be the key thing I was missing.

I’m also kind of surprised it lets this run without doing executeAsModal.

It can certainly only export, but errors are occurring and quality is not being specified, correct?

You can make the batchPlay as simple as this:

  await batchPlay(
    [
      {
        _obj: "save",
        as: {
          _obj: "JPEG",
          extendedQuality: 12,
          matteColor: {
            _enum: "matteColor",
            _value: "none",
          },
        },
        copy: true,
        in: {
          _kind: "local",
          _path: outputFileToken,
        },
        lowerCase: true,
      },
    ],
    {}
  );

1 Like

lowerCase: true → is that supposed to set the filename as lowercase? Because I noticed 1) it doesn’t do that, and 2) the save operation works fine without that line.

Yeah, I agree, it doesn’t work for me either. Could be a bug or something I am not aware of!

Just spitting, but I always assumed that lowercase applied only to the extension - e.g. .JPEG vs .jpeg and was a hangover from the old days of operating system discrepancies.
I’m totally willing to be corrected on this!