Hi all,
I have a workflow that kills both my mouse arm and my good mood and I figured I'd try to see if anyone has come up with a good solution for it. The thing is: I record classical music at times, where there's loads of takes. All these takes are recorded to the timeline and I drop a marker, for instance T22, marking that this is the 22nd take. I then note what part of the piece this is on my laptop or a piece of paper and hand it over to the producer at the end of the session. After the session I then have to export all these clips from Pro Tools, since the producer always seems to want to do the editing in every DAW imaginable other than Pro Tools. So what I do is that I have my groups set to all, and I use the grabber tool to mark all the recorded clips in a particular takes and I then go to the Export Clips as Files... dialog by pressing Shift+cmd+K. I then have to press Choose, navigate to where I want these files exported in the dialog box that pops up, and then I have to make a new folder with the correct title and then finally I can hit Open and Export. When you do that 123 times in a row you go to some real dark places mentally, haha.
Now, I already have had a kind soul help make a script that takes the name from the marker and renames my mixdown track to the marker name (which makes me very happy), so I should be able to use parts of this script to create a new folder, shouldn't I? You can find that here: Script to grab name data from a marker and rename clip
Does anyone have a quick and easy way to go about this? Hope this all makes sense and thanks in advance for any help.
- KKyrre Laastad @Kyrre_Laastad
Sooo, I've managed to make this part of the script work. It takes the name from the Memory Location and creates a folder named after it inside of a session folder named "Takes". Very useful! I'm still pretty new at scripting, so I'm not the most freshly sharpened pencil in the drawer, or however you might want to put it, but this functions and that is a good start, even though it's very unelegant and frankensteined together.
sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.invalidate(); function makeFolder() { // Grab start and end time on any counter - (.replace(/[:.+| ]/g, '') - works on any counter const selection = sf.ui.proTools.selectionGet(); const selectionStart = +selection.selectionStart.replace(/[:.+| ]/g, '') const selectionEnd = +selection.selectionEnd.replace(/[:.+| ]/g, '') // Filter mem locs within selection //(.replace(/[:.+| ]/g, '') - to work on any counter const memLocFromSelection = sf.proTools.memoryLocationsFetchFromGui().collection['list'].filter(m => +m.mainCounterValue.replace(/[:.+| ]/g, '') >= selectionStart && +m.mainCounterValue.replace(/[:.+| ]/g, '') <= selectionEnd ); var sessionDir = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/'); //Create a sub-directory under the session folder, called "Takes" sf.file.directoryCreate({ path: sessionDir + '/Takes' }); //Create a sub-directory under the session folder's sub-directory "Takes", with its name taken from the memory location sf.file.directoryCreate({ path: sessionDir + '/Takes/' + memLocFromSelection[0].name }); } makeFolder()
Now I just need to find a way to go export the files to the same folder, but I'll leave that for some other night.
samuel henriques @samuel_henriques
Great job Kyrre,
I just added the export function I'm using, its a bit long because I keep adding stuff to it. But for most uses it could be simpler.
I just did tiny organise on your script and added my export function to it.
And removed the create "Takes", you can just create the subfolder in "Takes" and it'll be done./// If Export clips win doesn't open, try to show audio and Auto-Created & try again function setClipListViews() { function clipListPopupMenu(path) { sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is('Clip List').first.popupMenuSelect({ menuPath: path, targetValue: "Enable" }); } // Get clip list open/closed const clipListWasClosed = sf.ui.proTools.getMenuItem("View", "Other Displays", "Clip List").isMenuChecked /// Show audio and auto-created sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Clip List"], targetValue: "Enable" }); let enableViewMenus = [ ["Clear Find"], ["Show", "Audio"], ["Show", "Auto-Created"], ] enableViewMenus.forEach(clipListPopupMenu) // If clear find was needed, the clips need to be selected again so they get selected on the clip list const selectedTracks = sf.ui.proTools.selectedTrackNames sf.ui.proTools.trackDeselectAll() sf.ui.proTools.trackSelectByName({ names: selectedTracks }) // count selected clips on clip list let clipsTable = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first; let selectedClipCount = clipsTable.getElements("AXSelectedRows").allItems.length; if (selectedClipCount === 0) { alert('Please select at least one whole clip to export and try again.') throw 0 } // Set clip list open/closed as it was if (!clipListWasClosed) sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Clip List"], targetValue: "Disable" }); // reset menus sf.keyboard.press({ keys: 'n', fast: true, repetitions: 2 }); sf.keyboard.press({ keys: 'shift+command+k' }); }; /** * @param {string} path */ function navigateToInDialog(path) { //Get a reference to the focused window in the frontmost app: const win = sf.ui.frontmostApp.focusedWindow; //Open the Go to... sheet sf.keyboard.type({ text: '/' }); //Wait for the sheet to appear const sheet = win.sheets.first.elementWaitFor({ timeout: 500 }, 'Could not find "Go to" sheet in the Save/Open dialog').element; //Set the value of the combo box if (sheet.comboBoxes.exists) { sheet.comboBoxes.first.value.value = path; } else {//OSX Monterey is different sheet.textFields.whoseValue.is("/").first.value.value = path; }; //Return or press GO sf.keyboard.press({ keys: "return" }); //Wait for sheet to close win.sheets.first.elementWaitFor({ waitForNoElement: true, timeout: 500 }, '"Go to" sheet didn\'t close in time'); }; /// Export clips as files Function with settings function exportClipsAsFiles({ userFileType, userFormat, userBitDepth, userSampleRate, userLocation }) { // reset menus sf.keyboard.press({ keys: 'n', fast: true, repetitions: 2 }); // export clips as files sf.keyboard.press({ keys: 'shift+command+k' }); const exportSelectedWin = sf.ui.proTools.windows.whoseTitle.is('Export Selected').first exportSelectedWin.elementWaitFor({ waitType: "Appear", timeout: 100 }, function () { setClipListViews() exportSelectedWin.elementWaitFor({ waitType: "Appear" }, 'Export selected Win didn\'t open') }) /// Get all children into variables const [ sampleRate, bitDepth, format, fileType, enforceAvidCompatibility, chooseBtn, replacingWithNewFiles, autoRenaming, promptingForEachDuplicate, cancelBtn, exportBtn ] = exportSelectedWin.children.filter(child => child.title.value != null) // FILE TYPE allItems[3] if (fileType.title.value != userFileType) fileType.popupMenuSelect({ menuPath: [userFileType] }); // FORMAT allItems[2] if (format.title.value != userFormat) format.popupMenuSelect({ menuPath: [userFormat] }); // BIT DEPTH allItems[1] if (bitDepth.title.value != userBitDepth) bitDepth.popupMenuSelect({ menuPath: [userBitDepth] }); // SAMPLE RATE allItems[0] if (sampleRate.title.value != userSampleRate) sampleRate.popupMenuSelect({ menuPath: [userSampleRate] }); // Prompting For Each Duplicate promptingForEachDuplicate.elementClick(); // Disable Enforce Avid Compatibility enforceAvidCompatibility.checkboxSet({ targetValue: "Disable" }); /// Choose... location chooseBtn.elementClick(); const openWin = sf.ui.proTools.windows.whoseTitle.is('Open').first; openWin.elementWaitFor({}, 'Open Win didn\'t open.'); navigateToInDialog(userLocation); openWin.buttons.whoseTitle.is("Open").first.elementClick(); openWin.elementWaitFor({ waitType: "Disappear" }, 'Open Win didn\'t close.') /// Export... Btn exportBtn.elementClick(); exportSelectedWin.elementWaitFor({ waitType: "Disappear" }, 'Export Selected Win didn\'t close'); /// MP3 options function would go here }; function getMemLocName() { try { // Grab start and end time on any counter - (.replace(/[:.+| ]/g, '') - works on any counter const selection = sf.ui.proTools.selectionGet(); const selectionStart = +selection.selectionStart.replace(/[:.+| ]/g, '') const selectionEnd = +selection.selectionEnd.replace(/[:.+| ]/g, '') // Filter mem locs within selection //(.replace(/[:.+| ]/g, '') - to work on any counter const memLocFromSelection = sf.proTools.memoryLocationsFetchFromGui().collection['list'].filter(m => +m.mainCounterValue.replace(/[:.+| ]/g, '') >= selectionStart && +m.mainCounterValue.replace(/[:.+| ]/g, '') <= selectionEnd ); return memLocFromSelection[0].name } catch (err) { return undefined; }; }; function action() { var sessionDir = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/'); const memLocName = getMemLocName(); if (!memLocName) return;//Move to next clip there is no memory location for selected clip const exportPath = sessionDir + '/Takes/' + memLocName; //Create a sub-directory under the session folder's sub-directory "Takes", with its name taken from the memory location sf.file.directoryCreate({ path: exportPath }); exportClipsAsFiles({ userFileType: "WAV", userFormat: "Interleaved", userBitDepth: "24 Bit", userSampleRate: "48 kHz", userLocation: exportPath }); }; function main() { sf.ui.proTools.appActivateMainWindow(); const selectedTracks = sf.ui.proTools.selectedTrackHeaders selectedTracks.forEach(track => { track.trackScrollToView(); track.trackSelect(); sf.ui.proTools.clipDoForEachSelectedClip({ action: () => action() }) }); sf.ui.proTools.trackSelectByName({ names: selectedTracks.map(th => th.normalizedTrackName) }) }; main();
- KKyrre Laastad @Kyrre_Laastad
OMG, it works! Brilliant! I'm very grateful for your help.
I had to add an sf.wait(1000) around line 145 for it to work:
// If destination is not correct, choose correct one if (!isCurrentDestinationCorrect(userLocation, exportSelectedWin)) { /// Choose... location chooseBtn.elementClick(); const openWin = sf.ui.proTools.windows.whoseTitle.is('Open').first.elementWaitFor({}, 'Open Win didn\'t open.').element navigateToInDialog(userLocation); sf.wait(1000) openWin.buttons.getByTitle('Open').elementClick() openWin.elementWaitFor({ waitType: "Disappear" }, 'Open Win didn\'t close.') }
Now, I've tried turning the last part of the script into a function so that I can call on it to do this on all selected clips sequentially, but it puts all the files into the same folder.
Do you see anything wrong with this right away? Again, I'm very grateful!
samuel henriques @samuel_henriques
this wait should't be needed, and it should be
sf.wait({intervalMs:1000})
but the default is 1000ms, so for 1000ms you can writesf.wait()
and its done.And this bit of code, it's an invention I made so it doesn't open the destination every time, I export mostly to the same folder of a session, so this saves one step. But it's an invention so it might not work so well for everyone.
Just updated removing this step and do for all selected clips, want all selected tracks as well?- KKyrre Laastad @Kyrre_Laastad
Thank you again! I definitely owe you several types of beverages and/or lunch. :)
This script works fine with exporting several clips into the same folder, but what I really want is to export the files into individual folders with their names taken from the Memory Location. I also want to be able to export multiple clips spread over different tracks at the same time, so that effectively what I do is to have the group on, mark the takes that I want to export (so that every clip for that/those particular take(s) are selected) and instantiate this script, and it will export all the clips associated with
each take into their own folders.I'm also getting an issue where there is no file in the folder after I execute the script from time to time, while other times it works fine. It gives me these error codes:
16.11.2022 11:46:29.20 [Backend]: Logging unknown error in action (02) DoMainCounterAction: Export clips into folders from MemLoc do for each: Line 135
Logging unknown error in action (02) DoForEachClipAction: Export clips into folders from MemLoc do for each: Line 13516.11.2022 11:46:29.20 [Backend]: Logging unknown error in action (02) DoForEachSelectedClipAction: Export clips into folders from MemLoc do for each: Line 135
JavaScript error with InnerException: Open Win didn't close. (Export clips into folders from MemLoc do for each: Line 135)16.11.2022 11:46:29.21 [Backend]: !! Command Error: Export clips into folders from MemLoc do for each [user:default:clai94vk20001uo10ahe2tm7b]:
Error: Export clips into folders from MemLoc do for each: Line 135
(Export clips into folders from MemLoc do for each line 195)I've taken a video grab of the screen when this happens, which you can find here: https://www.dropbox.com/s/1p75uzdil6yeklg/Skjermopptak 2022-11-16 kl. 11.43.45.mov?dl=0
Again, I am very grateful for your help here, and I'm sorry that I'm not skilled enough to be able to fix this myself (yet!).
samuel henriques @samuel_henriques
This link doesn't work for me, I think you need to upload and share the upload link.
The video might clarify whats missing.
What osx are you in? It might be something related to that.
Here, the script is creating a folder for each marker, then each clip goes to the folder with the marker name, I've just added on mine to work on all selected tracks, once I figure what could be wrong i'll update it to you.- KKyrre Laastad @Kyrre_Laastad
Ouch, sorry! Updated the link now, hopefully it should work now. I'm on Monterey 12.5.1. Will try the updated script right away.
Edit: and there I saw that there's a comment about Monterey being different in line 62, so that might have something to with this.
samuel henriques @samuel_henriques
wait, I haven't updated the script yet, let me check the video to see where your problem could be.
samuel henriques @samuel_henriques
could you double check for me if line 135 in your script is the same as in the post? It's weird it didn't give the custom error if it is in fact
openWin.elementWaitFor({ waitType: "Disappear" }, 'Open Win didn\'t close.')
samuel henriques @samuel_henriques
So, I can reproduce your problem if the clip name I'm exporting already exists in the folder.
Could you confirm if this makes sense?
To fix it, you either get unique clip names or we can set the auto renaming option.:
samuel henriques @samuel_henriques
UPDATED script to work on all selected tracks
- In reply tosamuel_henriques⬆:KKyrre Laastad @Kyrre_Laastad
Hmm, it seems that instead of exporting the files to the new folder inside of "Takes" it tries to export the files into Audio Files folder inside of the Pro Tools session folder hierarchy. That's probably why there's a problem with duplicate file names. I've made yet another video (which hopefully works the first time now!):
So, maybe this means that the problem is with how Monterey handles folders?
samuel henriques @samuel_henriques
Thats right, just before he clicks "Open" switches to "Audio Files"
samuel henriques @samuel_henriques
could you try list view?
- In reply tosamuel_henriques⬆:KKyrre Laastad @Kyrre_Laastad
Hi again, sorry got stuck in a meeting. Here's my line 135, seems I left out the first two lines when copying, sorry about that:
exportBtn.elementClick();
samuel henriques @samuel_henriques
Check if the updated script makes any difference. from the video it seams he's not setting the destination folder correctly. Right before it clicks "open" it changed to the audio files folder. That would cause it to export the clip to a folder that has that clip name.
samuel henriques @samuel_henriques
Also, I've read some people have problems with the column view, could you try to set it as list to see if it helps?
- KKyrre Laastad @Kyrre_Laastad
This works better, but it crashes when it starts to work on the second clip from the same take. Here's another video:
...and it's giving me this error message in the log:
16.11.2022 15:49:12.75 [Backend]: Logging unknown error in action (02) DoMainCounterAction: Export clips into folders from MemLoc do for each: Line 136
16.11.2022 15:49:12.75 [Backend]: Logging unknown error in action (02) DoForEachClipAction: Export clips into folders from MemLoc do for each: Line 136
Logging unknown error in action (02) DoForEachSelectedClipAction: Export clips into folders from MemLoc do for each: Line 136
JavaScript error with InnerException: Open Win didn't close. (Export clips into folders from MemLoc do for each: Line 136)16.11.2022 15:49:12.75 [Backend]: !! Command Error: Export clips into folders from MemLoc do for each [user:default:clai94vk20001uo10ahe2tm7b]:
Error: Export clips into folders from MemLoc do for each: Line 136
(Export clips into folders from MemLoc do for each line 204)Edit: this is with list view in the finder dialogue
samuel henriques @samuel_henriques
I'm sorry this is taking so long. I'm not in Monterey so it's harder for me to find the fault.
Could you open the pro tools undo window, maybe theres a clue there to figure why its pressing record btn- KKyrre Laastad @Kyrre_Laastad
No, I’m sorry to put it on you to make this work on my computer! I’ve left the studio for the day, and I only have Big Sur on my laptop, so I’ll post a screenshot of the undo history first thing tomorrow morning!
Thanks again!
samuel henriques @samuel_henriques
Managed to try it on monterey and its working fine.
Please double check there is nothing else on the script you might have left by mistake while trying things- KKyrre Laastad @Kyrre_Laastad
So, I've spent the morning trying out different stuff here, and once I added an sf.wait(); in line 134 it now works very stable on my computer as well. Thank you so much for all the help, Samuel, this is a real life-improver for me, and I'm sure many other with similar workflows could benefit from it as well. I owe you big time!
The whole script I'm using now is:
/// If Export clips win doesn't open, try to show audio and Auto-Created & try again function setClipListViews() { function clipListPopupMenu(path) { sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is('Clip List').first.popupMenuSelect({ menuPath: path, targetValue: "Enable" }); } // Get clip list open/closed const clipListWasClosed = sf.ui.proTools.getMenuItem("View", "Other Displays", "Clip List").isMenuChecked /// Show audio and auto-created sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Clip List"], targetValue: "Enable" }); let enableViewMenus = [ ["Clear Find"], ["Show", "Audio"], ["Show", "Auto-Created"], ] enableViewMenus.forEach(clipListPopupMenu) // If clear find was needed, the clips need to be selected again so they get selected on the clip list const selectedTracks = sf.ui.proTools.selectedTrackNames sf.ui.proTools.trackDeselectAll() sf.ui.proTools.trackSelectByName({ names: selectedTracks }) // count selected clips on clip list let clipsTable = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first; let selectedClipCount = clipsTable.getElements("AXSelectedRows").allItems.length; if (selectedClipCount === 0) { alert('Please select at least one whole clip to export and try again.') throw 0 } // Set clip list open/closed as it was if (!clipListWasClosed) sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Clip List"], targetValue: "Disable" }); // reset menus sf.keyboard.press({ keys: 'n', fast: true, repetitions: 2 }); sf.keyboard.press({ keys: 'shift+command+k' }); }; /** * @param {string} path */ function navigateToInDialog(path) { //Get a reference to the focused window in the frontmost app: const win = sf.ui.frontmostApp.focusedWindow; //Open the Go to... sheet sf.keyboard.type({ text: '/' }); //Wait for the sheet to appear const sheet = win.sheets.first.elementWaitFor({ timeout: 500 }, 'Could not find "Go to" sheet in the Save/Open dialog').element; //Set the value of the combo box if (sheet.comboBoxes.exists) { sheet.comboBoxes.first.value.value = path; } else {//OSX Monterey is different sheet.textFields.whoseValue.is("/").first.value.value = path; }; //Return or press GO sf.keyboard.press({ keys: "return" }); //Wait for sheet to close win.sheets.first.elementWaitFor({ waitForNoElement: true, timeout: 500 }, '"Go to" sheet didn\'t close in time'); }; /// Export clips as files Function with settings function exportClipsAsFiles({ userFileType, userFormat, userBitDepth, userSampleRate, userLocation }) { // reset menus sf.keyboard.press({ keys: 'n', fast: true, repetitions: 2 }); // export clips as files sf.keyboard.press({ keys: 'shift+command+k' }); const exportSelectedWin = sf.ui.proTools.windows.whoseTitle.is('Export Selected').first exportSelectedWin.elementWaitFor({ waitType: "Appear", timeout: 100 }, function () { setClipListViews() exportSelectedWin.elementWaitFor({ waitType: "Appear" }, 'Export selected Win didn\'t open') }) /// Get all children into variables const [ sampleRate, bitDepth, format, fileType, enforceAvidCompatibility, chooseBtn, replacingWithNewFiles, autoRenaming, promptingForEachDuplicate, cancelBtn, exportBtn ] = exportSelectedWin.children.filter(child => child.title.value != null) // FILE TYPE allItems[3] if (fileType.title.value != userFileType) fileType.popupMenuSelect({ menuPath: [userFileType] }); // FORMAT allItems[2] if (format.title.value != userFormat) format.popupMenuSelect({ menuPath: [userFormat] }); // BIT DEPTH allItems[1] if (bitDepth.title.value != userBitDepth) bitDepth.popupMenuSelect({ menuPath: [userBitDepth] }); // SAMPLE RATE allItems[0] if (sampleRate.title.value != userSampleRate) sampleRate.popupMenuSelect({ menuPath: [userSampleRate] }); // Prompting For Each Duplicate promptingForEachDuplicate.elementClick(); // Disable Enforce Avid Compatibility enforceAvidCompatibility.checkboxSet({ targetValue: "Disable" }); /// Choose... location chooseBtn.elementClick(); const openWin = sf.ui.proTools.windows.whoseTitle.is('Open').first; openWin.elementWaitFor({}, 'Open Win didn\'t open.'); navigateToInDialog(userLocation); sf.wait(); openWin.buttons.whoseTitle.is("Open").first.elementClick(); openWin.elementWaitFor({ waitType: "Disappear" }, 'Open Win didn\'t close.') /// Export... Btn exportBtn.elementClick(); exportSelectedWin.elementWaitFor({ waitType: "Disappear" }, 'Export Selected Win didn\'t close'); /// MP3 options function would go here }; function getMemLocName() { try { // Grab start and end time on any counter - (.replace(/[:.+| ]/g, '') - works on any counter const selection = sf.ui.proTools.selectionGet(); const selectionStart = +selection.selectionStart.replace(/[:.+| ]/g, '') const selectionEnd = +selection.selectionEnd.replace(/[:.+| ]/g, '') // Filter mem locs within selection //(.replace(/[:.+| ]/g, '') - to work on any counter const memLocFromSelection = sf.proTools.memoryLocationsFetchFromGui().collection['list'].filter(m => +m.mainCounterValue.replace(/[:.+| ]/g, '') >= selectionStart && +m.mainCounterValue.replace(/[:.+| ]/g, '') <= selectionEnd ); return memLocFromSelection[0].name } catch (err) { return undefined; }; }; function action() { var sessionDir = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/'); const memLocName = getMemLocName(); if (!memLocName) return;//Move to next clip there is no memory location for selected clip const exportPath = sessionDir + '/Takes/' + memLocName; //Create a sub-directory under the session folder's sub-directory "Takes", with its name taken from the memory location sf.file.directoryCreate({ path: exportPath }); exportClipsAsFiles({ userFileType: "WAV", userFormat: "Interleaved", userBitDepth: "24 Bit", userSampleRate: "48 kHz", userLocation: exportPath }); }; function main() { sf.ui.proTools.appActivateMainWindow(); const selectedTracks = sf.ui.proTools.selectedTrackHeaders selectedTracks.forEach(track => { track.trackScrollToView(); track.trackSelect(); sf.ui.proTools.clipDoForEachSelectedClip({ action: () => action() }) }); sf.ui.proTools.trackSelectByName({ names: selectedTracks.map(th => th.normalizedTrackName) }) }; main();