No internet connection
  1. Home
  2. How to

Get selected Clip Name

By Benjamin Hörbe @Benjamin_Horbe
    2021-01-19 10:31:36.089Z

    Hi Community,

    is there a more elegant way to get the name of the selected clip from the clip list? I know, I can get the clip name from the rename window, but this won't be an option for my usecase.

    The code below does the job, but is pretty slow and outputs very unhandy strings like "{"title":"Selected. Audio Clip\n\"test PT Text TOols_01\" ","role":"AXStaticText","fullRole":"AXStaticText"}" with "test PT Text TOols_01" being the clip name.

    var i = 1
    var y = -1
    
        for (i = 0; i < 16; i++) {
    
            var test = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first.children.whoseRole.is("AXRow").whoseValue.is('').allItems[i].children.whoseRole.is("AXCell").allItems[1].children.whoseRole.is("AXStaticText").whoseTitle.contains('Selected').exists
            if (test === true ){break ;}
            //log(i, test)
            //log ("y",y)
        }
    
        var y = i;
    
    
    var selectedClip = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first.children.whoseRole.is("AXRow").whoseValue.is('').allItems[y].children.whoseRole.is("AXCell").allItems[1].children.whoseRole.is("AXStaticText").whoseTitle.contains('Selected').first
    log(selectedClip)
    

    Any suggestions?
    Cheers from Berlin

    Solved in post #2, click to view
    • 21 replies

    There are 21 replies. Estimated reading time: 18 minutes

    1. Hey Benjamin!

      This should do it!

      function getSelectedClipNamesFromClipList() {
          const clipsTable = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first;
      
          // Get all selected clips
          const selectedClips = clipsTable.children.whoseRole.is("AXRow").allItems.map(row =>
              row.children[1].children.first.value.value).filter(c => c.startsWith('Selected. '));
      
          // Extract clip names
          const clipNames = selectedClips.map(c => c.split('"')[1]);
      
          return clipNames
      };
      
      const selectedClip = getSelectedClipNamesFromClipList()[0];
      
      log(selectedClip)
      
      Reply1 LikeSolution
      1. Benjamin Hörbe @Benjamin_Horbe
          2021-01-20 07:37:40.758Z

          It works like a charm!
          Thanks a lot!

        • B
          In reply toBenjamin_Horbe:
          Brian Mullany @Brian_Mullany
            2024-11-13 12:32:51.567Z

            Heya @raphaelsepulveda , this no longer works in PT 2024.10.

            Something around the clipsTable is returning nothing. Any chance something in the backend has changed and needs to be updated here?

            The log of selectedClip returns

            undefined

            .
            The log of clipNames returns

            []

            Any help?

            tying to log clipsTable bombs SoundFlow out with the following error:

            !! Command Error: test 2 [user:cl32uwy7y00010b10mqt1wp9o:cm3fu6a0q0000bo108nt9ysy7]:
            Error: Couldn't get item #0 as the array length was 0 - sf.ui.app('com.avid.ProTools')..tables.whoseTitle.is('CLIPS').first (AxElementArrayIndexedItem)
            (test 2 line 6)

            1. Raphael Sepulveda @raphaelsepulveda2024-11-13 18:13:26.353Z2025-05-03 21:26:12.674Z

              Hey @Brian_Mullany, nowadays I would opt for a different approach for this.

              • Updated May 3rd, 2025—Ensures "Link Track and Edit Selection" and "Link Timeline and Edit Selection" are engaged while fetching clips. Ensures only Audio tracks are selected (only audio clips can be fetched).
              • Updated Feb 4th, 2025 — Fades are now removed by default (Thanks Chris Shaw!)
              /** Ensures a menu item is menu checked (or not) while an action takes place.
               * @param {{ 
               * menuItemPath: [string, string, string?, string?, string?, string?], 
               * doWithout?: boolean, // Ensures the menu item is NOT menu checked
               * callback: function
               * }} args */
              function doWithMenuItemToggle({ menuItemPath, doWithout = false, callback }) {
                  const menuItem = sf.ui.proTools.getMenuItem(...menuItemPath);
                  const isMenuItemChecked = menuItem.isMenuChecked;
              
                  if (!isMenuItemChecked && !doWithout || isMenuItemChecked && doWithout) {
                      menuItem.elementClick();
                  }
              
                  const result = callback();
              
                  if (!isMenuItemChecked && !doWithout || isMenuItemChecked && doWithout) {
                      menuItem.elementClick();
                  }
              
                  return { result };
              }
              
              /** @param {{ includeFades?: boolean }} [args] */
              function getSelectedClipInfo({ includeFades = false } = {}) {
                  /** @typedef{{ clipName: string, trackName: string, startTime: number, endTime: number }[]} clips */
              
                  function main() {
                      const selectionInTime = Number(
                          sf.app.proTools.getTimelineSelection().inTime
                      );
                      const selectionOutTime = Number(
                          sf.app.proTools.getTimelineSelection().outTime
                      );
              
                      const tracks = sf.app.proTools.tracks.invalidate().allItems;
              
                      // Deselect non-Audio tracks
                      sf.app.proTools.selectTracksByName({
                          selectionMode: "Subtract",
                          trackNames: tracks
                              .filter(track => track.type !== "Audio")
                              .map(track => track.name)
                      });
              
                      /** @type{clips} */
                      const fullySelectedClips = sf.app.proTools.getSelectedClipInfo().clips
                          .reduce((clips, clip) => {
                              if (!includeFades) {
                                  if (clip.clipName.startsWith("(fade") || clip.clipName === "(cross fade)") {
                                      return clips;
                                  }
                              }
              
                              const clipStartTime = Number(clip.startTime);
                              const clipEndTime = Number(clip.endTime) - 1;
              
                              const isClipFullySelected = clipStartTime >= selectionInTime
                                  && clipEndTime < selectionOutTime;
              
                              if (isClipFullySelected) {
                                  // Remove any extensions from clipName (.L, .R...)
                                  const clipName = clip.clipName.replace(/\.\w+$/, "");
              
                                  // Filter repeated clips
                                  if (clips.some(clip => clip.clipName === clipName)) return clips;
              
                                  clips.push({
                                      clipName,
                                      trackName: clip.trackName,
                                      startTime: clipStartTime,
                                      endTime: clipEndTime
                                  });
                              }
                              return clips;
                          }, []);
              
              
                      const names = fullySelectedClips.map(clip => clip.clipName);
              
                      return { clips: fullySelectedClips, names };
                  }
              
                  /** @type {{ clips: clips, names: string[] }} */
                  const result = doWithMenuItemToggle({
                      menuItemPath: ["Options", "Link Track and Edit Selection"],
                      callback: () =>
                          doWithMenuItemToggle({
                              menuItemPath: ["Options", "Link Timeline and Edit Selection"],
                              callback: main
                          }).result,
                  }).result;
              
                  return result;
              }
              
              const selectedClipNames = getSelectedClipInfo().names;
              
              log(selectedClipNames);
              

              This script does the following:

              • Gets clip info through the PT SDK, which is more robust than scraping the UI.
              • Fixes an issue with the original SDK command where adjacent clips would be listed as selected.
              • Removes multichannel duplicates (i.e. stereo files would be listed as two different files).
              1. BBrian Mullany @Brian_Mullany
                  2024-11-13 18:24:27.376Z

                  You’re amazing thank you.

                  I’ll try tomorrow morning and let you know :)

                  Appreciate the super fast help

                  1. BBrian Mullany @Brian_Mullany
                      2024-11-14 09:51:28.611Z2024-11-14 10:00:02.641Z

                      @raphaelsepulveda

                      Unfortunately it doesn't work :(

                      It returns the stereo pair information (.L and .R) when I need it to just return the clip name in the edit window as a single line string.

                      14.11.2024 10:48:35.20 [Backend]: [LOG] [
                      "Reference Track_01.L",
                      "Reference Track_01.R"
                      ]

                      It should just return

                      Reference Track_01

                      in this example

                      :)

                      edit

                      I've frankenstined something together that works on stereo clips, but it is deeply inelegant because I'm a complete noob. And it wouldn't work on Mono Clips that don't append the .L or .R

                      const selectedClipFull = getSelectedClipNames().names[0];
                      let selectedClip = selectedClipFull.slice(0, -2);
                      
                      
                      log(selectedClip)
                      
                      1. BBrandon Jiaconia @Brandon_Jiaconia
                          2024-11-14 14:02:44.502Z

                          I use something similar to get rid of the .L .R in clips names. The cool thing about Raphaels script is it returns the names of multiple clips. I've been using this for only one selected clip. I'm also a noob so I'm sure there is a better way to go about it.

                          sf.ui.proTools.appActivateMainWindow();
                          
                          const selectedClipInfo = sf.app.proTools.getSelectedClipInfo().clips;
                          
                          const firstSelectedClipName = selectedClipInfo[0].clipName.replace(/\.[LR]$/, "");
                          log(firstSelectedClipName);
                          
                          1. Hey @Brian_Mullany, @Brandon_Jiaconia,
                            I've updated the script above to take care of this.
                            Give it another whirl!

                      2. Chris Shaw @Chris_Shaw2024-11-22 21:33:46.100Z2024-11-22 21:39:53.019Z

                        This is great!
                        The only issue is that it lists fades as clips so some additional filtering is needed if you only want clips. You can just add this to the code:

                        const clipsAndFadesInfo = getSelectedClipInfo()
                        
                        const clipsOnlyInfo = {
                            "clips": clipsAndFadesInfo.clips
                                .filter(c => !c.clipName.startsWith("(fade") && c.clipName !== "(cross fade)"),
                            "names": clipsAndFadesInfo.names.filter(n => !n.startsWith("(fade") && n == "(cross fade)")
                        }
                        
                        log(clipsOnlyInfo)
                        
                        1. Oops! Forgot to check for cross fades as well.
                          I've updated the code above.

                          1. Teamwork makes the dream work. Thanks @Chris_Shaw! 🙏🏼

                        2. SScott Robinson @Scott_Robinson
                            2025-04-30 03:15:53.266Z

                            Hey Raphael, I’m using this to get the names of the clips I’m exporting to disk in order to rename them with sf.file after export, however, it seems to (a) only collect the clip if the track is also selected, (b) fails when you have a selection on a non-audio track. Do you think this is a situation where we’d need to turn on Link Timeline and Edit Selection and reselect the selection appropriately, or can you see where this might be failing?

                            1. Hey @Scott_Robinson, that's some great sleuthing!

                              I can confirm the two issues you're seeing and will update this script later on today.

                              In the meantime, you'd have to have both "Link Timeline and Edit Selection" as well as "Link Track and Edit Selection" for this to work, in addition to only having audio tracks selected.

                              1. SScott Robinson @Scott_Robinson
                                  2025-05-02 06:33:54.815Z

                                  Cool, thanks man! I’m excited to see your approach and thinking!

                                  If I was to guess- I imagine you’d check the state of the two Link modes, set them appropriately, reapply the selection, and then reverse at the end? I don’t see anywhere to apply filtering to the tracks, so I’m guessing proTools.getSelectedClipInfo simply doesn’t return clips that are on unselected tracks?

                                  Or is easier to just select the tracks the clips are on via clip.trackName?

                                  1. @Scott_Robinson, I've updated the script above to avoid those edge cases!

                          • B
                            In reply toBenjamin_Horbe:
                            Brian Mullany @Brian_Mullany
                              2025-02-04 12:30:13.350Z

                              Hey Team, I'm struggling with this to just get the clip name out as a string.

                              Running Raphael's script I get

                              04.02.2025 13:28:02.49 <info> [Backend]: [LOG] [
                                  "(fade in)",
                                  "AMBRain_Rain Drops on Plastic Pool Tarp 01_tonic_48khz",
                                  "(fade out)"
                              ]
                              
                              

                              Then adding Chris's Filter I get:

                              04.02.2025 13:29:08.11 <info> [Backend]: [LOG] {
                                      "clips": [
                                      {
                                          "clipName": "AMBRain_Rain Drops on Plastic Pool Tarp 01_tonic_48khz",
                                          "trackName": "Mono",
                                          "startTime": 2983936,
                                          "endTime": 35831807
                                      }
                                  ],
                                      "names": []
                              

                              All I would love to have is the part AMBRain_Rain Drops on Plastic Pool Tarp 01_tonic_48khz as an output so I can copy it to my clipboard in my other script. Please help! I've tried a million little things but this is so beyond me

                              1. Hey @Brian_Mullany, I just updated my script above incorporating Chris' method of removing fades, that now allows us to easily get the first selected clip name like so:

                                const selectedClipNames = getSelectedClipInfo().names;
                                const firstSelectedClipName = selectedClipNames[0];
                                
                                1. BBrian Mullany @Brian_Mullany
                                    2025-02-06 10:03:35.350Z

                                    Thanks! Unfortunately it's not quite working.

                                    It outputs

                                    [
                                        "AMBRain_Rain Drops on Plastic Pool Tarp 01_tonic_48khz"
                                    ]
                                    

                                    When it should output:

                                    AMBRain_Rain Drops on Plastic Pool Tarp 01_tonic_48khz
                                    

                                    Without ][, line breaks, and "". Any ideas? :)

                                      1. BBrian Mullany @Brian_Mullany
                                          2025-02-06 21:45:09.848Z

                                          Oh I see! I needed that extra bit of code. Sorry I thought it was included :) thanks so much for the video walkthrough as well. Really appreciate it! Thanks!

                                          1. No problem! Glad it's all sorted 🙌🏼