Here’s the code I use to try to save the current Photoshop document as a JPEG:
async function saveAsJPEG(quality = 6) {
    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
[{…}]
 
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"
length: 1 
 
             
            
              
            
           
          
            
              
                sttk3  
              
                  
                    August 15, 2023,  1:37am
                   
                  3 
               
             
            
              
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.
             
            
              
            
           
          
            
              
                sttk3  
                
                  
                    August 15, 2023,  7:37am
                   
                  5 
               
             
            
              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.