No internet connection
  1. Home
  2. How to

Challenge for Soundflow wizards - Possible to select and and remove duplicate clips in Pro Tools?

By Ryan @Ryno
    2023-09-18 10:22:46.664Z

    Here's a challenge for the Soundflow wizards - Whenever you're tracking and you duplicate playlist to punch into the main playlist and make a working comp, it obviously results in a large build up of duplicate playlists, which you have to work around or cull before you start comping

    It would be great if Soundflow could look at a selection of clips, select and remove duplicates, leaving only one original clip.

    Would this be possible? It would be such a timesaver with this workflow.
    There's also the performance benefit as Pro Tools becomes laggy when working in playlist view with a very large number of alt playlists visible

    • 9 replies
    1. Chad Wahlbrink @Chad2023-09-21 17:43:35.899Z2023-09-21 23:07:58.642Z

      Hey @Ryno,

      This is a work in progress but a start in the right direction. Here's a little demo video of the script running currently:

      https://www.dropbox.com/scl/fi/5j0ugzzunydzd5f0zucfs/remove-dups.mp4?rlkey=4ktzynrq074e6rjg6jgscpby6&dl=0

      And here is a script to play with!

      if (!sf.ui.proTools.isRunning) throw `Pro Tools is not running`;
      
      // Activate Pro Tools
      sf.ui.proTools.appActivateMainWindow();
      sf.ui.proTools.mainWindow.invalidate();
      
      // Ensure Grabber Object Tool 
      const grabberToolBtn = sf.ui.proTools.mainWindow.groups.whoseTitle.is("Cursor Tool Cluster").first.buttons.whoseTitle.startsWith("Grabber tool").first;
      if (!grabberToolBtn.title.invalidate().value.endsWith('(Object)')) {
          grabberToolBtn.popupMenuSelect({
              isRightClick: true,
              menuPath: ['Object']
          });
      }
      
      // Ensure Clip List
      if (!sf.ui.proTools.getMenuItem('View', 'Other Displays', 'Clip List').isEnabled) {
          sf.ui.proTools.menuClick({ menuPath: ['View', 'Other Displays', 'Clip List'], });
      }
      
      // Function: Get Clips From the Clip List
      function getClipsFromClipList() {
          const clipListView = sf.ui.proTools.mainWindow.clipListView;
          const rows = clipListView.childrenByRole("AXRow");
          const cells = rows.map(row => {
              return {
                  name: row.childrenByRole("AXCell").allItems[1].childrenByRole("AXStaticText").first.value.invalidate().value,
                  selectionButton: row.childrenByRole("AXCell").allItems[1]
              }
          });
          return cells
      }
      
      // Map the CLip List
      let clipListObjs = getClipsFromClipList();
      
      // Find All Selected Clips in Clip List
      let selectedClipList = clipListObjs.filter(x => x.name.startsWith('Selected.'))
      
      // If More than One CLip is Selected, throw
      if (selectedClipList.length > 1) {
          throw `Please Select Only One Clip`
      }
      
      // Set the Selected Clip
      let selectedClip = selectedClipList[0];
      
      let selectedClipName = selectedClip.name.split('"')[1];
      
      sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is('Clip List').first.popupMenuSelect({menuPath:['Find...'], })
      
      sf.ui.proTools.windows.whoseTitle.is("Find Clips").first.elementWaitFor();
      
      sf.ui.proTools.windows.whoseTitle.is("Find Clips").first.textFields.first.elementSetTextFieldWithAreaValue({value:selectedClipName, onError:"Continue"})
      sf.ui.proTools.windows.whoseTitle.is("Find Clips").first.buttons.whoseTitle.is("OK").first.elementClick();
      sf.ui.proTools.windows.whoseTitle.is("Find Clips").first.elementWaitFor({waitType:"Disappear"});
      
      sf.ui.proTools.mainWindow.clipListView.popupMenuSelect({menuPath: ["Object Select in Edit Window"], relativePosition: { x: 25, y: 5 },isRightClick: true,})
      
      // Remove Last Clip from Selection
      sf.ui.proTools.menuClick({ menuPath: ['Edit', 'Selection', 'Remove Edit from Bottom'], });
      
      // Clear Selected Clips
      sf.ui.proTools.menuClick({ menuPath: ['Edit', 'Clear'], });
      
      sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is('Clip List').first.popupMenuSelect({menuPath:['Clear Find'], });
      
      1. RRyan @Ryno
          2023-09-21 20:37:59.946Z

          Wow, Chad. This is awesome!!
          Can you help diagnose what's stopping it from working on my system? I'm running the latest Pro Tools beta but it may be something unrelated. It throws the error "Cannot read property '0' of null" (line 48)

          1. Chad Wahlbrink @Chad2023-09-21 21:36:51.010Z

            Hey @Ryno!

            I just swapped my original code with an update. Let me know if this one works! I think I was trying to be too clever with Regex.

            If this still fails, could you show me what your "shown" properties look like from the clip list?

            1. RRyan @Ryno
                2023-09-21 22:25:56.270Z

                Thanks for your help with this, it's throwing a different this time. Also showing my clip list shown properties in the video:
                The error now is:

                "Value cannot be null. (Parameter 'str') (Kill Dups: Line 54)
                System.ArgumentNullException: Value cannot be null. (Parameter 'str')
                at CFString..ctor(String str, Boolean owns) + 0x78
                at SoundFlow.Shortcuts.AXUIElement.SetStringValue(String attribute, String value) + 0x58
                at SoundFlow.Shortcuts.Automation.Actions.SetTextFieldWithAreaValueAction.d__13.MoveNext() + 0x2f0
                --- End of stack trace from previous location ---
                "

                1. Chad Wahlbrink @Chad2023-09-21 23:21:08.264Z

                  Hey @Ryno,

                  Thanks for the patience! I'm not able to reproduce the same errors on my system. However, I added onError:"Continue" to line 54 because it seems like it IS added the correct name to the find window, just not continuing past that step for some reason. Copy the original script once more (it's been updated).

                  Make sure you are on the latest version of SoundFlow (5.4.8) as well. https://my.soundflow.org/install.

                  If this doesn't help, then I may have you log a bug report so I can get a bit more information about your system.

                  Update video:
                  https://www.dropbox.com/scl/fi/tonz1b6cr4436xxaz2nte/2023-09-21-dups-killing.mp4?rlkey=j4werud0pxhegcoxshk5ttvb8&dl=0

                  1. RRyan @Ryno
                      2023-09-22 00:08:59.959Z

                      It works!!
                      Thanks Chad, you're a true wizard. This is such a time saver, thank you so much

                      1. Chad Wahlbrink @Chad2023-09-22 00:15:07.755Z

                        Love to hear it, @Ryno !

                        1. RRyan @Ryno
                            2023-12-18 22:28:29.954Z

                            Hey Chad, I've just tried using this script and it's not working anymore :(

                            Can you help?

                            I'm getting:

                            Could not open popup menu (Line 50). Popup menu was not found
                            [Logging error in action (01) WaitForPopupMenuAction: Popup window was not found after waiting 2000 ms]

                            Running the latest Pro Tools release build

                            Thanks,

                            Ryan

                            1. Chad Wahlbrink @Chad2023-12-19 14:12:25.886Z

                              Hey @Ryno!

                              What version number of macOS and Pro Tools are you running currently? Are you by chance running macOS Sonoma on this machine?

                              I mainly ask because that specific error has been popping up on macOS Sonoma.

                              If you are not running macOS Sonoma, would you mind using the "Script or Macro Help Workflow" to create a new post for this issue:
                              https://soundflow.org/docs/help#script-help

                              That will collect the log files I would need to better diagnose this issue.