No internet connection
  1. Home
  2. Script Sharing

Remove Inactive Inserts or Sends or both

By Sreejesh Nair @Sreejesh_Nair
    2023-12-10 05:18:42.349Z2024-09-08 03:54:30.886Z

    Here's a script I built based on a discussion here:
    Remove all inactive plugins/sends

    I made a few changes to this.

    1. It passes all the tracks per inactive insert to an array and then does the removal in one go. This is faster than removing for each track.
    2. if there is no track selected it asks to proceed for all the tracks.
    3. There are some checks to compensate for unusual track heights or positions where it couldnt access the insert element.
    4. I've slightly optimised the code as well.
    5. It asks for a choice of all Plugins and Inserts or just Plugins or just inserts. For a more configurable one, check out the one I mention below. That code is open as well in the package in case you want to use it!

    There is a Soundflow Template version of this in my Sree - Pro Tools Editing Package where you can build your own presets and choose which inserts and / or sends need to be checked for inactive Plugins or sends or both. Hope it helps!

    // Closes Pro Tools confirmation dialogs if they contain "Remove" or "Change" buttons
    function closeDialog() {
        ['Remove', 'Change'].forEach(title => {
            const button = sf.ui.proTools.confirmationDialog.invalidate().buttons.whoseTitle.is(title).first;
            if (button.exists) {
                button.elementClick();
                sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: "Disappear" });
            }
        });
    }
    
    // Finds inactive inserts and sends in the provided tracks and maps them
    function findInactiveItems(tracks) {
        let map = { inserts: {}, sends: {} };
        let elements = { inserts: [], sends: [] };
        let notificationID = sf.system.newGuid().guid;
    
        tracks.forEach((track, index) => {
            // Notify progress of processing tracks
            sf.interaction.notify({
                uid: notificationID,
                title: "Processing Automation Data",
                progress: (index + 1) / tracks.length,
                message: `Analysing Session: ${index + 1} of ${tracks.length} tracks`
            });
    
            if (track) {
                // Process insert buttons
                track.insertSelectorButtons.forEach((button, i) => {
                    if (button.exists && button.invalidate().value.value.startsWith("inactive")) {
                        const slot = i + 1;
                        if (!map.inserts[slot]) {
                            map.inserts[slot] = [];
                            elements.inserts.push(slot);
                        }
                        map.inserts[slot].push(track.normalizedTrackName);
                    }
                });
    
                // Process send buttons
                track.sendSelectorButtons.forEach((button, i) => {
                    if (button.exists && button.invalidate().value.value.startsWith("inactive")) {
                        const slot = i + 1;
                        if (!map.sends[slot]) {
                            map.sends[slot] = [];
                            elements.sends.push(slot);
                        }
                        map.sends[slot].push(track.normalizedTrackName);
                    }
                });
            }
        });
    
        return { map, elements };
    }
    
    // Removes items (inserts or sends) from the specified tracks and slots
    function removeItems(names, type, slot) {
        sf.keyboard.press({ keys: "home" });
        sf.ui.proTools.trackSelectByName({ names, deselectOthers: true });
        const original = sf.ui.proTools.selectedTrackNames;
    
        if (original.length > 0) {
            sf.ui.proTools.trackGetByName({ name: original[0] }).track.trackScrollToView();
        }
    
        sf.ui.proTools.trackSelectByName({ names, deselectOthers: true });
        sf.ui.proTools.selectedTrack.trackInsertOrSendSelect({
            insertOrSend: type,
            pluginNumber: parseInt(slot, 10),
            selectForAllSelectedTracks: true,
            pluginPath: type === 'Insert' ? ['no insert'] : ['no send']
        });
    
        closeDialog();
        sf.wait({ intervalMs: 250 });
    }
    
    // Batch removes inactive inserts or sends based on the selected option
    function batchRemove(map, elements, option) {
        let processedItems = 0;
        let notificationID = sf.system.newGuid().guid;
        const totalItems = elements.inserts.length + elements.sends.length;
    
        const removeFromSlot = (slot, type, map) => {
            removeItems(map[slot], type, slot);
            // Notify progress of removal
            sf.interaction.notify({
                uid: notificationID,
                title: "Removing Inactive Items",
                progress: (++processedItems) / totalItems,
                message: `Removing ${type}s: ${processedItems} of ${totalItems}`
            });
        };
    
        if (option === "both" || option === "plugins") {
            elements.inserts.forEach(slot => removeFromSlot(slot, 'Insert', map.inserts));
        }
    
        if (option === "both" || option === "sends") {
            elements.sends.forEach(slot => removeFromSlot(slot, 'Send', map.sends));
        }
    
        sf.interaction.notify({
            uid: notificationID,
            title: "Removing Inactive Items",
            progress: 100,
            message: `Removal complete.`
        });
    }
    
    // Sets the track size for the selected track
    function setTrackSize(size) {
        const selectedTrackH = sf.ui.proTools.selectedTrackHeaders[0];
        try {
            // Try setting track size using the right popup menu
            selectedTrackH.popupMenuSelect({
                anchor: "MidRight",
                relativePosition: { x: -10, y: 0 },
                menuPath: [size],
                isOption: true,
                onError: "Continue"
            });
        } catch (err) {
            // If the right popup menu fails, try the left popup menu
            if (selectedTrackH.frame.h <= 79) {
                sf.ui.proTools.selectedTrack.popupButtons.allItems[2].popupMenuSelect({
                    menuPath: ["Track Height", size],
                    isOption: true
                });
            }
        }
    }
    
    // Activates the specified Pro Tools menu options
    function activateProToolsMenus(menuOptions) {
        menuOptions.forEach(menuOption =>
            sf.ui.proTools.menuClick({
                menuPath: ["View", "Edit Window Views", menuOption],
                targetValue: "Enable"
            })
        );
    }
    
    // Define Pro Tools menu options to be activated
    const proToolsMenuOptions = ["Inserts A-E", "Inserts F-J", "Sends A-E", "Sends F-J"];
    
    // Main script function that initiates the process based on user selection
    function startScript() {
        // User selection prompt
        const userSelection = sf.interaction.selectFromList({
            items: ["Remove all Inactive Plugins and Sends", "Remove all Inactive Plugins", "Remove all Inactive Sends"],
            allowMultipleSelections: false,
            prompt: "Choose what you want removed",
            title: "Inactive Cleaner"
        }).list;
    
        if (userSelection.length === 0) return alert("No option selected. Exiting script.");
    
        let chosenOption = userSelection[0];
        sf.ui.proTools.appActivate();
        sf.ui.proTools.mainWindow.invalidate();
    
        const wasLinked = sf.ui.proTools.getMenuItem('Options', 'Link Track and Edit Selection').isMenuChecked;
        if (wasLinked) sf.ui.proTools.menuClick({ menuPath: ["Options", "Link Track and Edit Selection"] });
    
        if (!sf.ui.proTools.mainWindow.title.invalidate().value.startsWith("Edit: ")) {
            sf.ui.proTools.menuClick({ menuPath: ["Window", "Edit"] });
        }
    
        activateProToolsMenus(proToolsMenuOptions);
        sf.ui.proTools.invalidate();
    
        let tracksForProcessing = sf.ui.proTools.selectedTrackHeaders;
        let firstSelection = sf.ui.proTools.selectedTrackNames;
    
        if (tracksForProcessing.length !== 0) {
            sf.ui.proTools.trackGetByName({ name: firstSelection[0] }).track.trackScrollToView();
            sf.ui.proTools.trackSelectByName({ names: firstSelection });
            setTrackSize('small');
        } else {
            if (!confirm("No tracks are selected. Would you like to proceed with all visible active tracks?")) {
                return alert("Operation cancelled.");
            }
    
            firstSelection = sf.ui.proTools.visibleTrackNames;
            sf.ui.proTools.trackGetByName({ name: firstSelection[0] }).track.trackScrollToView();
    
            const activeTrackNames = sf.app.proTools.tracks.invalidate().allItems.reduce((activeTrackNames, track) => {
                if (!track.isInactive) activeTrackNames.push(track.name);
                return activeTrackNames;
            }, []);
    
            sf.app.proTools.selectTracksByName({ trackNames: activeTrackNames });
            setTrackSize('small');
            tracksForProcessing = sf.ui.proTools.selectedTrackHeaders;
        }
    
        const { map, elements } = findInactiveItems(tracksForProcessing);
    
        if (elements.inserts.length === 0 && elements.sends.length === 0) {
            return alert("No inactive plugins or sends found.");
        }
    
        let option = chosenOption === "Remove all Inactive Plugins and Sends" ? "both" :
                     chosenOption === "Remove all Inactive Plugins" ? (elements.inserts.length === 0 ? (alert("No inactive plugins found."), undefined) : "plugins") :
                     (elements.sends.length === 0 ? (alert("No inactive sends found."), undefined) : "sends");
    
        if (option) batchRemove(map, elements, option);
    
        if (wasLinked) sf.ui.proTools.menuClick({ menuPath: ["Options", "Link Track and Edit Selection"] });
    
        sf.ui.proTools.trackSelectByName({ names: firstSelection, deselectOthers: true });
        alert("Operation completed.");
    }
    
    // Start the script
    startScript();
    
    Solved in post #22, click to view
    • 29 replies

    There are 29 replies. Estimated reading time: 49 minutes

    1. D
      danielkassulke @danielkassulke
        2024-02-03 08:22:43.231Z

        Hi @Sreejesh_Nair . Very excited to use this - I think the way you've described it working is a really big improvement on previous attempts at scripts like this. I can't get this to work on either my macbook pro or my studio computer (both ventura, 2023.12). I've tried it on new sessions with just inactive sends or inserts or both, and existing, busier sessions with sends, inserts and both. It seems do everything except remove the inserts. As you can see from the screen recording I took, there's nothing obstructing the send/insert buttons, and you've obviously incorporated track height handling. I see that you posted this script a couple of days before 2023.12 was released, and I'm wondering if that ProTools update has made this fail? For context, even though in the video I chose to remove inactive sends and inserts, I got the same result with choosing sends or inserts by themselves. The "Analysing session for inactive plugins/sends. Please wait" and "Analysis complete. Proceeding." messages still get logged. Any ideas what might be causing this?

        Thanks!

        1. SSreejesh Nair @Sreejesh_Nair
            2024-02-04 09:00:16.637Z2024-02-04 20:17:27.019Z

            Thank you for pointing that out. I had forgotten to add the word "all" in the chosenOption. It should be fixed now! And while at it, I also added a check to ignore all inactive tracks. This was an issue if it was part of the selection.

            1. Ddanielkassulke @danielkassulke
                2024-02-04 21:41:13.538Z

                Really amazing script. I am experiencing it working its magic now! A couple of other things I noticed: I assume if (wasLinked) should be if (!wasLinked)? The other thing that I think worth incorporating is some scroll to view functionality so that the insert/send buttons can be accessed if the first selected track isn't visible.

                1. SSreejesh Nair @Sreejesh_Nair
                    2024-02-05 04:48:08.428Z2024-02-05 08:26:20.218Z

                    Hi.. Thank you.

                    The wasLinked logic may look confusing at first sight. The first if (wasLinked) turns the link edit off if true. At the end of the script, it doesn’t check for a new value. It references the first value. And turns it back on if it was on before the script ran (it’s a toggle). So the logic is if it was on before the functions were called, it would have been turned off. So on the completion of the function it would turn it back on. If it was already off the. The if (wasLinked) would be false the first time the state is checked.

                    There are scroll functions. There is a scroll to track for the first one in the array selection referenced by firstSelection [0] on line 151 and another function in 162. Unfortunately it doesn’t seem to work consistently. I have now updated the code to be a bit more efficient in that because I noticed that in some cases the selection was lost after executing a scroll to track. I have fixed that and removed the topEditTrack() function as the new scroll is much more easier and efficient.

              • F
                In reply toSreejesh_Nair:
                Forrester Savell @Forrester_Savell
                  2024-06-14 00:02:26.278Z2024-09-06 08:04:06.777Z

                  Hello @Sreejesh_Nair

                  Thanks for writing/sharing this script. I was looking for ways to make the original remove inactive plugs/sends script faster by option-clicking and found this. You've pulled it off, great job!

                  I had a few issues running the script above, or the one from your package, perhaps things have changed with PT versions or our workflows are different. The script above breaks when trying to find the track height (line 90), so I replaced the function. I also had issues with line 17, so I've modified the findInactiveItems function. I also included a track filter so it only selects tracks with plugins/sends and brings them into view. Lastly I added some error handling so it restores the track heights if it fails.

                  This script generally seems to work well for me. Keen to hear if it works for you or @danielkassulke or if any issues? My only issue is that it takes an inordinate amount of time to analyze an entire session (i actually haven't let it run long enough to test it as its +15 minutes). I find it quicker to batch select tracks, so wondering if you have the same issue?

                  const ignorePlugins = ["Melodyne", "Auto-Tune"]
                  
                  const proToolsMenuOptions = ["Inserts A-E", "Inserts F-J", "Sends A-E", "Sends F-J"];
                  
                  function closeDialog() {
                      const titles = ['Remove', 'Change'];
                      titles.forEach(title => {
                          if (sf.ui.proTools.confirmationDialog.invalidate().buttons.whoseTitle.is(title).first.exists) {
                              sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is(title).first.elementClick();
                              sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: "Disappear" });
                          }
                      });
                  }
                  
                  function hideAllFloatingWindows() {
                      //Hide all floating windows
                      sf.ui.proTools.menuClick({
                          menuPath: ["Window", "Hide All Floating Windows"],
                          targetValue: "Enable",
                      });
                  }
                  
                  function scrollToTrackNamed(trackName, selectedTracks) {
                      // Open scroll to track dialog and enter track name
                      sf.ui.proTools.menuClick({ menuPath: ['Track', 'Scroll to Track...'] });
                      var confirmationDialogWin = sf.ui.proTools.confirmationDialog;
                  
                      confirmationDialogWin.elementWaitFor();
                  
                      confirmationDialogWin.textFields.first.elementSetTextFieldWithAreaValue({ value: trackName });
                  
                      confirmationDialogWin.buttons.whoseTitle.is('OK').first.elementClick();
                  
                      confirmationDialogWin.elementWaitFor({ waitType: 'Disappear' });
                  
                      //Re-select originally selected tracks
                      sf.ui.proTools.trackSelectByName({ names: selectedTracks })
                  };
                  
                  
                  function saveCurrentTrackView() {
                      sf.ui.proTools.appActivateMainWindow();
                      sf.keyboard.press({
                          keys: "numpad enter",
                      });
                      sf.ui.proTools.newMemoryLocationDialog.elementWaitFor();
                      //Wait for the Mem Location dialog to appear and assign it to the memLocDlg variable
                      var memLocDlg = sf.ui.proTools.dialogWaitForManual({
                          dialogTitle: 'New Memory Location'
                      }).dialog;
                      // We only want to store the track visibilty so clear all check boxes and then check the appropriate one
                      var checkBoxes = memLocDlg.getElements("AXChildren").filter(function (e) { return e.fullRole == "AXCheckBox" });
                      for (var i = 0; i < 6; i++) {
                          checkBoxes[i].checkboxSet({
                              targetValue: "Disable",
                          });
                      }
                      checkBoxes[2].checkboxSet({
                          targetValue: "Enable",
                      });
                      checkBoxes[3].checkboxSet({
                          targetValue: "Enable",
                      });
                      //Name the Location Track Visibilty
                  
                      sf.ui.proTools.newMemoryLocationDialog.textFields.allItems[0].elementSetTextFieldWithAreaValue({
                          value: "Track Visibility",
                      });
                      //Get the Location Number and pass it back so we can delete it when we're done
                      var locationNumber = sf.ui.proTools.newMemoryLocationDialog.textFields.allItems[1].value.value;
                  
                      // Set time Properties to none
                      sf.ui.proTools.newMemoryLocationDialog.radioButtons.allItems[2].elementClick();
                      //Create the Location
                      sf.ui.proTools.newMemoryLocationDialog.buttons.whoseTitle.is('OK').first.elementClick();
                  
                      sf.ui.proTools.newMemoryLocationDialog.elementWaitFor({
                          waitType: "Disappear",
                      });
                  
                      return locationNumber;
                  }
                  
                  function recallAndDeleteTrackView(locationNumber) {
                  
                      sf.ui.proTools.memoryLocationsGoto({
                          memoryLocationNumber: Number(locationNumber),
                      });
                  
                      sf.ui.proTools.memoryLocationsShowWindow();
                  
                      sf.ui.proTools.memoryLocationsWindow.popupButtons.whoseTitle.is('Memory Locations').first.popupMenuSelect({
                          menuSelector: items => items.filter(i => i.names[0].match(/^Delete \"/))[0]
                      });
                      sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is("Yes").first.elementClick();
                  
                  
                      sf.ui.proTools.confirmationDialog.elementWaitFor({
                          waitType: "Disappear",
                      });
                  
                      sf.ui.proTools.menuClick({
                          menuPath: ["Window", "Memory Locations"],
                          targetValue: 'Disable'  //or 'Enable' or 'Toggle'
                      });
                      sf.ui.proTools.memoryLocationsWindow.elementWaitFor({
                          waitType: "Disappear",
                      });
                  }
                  
                  
                  function findInactiveItems(tracks) {
                      let map = { inserts: {}, sends: {} };
                      let elements = { inserts: [], sends: [] };
                  
                      const getFrozenTrackState = (trackName) => {
                          const track = sf.ui.proTools.trackGetByName({ name: trackName.normalizedTrackName }).track;
                          const freezeBtn = track.buttons.whoseTitle.is("Frozen");
                          return freezeBtn ? !freezeBtn.exists : true;
                      };
                  
                      tracks.forEach(track => {
                          if (track && (getFrozenTrackState(track))) {
                              for (let i = 0; i < track.insertSelectorButtons.length; i++) {
                                  const button = track.insertSelectorButtons[i];
                                  const insertBtn = track.insertButtons[i];
                                  const plugName = insertBtn.value.invalidate().value;
                                  let plugExists = ignorePlugins.some(p => plugName.includes(p));
                                  if (button.exists
                                      && button.invalidate().value.value.startsWith("inactive")
                                      && (plugExists != true)) {
                  
                                      const slot = i + 1;
                                      if (!map.inserts[slot]) {
                                          map.inserts[slot] = [];
                                          elements.inserts.push(slot);
                                      }
                                      map.inserts[slot].push(track.normalizedTrackName);
                                  }
                              }
                          }
                  
                          for (let i = 0; i < track.sendSelectorButtons.length; i++) {
                              const button = track.sendSelectorButtons[i];
                              if (button.exists && button.invalidate().value.value.startsWith("inactive")) {
                                  const slot = i + 1;
                                  if (!map.sends[slot]) {
                                      map.sends[slot] = [];
                                      elements.sends.push(slot);
                                  }
                                  map.sends[slot].push(track.normalizedTrackName);
                              }
                          }
                      })
                  
                      return { map, elements };
                  }
                  
                  
                  function removeItems(names, type, slot) {
                  
                      sf.ui.proTools.trackSelectByName({ names: names, deselectOthers: true });
                      const original = sf.ui.proTools.selectedTrackNames;
                      if (original.length > 0) {
                          sf.ui.proTools.trackGetByName({ name: original[0] }).track.trackScrollToView();
                      }
                  
                      sf.ui.proTools.trackSelectByName({ names: names, deselectOthers: true });
                      sf.ui.proTools.selectedTrack.trackInsertOrSendSelect({
                          insertOrSend: type,
                          pluginNumber: parseInt(slot, 10),
                          selectForAllSelectedTracks: true,
                          pluginPath: type === 'Insert' ? ['no insert'] : ['no send']
                      });
                      sf.wait({ intervalMs: 500 });
                      closeDialog();
                  }
                  
                  
                  function batchRemove(map, elements, option) {
                  
                      if (option === "both" || option === "plugins") {
                          map.inserts = map.inserts || {};
                          elements.inserts.forEach(slot => {
                              if (map.inserts[slot]) {
                                  removeItems(map.inserts[slot], 'Insert', slot);
                              }
                          });
                      }
                  
                      if (option === "both" || option === "sends") {
                          map.sends = map.sends || {};
                          elements.sends.forEach(slot => {
                              if (map.sends[slot]) {
                                  removeItems(map.sends[slot], 'Send', slot);
                              }
                          });
                      }
                  }
                  
                  
                  function setTrackHeights(size) {
                  
                      const selectedTrackH = sf.ui.proTools.selectedTrackHeaders[0]
                      const hasHeaderHeightPopup = selectedTrackH.frame.h <= 79
                  
                      try {
                          selectedTrackH.popupMenuSelect({
                              anchor: "MidRight",
                              relativePosition: { x: - 10, y: 0 },
                              menuPath: [size],
                              isOption: true,
                              onError: "Continue",
                          })
                      } catch (err) {
                          // Try header left poput
                          if (hasHeaderHeightPopup) {
                              sf.ui.proTools.selectedTrack.popupButtons.allItems[2].popupMenuSelect({
                                  menuPath: ["Track Height", size],
                                  isOption: true,
                              });
                          };
                      };
                  };
                  
                  
                  function activateProToolsMenus(menuOptions) {
                      menuOptions.forEach(menuOption => {
                          sf.ui.proTools.menuClick({
                              menuPath: ["View", "Edit Window Views", menuOption],
                              targetValue: "Enable"
                          });
                      });
                  }
                  
                  
                  function selectAllVisibleTracks() {
                      sf.ui.proTools.trackDeselectAll();
                  
                      const topOfEditWindow = sf.ui.proTools.mainWindow.timelineFocusButton.frame.y;
                  
                      const topTrack = sf.ui.proTools.visibleTrackHeaders.filter(h => h.frame.y >= topOfEditWindow)[0];
                  
                      topTrack.titleButton.mouseClickElement({ isOption: true });
                  }
                  
                  
                  function filterTracks(initialSelection) {
                  
                      sf.ui.proTools.appActivateMainWindow();
                  
                      //Create array of tracks with Inserts & Sends, from initially selected tracks
                      var allVisibleSelectedTracks = initialSelection
                          .filter(t => t != null && (
                              t.title.value.includes("Audio Track")
                              || t.title.value.includes("Aux Track")
                              || t.title.value.includes('Inst Track')
                              || t.title.value.includes('Routing Folder Track')
                              || t.title.value.includes('Master Track'))
                          )
                  
                      //log(allVisibleSelectedTracks);
                      return allVisibleSelectedTracks
                  }
                  
                  
                  function onScreenMessage(job, x) {
                      sf.interaction.displayDialog({
                          prompt: job,
                          giveUpAfterSeconds: x
                      });
                  };
                  
                  
                  
                  function startScript() {
                      sf.ui.proTools.appActivate();
                      //sf.ui.proTools.mainWindow.invalidate();
                  
                      //Get the current window name
                      const focusedWindow = sf.ui.proTools.focusedWindow.title.invalidate().value.split(" ", 1)[0].slice(0, -1);
                  
                      //If not already in the Edit window, switch to the Edit window
                      if (focusedWindow !== 'Edit') {
                          try {
                              sf.ui.proTools.menuClick({ menuPath: ['Window', 'Edit'] });
                          }
                          catch (err) { log('Could not focus Edit Window') }
                      }
                  
                      let userViewNum = saveCurrentTrackView()
                  
                      hideAllFloatingWindows()
                  
                      let restoreTracks = sf.ui.proTools.selectedTrackHeaders.filter(t => t != null)
                  
                      // Check if Track List is Open
                      const isTrackListOpen = () => sf.ui.proTools.getMenuItem('View', 'Other Displays', 'Track List').isMenuChecked
                  
                      const initialState = isTrackListOpen()
                  
                      // // Open Track List
                      sf.ui.proTools.menuClick({ menuPath: ['View', 'Other Displays', 'Track List'], targetValue: "Enable" })
                  
                  
                      //Ensure Link Track and Edit Selection is enabled
                      const wasLinked = sf.ui.proTools.getMenuItem('Options', 'Link Track and Edit Selection').isMenuChecked;
                      if (wasLinked) {
                          sf.ui.proTools.menuClick({
                              menuPath: ["Options", "Link Track and Edit Selection"],
                          });
                      }
                  
                  
                      //Ensure Inserts and Sends are visible in Edit Window
                      activateProToolsMenus(proToolsMenuOptions);
                  
                      sf.ui.proTools.invalidate();
                  
                      //Hide Inactive Tracks for clarity
                      sf.ui.proTools.trackOpenListPopupMenu().popupMenu.menuClickPopupMenu({
                          menuPath: ["Hide", "Inactive Tracks"],
                      });
                  
                      //Store initial track selection
                      var firstSelection = sf.ui.proTools.invalidate().trackGetSelectedTracks().trackHeaders.filter(t => t != null);
                  
                      //If no tracks selected, ask if entire session is to be processed
                      if (firstSelection.length == 0) {
                          var procSession = sf.interaction.displayDialog({
                              buttons: ["Cancel", "Proceed"],
                              defaultButton: 'Proceed',
                              prompt: "No tracks selected, proceed with entire session?",
                          }).button;
                          if (procSession == 'Proceed') {
                  
                              sf.ui.proTools.trackSelectByName({
                                  names: sf.ui.proTools.visibleTrackNames
                              });
                  
                          } else throw 0;
                  
                          var firstSelection = sf.ui.proTools.invalidate().trackGetSelectedTracks().trackHeaders.filter(t => t != null);
                      }
                  
                      try {
                  
                          //Find and store tracks that have inactive Inserts and Sends (and isn't Frozen)
                          let tracksForProcessing = filterTracks(firstSelection)
                  
                          if (tracksForProcessing.length == 0) {
                              alert("There was an error, no tracks selected. Script cancelled.");
                              throw 0;
                          }
                  
                          //Select the suitable tracks ready for analysis
                          scrollToTrackNamed(tracksForProcessing[0].normalizedTrackName, tracksForProcessing.map(t => t.normalizedTrackName))
                  
                  
                          onScreenMessage("Analysing session for inactive plugins/sends. Please wait...", 3);
                  
                          const { map, elements } = findInactiveItems(tracksForProcessing);
                  
                          onScreenMessage("Analysis complete. Proceeding.", 2);
                  
                          //End script if no inactive Inserts or Sends found
                          if (elements.inserts.length === 0 && elements.sends.length === 0) {
                              onScreenMessage("No inactive plugins or sends found.", 1);
                  
                              throw 0;
                          }
                          //Set track height to small so can visually see more of session - mini is too small
                          setTrackHeights('small');
                  
                          /////////////////////////////////////////////////////////////////////
                          //Currently have removed options to remove only Inserts or only Sends - Script does both.  Plan to update with Command Template
                          /////////////////////////////////////////////////////////////////////
                          let option;
                          /* //if (chosenOption === "Remove all Inactive Plugins and Sends") {
                          //  option = "both";
                          //} else if (chosenOption === "Remove all Inactive Plugins") {
                          if (elements.inserts.length === 0) {
                              alert("No inactive plugins found.");
                              return;
                          }
                          //  option = "plugins";
                          //} else if (chosenOption === "Remove all Inactive Sends") {
                          if (elements.sends.length === 0) {
                              alert("No inactive sends found.");
                              return;
                          }
                          //  option = "sends";
                          //} */
                          option = 'both'
                  
                          batchRemove(map, elements, option);
                  
                          if (wasLinked) {
                              sf.ui.proTools.menuClick({
                                  menuPath: ["Options", "Link Track and Edit Selection"],
                              });
                          }
                      }
                      catch (err) {
                          //Handle the error
                          log('Could not remove plugins/sends');
                      }
                  
                      recallAndDeleteTrackView(userViewNum)
                  
                      if (restoreTracks.length != 0) {
                          scrollToTrackNamed(restoreTracks[0].normalizedTrackName, restoreTracks.map(t => t.normalizedTrackName))
                      }
                      else { scrollToTrackNamed(firstSelection[0].normalizedTrackName, firstSelection.map(t => t.normalizedTrackName)) }
                  
                      // Set Track List to Initial State
                      if (initialState != isTrackListOpen()) {
                          sf.ui.proTools.menuClick({ menuPath: ['View', 'Other Displays', 'Track List'], targetValue: "Disable" })
                      };
                  
                      onScreenMessage("Script Finished", 1)
                  }
                  
                  startScript()
                  
                  
                  1. SSreejesh Nair @Sreejesh_Nair
                      2024-06-14 05:09:30.711Z

                      This is great. It could be that things may have changed with a newer version of PT. I’ll give this a swirl!

                      1. FForrester Savell @Forrester_Savell
                          2024-07-31 06:03:33.962Z2024-07-31 08:26:40.531Z

                          Hi @Sreejesh_Nair and anyone else interested.

                          I've done a major upgrade to my previous version, available in the code above.

                          It's a lot more robust.

                          1. Tracks can be bulk selected from Track List or Edit Window and it will exclude inactive, hidden or non-Insert/Send related tracks, so they don't interfere with the process. I've based its functionality on my need for selecting an entire finished session and removing inactive items, ready for archiving.
                          2. I've added an option to ignore specific user defined plugins. Add them to the const ignorePlugins array at the top of the script.
                          3. I've also removed the option to choose between Inserts or Sends, it just does both for now. I can add that later as a Command Template.
                          4. Will remove sends on Frozen tracks, but will ignore any inserts
                          1. FForrester Savell @Forrester_Savell
                              2024-08-01 04:10:05.031Z

                              Hi @Sean_McDonald5

                              Thanks for the feedback. It was due to the Track List on the left not being visible. I've updated the script to ensure that it opens before running.

                              Let me know in this thread if you have any further issues with the script.

                              Cheers

                              Forrester

                              1. SSean McDonald @Sean_McDonald5
                                  2024-08-26 04:57:27.127Z

                                  Hey Forrester!
                                  Is the most recent/ updated version the last one in this thread dated jun 13?

                                  thanks!

                                  1. FForrester Savell @Forrester_Savell
                                      2024-08-26 05:07:48.377Z

                                      Yes, rather than reposting, I've updated the code above Remove Inactive Inserts or Sends or both #post-6

                                      1. SSean McDonald @Sean_McDonald5
                                          2024-08-28 04:06:44.478Z

                                          Forrester.......
                                          that was AMAZING!!
                                          worked perfectly, and VERY quickly!

                                          Can you share your Venmo/ Paypal etc? would love to send some dough.

                                          thanks!

                                          1. FForrester Savell @Forrester_Savell
                                              2024-09-04 05:37:32.390Z

                                              Hey @Sean_McDonald5

                                              Glad that it worked for you. No need for payment :)

                                            • SSean McLaughlin @Sean_McLaughlin
                                                2024-08-28 16:07:52.037Z

                                                Hi Forrester,

                                                First, thanks for doing this! Second, I'm running into an issue where it can't open the Track list popup menu (line 305).

                                                1. FForrester Savell @Forrester_Savell
                                                    2024-09-04 05:39:32.640Z

                                                    Hey @Sean_McLaughlin

                                                    Are you still having trouble with this? If you haven't already, try using the updated code posted above. This looks like the same issue the other Sean above was having, which I've since fixed.

                                                    1. SSean McLaughlin @Sean_McLaughlin
                                                        2024-09-05 16:17:49.613Z

                                                        I'm still having the same issue with the last code in this thread.

                                                        1. SSreejesh Nair @Sreejesh_Nair
                                                            2024-09-05 20:12:46.995Z

                                                            Does it happen if you run it on the Edit window? I think this may be because it’s the mix window?

                                                            1. SSean McLaughlin @Sean_McLaughlin
                                                                2024-09-05 23:22:49.228Z

                                                                Thank you! That didn't give me the same error code, but now it seems to start and not finish. I tried running the script on one track and it didn't remove any of the inactive plugins.

                                                                1. FForrester Savell @Forrester_Savell
                                                                    2024-09-06 06:29:21.522Z2024-09-06 08:03:13.766Z

                                                                    Hey @Sean_McLaughlin

                                                                    I've revised the code again to work even if you have the Mix Window open. Let me know if you're still having issues and perhaps capture a video so I can see what's happening.

                                                                    Thanks for the feedback too!

                                                  • S
                                                    In reply toSreejesh_Nair:
                                                    Sreejesh Nair @Sreejesh_Nair
                                                      2024-09-04 16:52:32.115Z
                                                      function batchRemove(map, elements, option) {
                                                          let processedItems = 0, notificationID = sf.system.newGuid().guid, totalItems = elements.inserts.length + elements.sends.length;
                                                          const removeFromSlot = (slot, type, map) => {
                                                              removeItems(map[slot], type, slot);
                                                              // Notify progress of removal
                                                              sf.interaction.notify({ uid: notificationID, title: "Removing Inactive Items", progress: (++processedItems) / totalItems, message: `Removing ${type}s: ${processedItems} of ${totalItems}` });
                                                          };
                                                          if (option === "both" || option === "plugins") elements.inserts.forEach(slot => removeFromSlot(slot, 'Insert', map.inserts, elements.inserts));
                                                          if (option === "both" || option === "sends") elements.sends.forEach(slot => removeFromSlot(slot, 'Send', map.sends, elements.sends));
                                                          sf.interaction.notify({ uid: notificationID, title: "Removing Inactive Items", progress: 100, message: `Removal complete.` });
                                                      }
                                                      

                                                      This is the new batch remove that I use with the notification that shows the progress of the removal as well. The option is because I have an interaction list that asks for all, or just sends or plugins.

                                                      1. FForrester Savell @Forrester_Savell
                                                          2024-09-06 06:32:27.709Z

                                                          Nice one @Sreejesh_Nair , this will be handy. Does this work in your original script or my alternate code?

                                                          When I swap it out for my Batch Rename function in my script, I'm getting this error. Haven't had a chance to dig into it, but perhaps you know what's happening?

                                                          1. SSreejesh Nair @Sreejesh_Nair
                                                              2024-09-06 07:02:55.418Z

                                                              Its because I defined elements here

                                                              function findInactiveItems(tracks) {
                                                                  let map = { inserts: {}, sends: {} }, elements = { inserts: [], sends: [] }, notificationID = sf.system.newGuid().guid;
                                                                  tracks.forEach((track, index) => {
                                                                      // Notify progress of processing tracks
                                                                      sf.interaction.notify({ uid: notificationID, title: "Processing Automation Data", progress: (index + 1) / tracks.length, message: `Analysing Session: ${index + 1} of ${tracks.length} tracks` });
                                                                      if (track) {
                                                                          // Process insert buttons
                                                                          track.insertSelectorButtons.forEach((button, i) => {
                                                                              if (button.exists && button.invalidate().value.value.startsWith("inactive")) {
                                                                                  const slot = i + 1;
                                                                                  if (!map.inserts[slot]) { map.inserts[slot] = []; elements.inserts.push(slot); }
                                                                                  map.inserts[slot].push(track.normalizedTrackName);
                                                                              }
                                                                          });
                                                                          // Process send buttons
                                                                          track.sendSelectorButtons.forEach((button, i) => {
                                                                              if (button.exists && button.invalidate().value.value.startsWith("inactive")) {
                                                                                  const slot = i + 1;
                                                                                  if (!map.sends[slot]) { map.sends[slot] = []; elements.sends.push(slot); }
                                                                                  map.sends[slot].push(track.normalizedTrackName);
                                                                              }
                                                                          });
                                                                      }
                                                                  });
                                                                  return { map, elements };
                                                              }
                                                              

                                                              Here is my full code that I use now. You could modify it to include the changes you made

                                                              // Closes Pro Tools confirmation dialogs if they contain "Remove" or "Change" buttons
                                                              function closeDialog() {
                                                                  ['Remove', 'Change'].forEach(title => {
                                                                      const button = sf.ui.proTools.confirmationDialog.invalidate().buttons.whoseTitle.is(title).first;
                                                                      if (button.exists) {
                                                                          button.elementClick();
                                                                          sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: "Disappear" });
                                                                      }
                                                                  });
                                                              }
                                                              
                                                              // Finds inactive inserts and sends in the provided tracks and maps them
                                                              function findInactiveItems(tracks) {
                                                                  let map = { inserts: {}, sends: {} };
                                                                  let elements = { inserts: [], sends: [] };
                                                                  let notificationID = sf.system.newGuid().guid;
                                                              
                                                                  tracks.forEach((track, index) => {
                                                                      // Notify progress of processing tracks
                                                                      sf.interaction.notify({
                                                                          uid: notificationID,
                                                                          title: "Processing Automation Data",
                                                                          progress: (index + 1) / tracks.length,
                                                                          message: `Analysing Session: ${index + 1} of ${tracks.length} tracks`
                                                                      });
                                                              
                                                                      if (track) {
                                                                          // Process insert buttons
                                                                          track.insertSelectorButtons.forEach((button, i) => {
                                                                              if (button.exists && button.invalidate().value.value.startsWith("inactive")) {
                                                                                  const slot = i + 1;
                                                                                  if (!map.inserts[slot]) {
                                                                                      map.inserts[slot] = [];
                                                                                      elements.inserts.push(slot);
                                                                                  }
                                                                                  map.inserts[slot].push(track.normalizedTrackName);
                                                                              }
                                                                          });
                                                              
                                                                          // Process send buttons
                                                                          track.sendSelectorButtons.forEach((button, i) => {
                                                                              if (button.exists && button.invalidate().value.value.startsWith("inactive")) {
                                                                                  const slot = i + 1;
                                                                                  if (!map.sends[slot]) {
                                                                                      map.sends[slot] = [];
                                                                                      elements.sends.push(slot);
                                                                                  }
                                                                                  map.sends[slot].push(track.normalizedTrackName);
                                                                              }
                                                                          });
                                                                      }
                                                                  });
                                                              
                                                                  return { map, elements };
                                                              }
                                                              
                                                              // Removes items (inserts or sends) from the specified tracks and slots
                                                              function removeItems(names, type, slot) {
                                                                  sf.keyboard.press({ keys: "home" });
                                                                  sf.ui.proTools.trackSelectByName({ names, deselectOthers: true });
                                                                  const original = sf.ui.proTools.selectedTrackNames;
                                                              
                                                                  if (original.length > 0) {
                                                                      sf.ui.proTools.trackGetByName({ name: original[0] }).track.trackScrollToView();
                                                                  }
                                                              
                                                                  sf.ui.proTools.trackSelectByName({ names, deselectOthers: true });
                                                                  sf.ui.proTools.selectedTrack.trackInsertOrSendSelect({
                                                                      insertOrSend: type,
                                                                      pluginNumber: parseInt(slot, 10),
                                                                      selectForAllSelectedTracks: true,
                                                                      pluginPath: type === 'Insert' ? ['no insert'] : ['no send']
                                                                  });
                                                              
                                                                  closeDialog();
                                                                  sf.wait({ intervalMs: 250 });
                                                              }
                                                              
                                                              // Batch removes inactive inserts or sends based on the selected option
                                                              function batchRemove(map, elements, option) {
                                                                  let processedItems = 0;
                                                                  let notificationID = sf.system.newGuid().guid;
                                                                  const totalItems = elements.inserts.length + elements.sends.length;
                                                              
                                                                  const removeFromSlot = (slot, type, map) => {
                                                                      removeItems(map[slot], type, slot);
                                                                      // Notify progress of removal
                                                                      sf.interaction.notify({
                                                                          uid: notificationID,
                                                                          title: "Removing Inactive Items",
                                                                          progress: (++processedItems) / totalItems,
                                                                          message: `Removing ${type}s: ${processedItems} of ${totalItems}`
                                                                      });
                                                                  };
                                                              
                                                                  if (option === "both" || option === "plugins") {
                                                                      elements.inserts.forEach(slot => removeFromSlot(slot, 'Insert', map.inserts));
                                                                  }
                                                              
                                                                  if (option === "both" || option === "sends") {
                                                                      elements.sends.forEach(slot => removeFromSlot(slot, 'Send', map.sends));
                                                                  }
                                                              
                                                                  sf.interaction.notify({
                                                                      uid: notificationID,
                                                                      title: "Removing Inactive Items",
                                                                      progress: 100,
                                                                      message: `Removal complete.`
                                                                  });
                                                              }
                                                              
                                                              // Sets the track size for the selected track
                                                              function setTrackSize(size) {
                                                                  const selectedTrackH = sf.ui.proTools.selectedTrackHeaders[0];
                                                                  try {
                                                                      // Try setting track size using the right popup menu
                                                                      selectedTrackH.popupMenuSelect({
                                                                          anchor: "MidRight",
                                                                          relativePosition: { x: -10, y: 0 },
                                                                          menuPath: [size],
                                                                          isOption: true,
                                                                          onError: "Continue"
                                                                      });
                                                                  } catch (err) {
                                                                      // If the right popup menu fails, try the left popup menu
                                                                      if (selectedTrackH.frame.h <= 79) {
                                                                          sf.ui.proTools.selectedTrack.popupButtons.allItems[2].popupMenuSelect({
                                                                              menuPath: ["Track Height", size],
                                                                              isOption: true
                                                                          });
                                                                      }
                                                                  }
                                                              }
                                                              
                                                              // Activates the specified Pro Tools menu options
                                                              function activateProToolsMenus(menuOptions) {
                                                                  menuOptions.forEach(menuOption =>
                                                                      sf.ui.proTools.menuClick({
                                                                          menuPath: ["View", "Edit Window Views", menuOption],
                                                                          targetValue: "Enable"
                                                                      })
                                                                  );
                                                              }
                                                              
                                                              // Define Pro Tools menu options to be activated
                                                              const proToolsMenuOptions = ["Inserts A-E", "Inserts F-J", "Sends A-E", "Sends F-J"];
                                                              
                                                              // Main script function that initiates the process based on user selection
                                                              function startScript() {
                                                                  // User selection prompt
                                                                  const userSelection = sf.interaction.selectFromList({
                                                                      items: ["Remove all Inactive Plugins and Sends", "Remove all Inactive Plugins", "Remove all Inactive Sends"],
                                                                      allowMultipleSelections: false,
                                                                      prompt: "Choose what you want removed",
                                                                      title: "Inactive Cleaner"
                                                                  }).list;
                                                              
                                                                  if (userSelection.length === 0) return alert("No option selected. Exiting script.");
                                                              
                                                                  let chosenOption = userSelection[0];
                                                                  sf.ui.proTools.appActivate();
                                                                  sf.ui.proTools.mainWindow.invalidate();
                                                              
                                                                  const wasLinked = sf.ui.proTools.getMenuItem('Options', 'Link Track and Edit Selection').isMenuChecked;
                                                                  if (wasLinked) sf.ui.proTools.menuClick({ menuPath: ["Options", "Link Track and Edit Selection"] });
                                                              
                                                                  if (!sf.ui.proTools.mainWindow.title.invalidate().value.startsWith("Edit: ")) {
                                                                      sf.ui.proTools.menuClick({ menuPath: ["Window", "Edit"] });
                                                                  }
                                                              
                                                                  activateProToolsMenus(proToolsMenuOptions);
                                                                  sf.ui.proTools.invalidate();
                                                              
                                                                  let tracksForProcessing = sf.ui.proTools.selectedTrackHeaders;
                                                                  let firstSelection = sf.ui.proTools.selectedTrackNames;
                                                              
                                                                  if (tracksForProcessing.length !== 0) {
                                                                      sf.ui.proTools.trackGetByName({ name: firstSelection[0] }).track.trackScrollToView();
                                                                      sf.ui.proTools.trackSelectByName({ names: firstSelection });
                                                                      setTrackSize('small');
                                                                  } else {
                                                                      if (!confirm("No tracks are selected. Would you like to proceed with all visible active tracks?")) {
                                                                          return alert("Operation cancelled.");
                                                                      }
                                                              
                                                                      firstSelection = sf.ui.proTools.visibleTrackNames;
                                                                      sf.ui.proTools.trackGetByName({ name: firstSelection[0] }).track.trackScrollToView();
                                                              
                                                                      const activeTrackNames = sf.app.proTools.tracks.invalidate().allItems.reduce((activeTrackNames, track) => {
                                                                          if (!track.isInactive) activeTrackNames.push(track.name);
                                                                          return activeTrackNames;
                                                                      }, []);
                                                              
                                                                      sf.app.proTools.selectTracksByName({ trackNames: activeTrackNames });
                                                                      setTrackSize('small');
                                                                      tracksForProcessing = sf.ui.proTools.selectedTrackHeaders;
                                                                  }
                                                              
                                                                  const { map, elements } = findInactiveItems(tracksForProcessing);
                                                              
                                                                  if (elements.inserts.length === 0 && elements.sends.length === 0) {
                                                                      return alert("No inactive plugins or sends found.");
                                                                  }
                                                              
                                                                  let option = chosenOption === "Remove all Inactive Plugins and Sends" ? "both" :
                                                                               chosenOption === "Remove all Inactive Plugins" ? (elements.inserts.length === 0 ? (alert("No inactive plugins found."), undefined) : "plugins") :
                                                                               (elements.sends.length === 0 ? (alert("No inactive sends found."), undefined) : "sends");
                                                              
                                                                  if (option) batchRemove(map, elements, option);
                                                              
                                                                  if (wasLinked) sf.ui.proTools.menuClick({ menuPath: ["Options", "Link Track and Edit Selection"] });
                                                              
                                                                  sf.ui.proTools.trackSelectByName({ names: firstSelection, deselectOthers: true });
                                                                  alert("Operation completed.");
                                                              }
                                                              
                                                              // Start the script
                                                              startScript();
                                                              
                                                              Reply1 LikeSolution
                                                              1. FForrester Savell @Forrester_Savell
                                                                  2024-09-06 07:25:31.197Z

                                                                  That's very cool!!

                                                                  I'll try to implement it in my code, as I still run into issues with frozen tracks (where an inactive plugin is sandwiched between frozen ones) and some dialogs like the image.

                                                                  But wow, that is a handy feature add.

                                                                  1. SSreejesh Nair @Sreejesh_Nair
                                                                      2024-09-06 15:14:32.375Z

                                                                      If you want to skip frozen tracks, you can change line 189 to

                                                                      const activeTrackNames = sf.app.proTools.tracks.invalidate().allItems.reduce((activeTrackNames, track) => {
                                                                          if (!track.isInactive && !track.isFrozen) activeTrackNames.push(track.name);
                                                                          return activeTrackNames;
                                                                      }, [])
                                                                      
                                                                    • In reply toSreejesh_Nair:
                                                                      SSean McLaughlin @Sean_McLaughlin
                                                                        2024-09-06 16:21:14.783Z

                                                                        Success! Thanks so much for doing this!

                                                                  2. J
                                                                    In reply toSreejesh_Nair:

                                                                    This is amazing, and thank you to all involved.

                                                                    I am dedicated to using my mouse as little as possible, as clicking through tiny sub-menus hidden behind tiny little dots in the Pro Tools Edit window is taking its toll on my mousing hand.

                                                                    One thought/proposal: it would be REALLY nice to have the popup window auto-focus, instead of needing to click on the popup window with my mouse to bring into focus. I am going to dive into this when I have a chance, just wanted to make sure that I am not missing something that has already been implemented.

                                                                    1. S
                                                                      In reply toSreejesh_Nair:
                                                                      Sreejesh Nair @Sreejesh_Nair
                                                                        2024-12-16 03:45:35.067Z

                                                                        Which pop up window are you referring to?

                                                                        1. The popup that shows the three options to choose from: sends, plugins, or both. Since it it not initially focused, one must use the mouse to focus it.

                                                                          Once it is focused, I can then use the arrow and enter keys to initiate the plugin removal. I just don't want to use the mouse to focus it.

                                                                          1. SSreejesh Nair @Sreejesh_Nair
                                                                              2024-12-16 20:12:28.526Z

                                                                              Ah that. It seems to be a soundflow thing. Another workaround is to create a template and make three presets one for each of the options. That way you can trigger the choice from streamdeck or a keyboard shortcut without a mouse interaction.

                                                                              1. Ok, I'll give it a try.