No internet connection
  1. Home
  2. How to

batch import session data + auto match tracks + video

By samuel henriques @samuel_henriques
    2020-11-25 01:20:56.853Z2020-12-07 12:06:38.920Z

    Hello everyone,
    this is a script I'm working on to automate the process of importing loads of AAF to a session.

    If you see better way to organise, or improve any part, would be great to hear from you and improve it.

    One technical "pickle" I encountered is that the "Import Session Data" window must have all tracks visible so SF can see them all, there is no "scroll to track" for the menu, at least I don't know how. For me that means moving the window to a screen rotated 90º, fortunately my setup has one this way.

    One thing I'm struggling with is the occasions where a AAF has a track that is not already present on the session. The ideal solution would be to set it to "New Track" but I'm failing miserably on figuring out where in the sequence this should be done. Usually a set of AAF to import will all have the same tracks, but occasionally a new track will be present.

    As is, if an AAF has a new track, the script will fail and stop on the function selectPath(i, path) , witch makes cense since I'm asking to select a path that doesn't exist.
    So I tried to do a catch (err) , this https://forum.soundflow.org/-348#post-11, was very helpful but for reasons I don't understand, there is an error already leaving the function, I see it only in the log file, and it needs to be there. If I catch the error, I'll be able to set the tracks but will loose the correct sequence of match track.

    Any Ideas on what can I do to fix this?
    If it becomes too complicated, I think an option could be to cancel the import, moving to the next one and write the name of the AAF on a text file as report of failure. But as I see it now, the place to do any action would be where its fails, and I'm unable to do any action without messing the sequence.

    Here's a video of the script in action:
    https://we.tl/t-fSZL72JCc4

    Thank you all so much !

    
    function letsStartWin() {
        const startWin = sf.interaction.displayDialog({
            defaultAnswer: "",
            buttons: ["Video Location & Go!", "Go!", "Cancel"],
            defaultButton: "Go!",
            cancelButton: "Cancel",
            title: "Batch Import Session Data",
            prompt: "Manualy Import the first AAF.\nThis will ensure a set of tracks is in place for eficient Match Track. \nCreate a new video track for the imported video.\n\nBE AWARE !!! \nVideo files will be added on the ONLINE VIDEO TRACK,\nSo make sure the NEW TRACK IS ONLINE! \nOtherwise the imported videos might go to the wrong track and will not end well !!! 😡 \n\n\By default, the script searches for video files in the session video folder, if you want a diferent location choose one.\n\nTo use \"Map Start Time To...\" enter the Time Code of the interval you want.\nLeave blank to use AAF start time.\n\nhh:mm:ss:ff"
        });
        return startWin
    }
    
    function alertSound() {
        sf.system.volumeSet({ volume: 0.5 });
        sf.system.exec({
            commandLine: `afplay /System/Library/Sounds/Glass.aiff`
        });
        sf.system.volumeSet({ volume: 1.0 });
    }
    
    function moveWindowandsize() {
        var screenIndex = 2;
        var screenFrame = sf.ui.screens.allScreens[screenIndex].frame;
    
        sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.windowMove({
            position: { x: screenFrame.x + 0, y: screenFrame.y + 22 },
            size: { x: screenFrame.x + 0, y: screenFrame.y + 3300 },
        });
    }
    
    function importWindow(importMenu, list) {
    
        sf.ui.proTools.appActivateMainWindow();
    
        sf.ui.proTools.appWaitForActive({
            timeout: -1
        });
    
        sf.ui.proTools.menuClick({
            menuPath: ["File", "Import", importMenu],
        });
    
        var win = sf.ui.frontmostApp.focusedWindow;
    
        sf.keyboard.type({ text: '/' });
    
        var sheet = win.sheets.first.elementWaitFor({ timeout: 500 }, 'Could not find "Go to" sheet in the Save/Open dialog').element;
        //// //////////////////////////Set Path
        sheet.comboBoxes.first.value.value = list;
    
        sheet.buttons.whoseTitle.is('Go').first.elementClick({}, 'Could not click "Go"');
    
        win.sheets.first.elementWaitFor({ waitForNoElement: true, timeout: 500 }, '"Go to" sheet didn\'t close in time');
    
        sf.ui.proTools.windows.whoseTitle.is('Open').first.buttons.whoseTitle.is('Open').first.elementClick();
    
        sf.ui.proTools.children.whoseRole.is("AXWindow").whoseTitle.is('Open').first.elementWaitFor({ waitType: "Disappear" });
    
        if (sf.ui.proTools.confirmationDialog.invalidate().buttons.whoseTitle.is('OK').first.exists) {
            sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is('OK').first.elementClick();
            sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: "Disappear", onError: "Continue" });
        }
    }
    
    function dismissSessionNotes() {
    
        var sessionNotesWin = sf.ui.proTools.windows.whoseTitle.is('Session Notes')
    
        if (sessionNotesWin.exists) {
    
            sessionNotesWin.first.buttons.whoseTitle.is('No').first.elementClick();
    
            sessionNotesWin.first.elementWaitFor({
                waitType: "Disappear",
            });
        }
    
    }
    
    function closeImportDataWin() {
    
        var button = sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first
    
        button.buttons.allItems[1].elementClick();
    
        button.elementWaitFor({ waitType: "Disappear" });
    
        dismissSessionNotes()
    
    
    }
    
    function getAafDetails() {
        const win = sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first
        const sourcePanel = win.groups.whoseTitle.is('Source Properties:').invalidate().first;
        const tableData = sourcePanel.childrenByRole('AXTable').first.childrenByRole('AXRow')
            .map(fields => fields.childrenByRole('AXCell').first.children.first.title.invalidate().value);
    
        return tableData
    }
    
    
    function selectionSetAafStartTime(importingAaf) {    /////////////////   THIS WILL CLOSE IMPORT SESSION WINDOW ///////////////////////
    
        let aafDetails = getAafDetails();
    
        let aafStartTime = aafDetails.map(x => x.split("Start time:").slice(1)).join("")
    
        closeImportDataWin()
    
        sf.ui.proTools.selectionSetTimecode({ mainCounter: aafStartTime });
    
        var newMemLocWin = sf.ui.proTools.newMemoryLocationDialog
        sf.keyboard.press({ keys: "numpad enter" });
        newMemLocWin.elementWaitFor();
        newMemLocWin.textFields.allItems[1].elementSetTextFieldWithAreaValue({ value: importingAaf.join().split(".").slice(0, -1).join("") });
        newMemLocWin.radioButtons.first.checkboxSet({ targetValue: "Enable" });
        newMemLocWin.buttons.whoseTitle.is('OK').first.elementClick();
        newMemLocWin.elementWaitFor({ waitType: "Disappear" });
    }
    
    function getNextTcStart(intervalStartTime) {
      
        sf.ui.proTools.selectionSetTimecode({ selectionLength: intervalStartTime });
        var getEndTimecode = sf.ui.proTools.mainWindow.groups.whoseTitle.is('Counter Display Cluster').first.textFields.whoseTitle.is('Edit Selection End').first.invalidate().value.value;
        return getEndTimecode
    }
    
    
    function selectionSetUserInterval(intervalStartTime, importingAaf) {    /////////////////   THIS WILL CLOSE IMPORT SESSION WINDOW ///////////////////////
    
        let importSessionWin = sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first
    
        importSessionWin.groups.whoseTitle.is('Timecode Mapping Options:').first.popupButtons.first.popupMenuSelect({
            menuPath: ["Map start timecode to"]
        });
    
        importSessionWin.mouseClickElement({ relativePosition: { "x": 390, "y": 97 } });
    
        sf.keyboard.type({ text: intervalStartTime });
    
        importSessionWin.mouseClickElement({ relativePosition: { "x": 530, "y": 100 } });
    
        closeImportDataWin()
    
        sf.ui.proTools.selectionSetTimecode({ mainCounter: intervalStartTime });
    
        var newMemLocWin = sf.ui.proTools.newMemoryLocationDialog
        sf.keyboard.press({ keys: "numpad enter" });
        newMemLocWin.elementWaitFor();
        newMemLocWin.textFields.allItems[1].elementSetTextFieldWithAreaValue({ value: importingAaf.join().split(".").slice(0, -1).join("") });
        newMemLocWin.radioButtons.first.checkboxSet({ targetValue: "Enable" });
        newMemLocWin.buttons.whoseTitle.is('OK').first.elementClick();
        newMemLocWin.elementWaitFor({ waitType: "Disappear" });
    }
    
    function setVideoImportOptions() {
    
        const videoImportOprionsWin = sf.ui.proTools.windows.whoseTitle.is('Video Import Options').first
        const radioButtons = videoImportOprionsWin.radioButtons
        const checkBoxes = videoImportOprionsWin.checkBoxes
    
        videoImportOprionsWin.elementWaitFor({ waitType: "Appear" });
    
        if (radioButtons.allItems[1].invalidate().isEnabled == true) {
            radioButtons.allItems[1].checkboxSet({ targetValue: "Enable" });
    
        } else if (radioButtons.allItems[1].invalidate().isEnabled == false) {
            radioButtons.allItems[2].checkboxSet({ targetValue: "Enable" });
        }
    
        if (videoImportOprionsWin.popupButtons.first.invalidate().value.value !== "Selection") {
            videoImportOprionsWin.popupButtons.first.popupMenuSelect({ menuPath: ["Selection"] });
        }
    
        checkBoxes.allItems[0].checkboxSet({ targetValue: "Disable" });
    
        checkBoxes.allItems[1].checkboxSet({ targetValue: "Disable" });
    
        checkBoxes.allItems[2].checkboxSet({ targetValue: "Disable" });
    
        checkBoxes.allItems[3].checkboxSet({ targetValue: "Disable" });
    
        videoImportOprionsWin.buttons.whoseTitle.is('OK').first.elementClick();
    
        videoImportOprionsWin.elementWaitFor({ waitType: "Disappear" });
    
    }
    
    function setDataImportOptions() {
        let importSessionButtons = sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.groups.whoseTitle.is('Session Data').first.checkBoxes
    
        moveWindowandsize()
    
        sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.groups.whoseTitle.is('Tracks').first.buttons.whoseTitle.is('Match Tracks').first.elementClick();
    
        importSessionButtons.allItems[3].checkboxSet({ targetValue: "Disable" });
    
        importSessionButtons.first.checkboxSet({ targetValue: "Disable" });
    
        importSessionButtons.allItems[2].checkboxSet({ targetValue: "Disable" });
    
        importSessionButtons.allItems[4].checkboxSet({ targetValue: "Enable" });
    
        importSessionButtons.allItems[1].checkboxSet({ targetValue: "Disable" });
    
        sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.radioButtons.allItems[1].checkboxSet({ targetValue: "Enable" });
    
        var trackDataToImportWin = sf.ui.proTools.windows.whoseTitle.is('Track Data to Import').first
    
        var checkBoxes = trackDataToImportWin.groups.whoseTitle.is('Track Data').first.checkBoxes
    
        sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.groups.whoseTitle.is('Session Data').first.buttons.whoseTitle.is('Choose...').first.elementClick();
    
        trackDataToImportWin.elementWaitFor({ waitType: "Appear" });
    
        checkBoxes.allItems[0].checkboxSet({ targetValue: "Enable" });
    
        checkBoxes.allItems[1].checkboxSet({ targetValue: "Enable" });
    
        checkBoxes.allItems[2].checkboxSet({ targetValue: "Disable" });
    
        checkBoxes.allItems[3].checkboxSet({ targetValue: "Disable" });
    
        checkBoxes.allItems[4].checkboxSet({ targetValue: "Disable" });
    
        checkBoxes.allItems[5].checkboxSet({ targetValue: "Disable" });
    
        checkBoxes.allItems[6].checkboxSet({ targetValue: "Disable" });
    
        trackDataToImportWin.buttons.whoseTitle.is('OK').first.elementClick();
    
        trackDataToImportWin.elementWaitFor({ waitType: "Disappear" });
    }
    
    function selectPath(i, path) {
        try {
            let list = sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.groups.whoseTitle.is('Tracks').first.tables.whoseTitle.is('Source Destination').first.children.whoseRole.is("AXRow")
            let poppupMenu = list.allItems[i].children.whoseRole.is("AXCell").allItems[1].children.whoseRole.is("AXMenuButton").first
            poppupMenu.popupMenuSelect({
                menuPath: [path],
                targetValue: "Enable"
            });
        } catch (err) {
            selectPath(i, "New Track")
        }
    }
    
    
    function isMonoMenu() {
        let tracksTable = sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.groups.whoseTitle.is('Tracks').first.tables.whoseTitle.is('Source Destination').first.children.whoseRole.is("AXRow")
        let numberOfTracks = tracksTable.allItems.count
        for (var i = 0; i < numberOfTracks; i++) {
            let stereoOrMono = tracksTable.allItems[i].children.whoseRole.is("AXCell").first.children.whoseRole.is("AXStaticText").first.invalidate().value.value.split(" (").splice(1).join()
    
            if (stereoOrMono == "Mono audio)") {
                var monoDestinationMenu = tracksTable.allItems[i].children.whoseRole.is("AXCell").allItems[1].children.whoseRole.is("AXMenuButton").first.popupMenuFetchAllItems().menuItems.map(mi => mi.path.join(' -> '));
                sf.ui.proTools.appActivateMainWindow();
                return monoDestinationMenu
            }
        }
    }
    
    function isStereoMenu() {
        let tracksTable = sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.groups.whoseTitle.is('Tracks').first.tables.whoseTitle.is('Source Destination').first.children.whoseRole.is("AXRow")
        let numberOfTracks = tracksTable.allItems.count
        for (var i = 0; i < numberOfTracks; i++) {
            let stereoOrMono = tracksTable.allItems[i].children.whoseRole.is("AXCell").first.children.whoseRole.is("AXStaticText").first.invalidate().value.value.split(" (").splice(1).join()
    
            if (stereoOrMono == "Stereo audio)") {
                var stereoDestinationMenu = tracksTable.allItems[i].children.whoseRole.is("AXCell").allItems[1].children.whoseRole.is("AXMenuButton").first.popupMenuFetchAllItems().menuItems.map(mi => mi.path.join(' -> '));
                sf.ui.proTools.appActivateMainWindow();
                return stereoDestinationMenu
            }
        }
    }
    
    
    function autoMatchNext(firstIndex, nameFromSource, numberOfDups) {
    
        let destinationbutton = sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.groups.whoseTitle.is('Tracks').first.tables.whoseTitle.is('Source Destination').first.children.whoseRole.is("AXRow")
    
        const duplicates = numberOfDups.filter(x => x.startsWith(nameFromSource)).length - 1
    
        for (var i = 0; i < duplicates; i++) {
    
            let dotNumber = 1 + i
    
            let n = firstIndex + i
    
            let nameFromSourcePlusDot = nameFromSource + "." + dotNumber
    
            selectPath(n, nameFromSourcePlusDot);
        }
    }
    
    
    function autoMatchFirst() {
        let tracksTable = sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.groups.whoseTitle.is('Tracks').first.tables.whoseTitle.is('Source Destination').first.children.whoseRole.is("AXRow")
        let numberOfTracks = tracksTable.allItems.count
        let destinationList
        let nameFromSource
    
        let stereoMenu = isStereoMenu()
        let monoMenu = isMonoMenu()
        let destinationMenu = monoMenu.concat(stereoMenu)
    
        let numberOfDups = tracksTable.allItems.slice().map(l => l.children.whoseRole.is("AXCell").first.children.whoseRole.is("AXStaticText").first.invalidate().value.value.split(" (").slice(0, -1).join())
    
    
        for (var i = 0; i < numberOfTracks; i++) {
            var firstIndex = i + 1
    
            destinationList = tracksTable.allItems[i].children.whoseRole.is("AXCell").allItems[1].children.whoseRole.is("AXMenuButton").first.invalidate().value.value
    
            nameFromSource = tracksTable.allItems[i].children.whoseRole.is("AXCell").first.children.whoseRole.is("AXStaticText").first.invalidate().value.value.split(" (").slice(0, -1).join();
    
            var sourcePlusDot = nameFromSource + ".1"
    
            if (destinationList === "(none)" && destinationMenu.indexOf(nameFromSource) !== -1) {
                selectPath(i, nameFromSource)
            }
            if (destinationList === "(none)" && destinationMenu.indexOf(sourcePlusDot) !== -1) {
                autoMatchNext(firstIndex, nameFromSource, numberOfDups)
            }
            if (destinationList === "(none)" && destinationMenu.indexOf(nameFromSource) == -1) {
                selectPath(i, "New Track")
            }
        }
    }
    
    
    function sessionVideoFolder() {
    
    
        try {
            var sessionVFolder = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/') + '/Video Files';
    
            var sessionVideoFolder = sf.file.directoryGetFiles({
                path: sessionVFolder,
                isRecursive: true,
            }).paths.filter(p => p.match(/\.(mov|mp4|mxf)$/i));
    
            return sessionVideoFolder
    
        } catch (err) {
            alert("Whoops something went wrong!")
            throw 0
        }
    }
    
    
    function altVideoFolder() {
    
        var sessionVFolder = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/') + '/Video Files';
    
        const videoFolderPath = sessionVideoFolder()
    
        var folderPath = sf.interaction.selectFolder({
            defaultLocation: sessionVFolder,
            prompt: 'Select Video Files Folder',
        }).path
    
        try {
            var altVideoFolder = sf.file.directoryGetFiles({
                path: folderPath,
                isRecursive: true,
            }).paths.filter(p => p.match(/\.(mov|mp4|mxf)$/i));
    
            return altVideoFolder
    
        } catch (err) {
            log("No Files on Selected Folder")
        }
    }
    
    
    ////////////////////////////////////////////////////
    ///////////////////     Main   /////////////////////
    
    
    function batchImport() {
    
        //// Start Prompt
        const startWinAnswer = letsStartWin()
    
    
        //// Check if user interval is valid
        /////https://gist.github.com/allensarkisyan/5441601
        var isValidSMPTETimeCode = /(^(?:(?:[0-1][0-9]|[0-2][0-3]):)(?:[0-5][0-9]:){2}(?:[0-2][0-9])$)/;
        if (isValidSMPTETimeCode.test(startWinAnswer.text) == false && startWinAnswer.text.length > 1) {
            alert("Time Code Not Valid!")
            throw 0
        }
    
    
        /// decide video location
        let videoFilePath
        if (startWinAnswer.button == "Go!") {
            videoFilePath = sessionVideoFolder()
    
        }
        if (startWinAnswer.button == "Video Location & Go!") {
            videoFilePath = altVideoFolder()
        }
    
        /////////////////////////////////////////////////////////////
        sf.ui.proTools.appActivateMainWindow();
        sf.ui.proTools.invalidate();
        /////////////////////////////////////////////////////////////
        var aafPath = sf.ui.finder.selectedPaths;
        var importSessionData = "Session Data..."
        var ImportVideo = "Video..."
    
    
    
    
    
        for (let i = 0; i < aafPath.length; i++) {
    
            ////////////////   AAF   ////////////////
            let aafList = aafPath[i]
    
            let importingAaf = aafList.split("/").slice(-1)
    
            log("Import Session Data: \n" + importingAaf)
    
            //////////////////   VIDEO  ///////////////
            var getVideoFile = videoFilePath.filter(x => x.includes(aafList.split("/").slice(-1).join("/").split(".").shift()))
    
            if (getVideoFile.length == 0) {
                log("No Video Found.")
            }
    
            //////////////////   Map Start Time To  //////////////      
            let intervalStartTime
            if (isValidSMPTETimeCode.test(startWinAnswer.text) == true) {
                intervalStartTime = getNextTcStart(startWinAnswer.text)
            }
    
            //=================================================================//
            importWindow(importSessionData, aafList)
    
            setDataImportOptions()
    
            autoMatchFirst()
    
            if (isValidSMPTETimeCode.test(startWinAnswer.text) == true) {
                selectionSetUserInterval(intervalStartTime, importingAaf)
    
            } else {
                selectionSetAafStartTime(importingAaf)
            }
    
            if (getVideoFile.length == 1) {
                importWindow(ImportVideo, getVideoFile.join())
    
                setVideoImportOptions()
            }
    
            sf.ui.proTools.menuClick({ menuPath: ["File", "Save"] });
        }
        alertSound()
        alert("Done!")
    
    }
    
    
    batchImport()
    
    
    
    
    
    
    
    Solved in post #8, click to view
    • 8 replies
    1. samuel henriques @samuel_henriques
        2020-11-26 00:34:31.958Z

        In the meantime I fixed the new tracks problem. As soon as I can I'll uptate the script.
        Thank you.

        1. Kitch Membery @Kitch2020-11-26 02:24:35.554Z

          Great work Samuel!

          What is the error message when the AAF has a new track?

          1. samuel henriques @samuel_henriques
              2020-11-26 12:42:32.105Z

              Hello master Kitch, thank you so much,
              When a track to import had no match, it would stop on the "poppup menu select" since there was no name, It made sense because I didn't write anything to account for this, so I was trying to catch the error and do something.
              Anyway, I made a new set of rules to account for this and that bit is fixed now. I have a new error only if there the last track to import in a new track :), I will fix it soon. and I want to clean a bit the second loop witch is making popup menu select "track name.1" increment the .1 until there is no more numbers on the popup list, and It doesn't seam good. I will make it know when to stop in a more clean manner. If you are into jumping into this, I can send you some aaf I'm using to test, some without confusion but others with lots of problems. Unfortunately I'm not in position to share them on the forum because there is content on them that is not mine.

              Thank you so much and thank you to the amazing forum.

              1. samuel henriques @samuel_henriques
                  2020-11-26 22:54:16.882Z

                  Updated, fixed the error (was pretty stupid and I took a few hours to figure) and removed a catch error not needed anymore. I might need help with this later, but I couldn't figure out the rule to implement yet.

                  1. samuel henriques @samuel_henriques
                      2020-11-29 23:48:48.121Z2020-11-30 19:39:14.061Z

                      Hello everyone,
                      Updated again above.

                      next step is provide option for a report
                      and there is still a thing with one of the loops, that should be improved, but i'll need some time to figure the workflow.

                      But, as is, its working pretty well!!

                      Thank you so much.

                      1. samuel henriques @samuel_henriques
                          2020-11-30 19:40:01.438Z

                          solution in question.

                          Reply1 LikeSolution
                          1. samuel henriques @samuel_henriques
                              2020-12-05 23:42:57.874Z2020-12-06 19:02:09.479Z

                              New update, I think its working pretty well.
                              I've been using it for the past weeks and so far....happy monkey!

                              Now I can choose to import the AAF using "Map Start Time To..." and set an interval.

                              What I know It Doesn't do:
                              If the AAF has more than one group of tracks with same name, separated by other tracks, the script wont fail, but wont map the tracks to the correct session tracks, for example if AAF is like this:
                              Track 1
                              Track 2
                              Music
                              Music
                              Music
                              Track 3
                              Track 4
                              Music
                              Music

                              I figured almost how to fix it, but it became apparent (at least to me) that it would become really complicated to figure out a solution for all situations. So I made a different script to help situations like this one.
                              When I get really doggy and disorganized AAF what I do is create a session for each and then import those to my main session. This way all the sessions will have all the .1, .2, .3 on the duplicates. I used to to this by hand, now I've built an "Batch Open AAF to new session" then, I can use the "Batch import session data to join them" This is still being tested though.

                              So Batch Import Session Data, is updated above,
                              and batch Open AAF to New Session is here:

                              function alertSound() {
                                  sf.system.volumeSet({ volume: 0.5 });
                                  sf.system.exec({
                                      commandLine: `afplay /System/Library/Sounds/Glass.aiff`
                                  });
                                  sf.system.volumeSet({ volume: 1.0 });
                              }
                              
                              
                              function dismissSessionNotes() {
                              
                                  var sessionNotesWin = sf.ui.proTools.windows.whoseTitle.is('Session Notes')
                              
                                  if (sessionNotesWin.exists) {
                              
                                      sessionNotesWin.first.buttons.whoseTitle.is('No').first.elementClick();
                              
                                      sessionNotesWin.first.elementWaitFor({
                                          waitType: "Disappear",
                                      });
                                  }
                              }
                              
                              function closeImportDataWin() {
                              
                                  var button = sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first
                              
                                  button.buttons.allItems[1].elementClick();
                              
                                  button.elementWaitFor({ waitType: "Disappear" });
                              
                                  dismissSessionNotes()
                              }
                              
                              function dashboard(sessionName, savePath, aafList) {
                              
                                  let dashboardWin = sf.ui.proTools.windows.whoseTitle.is('Dashboard').first
                              
                                  dashboardWin.elementWaitFor({
                                      waitType: "Appear",
                                  });
                              
                                  dashboardWin.textFields.first.elementSetTextFieldWithAreaValue({ value: sessionName });
                              
                                  dashboardWin.radioButtons.allItems[0].checkboxSet({ targetValue: "Disable" });
                              
                                  dashboardWin.radioButtons.allItems[1].checkboxSet({ targetValue: "Enable" });
                              
                                  dashboardWin.checkBoxes.allItems[1].checkboxSet({ targetValue: "Enable" });
                              
                                  dashboardWin.radioButtons.allItems[3].checkboxSet({ targetValue: "Enable" });
                              
                                  if (dashboardWin.popupButtons.allItems[0].invalidate().value.value !== "BWF (.WAV)") {
                                      dashboardWin.popupButtons.allItems[0].popupMenuSelect({ menuPath: ["BWF (.WAV)"] });
                                  }
                              
                                  if (dashboardWin.popupButtons.allItems[1].invalidate().value.value !== "48 kHz") {
                                      dashboardWin.popupButtons.allItems[1].popupMenuSelect({ menuPath: ["48 kHz"] });
                                  }
                              
                                  if (dashboardWin.popupButtons.allItems[1].invalidate().value.value !== "24-bit") {
                                      dashboardWin.popupButtons.allItems[2].popupMenuSelect({ menuPath: ["24-bit"] });
                                  }
                              
                                  dashboardWin.buttons.whoseTitle.is('Create').first.elementClick();
                              
                                  dashboardWin.elementWaitFor({ waitType: "Disappear" });
                              
                                  openSession(sessionName, aafList)
                              
                              }
                              
                              function setDataImportOptions() {
                                  let importSessionButtons = sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.groups.whoseTitle.is('Session Data').first
                              
                                  sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.elementWaitFor({ waitType: "Appear" });
                              
                                  importSessionButtons.checkBoxes.allItems[3].checkboxSet({ targetValue: "Enable" });
                              
                                  importSessionButtons.checkBoxes.allItems[0].checkboxSet({ targetValue: "Disable" });
                              
                                  importSessionButtons.checkBoxes.allItems[2].checkboxSet({ targetValue: "Disable" });
                              
                                  importSessionButtons.checkBoxes.allItems[4].checkboxSet({ targetValue: "Enable" });
                              
                                  importSessionButtons.checkBoxes.allItems[1].checkboxSet({ targetValue: "Disable" });
                              
                                  if (importSessionButtons.popupButtons.first.invalidate().value.value !== "All") {
                                      importSessionButtons.popupButtons.first.popupMenuSelect({ menuPath: ["All"] });
                                  }
                              
                                  sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.radioButtons.allItems[2].checkboxSet({ targetValue: "Enable" });
                              
                                  sf.ui.proTools.windows.whoseTitle.is('Import Session Data').first.radioButtons.allItems[1].checkboxSet({ targetValue: "Enable" });
                              
                                  var trackDataToImportWin = sf.ui.proTools.windows.whoseTitle.is('Track Data to Import').first
                              
                                  var checkBoxes = trackDataToImportWin.groups.whoseTitle.is('Track Data').first.checkBoxes
                              
                                  importSessionButtons.buttons.whoseTitle.is('Choose...').first.elementClick();
                              
                                  trackDataToImportWin.elementWaitFor({ waitType: "Appear" });
                              
                                  checkBoxes.allItems[0].checkboxSet({ targetValue: "Enable" });
                              
                                  checkBoxes.allItems[1].checkboxSet({ targetValue: "Enable" });
                              
                                  checkBoxes.allItems[2].checkboxSet({ targetValue: "Disable" });
                              
                                  checkBoxes.allItems[3].checkboxSet({ targetValue: "Disable" });
                              
                                  checkBoxes.allItems[4].checkboxSet({ targetValue: "Disable" });
                              
                                  checkBoxes.allItems[5].checkboxSet({ targetValue: "Disable" });
                              
                                  checkBoxes.allItems[6].checkboxSet({ targetValue: "Disable" });
                              
                                  trackDataToImportWin.buttons.whoseTitle.is('OK').first.elementClick();
                              
                                  trackDataToImportWin.elementWaitFor({ waitType: "Disappear" });
                              }
                              
                              function openSession(sessionName, aafList) {
                              
                                  sf.ui.proTools.appActivate();
                              
                                  var win = sf.ui.frontmostApp.focusedWindow;
                              
                                  sf.keyboard.type({ text: '/' });
                              
                                  var sheet = win.sheets.first.elementWaitFor({ timeout: 500 }, 'Could not find "Go to" sheet in the Save/Open dialog').element;
                                  //// //////////////////////////Set Path
                                  sheet.comboBoxes.first.value.value = aafList;
                              
                                  sheet.buttons.whoseTitle.is('Go').first.elementClick({}, 'Could not click "Go"');
                              
                                  win.sheets.first.elementWaitFor({ waitForNoElement: true, timeout: 500 }, '"Go to" sheet didn\'t close in time');
                              
                                  let openWin = sf.ui.proTools.windows.whoseTitle.is('Open').first.buttons.whoseTitle
                              
                                  if (sf.ui.proTools.windows.whoseTitle.is('Save').first.textFields.exists) {
                                      sf.ui.proTools.windows.whoseTitle.is('Save').first.textFields.first.elementSetTextFieldWithAreaValue({
                                          value: sessionName,
                                      });
                                  }
                                  if (openWin.is('Open').exists) {
                                      openWin.is('Open').first.elementClick();
                                  }
                              
                                  if (sf.ui.proTools.windows.whoseTitle.is('Save').exists) {
                                      sf.ui.proTools.windows.whoseTitle.is('Save').first.buttons.allItems[2].elementClick();
                                  }
                              
                                  win.sheets.first.elementWaitFor({ waitForNoElement: true, timeout: 500 }, 'Save didn\'t close in time');
                              }
                              
                              
                              function saveAndClose() {
                                  sf.ui.proTools.appActivate();
                                  while (true) {
                              
                                      //// wait for complete tasks
                                      if (sf.ui.proTools.windows.whoseTitle.is('Task Manager').first.children.whoseRole.is("AXStaticText").first.invalidate().value.value == "0 items")
                                          //// save and exit procedures
                                          if ((sf.ui.proTools.mainWindow.invalidate().sessionPath !== null || sf.ui.proTools.getMenuItem('File', 'Close Session').isEnabled)
                                              || sf.ui.proTools.confirmationDialog.exists) {
                              
                                              sf.ui.proTools.waitForNoModals();
                              
                                              sf.ui.proTools.menuClick({ menuPath: ["File", "Save"] });
                              
                                              sf.ui.proTools.menuClick({ menuPath: ["File", "Close Session"] });
                              
                                              if (sf.ui.proTools.confirmationDialog.invalidate().buttons.whoseTitle.is('Save').first.exists) {
                                                  sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is('Save').first.elementClick();
                                                  sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: "Disappear", onError: "Continue" });
                                              }
                                              if (sf.ui.proTools.confirmationDialog.invalidate().buttons.whoseTitle.is('Cancel').first.exists) {
                                                  sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is('Cancel').first.elementClick();
                                                  sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: "Disappear", onError: "Continue" });
                                              }
                              
                                              sf.ui.proTools.waitForNoModals();
                              
                                          } else {
                                              break
                                          }
                                  }
                                  sf.wait({ intervalMs: 500 })
                              
                              }
                              
                              ///================== MAIN ====================///
                              
                              function batchNewSession() {
                                  /////////////////////////////////////////////////////////////
                                  sf.ui.proTools.appActivate();
                                  /////////////////////////////////////////////////////////////
                              
                                  var aafPath = sf.ui.finder.selectedPaths;
                              
                                  for (let i = 0; i < aafPath.length; i++) {
                              
                                      ////////////////   AAF   ////////////////
                                      let aafList = aafPath[i]
                              
                                      sf.ui.proTools.menuClick({ menuPath: ["File", "Open Session..."] });
                              
                              
                                      let savePath = aafList.split("/").slice(0, -1).join("/")
                              
                                      let sessionName = aafList.split("/").slice(-1).join().split(".").slice(0, -1).join(".")
                              
                              
                              
                                      openSession(sessionName, aafList)
                              
                                      dashboard(sessionName, savePath, aafList)
                              
                                      setDataImportOptions()
                              
                                      closeImportDataWin()
                              
                                      saveAndClose()
                              
                              
                                  }
                                  alertSound()
                                  alert("Done!")
                              }
                              
                              batchNewSession()
                              
                              

                              Fixes and lessons on better solutions will be welcome.

                              Thank you so much everyone.

                              1. Kitch Membery @Kitch2020-12-07 10:17:15.385Z

                                Nice work mate!