is there a way to script "spot to original time stamp"?
Linked from:
- Daniel Perez @daniel_perez
Dustin Harris @Dustin_Harris
I think I make something like this a while ago....
try this:
function getClipInfo() { try { //shorten name const clipNameWin = sf.ui.proTools.windows.whoseTitle.is('Name').first; //show clip list if it's not currently shown let clipListIsClosed = !sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is("Clip List").first.exists; if (clipListIsClosed) { sf.ui.proTools.mainWindow.buttons.whoseTitle.is("Show/Hide Clip List").first.elementClick({}, `could not show clip list`); } //shorten name const clipInfoGrp = clipNameWin.groups.whoseTitle.is('Clip Info').first.children.whoseRole.is("AXStaticText"); sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is('Clip List').first.popupMenuSelect({ isRightClick: true, menuPath: ["Show", "Auto-Created"], targetValue: "Enable" }, `could not enable show auto-created regions`); sf.ui.proTools.menuClick({ menuPath: ["Clip", "Rename..."], }); clipNameWin.elementWaitFor({}, `could not open clip rename window`); //check to see if window needs expanding, and expand it if necessary if (!clipInfoGrp.whoseValue.is('User Time Stamp:').first.exists) { clipNameWin.groups.whoseTitle.is('Clip Info').first.mouseClickElement({ relativePosition: { "x": 12, "y": 12 }, }); }; let clipInfo = { originalTimeStamp: clipInfoGrp.allItems[21].value.invalidate().value.split('.')[0], duration: clipInfoGrp.allItems[19].value.invalidate().value.split('.')[0], }; clipNameWin.buttons.whoseTitle.is('Cancel').first.elementClick({}, `could not cancel out of clip rename window`); if (clipListIsClosed) { sf.ui.proTools.mainWindow.buttons.whoseTitle.is("Show/Hide Clip List").first.elementClick({}, `could not show clip list`); } return clipInfo; } catch (err) { throw (err === 0) ? 0 : `Error in getClipInfo: ${err}` } } function main() { sf.ui.proTools.appActivateMainWindow() const selectedTracks = sf.ui.proTools.selectedTrackNames; const { selectionStart, selectionEnd } = sf.ui.proTools.selectionGet(); let clipInfo = getClipInfo(); sf.ui.proTools.selectionSet({ selectionStart: clipInfo.originalTimeStamp, selectionLength: clipInfo.duration }) sf.keyboard.press({ keys: "left" }) let consent = confirm('click OK to return to original selection, cancel to cancel') if (consent) { sf.ui.proTools.trackSelectByName({ names: selectedTracks }) sf.ui.proTools.selectedTrackHeaders[0].trackScrollToView(); sf.ui.proTools.selectionSet({ selectionStart, selectionEnd }) sf.keyboard.press({ keys: "left" }) } else { log('Cancelled') } } main();
Wrong script, that one goes to the original timestamp location of the selected clip.
Dustin Harris @Dustin_Harris
This one; and I have it set to a window trigger of 'When a window titled 'Spot Dialog' is focused in Pro Tools.
//hack to make sure everything loads sf.ui.proTools.clipSpotDialog.invalidate().buttons.whoseTitle.is("OK").first.elementWaitFor(); const currentStart = () => sf.ui.proTools.clipSpotDialog.textFields.first.value.invalidate().value; const originalTimeStamp = () => sf.ui.proTools.clipSpotDialog.children.whoseRole.is("AXStaticText").allItems[7].value.invalidate().value.split(".")[0]; if (originalTimeStamp() != currentStart()) { while (originalTimeStamp() != currentStart()) { sf.ui.proTools.clipSpotDialog.mouseClickElement({ relativePosition: { x: 274, y: 256 } }) sf.wait({intervalMs:100}) } sf.ui.proTools.clipSpotDialog.buttons.whoseTitle.is("OK").first.elementClick(); } else { sf.ui.proTools.clipSpotDialog.buttons.whoseTitle.is("Cancel").first.elementClick(); } sf.ui.proTools.clipSpotDialog.elementWaitFor({ waitForNoElement: true })
Daniel Perez @daniel_perez
can i trigger it with a standard keyboard shortcut, without having the spot to dialogue open?
by the way, will we actually have more or less everything as "commands", directly manipulating all the pt functions without the need to open and close windows and dialogues in a not too distant future?
basically, will pt open up to soundflow so much, that we will be able to control what we need, without being dependant on the pt UI?
i dont really get, why we dont have this yet, as the commands inside pt are there, the UIs just connect to them, right?
but avid goes already into a very good direction i'd say.Chad Wahlbrink @Chad2023-09-26 19:57:39.495Z
@daniel_perez - regarding the idea of interfacing directly with Pro Tools. This is the benefit of the Pro Tools Scripting SDK. Currently, only a limited number of commands are exposed by Avid, and SoundFlow is reliant on Avid continuing to update and maintain the available commands.
We do not have access to every command in Pro Tools yet, but the future is promising.
If you are interested, you can poke around the available actions by usingsf.app.proTools.
in a script and seeing what actions are currently available through SoundFlow:
Also, a fun example from recent forum projects for me was this script, which will export a CSV of the markers in the current session without touching the PT UI.
- In reply todaniel_perez⬆:
Chad Wahlbrink @Chad2023-09-26 19:33:29.753Z
Hey @daniel_perez,
Spot first clip in clip list to original time stamp #post-8
There is a lot of discussion on this thread about this workflow.
Here's my take on it that works well from a keyboard shortcut:// If Auto Spot Clips is On, turn it off if(sf.ui.proTools.getMenuItem('Options', 'Auto-Spot Clips').isMenuChecked) { sf.ui.proTools.menuClick({ menuPath: ['Options', 'Auto-Spot Clips'], }); } //Switch to the "Grabber" tool sf.ui.proTools.toolsSelect({ tool: "Grabber", }); //Open the “Spot Dialog” window sf.ui.proTools.clipOpenSpotDialog(); //Wait for “Spot Dialog” window sf.ui.proTools.clipSpotDialog.elementWaitFor(); //Click "Original Time Stamp button" in “Spot Dialog” window sf.ui.proTools.clipSpotDialog.mouseClickElement({ relativePosition: {"x":270,"y":250}, }); //Click “OK” button in “Spot Dialog” window sf.ui.proTools.clipSpotDialog.buttons.whoseTitle.is('OK').first.elementClick(); // Set back to Smart Tool sf.ui.proTools.toolsSelect({ tool: "Smart", }); // Set back to slip mode sf.ui.proTools.editModeSet({ mode: "Slip", });
Chad Wahlbrink @Chad2023-09-26 19:45:37.103Z
I think some errors are possible with
.clipOpenSpotDialog()
currently.
I do think @Dustin_Harris's script based on the Spot Dialog being focused may be your best bet for now:
- In reply todaniel_perez⬆:Daniel Perez @daniel_perez
this one works:
var menu = sf.ui.proTools.clipOpenContextMenu().popupMenu; menu.menuClickPopupMenu({ menuPath: ['Spot...'], }); sf.ui.proTools.clipSpotDialog.elementWaitFor(); //hack to make sure everything loads sf.ui.proTools.clipSpotDialog.invalidate().buttons.whoseTitle.is("OK").first.elementWaitFor(); const currentStart = () => sf.ui.proTools.clipSpotDialog.textFields.first.value.invalidate().value; const originalTimeStamp = () => sf.ui.proTools.clipSpotDialog.children.whoseRole.is("AXStaticText").allItems[7].value.invalidate().value.split(".")[0]; if (originalTimeStamp() != currentStart()) { while (originalTimeStamp() != currentStart()) { sf.ui.proTools.clipSpotDialog.mouseClickElement({ relativePosition: { x: 274, y: 256 } }) sf.wait({intervalMs:100}) } sf.ui.proTools.clipSpotDialog.buttons.whoseTitle.is("OK").first.elementClick(); } else { sf.ui.proTools.clipSpotDialog.buttons.whoseTitle.is("Cancel").first.elementClick(); } sf.ui.proTools.clipSpotDialog.elementWaitFor({ waitForNoElement: true })
Chad Wahlbrink @Chad2023-09-27 11:58:12.635Z
Excellent, nice work!
- In reply todaniel_perez⬆:MMatt Friedman @Matt_Friedman
Line 14 does not always work
const originalTimeStamp = () => sf.ui.proTools.clipSpotDialog.children.whoseRole.is("AXStaticText").allItems[7].value.invalidate().value.split(".")[0];
Sometime it gives the value, other times, it gives an error:
Couldn't get item #7 as the array length was 0 - sf.ui.app('com.avid.ProTools')..children.whoseRole.is('AXStaticText')[7] (AxElementArrayIndexedItem)One way to reproduce, open spot win 1st time and it should work. open spot win a 2nd time and it won't work anymore.
clipSpotDialog
needs aninvalidate()
Also
currentStart
should return a timecode with no subframes too (in case the subframes box is ticked).I changed lines 13 and 14 to this to prevent an infinite while loop:
const currentStart = () => spotWin.invalidate().textFields.first.value.invalidate().value.split(".")[0]; const originalTimeStamp = () => spotWin.invalidate().children.whoseRole.is("AXStaticText").allItems[7].value.invalidate().value.split(".")[0];