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

Select/Show Tracks in Folder throwing errors

By Daniel Knobler @Daniel_Knobler
    2022-05-03 20:20:55.576Z

    Title

    Select/Show Tracks in Folder throwing errors

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

    This has worked for a while and stopped working recently. It is supposed to select/show tracks within a folder. When I run it, I now get the error message:

    TypeError: Cannot read property 'titleButton' of null
    

    (Select/Show Tracks in Folder line 8)

    Are you seeing an error?

    TypeError: Cannot read property 'titleButton' of null (Select/Show Tracks in Folder line 8)

    What happens when you run this script?

    It seems like whenever I run it I get that error message. I have it as part of several different macros and they all give me the same error message.

    How were you running this script?

    I used a keyboard shortcut within the target app

    How important is this issue to you?

    4

    Details

    {
        "inputExpected": "This has worked for a while and stopped working recently. It is supposed to select/show tracks within a folder. When I run it, I now get the error message:\n\n    TypeError: Cannot read property 'titleButton' of null\n(Select/Show Tracks in Folder line 8) ",
        "inputIsError": true,
        "inputError": "    TypeError: Cannot read property 'titleButton' of null\n(Select/Show Tracks in Folder line 8) ",
        "inputWhatHappens": "It seems like whenever I run it I get that error message. I have it as part of several different macros and they all give me the same error message.",
        "inputHowRun": {
            "key": "-Mpfwh4RkPLb2LPwjePT",
            "title": "I used a keyboard shortcut within the target app"
        },
        "inputImportance": 4,
        "inputTitle": "Select/Show Tracks in Folder throwing errors"
    }

    Source

    
    const { folder, action, includeFolder } = event.props;
    
    /**
     * @param {AxPtTrackHeader} track
     */
    function getTrackDepth(track) {
        return Math.floor((track.titleButton.frame.x - track.frame.x) / 15);
    }
    
    function getTrackFolderStructure() {
        var root = { children: [] };
        var stack = [];
        stack[0] = root;
        var flatList = sf.ui.proTools.visibleTrackHeaders.map(t => ({ track: t, depth: getTrackDepth(t) }));
        for (var i = 0; i < flatList.length; i++) {
            var flatItem = flatList[i];
            var node = {
                name: flatItem.track.normalizedTrackName,
                type: flatItem.track.title.value.match(/ - (.*)$/)[1].trim().replace(/track$/i, '').trim().toLowerCase(),
                track: flatItem.track,
                children: []
            };
            stack[flatItem.depth] = node;
            stack[flatItem.depth - 1].children.push(node);
        }
        return root;
    }
    
    function findNearestParentFolder(root, trackName) {
        var folderStack = [];
        function walk(node) {
            var isFolder = node.type && node.type.indexOf('folder') >= 0;
            if (isFolder) {
                folderStack.push(node);
            }
            if (node.name === trackName) {
                return folderStack.slice(-1)[0];
            }
            for (var i = 0; i < node.children.length; i++) {
                var res = walk(node.children[i]);
                if (res) return res;
            }
            if (isFolder) {
                folderStack.pop();
            }
        }
        return walk(root);
    }
    
    /**
     * @param {AxPtTrackHeader} folderTrack
     * @param {boolean} includeFolder
     * @return {AxPtTrackHeader[]}
     */
    function getTracksInFolder(folderTrack, includeFolder) {
        var root = getTrackFolderStructure();
    
        var nearestFolder = findNearestParentFolder(root, folderTrack.normalizedTrackName);
        if (!nearestFolder)
            return [];
    
        let subTracks = nearestFolder.children.map(i => i.track);
        return includeFolder ? [folderTrack, ...subTracks] : subTracks;
    }
    
    function selectTracksInFolder(folderTrack, includeFolder) {
        sf.ui.proTools.trackSelectByName({
            names: getTracksInFolder(folderTrack, includeFolder).map(t => t.normalizedTrackName),
        });
    }
    
    function getInputPathOfSelectedTrack() {
        sf.ui.proTools.appActivateMainWindow();
        var path = sf.ui.proTools.selectedTrack.inputPathButton.popupMenuFetchAllItems().menuItems.filter(mi => mi.element.isMenuChecked)[0].names;
        sf.ui.proTools.appActivateMainWindow();
        return path;
    }
    
    function main() {
        if (!sf.ui.proTools.isRunning) throw `Pro Tools is not running`;
    
        sf.ui.proTools.appActivateMainWindow();
    
        sf.ui.proTools.invalidate();
    
        //Get folder track
        let track = folder;
    
        //Make sure current folder is open
        track.folderTrackSetOpen({
            targetValue: "Enable",
        });
    
        //Select all audio tracks in folder
        selectTracksInFolder(track, includeFolder);
    
        //Only show these
        if (action === "ShowOnly") {
            sf.ui.proTools.trackListMenu({
                menuItemName: 'Show Only Selected Tracks',
            });
        }
    }
    
    main();
    
    

    Links

    User UID: 04vQUq1KmYSCMZHeEkMslw0IpRu2

    Feedback Key: sffeedback:04vQUq1KmYSCMZHeEkMslw0IpRu2:-N1A_ItcgOp47Fvx8myj

    Feedback ZIP

    Solved in post #4, click to view
    • 9 replies
    1. Hey Daniel,

      There were some changes in PT2022.4 to how folder track titles are presented. I'll take a look.

      1. What would be examples of the input values for the template? How would we test this?

        1. DDaniel Knobler @Daniel_Knobler
            2022-05-04 16:09:00.759Z

            I have a basic folder named * that contains all of the tracks for a mix (which are organized into different folders). I use that script to select all folders within * and then I use another script to toggle them open or closed.

        2. In reply toDaniel_Knobler:

          Try this:

          
          const { folder, action, includeFolder } = event.props;
          
          /**
           * @param {AxPtTrackHeader} track
           */
          function getTrackDepth(track) {
              if (track === null) return 0;
              return Math.floor((track.titleButton.frame.x - track.frame.x) / 15);
          }
          
          function getTrackFolderStructure() {
              var root = { children: [] };
              var stack = [];
              stack[0] = root;
              var flatList = sf.ui.proTools.visibleTrackHeaders.filter(t => !!t).map(t => ({ track: t, depth: getTrackDepth(t) }));
              for (var i = 0; i < flatList.length; i++) {
                  var flatItem = flatList[i];
                  var node = {
                      name: flatItem.track.normalizedTrackName,
                      type: flatItem.track.title.value.match(/ - (.*)$/)[1].trim().replace(/track$/i, '').trim().toLowerCase(),
                      track: flatItem.track,
                      children: []
                  };
                  stack[flatItem.depth] = node;
                  stack[flatItem.depth - 1].children.push(node);
              }
              return root;
          }
          
          function findNearestParentFolder(root, trackName) {
              var folderStack = [];
              function walk(node) {
                  var isFolder = node.type && node.type.indexOf('folder') >= 0;
                  if (isFolder) {
                      folderStack.push(node);
                  }
                  if (node.name === trackName) {
                      return folderStack.slice(-1)[0];
                  }
                  for (var i = 0; i < node.children.length; i++) {
                      var res = walk(node.children[i]);
                      if (res) return res;
                  }
                  if (isFolder) {
                      folderStack.pop();
                  }
              }
              return walk(root);
          }
          
          /**
           * @param {AxPtTrackHeader} folderTrack
           * @param {boolean} includeFolder
           * @return {AxPtTrackHeader[]}
           */
          function getTracksInFolder(folderTrack, includeFolder) {
              var root = getTrackFolderStructure();
          
              var nearestFolder = findNearestParentFolder(root, folderTrack.normalizedTrackName);
              if (!nearestFolder)
                  return [];
          
              let subTracks = nearestFolder.children.map(i => i.track);
              return includeFolder ? [folderTrack, ...subTracks] : subTracks;
          }
          
          function selectTracksInFolder(folderTrack, includeFolder) {
              sf.ui.proTools.trackSelectByName({
                  names: getTracksInFolder(folderTrack, includeFolder).map(t => t.normalizedTrackName),
              });
          }
          
          function getInputPathOfSelectedTrack() {
              sf.ui.proTools.appActivateMainWindow();
              var path = sf.ui.proTools.selectedTrack.inputPathButton.popupMenuFetchAllItems().menuItems.filter(mi => mi.element.isMenuChecked)[0].names;
              sf.ui.proTools.appActivateMainWindow();
              return path;
          }
          
          function main() {
              if (!sf.ui.proTools.isRunning) throw `Pro Tools is not running`;
          
          
              sf.ui.proTools.appActivateMainWindow();
          
              sf.ui.proTools.invalidate();
          
              //Get folder track
              let track = folder;
          
              //Make sure current folder is open
              track.folderTrackSetOpen({
                  targetValue: "Enable",
              });
          
              sf.ui.proTools.invalidate();
          
              //Select all audio tracks in folder
              selectTracksInFolder(track, includeFolder);
          
              //Only show these
              if (action === "ShowOnly") {
                  sf.ui.proTools.trackListMenu({
                      menuItemName: 'Show Only Selected Tracks',
                  });
              }
          }
          
          main();
          
          ReplySolution
          1. DDaniel Knobler @Daniel_Knobler
              2022-05-04 16:17:38.981Z

              This works. Thank you!

            • S
              In reply toDaniel_Knobler:
              SoundFlow Bot @soundflowbot
                2022-05-04 11:16:21.896Z

                This issue is now tracked internally by SoundFlow as SF-581

                1. We have this fixed now so that the original code/script you had will start working again.
                  The fix will be part of 5.1.5 when it comes out.

                2. D
                  In reply toDaniel_Knobler:
                  Daniel Knobler @Daniel_Knobler
                    2022-05-05 17:53:37.744Z

                    Is there any way to make this script select/show ALL selected tracks? It seems like at the moment it can only do a single selected track "by using the "" option. If multiple tracks are selected when the script is run it only works on the first selected track.

                    Thanks!

                    1. S
                      In reply toDaniel_Knobler:
                      SoundFlow Bot @soundflowbot
                        2022-05-11 10:24:22.201Z

                        This issue has been marked as Done