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
Linked from:
- Raphael Sepulveda @raphaelsepulveda2021-01-19 22:11:24.469Z
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)
Benjamin Hörbe @Benjamin_Horbe
It works like a charm!
Thanks a lot!
- BIn reply toBenjamin_Horbe⬆:Brian Mullany @Brian_Mullany
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)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).
- BBrian Mullany @Brian_Mullany
You’re amazing thank you.
I’ll try tomorrow morning and let you know :)
Appreciate the super fast help
- BBrian Mullany @Brian_Mullany
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)
- BBrandon Jiaconia @Brandon_Jiaconia
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);
Raphael Sepulveda @raphaelsepulveda2024-11-14 16:56:55.762Z
Hey @Brian_Mullany, @Brandon_Jiaconia,
I've updated the script above to take care of this.
Give it another whirl!
- In reply toraphaelsepulveda⬆:
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)
Chris Shaw @Chris_Shaw2024-11-22 21:40:37.832Z
Oops! Forgot to check for cross fades as well.
I've updated the code above.Raphael Sepulveda @raphaelsepulveda2024-11-23 04:14:47.337Z
Teamwork makes the dream work. Thanks @Chris_Shaw! 🙏🏼
- In reply toraphaelsepulveda⬆:SScott Robinson @Scott_Robinson
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?
Raphael Sepulveda @raphaelsepulveda2025-04-30 16:21:34.308Z
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.
- SScott Robinson @Scott_Robinson
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?
Raphael Sepulveda @raphaelsepulveda2025-05-03 21:28:18.033Z
@Scott_Robinson, I've updated the script above to avoid those edge cases!
- BIn reply toBenjamin_Horbe⬆:Brian Mullany @Brian_Mullany
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
Raphael Sepulveda @raphaelsepulveda2025-02-04 17:57:39.686Z
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];
- BBrian Mullany @Brian_Mullany
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? :)
Raphael Sepulveda @raphaelsepulveda2025-02-06 21:41:46.372Z
@Brian_Mullany, here's a quick walkthrough on how to get it working correctly 👇🏼
- BBrian Mullany @Brian_Mullany
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!
Raphael Sepulveda @raphaelsepulveda2025-02-06 21:46:29.793Z
No problem! Glad it's all sorted 🙌🏼