Search code examples
javascriptdom-eventsair

Adobe Air with Javascript, making a program wait while running nativeProcess


I'm trying to extract metadata from video files by running ffprobe using nativeProcess. The code below works just as it should for one file, but causes an error when trying to loop through a series of files.

I know the cause of the problem is that Air tries to start a new nativeProcess before the old one is finished. I know it's something to do with listening to the air.NativeProcessExitEvent.EXIT. I just can't get it to work. Any suggestions would be greatly appreciated.

function fileOpen(){
    
  var directory = air.File.userDirectory;
  
  try
  {
      directory.browseForDirectory("Select Directory");
      directory.addEventListener(air.Event.SELECT, directorySelected);
  }
  catch (error)
  {
      air.trace("Failed:", error.message)
  }
  
  function directorySelected(event) 
  {
      directory = event.target ;
      var files = directory.getDirectoryListing();
      for(i=0; i < files.length; i++){
        getMetadata(files[0].nativePath)
                    //wait here for nativeProcess to finish
        }
      }
  } 

function getMetadata(filePathIn){
    
         
        if(air.NativeProcess.isSupported)
        {
            
        }
        else
        {
            air.trace("NativeProcess not supported.");
        }
    
        fileIn = filePathIn.toString()

        var nativeProcessStartupInfo = new air.NativeProcessStartupInfo();
        var file = air.File.applicationStorageDirectory.resolvePath("ffprobe");
        nativeProcessStartupInfo.executable = file;

        args = new air.Vector["<String>"]();
        args.push("-sexagesimal","-show_format","-loglevel","quiet","-show_streams","-print_format","json",filePathIn)
        nativeProcessStartupInfo.arguments = args;

        process = new air.NativeProcess();
        process.start(nativeProcessStartupInfo); 
        process.addEventListener(air.ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
        process.addEventListener(air.ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
        process.addEventListener(air.NativeProcessExitEvent.EXIT, onExit);
        process.addEventListener(air.IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
        process.addEventListener(air.IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
        
        
    }

    function onOutputData()
    {
      var fileMetadataJSON = process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable);
      air.trace(fileMetadataJSON)
    }
    
    function onErrorData(event)
    {
        air.trace("ERROR -", process.standardError.readUTFBytes(process.standardError.bytesAvailable)); 
    }
    
    function onExit(event)
    {
        air.trace("Process exited with ", event.exitCode);
    
    }
    
    function onIOError(event)
    {
         air.trace(event.toString());
    }
    

Solution

  • Here an outline that should give you an idea of what to do:

    • In your directorySelected() method, promote the local variable files (an Array) to a member variable. This will make the list of files that was selected available so that other methods can access it.
    • Remove the for loop in your directorySelected() method. Instead of running the loop and trying to run the native process here, call a new method, let's call it dequeueFileAndRunNativeProcess().
    • This new dequeueFileAndRunNativeProcess() method should check the length of the files array, and if it is greater than 0, it should pop() the first file in the files array and execute the native process on it.
    • In your onExit() method, call the dequeueFileAndRunNativeProcess() method again.

    The idea here is to move the code that runs the native process into a method that you can trigger from two places: once immediately after the user finishes browsing for files, and then again after the native process finishes it's work. The process ends when the files array has no more files in it.