Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

How to detect if project has unsaved changes?

Explorer ,
Jun 01, 2020 Jun 01, 2020

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
2.5K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 01, 2020 Jun 01, 2020

AEGP_ProjectIsDirty ?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jun 01, 2020 Jun 01, 2020

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 01, 2020 Jun 01, 2020

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jun 01, 2020 Jun 01, 2020

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 01, 2020 Jun 01, 2020

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 01, 2020 Jun 01, 2020

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)

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jun 01, 2020 Jun 01, 2020
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. 😞
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 01, 2020 Jun 01, 2020

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jun 01, 2020 Jun 01, 2020
The tasklist solution was javascript, yes. But it still won't work on Mac.
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jun 02, 2020 Jun 02, 2020

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?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Oct 23, 2023 Oct 23, 2023

Sorry to revive such an old post, but do you have any suggestions regarding building an externalobject with AEGP functionality?

Everything I seem to be trying isn't working, and I think its related to establishing that initial connection with AE.

For a test I'm simply trying to run AEGP_executeScript, but the code fails without exception when I try to set my SuiteHandler like I would in an actual AEGP. 
```

    sP = pica_basicP;
   AEGP_SuiteHandler suites(sP); <---- Doesn't get past here
```
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Oct 24, 2023 Oct 24, 2023
LATEST

can you elborate on that snippet in some wider context? it's impossible to tell by these two lines...

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