No internet connection
  1. Home
  2. Macro and Script Help

My checkerboard script no longer works after ProTools update.

By Christopher Cleator @Christopher_Cleator
    2024-07-24 16:04:57.042Z

    Title

    My checkerboard script no longer works after ProTools update.

    What do you expect to happen when you run the script/macro?

    "Could not click popup menu item - Line 5" error? This script used to run perfectly and now it doesn't. I'm not much of a coder, so I can't figure out why. Please help.

    Are you seeing an error?

    Could not click popup menu item - Line 5

    What happens when you run this script?

    The window "WAIT! Have you batch renamed all clips to start with 2 digit scene numbers? (3 means Yes)" Does come up, bus as soon as I press Okay, I get the error, and nothing else happens.

    How were you running this script?

    I used a Stream Deck button

    How important is this issue to you?

    5

    Details

    {
        "inputExpected": "\"Could not click popup menu item - Line 5\" error? This script used to run perfectly and now it doesn't. I'm not much of a coder, so I can't figure out why. Please help.",
        "inputIsError": true,
        "inputError": "Could not click popup menu item - Line 5",
        "inputWhatHappens": "The window \"WAIT! Have you batch renamed all clips to start with 2 digit scene numbers? (3 means Yes)\" Does come up, bus as soon as I press Okay, I get the error, and nothing else happens.",
        "inputHowRun": {
            "key": "-MpfwmPg-2Sb-HxHQAff",
            "title": "I used a Stream Deck button"
        },
        "inputImportance": 5,
        "inputTitle": "My checkerboard script no longer works after ProTools update."
    }

    Source

    /**
     * @param {array} path -  path menu path
     */
    function clipListEnableMenu(path) {
        sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is('Clip List').first.popupMenuSelect({
            menuPath: path, targetValue: "Enable",
        });
        sf.ui.proTools.appActivateMainWindow();
    };
    
    //    Enable menus from "clipListMenuItems" that are not yet enabled
    function clipListEnableSubmenu({ menu, subMenu }) {
        sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Clip List"], targetValue: "Enable" });
    
        clipListEnableMenu(["Clear Find"])
        const menuCheckedInShowMenu = sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is('Clip List').first.popupMenuFetchAllItems().menuItems.filter(x =>
            !x.element.isMenuChecked && //  Not MenuChecked
            x.path[0] == menu).map(x => x.path[1]) // submenu item name
        sf.ui.proTools.appActivateMainWindow()
    
        // Get unchecked from "clipListMenuItemsShow list and create path
        let menuToCheck = menuCheckedInShowMenu.filter(x => subMenu.includes(x)).map(x => [menu, x])
        menuToCheck.forEach(clipListEnableMenu)
    }
    
    
    function onlyUnique(value, index, self) {
        return self.indexOf(value) === index;
    }
    
    function goToStartOfSession() {
        sf.ui.proTools.transportEnsureCluster();
    
        const trannsportCluster = sf.ui.proTools.mainWindow.transportViewCluster;
        const transportButtons = trannsportCluster.groups.whoseTitle.is("Normal Transport Buttons").first;
        transportButtons.buttons.whoseTitle.is("Return to Zero").first.elementClick();
    }
    
    function selectedClipCount() {
        const clipsTable = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first;
        const selectedClipCount = clipsTable.getElements("AXSelectedRows").allItems.length;
        return selectedClipCount
    
    }
    
    function askNumberChar() {
        const chNumber = sf.interaction.displayDialog({
            prompt: "WAIT! Have you batch renamed all clips to start with 2 digit scene numbers? (3 means Yes)",
            defaultAnswer: "3" // Default number of characters here
        }).text.match(/\d+/g)
        return +chNumber
    }
    
    
    function duplicateSelectedTrack({ duplicateActivePlaylist: targetValue }) {
        sf.ui.proTools.trackDuplicateSelected({
            duplicateActivePlaylist: targetValue,
            duplicateAlternatePlaylists: false,
            duplicateAutomation: false,
            duplicateInserts: false,
        duplicateSends: false,  
            duplicateGroupAssignments: false,
            insertAfterLastSelectedTrack: true,
            onError: "ThrowError"
        });
    }
    
    
    function timeCounter() {
    
        // get current counter
        const mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue
        let originalTimeCounter
    
        /// Bars Beats .  
        if (mainCounter.includes("|")) originalTimeCounter = "Bars|Beats"
        //  Min Secs .    
        else if (mainCounter.includes(":") && mainCounter.includes(".")) originalTimeCounter = "Min:Secs"
        //  Time code
        else if (mainCounter.split(":").length == 4) originalTimeCounter = "Timecode"
        //  Feet+Frames
        else if (mainCounter.includes("+")) originalTimeCounter = "Feet+Frames"
        //  Samples.     
        else originalTimeCounter = "Samples"
    
        return originalTimeCounter
    }
    
    
    function createNewTrack(newTrackName) {
    
        sf.ui.proTools.menuClick({ menuPath: ["Track", "New..."] });
    
        const newTrackWin = sf.ui.proTools.windows.whoseTitle.is("New Tracks")
    
        newTrackWin.first.elementWaitFor({ waitType: "Appear" });
    
        newTrackWin.first.textFields.allItems[1].elementSetTextFieldWithAreaValue({
            value: newTrackName,
        });
    
        newTrackWin.first.buttons.whoseTitle.is("Create").first.elementClick();
    
        newTrackWin.first.elementWaitFor({ waitType: "Disappear" });
    
    }
    
    
    function getClipNames(chNumber) {
        let selectedClips = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first.children.whoseRole.is("AXRow").allItems.map(row =>
            row.children[1].children.first.value.value).filter(c => c.startsWith('Selected. ')).map(l =>
                l.split("\n")[1]).map(n => n
                    .replace(/["\"]/g, '').trim().slice(2, chNumber))// Number of Characters to Grab
        return selectedClips
    }
    
    
    function findTrackNameAndPaste(selectedClipsNames, processTrack) {
    
        const trackByName = sf.ui.proTools.trackGetByName({ name: selectedClipsNames })
    
    
        if (trackByName.track == null) {
    
            createNewTrack(selectedClipsNames)
            sf.ui.proTools.invalidate()
    
            sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] });
    
        } else if (trackByName.track != null) {
    
            trackByName.track.trackSelect()
    
            //  Reset menu
            sf.keyboard.press({ keys: "left" });
    
            const selectionHasClip = sf.ui.proTools.getMenuItem("Edit", "Trim Clip").isEnabled
    
            if (selectionHasClip) {
                //  Reset menu
                sf.keyboard.press({ keys: "left" });
    
                duplicateSelectedTrack({ duplicateActivePlaylist: false })
                sf.ui.proTools.invalidate()
    
                sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] });
    
            } else {
    
                sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] });
            }
        }
        sf.ui.proTools.trackSelectByName({ names: processTrack })
    }
    
    
    function mainProcess(processTrack, originaSelectionEndToString, numberOfCharacters, originalSelectionStart) {
    
        goToStartOfSession()
    
        // one tab and select first clip
        sf.keyboard.press({ keys: "tab" });
        sf.keyboard.press({ keys: "shift+tab" })
    
        //let selectionInfo
        let selectedClipsNames
        let selectedArray = []
        let selectionEnd
        let halfCrossFadeDuration
        while (true) {
    
            let currentSelectionEndNumber = +sf.ui.proTools.selectionGet().selectionEnd.replace(/[ :\+\|.]/g, '')
    
            selectedClipsNames = getClipNames(numberOfCharacters)
    
    
            if (selectedClipsNames[0] != null) {
                selectedArray.push(selectedClipsNames[0])
            }
    
            if (selectedArray.filter(onlyUnique).length <= 1) {
    
                selectionEnd = sf.ui.proTools.selectionGetInSamples().selectionEnd
    
                if (sf.ui.proTools.selectionGetInfo().hasCrossFade) {
    
                    halfCrossFadeDuration = +sf.ui.proTools.selectionGetInSamples().selectionLength / 2
    
                    selectionEnd = +sf.ui.proTools.selectionGetInSamples().selectionEnd - halfCrossFadeDuration
                }
            }
    
    
            if (selectedArray.filter(onlyUnique).length <= 1 &&
                !sf.ui.proTools.selectionGetInfo().hasEndFade &&
                currentSelectionEndNumber < originaSelectionEndToString) {
    
                sf.keyboard.press({ keys: "ctrl+tab" });
    
            } else {
    
                sf.ui.proTools.selectionSetInSamples({
                    selectionStart: +originalSelectionStart,
                    selectionEnd: +selectionEnd
                })
    
                sf.ui.proTools.menuClick({ menuPath: ["Edit", "Cut"] });
    
                sf.ui.proTools.selectionSetInSamples({
                    selectionStart: +originalSelectionStart,
                    selectionEnd: +selectionEnd
                })
    
                findTrackNameAndPaste(selectedArray[0], processTrack)
                break;
            }
            //sf.wait({ intervalMs: 1000 })
        }
    }
    
    
    
    function main() {
    
        //  Reset menu
        sf.keyboard.press({ keys: "left" });
    
        duplicateSelectedTrack({ duplicateActivePlaylist: true })
    
        let processTrack = sf.ui.proTools.selectedTrackNames
    
        sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" })
    
        while (true) {
    
            sf.ui.proTools.invalidate()
    
            sf.ui.proTools.menuClick({ menuPath: ["Edit", "Select All"] });
    
            let originaSelectionEndToString = +sf.ui.proTools.selectionGet().selectionEnd.replace(/[ :\+\|.]/g, '')
    
            let originalSelectionStart = sf.ui.proTools.selectionGet().selectionStart
    
            let selectedClipsNames = getClipNames(numberOfCharacters)
    
            if (selectedClipsNames.length >= 1) {
    
                mainProcess(processTrack, originaSelectionEndToString, numberOfCharacters, originalSelectionStart)
    
            } else {
    
                //  goToStartOfSession()
    
                // Option to delete empy track in the end
                sf.ui.proTools.menuClick({ menuPath: ["Track", "Delete"] });
                break
            }
        }
    }
    
    
    
    
    
    /////////         MAIN          ///////// 
    /////////         MAIN          ///////// 
    sf.ui.proTools.appActivateMainWindow()
    
    
    // Ask how many characters to grab
    const numberOfCharacters = askNumberChar()
    
    if (numberOfCharacters == 0) {
        alert("Numbers only, minimum  is 1.")
    }
    
    
    //  List of menus to enable
    clipListEnableSubmenu({
        menu: "Show",
        subMenu: [
            "Audio",
            "Groups",
            "Auto-Created",
        ]
    })
    
    let originalTimeCOunter = timeCounter()
    
    sf.ui.proTools.selectedTrackHeaders.slice().map(track => {
    
        sf.ui.proTools.invalidate()
    
        track.trackSelect();
    
        track.trackScrollToView();
    
        main()
    });
    
    sf.ui.proTools.mainCounterSetValue({ targetValue: originalTimeCOunter })
    
    alert(`Done!`)
    

    Links

    User UID: 5LUE5yrcgqbHGGoE1faKvuJFCi33

    Feedback Key: sffeedback:5LUE5yrcgqbHGGoE1faKvuJFCi33:-O2_UFzfvJBUXXf_gBdB

    Feedback ZIP: QUo2TfdpgaDdOR9wTThqWe4UGXzA+hFXJVTp0goOEz53gREJufvQIHMKaRkpyi6Jor7lOxUWtXrfsXZrQJS9x4CB+3UXsWXjDdhBGhGZkUCTrF+yYCNnwBJPywFOtiMrOwlgeKHy0++XloBQa5nkUh67KoyKHR6qrShAuwWXu7BghjfIQ21pNPVctzMoMIB9os57r5pEZx05KfBkpkNeDKLXDwBn3T8G7roHzA9YKrxtZoyI76YEFQSCLO6bAgLZEH23ThiRZNwoIw8AxrxDuY/sWPyW56uDozcm52MRlci1CvDa+djc2rAYoFdnrpqWuCCm8ISI/HgUhd8qWUuYfDI/fEnejGsKJBgHKP/5RLc=

    Solved in post #2, click to view
    • 2 replies
    1. Dustin Harris @Dustin_Harris
        2024-07-25 14:48:02.722Z

        Heyya @Christopher_Cleator try this version, I haven't tested it completely but my adjustments worked in isolation :)

        /**
         * @param {string} name -  path menu path
         */
        function clipListEnableMenu(name) {
            //sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is('Clip List').first.popupMenuSelect({
        
            sf.ui.proTools.mainWindow.buttons.whoseTitle.is("Plate").first.popupMenuSelect({
                menuPath: [name], targetValue: "Enable",
            });
            sf.ui.proTools.appActivateMainWindow();
        };
        
        
        /**
         * @param {{menuItemNames: string[]}} args - input object
         */
        function clipListEnableSubmenu({ menuItemNames }) {
            sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Clip List"], targetValue: "Enable" });
        
            menuItemNames.forEach(name => clipListEnableMenu(name))
        }
        
        
        function onlyUnique(value, index, self) {
            return self.indexOf(value) === index;
        }
        
        function goToStartOfSession() {
            sf.ui.proTools.transportEnsureCluster();
        
            const trannsportCluster = sf.ui.proTools.mainWindow.transportViewCluster;
            const transportButtons = trannsportCluster.groups.whoseTitle.is("Normal Transport Buttons").first;
            transportButtons.buttons.whoseTitle.is("Return to Zero").first.elementClick();
        }
        
        function selectedClipCount() {
            const clipsTable = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first;
            const selectedClipCount = clipsTable.getElements("AXSelectedRows").allItems.length;
            return selectedClipCount
        
        }
        
        function askNumberChar() {
            const chNumber = sf.interaction.displayDialog({
                prompt: "WAIT! Have you batch renamed all clips to start with 2 digit scene numbers? (3 means Yes)",
                defaultAnswer: "3" // Default number of characters here
            }).text.match(/\d+/g)
            return +chNumber
        }
        
        
        function duplicateSelectedTrack({ duplicateActivePlaylist: targetValue }) {
            sf.ui.proTools.trackDuplicateSelected({
                duplicateActivePlaylist: targetValue,
                duplicateAlternatePlaylists: false,
                duplicateAutomation: false,
                duplicateInserts: false,
            duplicateSends: false,  
                duplicateGroupAssignments: false,
                insertAfterLastSelectedTrack: true,
                onError: "ThrowError"
            });
        }
        
        
        function timeCounter() {
        
            // get current counter
            const mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue
            let originalTimeCounter
        
            /// Bars Beats .  
            if (mainCounter.includes("|")) originalTimeCounter = "Bars|Beats"
            //  Min Secs .    
            else if (mainCounter.includes(":") && mainCounter.includes(".")) originalTimeCounter = "Min:Secs"
            //  Time code
            else if (mainCounter.split(":").length == 4) originalTimeCounter = "Timecode"
            //  Feet+Frames
            else if (mainCounter.includes("+")) originalTimeCounter = "Feet+Frames"
            //  Samples.     
            else originalTimeCounter = "Samples"
        
            return originalTimeCounter
        }
        
        
        function createNewTrack(newTrackName) {
        
            sf.ui.proTools.menuClick({ menuPath: ["Track", "New..."] });
        
            const newTrackWin = sf.ui.proTools.windows.whoseTitle.is("New Tracks")
        
            newTrackWin.first.elementWaitFor({ waitType: "Appear" });
        
            newTrackWin.first.textFields.allItems[1].elementSetTextFieldWithAreaValue({
                value: newTrackName,
            });
        
            newTrackWin.first.buttons.whoseTitle.is("Create").first.elementClick();
        
            newTrackWin.first.elementWaitFor({ waitType: "Disappear" });
        
        }
        
        
        function getClipNames(chNumber) {
            let selectedClips = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first.children.whoseRole.is("AXRow").allItems.map(row =>
                row.children[1].children.first.value.value).filter(c => c.startsWith('Selected. ')).map(l =>
                    l.split("\n")[1]).map(n => n
                        .replace(/["\"]/g, '').trim().slice(2, chNumber))// Number of Characters to Grab
            return selectedClips
        }
        
        
        function findTrackNameAndPaste(selectedClipsNames, processTrack) {
        
            const trackByName = sf.ui.proTools.trackGetByName({ name: selectedClipsNames })
        
        
            if (trackByName.track == null) {
        
                createNewTrack(selectedClipsNames)
                sf.ui.proTools.invalidate()
        
                sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] });
        
            } else if (trackByName.track != null) {
        
                trackByName.track.trackSelect()
        
                //  Reset menu
                sf.keyboard.press({ keys: "left" });
        
                const selectionHasClip = sf.ui.proTools.getMenuItem("Edit", "Trim Clip").isEnabled
        
                if (selectionHasClip) {
                    //  Reset menu
                    sf.keyboard.press({ keys: "left" });
        
                    duplicateSelectedTrack({ duplicateActivePlaylist: false })
                    sf.ui.proTools.invalidate()
        
                    sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] });
        
                } else {
        
                    sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] });
                }
            }
            sf.ui.proTools.trackSelectByName({ names: processTrack })
        }
        
        
        function mainProcess(processTrack, originaSelectionEndToString, numberOfCharacters, originalSelectionStart) {
        
            goToStartOfSession()
        
            // one tab and select first clip
            sf.keyboard.press({ keys: "tab" });
            sf.keyboard.press({ keys: "shift+tab" })
        
            //let selectionInfo
            let selectedClipsNames
            let selectedArray = []
            let selectionEnd
            let halfCrossFadeDuration
            while (true) {
        
                let currentSelectionEndNumber = +sf.ui.proTools.selectionGet().selectionEnd.replace(/[ :\+\|.]/g, '')
        
                selectedClipsNames = getClipNames(numberOfCharacters)
        
        
                if (selectedClipsNames[0] != null) {
                    selectedArray.push(selectedClipsNames[0])
                }
        
                if (selectedArray.filter(onlyUnique).length <= 1) {
        
                    selectionEnd = sf.ui.proTools.selectionGetInSamples().selectionEnd
        
                    if (sf.ui.proTools.selectionGetInfo().hasCrossFade) {
        
                        halfCrossFadeDuration = +sf.ui.proTools.selectionGetInSamples().selectionLength / 2
        
                        selectionEnd = +sf.ui.proTools.selectionGetInSamples().selectionEnd - halfCrossFadeDuration
                    }
                }
        
        
                if (selectedArray.filter(onlyUnique).length <= 1 &&
                    !sf.ui.proTools.selectionGetInfo().hasEndFade &&
                    currentSelectionEndNumber < originaSelectionEndToString) {
        
                    sf.keyboard.press({ keys: "ctrl+tab" });
        
                } else {
        
                    sf.ui.proTools.selectionSetInSamples({
                        selectionStart: +originalSelectionStart,
                        selectionEnd: +selectionEnd
                    })
        
                    sf.ui.proTools.menuClick({ menuPath: ["Edit", "Cut"] });
        
                    sf.ui.proTools.selectionSetInSamples({
                        selectionStart: +originalSelectionStart,
                        selectionEnd: +selectionEnd
                    })
        
                    findTrackNameAndPaste(selectedArray[0], processTrack)
                    break;
                }
                //sf.wait({ intervalMs: 1000 })
            }
        }
        
        
        
        function main() {
        
            //  Reset menu
            sf.keyboard.press({ keys: "left" });
        
            duplicateSelectedTrack({ duplicateActivePlaylist: true })
        
            let processTrack = sf.ui.proTools.selectedTrackNames
        
            sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" })
        
            while (true) {
        
                sf.ui.proTools.invalidate()
        
                sf.ui.proTools.menuClick({ menuPath: ["Edit", "Select All"] });
        
                let originaSelectionEndToString = +sf.ui.proTools.selectionGet().selectionEnd.replace(/[ :\+\|.]/g, '')
        
                let originalSelectionStart = sf.ui.proTools.selectionGet().selectionStart
        
                let selectedClipsNames = getClipNames(numberOfCharacters)
        
                if (selectedClipsNames.length >= 1) {
        
                    mainProcess(processTrack, originaSelectionEndToString, numberOfCharacters, originalSelectionStart)
        
                } else {
        
                    //  goToStartOfSession()
        
                    // Option to delete empy track in the end
                    sf.ui.proTools.menuClick({ menuPath: ["Track", "Delete"] });
                    break
                }
            }
        }
        
        
        
        
        
        /////////         MAIN          ///////// 
        /////////         MAIN          ///////// 
        sf.ui.proTools.appActivateMainWindow()
        
        
        // Ask how many characters to grab
        const numberOfCharacters = askNumberChar()
        
        if (numberOfCharacters == 0) {
            alert("Numbers only, minimum  is 1.")
        }
        
        
        //  List of menus to enable
        clipListEnableSubmenu({
            menuItemNames: [
                "Audio",
                "Groups",
                "Auto-Created",
            ]
        })
        
        
        let originalTimeCOunter = timeCounter()
        
        sf.ui.proTools.selectedTrackHeaders.slice().map(track => {
        
            sf.ui.proTools.invalidate()
        
            track.trackSelect();
        
            track.trackScrollToView();
        
            main()
        });
        
        sf.ui.proTools.mainCounterSetValue({ targetValue: originalTimeCOunter })
        
        alert(`Done!`)
        
        ReplySolution
        1. C

          WOW!!! Thank you so much, @Dustin_Harris! Not only is it working perfectly again, but it's somehow going waaay faster than it used to. You're a genius! Thank you.