Highlighted

How to detect if project has unsaved changes?

Explorer ,
Jun 01, 2020

Copy link to clipboard

Copied

I need to test if there are unsaved changes to the project in the After Effects application. Is there any way to do that? I get really close with this hacky solution -- save the project to a temp location and compare it to the current project:

 

function projectHasUnsavedChanges() {
  if (!app.project || !app.project.file) {
    return false;
  }
  var currentFile = app.project.file;
  // create_uuid pulled from https://stackoverflow.com/questions/105034/how-to-create-guid-uuid
  var tempProjectFile = new File(Folder.temp.fsName + '/' + create_uuid() + '.tmp.aep');
  app.project.save(tempProjectFile);
  var filesIdentical = byte_compare_files(tempProjectFile, currentFile);
  tempProjectFile.remove();
  return !filesIdentical;
}

 

This solution has a huge side-effect making it unacceptable. Basically whenever .save() is called, it changes the After Effects active project file to the saved location. Furthermore it adds the saved file to the recent history. Given I am just trying to do a test for unsaved changes, this doesn't work.

Are there any other options out there?

Thanks!

TOPICS
FAQ, How to, Scripting

Views

682

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more

How to detect if project has unsaved changes?

Explorer ,
Jun 01, 2020

Copy link to clipboard

Copied

I need to test if there are unsaved changes to the project in the After Effects application. Is there any way to do that? I get really close with this hacky solution -- save the project to a temp location and compare it to the current project:

 

function projectHasUnsavedChanges() {
  if (!app.project || !app.project.file) {
    return false;
  }
  var currentFile = app.project.file;
  // create_uuid pulled from https://stackoverflow.com/questions/105034/how-to-create-guid-uuid
  var tempProjectFile = new File(Folder.temp.fsName + '/' + create_uuid() + '.tmp.aep');
  app.project.save(tempProjectFile);
  var filesIdentical = byte_compare_files(tempProjectFile, currentFile);
  tempProjectFile.remove();
  return !filesIdentical;
}

 

This solution has a huge side-effect making it unacceptable. Basically whenever .save() is called, it changes the After Effects active project file to the saved location. Furthermore it adds the saved file to the recent history. Given I am just trying to do a test for unsaved changes, this doesn't work.

Are there any other options out there?

Thanks!

TOPICS
FAQ, How to, Scripting

Views

683

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Jun 01, 2020 0
Adobe Community Professional ,
Jun 01, 2020

Copy link to clipboard

Copied

AEGP_ProjectIsDirty ?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jun 01, 2020 0
Explorer ,
Jun 01, 2020

Copy link to clipboard

Copied

Thank you for the reference. But how do I leverage this from ExtendScript, i.e. via ExternalObject or something?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jun 01, 2020 0
Adobe Community Professional ,
Jun 01, 2020

Copy link to clipboard

Copied

either an ExternalObject or if you already have an AEGP running you could have it check the flag in idle hook and write it's status to a javascript global.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jun 01, 2020 0
Explorer ,
Jun 01, 2020

Copy link to clipboard

Copied

I do not have an AEGP running. My project is just an extension with Script UI, so I would be trying to invoke AEGP_ProjectIsDirty from ExtendScript. But how to do that? For example, I don't know what to put in ExternalObject:

// Completely non-working and invalid code...
var projectSuite6 = new ExternalObject('lib:?AEGP_ProjSuite6?');
var project = projectSuite6.AEGP_GetProjectProjectByIndex(0);
var isDirty = project.AEGP_ProjectIsDirty();

Thanks

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jun 01, 2020 0
Adobe Community Professional ,
Jun 01, 2020

Copy link to clipboard

Copied

you'll need to compile an external object handling dll, which would put you in the C realm anyways...

AEGP calls are accessible only via the C API, so unless you're willing to travel that road i guess that's not a valid solution for you.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jun 01, 2020 0
Adobe Community Professional ,
Jun 01, 2020

Copy link to clipboard

Copied

here's a hacky thought:
through javascript, do a system call like this:
tasklist /v /fo list /fi "imagename eq afterfx*"| find /i "window title:"

it will retreive the name text of the title of AE's window. if the name ends with an asterix then the project has unsaved changed.

(the command i showed here is very slow. perhaps there's a faster way to find AE's process)

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jun 01, 2020 0
Explorer ,
Jun 01, 2020

Copy link to clipboard

Copied

Thanks so much for the information. I am open to using a hack but the problem is it must work on a Mac as well -- which is why I am resistant to jumping into C as well. Bummer. 😞

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jun 01, 2020 0
Adobe Community Professional ,
Jun 01, 2020

Copy link to clipboard

Copied

that wasn't a C solution. it was purely javascript.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jun 01, 2020 0
Explorer ,
Jun 01, 2020

Copy link to clipboard

Copied

The tasklist solution was javascript, yes. But it still won't work on Mac.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jun 01, 2020 0
Devin- LATEST
Explorer ,
Jun 02, 2020

Copy link to clipboard

Copied

I figured out a way, sort of, to do this. I am hoping to get feedback on it's viability and maybe a way to improve the idea. It takes advantage of three facts:

 

  1. app.project.file is falsy when a project is not loaded, and truthy when a project is loaded.
  2. app.project.revision is updated every time a change is made.
  3. XMP's 'ModifyDate' is updated every time the project file is saved.

 

Armed with this information, I can keep track of what revision the project has when the project is saved:

 

 

var projectState = (function () {
  var xmpLib = new ExternalObject('lib:AdobeXMPScript');
  var currentModifyDate;
  var revisionWhenReadModifyDate;
  var currentState;

  function getState() {
    return currentState;
  }

  function update() {
    var actualModifyDate = readModifyDateFromXmp();
    var actualRevision = (app.project && app.project.revision) || undefined;
    if (actualModifyDate !== currentModifyDate) {
      currentModifyDate = actualModifyDate;
      revisionWhenReadModifyDate = actualRevision;
    }
    // 0 = project not loaded, 1 = project loaded with no changes, 2 = project loaded with changes
    currentState = !currentModifyDate ? 0 : ((revisionWhenReadModifyDate === actualRevision) ? 1 : 2);
  }

  function readModifyDateFromXmp() {
    if (!app.project || !app.project.file || !app.project.xmpPacket) {
      return undefined;
    }
    var xmpMeta = new XMPMeta(app.project.xmpPacket);
    return xmpMeta.getProperty(XMPConst.NS_XMP, 'ModifyDate').toString();
  }

  return {
    getState: getState,
    update: update
  };
})();

 

 

The major drawback here is I need read the project revision immediately when the project is saved or opened. I cannot find events for either of these, so in order to keep the state up to date I need to poll projectState.update() every second or so. If I had events for project saving and opening that would solve the problem, I think. But if I do that, then I projectState.getState() gives me an accurate reading of 'dirty' state. There is of course a race condition that if the user saves the file and makes a quick modification before projectState.update() is called, the state will be incorrect. Events for project save or opened would solve that as well...

 

Oh also, if the extension is started while the project state is dirty, the result will be inaccurate also. So this doesn't totally work, but it gets close.

 

Thoughts?

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jun 02, 2020 0