Hi guys. every day I have to export a text file from protools. load it in pgpt session and export an excel file that the production assistant will transform into a beautiful music cue sheet.
Is there the possibility of automating this process in your opinion?
Linked from:
- Dav3 @D_av_3
- RRandall Smith @Randall_Smith
I have a script that does this using PGPT.
It does require that the template you use has the tracks you want to create a cue sheet for named with brackets; ie [MX} 1, [MX] 2.
In PGPT, you can set the program up to 'load only ADR tracks' (under the import window).
After that, it is a bunch of simulated mouse clicks - although I think there are some hidden keyboard shortcuts for open, select all, export selected.
Randall
Dav3 @D_av_3
Cool!!
- In reply toRandall_Smith⬆:
Dav3 @D_av_3
by any chance have you published this script somewhere?
- RRandall Smith @Randall_Smith
'''
let folderPath = (sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/')); let outputDirectory = folderPath + "/" + "02 File Based Deliveries" + "/" + "05 Cue Sheets"; var sessionName = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join('.'); let sessionNamePtx = sessionName + "." + "ptx"; let fullPath = folderPath + "/" + sessionNamePtx; let fullOutputDir = outputDirectory + "/" + sessionName; //alert(fullOutputDir);
function getProToolsSession(){
sf.app.launch({
path: "/Applications/PGPTSession.app",
});sf.wait({
intervalMs: 1500,
});sf.ui.app('PGPTSession').mainWindow.windowMove({
position: {"x":10,"y":10},
});sf.ui.app('PGPTSession').mainWindow.mouseClickElement({
relativePosition: {"x":1000,"y":50},
});sf.keyboard.press({
keys: "numpad enter",
});sf.keyboard.press({
keys: "backspace",
});sf.keyboard.type({
text: fullPath,
});sf.keyboard.press({
keys: "enter",
});sf.ui.app('PGPTSession').windows.whoseTitle.is('Open').first.getElement('AXDefaultButton').elementClick();
sf.wait({
intervalMs: 1500,
});sf.keyboard.press({
keys: "enter",
});
}function exportCueSheet(){
sf.wait({
intervalMs: 1500,
});sf.ui.app('PGPTSession').mainWindow.mouseClickElement({
relativePosition: {"x":600,"y":710},
});sf.ui.app('PGPTSession').mainWindow.mouseClickElement({
relativePosition: {"x":1050,"y":70},
});//sf.keyboard.press({
// keys: "numpad enter",
//});
//sf.keyboard.press({
// keys: "/",
//});sf.keyboard.press({
keys: "slash",
});sf.keyboard.press({
keys: "backspace",
});sf.keyboard.type({
text: outputDirectory,
});sf.keyboard.press({
keys: "enter",
});sf.ui.app('PGPTSession').windows.whoseTitle.is('Select / create folder to output files').first.getElement('AXDefaultButton').elementClick();
}
function closePgpt(){
sf.ui.app('PGPTSession').appActivateMainWindow();sf.keyboard.press({
keys: "cmd+q",
});
}function openSessionDir(){
sf.appleScript.finder.open(fullOutputDir)
// sf.appleScript.finder.select((sf.ui.proTools.mainWindow.sessionPath) + "/" + "01 AAF EDL");
sf.ui.finder.windows.first.elementRaise();
sf.ui.finder.appActivateMainWindow();}
getProToolsSession();
exportCueSheet();
closePgpt();
openSessionDir();'''
samuel henriques @samuel_henriques
Hey @Davide_Docente,
I made this one:
Automated Music cue sheet #post-27you need to adapt it to your specific workflow.
- In reply toD_av_3⬆:Dav3 @D_av_3
Thank you so much Samuel.. but I need to do it with PgPt session.
- In reply toD_av_3⬆:Dav3 @D_av_3
Hi Guys sorry. I made this script to export Session Info as Text. How do I insert a variable that allows me to save this file in the Bounced Files folder of the Protools session?
sf.ui.proTools.appActivateMainWindow(); if (sf.ui.proTools.selectedTrackNames[0] == undefined) { log("No tracks are selected"); throw 0; } else {} sf.ui.proTools.menuClick({ menuPath: ["File", "Export", "Session Info as Text..."], }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include File List').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Clip List').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Markers').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Plug-In List').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Track EDL\'s').first.checkboxSet({ targetValue: "Enable", }); let exportSessionTextWin = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportSessionTextWin.radioButtons.whoseTitle.startsWith('Selected Tracks Only').first.elementClick(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Combine Crossfaded Clips').first.elementClick(); exportSessionTextWin.buttons.whoseTitle.startsWith('OK').first.elementClick();`
Dustin Harris @Dustin_Harris
Hello Davide, try this and let me know how it goes.
It's entirely possible some of the Pro Tools windows have changed in version 2021.3.
Also PGPT doesn't give soundflow direct access to the UI elements in PGPT so I had to do a lot of relative mouse clicks, so unfortunately I can't programmatically tell of the export options checkboxes are set correctly or not. I get around this by opening the window and asking the user to verify if they're set right or not.I haven't set the script to automatically export the files from PGPT either because I'm not sure if your workflow includes 'select all tracks' or not, but let me know and I can add it to the script too!
🤟
function setProToolsTextExportCheckboxes() { //Export Session As Text checkbox options const checkBoxes = [ { Title: "Include File List", State: "Disable" }, { Title: "Include Clip List", State: "Disable" }, { Title: "Include Markers", State: "Disable" }, { Title: "Include Plug-In List", State: "Disable" }, { Title: "Include Track EDL's", State: "Enable" }, ]; function setCheckboxState(item) { try { let exportTextOptions = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportTextOptions.checkBoxes.whoseTitle.is(item.Title).first.checkboxSet({ targetValue: (item.State), }); } catch (err) { throw `error in setCheckbox(): ${err}` } } checkBoxes.forEach(setCheckboxState); } function setPGPTExportOptions() { let pgptSession = sf.ui.app("PGPTSession"); //Export Options Button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 104 }, }); sf.wait({ intervalMs: 250 }); let exportOptionsCheckboxes = [ { "x": 42, "y": 145 }, //Export ADR format text files { "x": 42, "y": 169 }, //Export Master and Summary Excel files { "x": 42, "y": 193 }, //Export Master and Summary PDF files { "x": 42, "y": 220 }, //Export PDF Actor Files { "x": 42, "y": 243 }, //Export PDF Engineer Files { "x": 42, "y": 257 }, //Export Detailed Files { "x": 42, "y": 290 }, //Export MIDI Files { "x": 42, "y": 355 }, //Remove common suffixes { "x": 375, "y": 146 }, //Export Music format text files { "x": 375, "y": 170 }, //Export Music Excel file { "x": 375, "y": 320 }, //Show Scenes in detailed PDFs ]; function setPGPTCheckboxState(item) { let exportOptionsWin = sf.ui.app("PGPTSession").windows.whoseTitle.is("Export Options").first; try { exportOptionsWin.mouseClickElement({ relativePosition: item, }); } catch (err) { throw `error in setCheckbox(): ${err}` } } let consent = confirm(`Do the checkboxes need to be set?\n\nClick 'Yes' to change them or 'No' to leave them`) if (consent) exportOptionsCheckboxes.forEach(setPGPTCheckboxState); pgptSession.windows.whoseTitle.is("Export Options").first.mouseClickElement({ relativePosition: { "x": 652, "y": 501 }, }); } function main() { sf.ui.proTools.appActivateMainWindow(); let sessionRootFolder = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/'); let bouncedFilesFolder = sessionRootFolder + '/Bounced Files/' let defaultSaveName = sessionRootFolder.split('/').slice(-1)[0] + ".txt" let pgptFile = sf.interaction.selectSaveFileName({ prompt: "Please Enter Name For PTPG Session Text Document", defaultLocation: bouncedFilesFolder, defaultName: defaultSaveName, onCancel: "Continue", onError: "ThrowError" }); if (pgptFile.userCancelled == true) throw 0; let savePath = pgptFile.path; if (savePath.slice(savePath.lastIndexOf(".")) !== '.txt') savePath = savePath + ".txt"; sf.ui.proTools.menuClick({ menuPath: ["File", "Export", "Session Info as Text..."], }); //shorten the UI element names let exportSessionTextWin = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportSessionTextWin.elementWaitFor().element; //this function sets the checkbox options setProToolsTextExportCheckboxes(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Selected Tracks Only').first.elementClick(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Combine Crossfaded Clips').first.elementClick(); exportSessionTextWin.buttons.whoseTitle.startsWith('OK').first.elementClick(); let saveWin = sf.ui.proTools.windows.whoseTitle.is('Save').first; saveWin.elementWaitFor(); //open the text path bar sf.keyboard.press({ keys: "slash", }); //wait for window animation to finish sf.wait({ intervalMs: 250 }); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor(); saveWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementSetTextFieldWithAreaValue({ value: savePath, }); //OK buttons saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor(); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementClick(); saveWin.buttons.whoseTitle.is("Save").first.elementClick(); sf.wait({ intervalMs: 250 }) //if a file already exists and replace was chosen buy the user, this will automatically click 'replace' let replaceButton = sf.ui.proTools.windows.whoseTitle.is("Save").first.sheets.first.buttons.whoseTitle.is("Replace").first; if (replaceButton.exists) replaceButton.elementClick(); /* Beginning of PGPT Part of script */ let pgptSession = sf.ui.app("PGPTSession"); const pgptPath = '/Applications/PGPTSession.app'; if (!pgptSession.exists) { sf.app.launch({ path: pgptPath }) } //wait for main window while (!pgptSession.mainWindow.exists) sf.wait({ intervalMs: 100 }); pgptSession.appActivateMainWindow(); //open button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 45 }, }); //wait for file open dialog box to appear pgptSession.windows.whoseTitle.is("Open").first.elementWaitFor(); //open the text path bar sf.keyboard.press({ keys: "slash", }); //wait for the text box to appear pgptSession.windows.whoseTitle.is("Open").first.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor(); pgptSession.windows.whoseTitle.is("Open").first.sheets.first.comboBoxes.whoseValue.is("/").first.elementSetTextFieldWithAreaValue({ value: savePath, }); //click GO and Open buttons pgptSession.windows.whoseTitle.is("Open").first.sheets.first.buttons.whoseTitle.is("Go").first.elementClick(); pgptSession.windows.whoseTitle.is("Open").first.buttons.whoseTitle.is("Open").first.elementClick(); sf.keyboard.press({ keys: 'return' }); //Set the export option checkboxes setPGPTExportOptions(); } main();
Dav3 @D_av_3
Hi Dustin, this is what I need to export from PGPT. from protools I export only the tracks containing music (I don't know if we can automate this too ... I call them music 1, music 2, ...). I tried to do it by myself but ... guys I give up ... it's not really my job ... I have the impression that I miss the theoretical bases ..
Dustin Harris @Dustin_Harris
Hi Davide!
There is currently no way for SoundFlow to tell what the tracks are named in PGPTSession because it doesn’t expose those UI elements. It’s a kind of brick wall we can’t really get around at the moment.Dav3 @D_av_3
Ok .. maybe I can export only the desired ones from protools then ... What I just can't understand is how to automatically open the TXT file exported from protools with PGPT ... and then export an excel file ... some of you do you have any suggestions?
Dustin Harris @Dustin_Harris
The txt file SHOULD automatically open in PGPT with the script I sent, is that not happening for you? I can also make protools only export tracks with the name ‘Music’ in it, and use that to TRY to export the excel file, but PGPT is very finicky and might not give reliable results... worth a shot though maybe? :)
Dav3 @D_av_3
No actually the TXT file is not opened automaticcally.
what's happening is this: I'm saving a txt file with the correct characteristics from Protools. But nothing else.Dav3 @D_av_3
sf.ui.proTools.appActivate(); sf.ui.proTools.invalidate(); //Save Session As / Rename var sessionPath = sf.ui.proTools.mainWindow.sessionPath; var sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join('.'); var newname = sessionName + "_cue Sheet"; sf.ui.proTools.menuClick({ menuPath: ["File","Save As..."], }); var saveWin = sf.ui.proTools.windows.whoseTitle.is('Save').first; saveWin.elementWaitFor({ failIfNotFound: true, }); saveWin.textFields.first.elementSetTextFieldWithAreaValue({ value: newname, }); saveWin.buttons.whoseTitle.is('Save').first.elementClick(); saveWin.elementWaitFor({ waitType: "Disappear", timeout: -1, }); sf.ui.proTools.appActivate(); sf.ui.proTools.invalidate(); //Select Music tracks sf.ui.proTools.menuClick({menuPath: ["Window","Edit"]}); // UI bug - active edit window sf.ui.proTools.trackSelectByName({ deselectOthers: true, names: ['Music 1','Music 2','Music 3',]}); sf.ui.proTools.menuClick({menuPath: ["Edit","Select All"]}); function setProToolsTextExportCheckboxes() { //Export Session As Text checkbox options const checkBoxes = [ { Title: "Include File List", State: "Disable" }, { Title: "Include Clip List", State: "Disable" }, { Title: "Include Markers", State: "Disable" }, { Title: "Include Plug-In List", State: "Disable" }, { Title: "Include Track EDL's", State: "Enable" }, ]; function setCheckboxState(item) { try { let exportTextOptions = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportTextOptions.checkBoxes.whoseTitle.is(item.Title).first.checkboxSet({ targetValue: (item.State), }); } catch (err) { throw `error in setCheckbox(): ${err}` } } checkBoxes.forEach(setCheckboxState); } function setPGPTExportOptions() { let pgptSession = sf.ui.app("PGPTSession"); //Export Options Button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 104 }, }); sf.wait({ intervalMs: 250 }); let exportOptionsCheckboxes = [ { "x": 42, "y": 145 }, //Export ADR format text files { "x": 42, "y": 169 }, //Export Master and Summary Excel files { "x": 42, "y": 193 }, //Export Master and Summary PDF files { "x": 42, "y": 220 }, //Export PDF Actor Files { "x": 42, "y": 243 }, //Export PDF Engineer Files { "x": 42, "y": 257 }, //Export Detailed Files { "x": 42, "y": 290 }, //Export MIDI Files { "x": 42, "y": 355 }, //Remove common suffixes { "x": 375, "y": 146 }, //Export Music format text files { "x": 375, "y": 170 }, //Export Music Excel file { "x": 375, "y": 320 }, //Show Scenes in detailed PDFs ]; function setPGPTCheckboxState(item) { let exportOptionsWin = sf.ui.app("PGPTSession").windows.whoseTitle.is("Export Options").first; try { exportOptionsWin.mouseClickElement({ relativePosition: item, }); } catch (err) { throw `error in setCheckbox(): ${err}` } } let consent = confirm(`Do the checkboxes need to be set?\n\nClick 'Yes' to change them or 'No' to leave them`) if (consent) exportOptionsCheckboxes.forEach(setPGPTCheckboxState); pgptSession.windows.whoseTitle.is("Export Options").first.mouseClickElement({ relativePosition: { "x": 652, "y": 501 }, }); } function main() { sf.ui.proTools.appActivateMainWindow(); let sessionRootFolder = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/'); let bouncedFilesFolder = sessionRootFolder + '/Bounced Files/' let defaultSaveName = sessionRootFolder.split('/').slice(-1)[0] + ".txt" let pgptFile = sf.interaction.selectSaveFileName({ prompt: "Please Enter Name For PTPG Session Text Document", defaultLocation: bouncedFilesFolder, defaultName: defaultSaveName, onCancel: "Continue", onError: "ThrowError" }); if (pgptFile.userCancelled == true) throw 0; let savePath = pgptFile.path; if (savePath.slice(savePath.lastIndexOf(".")) !== '.txt') savePath = savePath + ".txt"; sf.ui.proTools.menuClick({ menuPath: ["File", "Export", "Session Info as Text..."], }); //shorten the UI element names let exportSessionTextWin = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportSessionTextWin.elementWaitFor().element; //this function sets the checkbox options setProToolsTextExportCheckboxes(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Selected Tracks Only').first.elementClick(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Combine Crossfaded Clips').first.elementClick(); exportSessionTextWin.buttons.whoseTitle.startsWith('OK').first.elementClick(); let saveWin = sf.ui.proTools.windows.whoseTitle.is('Save').first; saveWin.elementWaitFor(); //open the text path bar sf.keyboard.press({ keys: "slash", }); //wait for window animation to finish sf.wait({ intervalMs: 250 }); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor(); saveWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementSetTextFieldWithAreaValue({ value: savePath, }); //OK buttons saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor(); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementClick(); saveWin.buttons.whoseTitle.is("Save").first.elementClick(); sf.wait({ intervalMs: 250 }) //if a file already exists and replace was chosen buy the user, this will automatically click 'replace' let replaceButton = sf.ui.proTools.windows.whoseTitle.is("Save").first.sheets.first.buttons.whoseTitle.is("Replace").first; if (replaceButton.exists) replaceButton.elementClick(); /* Beginning of PGPT Part of script */ let pgptSession = sf.ui.app("PGPTSession"); const pgptPath = '/Applications/PGPTSession.app'; if (!pgptSession.exists) { sf.app.launch({ path: pgptPath }) } //wait for main window while (!pgptSession.mainWindow.exists) sf.wait({ intervalMs: 100 }); pgptSession.appActivateMainWindow(); //open button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 45 }, }); //wait for file open dialog box to appear pgptSession.windows.whoseTitle.is("Open").first.elementWaitFor(); //open the text path bar sf.keyboard.press({ keys: "slash", }); //wait for the text box to appear pgptSession.windows.whoseTitle.is("Open").first.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor(); pgptSession.windows.whoseTitle.is("Open").first.sheets.first.comboBoxes.whoseValue.is("/").first.elementSetTextFieldWithAreaValue({ value: savePath, }); //click GO and Open buttons pgptSession.windows.whoseTitle.is("Open").first.sheets.first.buttons.whoseTitle.is("Go").first.elementClick(); pgptSession.windows.whoseTitle.is("Open").first.buttons.whoseTitle.is("Open").first.elementClick(); sf.keyboard.press({ keys: 'return' }); //Set the export option checkboxes setPGPTExportOptions(); } main();
Dav3 @D_av_3
now the script saves a version of the session with a "cue sheet" suffix then selects the tracks "Music1, Music2 and Music3" ...
PGPT still doesn't work ...Dustin Harris @Dustin_Harris
Where is your PGPT app existing currently? The script assumes it’s just in the Applications folder...
Dav3 @D_av_3
Yes !! Application folder....I'm italian so "Applicazioni"
Dav3 @D_av_3
so i have to write "Applicazioni" instead of applications in the script?Right?.....mmmm
When I run the script a first window asks me
"Please Enter Name For PTPG Session Text Document"
.... then another window asks me "Name for session TEXT"
... then I press on Save ..
but nothing else happensDustin Harris @Dustin_Harris
Just a quick note to tell you I haven't forgot about this :) I'm just busy at the moment, I'll see what I can do with the script this weekend :)
- In reply toD_av_3⬆:
Dustin Harris @Dustin_Harris
Hi @Davide_Docente I'm wondering if my script doesn't work because it is expecting english button names instead of Italian ones? I haven't dealt with language localization before. @chrscheuer do you happen to know if we would need to change 'whoseTitleIs' calls to the system language?
//Change 'Go' to and 'Save' to Italian versions? saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor(); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementClick(); saveWin.buttons.whoseTitle.is("Save").first.elementClick(); sf.wait({ intervalMs: 250 })
Christian Scheuer @chrscheuer2021-05-08 14:08:40.068Z
I think you would probably have to change these, yea. I think it depends from app to app whether the AX titles are localized or not.
Christian Scheuer @chrscheuer2021-05-08 14:14:57.170Z
You could use the new elementDebug to get info from Davide :)
Dustin Harris @Dustin_Harris
Oh yeah I completely forgot about elementDebug! PGPT doesn't have many elements exposed, but now I can see exactly which ones are :)
Dustin Harris @Dustin_Harris
What about Pro Tools?
Dav3 @D_av_3
protools is also running in English...Only Finder run in Italian
Dustin Harris @Dustin_Harris
if you can, select the PGPTSession application in the finder, press option-command-c and paste the result here :)
Dav3 @D_av_3
/Applications/PGPTSession.app
Dustin Harris @Dustin_Harris
try this and report back:
function setProToolsTextExportCheckboxes() { //Export Session As Text checkbox options const checkBoxes = [ { Title: "Include File List", State: "Disable" }, { Title: "Include Clip List", State: "Disable" }, { Title: "Include Markers", State: "Disable" }, { Title: "Include Plug-In List", State: "Disable" }, { Title: "Include Track EDL's", State: "Enable" }, ]; function setCheckboxState(item) { try { let exportTextOptions = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportTextOptions.checkBoxes.whoseTitle.is(item.Title).first.checkboxSet({ targetValue: (item.State), }); } catch (err) { throw `error in setCheckbox(): ${err}` } } checkBoxes.forEach(setCheckboxState); } function setPGPTExportOptions() { try { let pgptSession = sf.ui.app("PGPTSession"); let exportOptionsCheckboxes = [ { "x": 42, "y": 145 }, //Export ADR format text files { "x": 42, "y": 169 }, //Export Master and Summary Excel files { "x": 42, "y": 193 }, //Export Master and Summary PDF files { "x": 42, "y": 220 }, //Export PDF Actor Files { "x": 42, "y": 243 }, //Export PDF Engineer Files { "x": 42, "y": 257 }, //Export Detailed Files { "x": 42, "y": 290 }, //Export MIDI Files { "x": 42, "y": 355 }, //Remove common suffixes { "x": 375, "y": 146 }, //Export Music format text files { "x": 375, "y": 170 }, //Export Music Excel file { "x": 375, "y": 320 }, //Show Scenes in detailed PDFs ]; //Export Options Button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 104 }, }, `could not click Export Options Button in PGPT`); sf.wait({ intervalMs: 250 }); function setPGPTCheckboxState(item) { let exportOptionsWin = sf.ui.app("PGPTSession").windows.whoseTitle.is("Export Options").first; try { exportOptionsWin.mouseClickElement({ relativePosition: item, }); } catch (err) { throw `error in setCheckbox(): ${err}` } } let consent = confirm(`Do the checkboxes need to be set?\n\nClick 'Yes' to change them or 'No' to leave them`) if (consent) exportOptionsCheckboxes.forEach(setPGPTCheckboxState); pgptSession.windows.whoseTitle.is("Export Options").first.mouseClickElement({ relativePosition: { "x": 652, "y": 501 }, }); } catch (err) { throw `error in setPGPTExportOptions: ${err}` } } function main() { sf.ui.proTools.appActivateMainWindow(); let sessionRootFolder = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/'); let bouncedFilesFolder = sessionRootFolder + '/Bounced Files/' let defaultSaveName = sessionRootFolder.split('/').slice(-1)[0] + ".txt" let pgptFile = sf.interaction.selectSaveFileName({ prompt: "Please Enter Name For EdiCue Text Document", defaultLocation: bouncedFilesFolder, defaultName: defaultSaveName, onCancel: "Continue", onError: "ThrowError" }); if (pgptFile.userCancelled === true) throw 0; let savePath = pgptFile.path; if (savePath.slice(savePath.lastIndexOf(".")) !== '.txt') savePath = savePath + ".txt"; sf.ui.proTools.menuClick({ menuPath: ["File", "Export", "Session Info as Text..."], }); //shorten the UI element names let exportSessionTextWin = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportSessionTextWin.elementWaitFor({}), `Export Session window did not appear`; //this function sets the checkbox options setProToolsTextExportCheckboxes(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Selected Tracks Only').first.elementClick(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Combine Crossfaded Clips').first.elementClick(); exportSessionTextWin.buttons.whoseTitle.startsWith('OK').first.elementClick(); let saveWin = sf.ui.proTools.windows.whoseTitle.is('Save').first; saveWin.elementWaitFor(); //open the text path bar sf.keyboard.type({ text: "/", }); //wait for window animation to finish sf.wait({ intervalMs: 250 }); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor({}, `Pro Tools file path bar was not found`); saveWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementSetTextFieldWithAreaValue({ value: savePath, }, `could not enter savePath in filePath bar`); //OK buttons saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor({}, `GO button did not appear`); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementClick({}, `unable to click Go Button`); saveWin.buttons.whoseTitle.is("Save").first.elementWaitFor({}, `save button did not appear`); saveWin.buttons.whoseTitle.is("Save").first.elementClick({}, `could not click save button`); sf.wait({ intervalMs: 250 }) //if a file already exists and replace was chosen buy the user, this will automatically click 'replace' let replaceButton = sf.ui.proTools.windows.whoseTitle.is("Save").first.sheets.first.buttons.whoseTitle.is("Replace").first; if (replaceButton.exists) replaceButton.elementClick({}, `unable to click replaceButton`); /* Beginning of PGPT Part of script */ let pgptSession = sf.ui.app("PGPTSession"); const pgptPath = '/Applications/PGPTSession.app'; if (!pgptSession.exists) { sf.app.launch({ path: pgptPath }, `could not launch PGPTSession`) } //wait for main window while (!pgptSession.mainWindow.exists) sf.wait({ intervalMs: 100 }); pgptSession.appActivateMainWindow({}, `could not activate PGPT Main window`); //open button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 45 }, }, `could not click PGPT Open Button`); //wait for file open dialog box to appear pgptSession.windows.whoseTitle.is("Open").first.elementWaitFor({}, 'PGPT Open window did not appear'); //open the text path bar sf.keyboard.type({ text: "/", }); //wait for the text box to appear pgptSession.windows.whoseTitle.is("Open").first.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor(); pgptSession.windows.whoseTitle.is("Open").first.sheets.first.comboBoxes.whoseValue.is("/").first.elementSetTextFieldWithAreaValue({ value: savePath, }); //click GO and Open buttons pgptSession.windows.whoseTitle.is("Open").first.sheets.first.buttons.whoseTitle.is("Go").first.elementClick(); pgptSession.windows.whoseTitle.is("Open").first.buttons.whoseTitle.is("Open").first.elementClick(); sf.keyboard.press({ keys: 'return' }); //Set the export option checkboxes setPGPTExportOptions(); } main();
Dav3 @D_av_3
LOG
Line 125
"( saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor({},Pro Tools file path bar was not found
);) "element was not found or removed after waiting 2000ms
Dustin Harris @Dustin_Harris
ok I've edited the above script to break at the line you mentioned. if you could screen grab what windows are open in pro tools at that point and post it, that would be helpful :)
Thanks!
Dav3 @D_av_3
Here is it
i was thinking ... if i use osx in english could it help?Dustin Harris @Dustin_Harris
OK that is interesting. The script is supposed to open the menu path bar by simulating a "/" keypress, but on your system it simulates a "-" instead. Are you using a non-US keyboard layout maybe? (@chrscheuer or @Kitch is my thinking correct here?)
Dav3 @D_av_3
Yessss... I’m italian....so QWERTY keyboard
I mean ... I'm putting you in a bit of trouble here, huh? ah aha aha haChristian Scheuer @chrscheuer2021-05-11 14:50:09.870Z
Yes, this is wrong:
//open the text path bar sf.keyboard.press({ keys: "slash", });
It should be:
//open the text path bar sf.keyboard.type({ text: "/", });
Dav3 @D_av_3
OK____I replaced all "SLASH" with "/" (Guns'n'Roses fans forgive me aha ah ahaaa)...
there is one thing I don't understand .. as soon as I launch the script this window opens___then I click on SAVE and this other window opens ...
is that correct ?? ... after which nothing happens. It seems that the PGPT part of the script does not start.
Dustin Harris @Dustin_Harris
it should automatically hit save in the second window, so let me look into THAT problem now :)
- In reply toD_av_3⬆:
Dustin Harris @Dustin_Harris
I've edited the script above again. Let me know what happens now :)
Dav3 @D_av_3
Cool!
seems to work perfectly now !!
so what was the problem?Dustin Harris @Dustin_Harris
I needed to make the script wait for the save button to appear :)
I added this line:saveWin.buttons.whoseTitle.is("Save").first.elementWaitFor({}, `save button did not appear`);
Dav3 @D_av_3
Guys. Thanks so much.
- In reply toDustin_Harris⬆:
Dav3 @D_av_3
Hi Dustin. I'm trying to add some features to your super script.
- Save the session with a suffix
- Select Tracks called music and select all the regions they contain fade and crossfade all regions.
points one and two work .. even three but when this window comes out,
everything freezes ...
can I still take advantage of your genius to overcome this obstacle?
Here the script
sf.ui.proTools.appActivate(); sf.ui.proTools.invalidate(); //Save Session As / Rename var sessionPath = sf.ui.proTools.mainWindow.sessionPath; var sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join('.'); var newname = sessionName + "_d@ve_CueSheet"; sf.ui.proTools.menuClick({ menuPath: ["File","Save As..."], }); var saveWin = sf.ui.proTools.windows.whoseTitle.is('Save').first; saveWin.elementWaitFor({ failIfNotFound: true, }); saveWin.textFields.first.elementSetTextFieldWithAreaValue({ value: newname, }); saveWin.buttons.whoseTitle.is('Save').first.elementClick(); saveWin.elementWaitFor({ waitType: "Disappear", timeout: -1, }); //Select Music tracks and ungroup clips sf.ui.proTools.menuClick({menuPath: ["Window","Edit"]}); // UI bug - active edit window sf.ui.proTools.trackSelectByName({ deselectOthers: true, names: ['Music 1','Music 2','Music 3']}); sf.ui.proTools.menuClick({menuPath: ["Edit","Select All"]}); //Unlock Clips sf.ui.proTools.menuClick({menuPath: ["Window","Edit"]}); //FADES function addFadesToAllClipsOnSelectedTracks() { sf.ui.proTools.menuClick({ menuPath: ["Options", "Link Track and Edit Selection"], targetValue: 'Enable' }); sf.ui.proTools.menuClick({ menuPath: ["Edit", "Select All"], }); sf.ui.proTools.menuClick({ menuPath: ["Edit", "Fades", 'Create...'], }); const win = sf.ui.proTools.windows.whoseTitle.is("Batch Fades").first; win.elementWaitFor(); win.buttons.whoseTitle.is("OK").first.elementClick(); win.elementWaitFor({ waitType: "Disappear", }); } addFadesToAllClipsOnSelectedTracks(); function ensureGridIsOneFrame() { var nudgeValueField = sf.ui.proTools.mainWindow.gridNudgeCluster.nudgeControls.nudgeValue.invalidate(); var nudgeValue = (nudgeValueField.value.value + '').trim(); if (nudgeValue.indexOf('+') >= 0) { //Feet+Frames if (nudgeValue != '0+01.00') { nudgeValueField.elementClick(); sf.keyboard.press({ keys: '0, right, 1, right, 0, enter' }); } } else { //Assume timecode if (nudgeValue != '00:00:00:01.00') { sf.ui.proTools.nudgeSet({ value: '00:00:00:01.00' }); } } } function extendSelectionOnEitherSide(frameCount) { ensureGridIsOneFrame(); //You can remove this if it doesn't work for you; optimized for PT2019+ sf.keyboard.press({ keys: 'alt+shift+numpad minus', repetitions: frameCount }); sf.keyboard.press({ keys: 'cmd+shift+numpad plus', repetitions: frameCount }); } function trim() { sf.ui.proTools.getMenuItem('Edit', 'Trim Clip', 'To Selection').elementClick(); } function createFade(presetNum) { sf.ui.proTools.getMenuItem('Edit', 'Fades', 'Create...').elementClick(); var win = sf.ui.proTools.windows.whoseTitle.is('Batch Fades').first.elementWaitFor({ timeout: 500 }, 'Could not find Batch Fades window').element; win.buttons.whoseTitle.is('Fade Preset Toggle').allItems[presetNum - 1].elementClick({}, 'Could not click preset button'); win.buttons.whoseTitle.is('OK').first.elementClick(); } sf.ui.proTools.appActivateMainWindow(); extendSelectionOnEitherSide(2); //Change to 5 for 5 frames trim(); createFade(1); //Change to a different number for a different Batch Fades preset. function setProToolsTextExportCheckboxes() { //Export Session As Text checkbox options const checkBoxes = [ { Title: "Include File List", State: "Disable" }, { Title: "Include Clip List", State: "Disable" }, { Title: "Include Markers", State: "Disable" }, { Title: "Include Plug-In List", State: "Disable" }, { Title: "Include Track EDL's", State: "Enable" }, ]; function setCheckboxState(item) { try { let exportTextOptions = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportTextOptions.checkBoxes.whoseTitle.is(item.Title).first.checkboxSet({ targetValue: (item.State), }); } catch (err) { throw `error in setCheckbox(): ${err}` } } checkBoxes.forEach(setCheckboxState); } function setPGPTExportOptions() { try { let pgptSession = sf.ui.app("PGPTSession"); let exportOptionsCheckboxes = [ { "x": 42, "y": 145 }, //Export ADR format text files { "x": 42, "y": 169 }, //Export Master and Summary Excel files { "x": 42, "y": 193 }, //Export Master and Summary PDF files { "x": 42, "y": 220 }, //Export PDF Actor Files { "x": 42, "y": 243 }, //Export PDF Engineer Files { "x": 42, "y": 257 }, //Export Detailed Files { "x": 42, "y": 290 }, //Export MIDI Files { "x": 42, "y": 355 }, //Remove common suffixes { "x": 375, "y": 146 }, //Export Music format text files { "x": 375, "y": 170 }, //Export Music Excel file { "x": 375, "y": 320 }, //Show Scenes in detailed PDFs ]; //Export Options Button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 104 }, }, `could not click Export Options Button in PGPT`); sf.wait({ intervalMs: 250 }); function setPGPTCheckboxState(item) { let exportOptionsWin = sf.ui.app("PGPTSession").windows.whoseTitle.is("Export Options").first; try { exportOptionsWin.mouseClickElement({ relativePosition: item, }); } catch (err) { throw `error in setCheckbox(): ${err}` } } let consent = confirm(`Do the checkboxes need to be set?\n\nClick 'Yes' to change them or 'No' to leave them`) if (consent) exportOptionsCheckboxes.forEach(setPGPTCheckboxState); pgptSession.windows.whoseTitle.is("Export Options").first.mouseClickElement({ relativePosition: { "x": 652, "y": 501 }, }); } catch (err) { throw `error in setPGPTExportOptions: ${err}` } } function main() { sf.ui.proTools.appActivateMainWindow(); let sessionRootFolder = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/'); let bouncedFilesFolder = sessionRootFolder + '/Bounced Files/' let defaultSaveName = sessionRootFolder.split('/').slice(-1)[0] + ".txt" let pgptFile = sf.interaction.selectSaveFileName({ prompt: "Please Enter Name For EdiCue Text Document", defaultLocation: bouncedFilesFolder, defaultName: defaultSaveName, onCancel: "Continue", onError: "ThrowError" }); if (pgptFile.userCancelled === true) throw 0; let savePath = pgptFile.path; if (savePath.slice(savePath.lastIndexOf(".")) !== '.txt') savePath = savePath + ".txt"; sf.ui.proTools.menuClick({ menuPath: ["File", "Export", "Session Info as Text..."], }); //shorten the UI element names let exportSessionTextWin = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportSessionTextWin.elementWaitFor({}), `Export Session window did not appear`; //this function sets the checkbox options setProToolsTextExportCheckboxes(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Selected Tracks Only').first.elementClick(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Combine Crossfaded Clips').first.elementClick(); exportSessionTextWin.buttons.whoseTitle.startsWith('OK').first.elementClick(); let saveWin = sf.ui.proTools.windows.whoseTitle.is('Save').first; saveWin.elementWaitFor(); //open the text path bar sf.keyboard.type({ text: "/", }); //wait for window animation to finish sf.wait({ intervalMs: 250 }); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor({}, `Pro Tools file path bar was not found`); saveWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementSetTextFieldWithAreaValue({ value: savePath, }, `could not enter savePath in filePath bar`); //OK buttons saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor({}, `GO button did not appear`); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementClick({}, `unable to click Go Button`); saveWin.buttons.whoseTitle.is("Save").first.elementWaitFor({}, `save button did not appear`); saveWin.buttons.whoseTitle.is("Save").first.elementClick({}, `could not click save button`); sf.wait({ intervalMs: 250 }) //if a file already exists and replace was chosen buy the user, this will automatically click 'replace' let replaceButton = sf.ui.proTools.windows.whoseTitle.is("Save").first.sheets.first.buttons.whoseTitle.is("Replace").first; if (replaceButton.exists) replaceButton.elementClick({}, `unable to click replaceButton`); /* Beginning of PGPT Part of script */ let pgptSession = sf.ui.app("PGPTSession"); const pgptPath = '/Applications/PGPTSession.app'; if (!pgptSession.exists) { sf.app.launch({ path: pgptPath }, `could not launch PGPTSession`) } //wait for main window while (!pgptSession.mainWindow.exists) sf.wait({ intervalMs: 100 }); pgptSession.appActivateMainWindow({}, `could not activate PGPT Main window`); //open button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 45 }, }, `could not click PGPT Open Button`); //wait for file open dialog box to appear pgptSession.windows.whoseTitle.is("Open").first.elementWaitFor({}, 'PGPT Open window did not appear'); //open the text path bar sf.keyboard.type({ text: "/", }); //wait for the text box to appear pgptSession.windows.whoseTitle.is("Open").first.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor(); pgptSession.windows.whoseTitle.is("Open").first.sheets.first.comboBoxes.whoseValue.is("/").first.elementSetTextFieldWithAreaValue({ value: savePath, }); //click GO and Open buttons pgptSession.windows.whoseTitle.is("Open").first.sheets.first.buttons.whoseTitle.is("Go").first.elementClick(); pgptSession.windows.whoseTitle.is("Open").first.buttons.whoseTitle.is("Open").first.elementClick(); sf.keyboard.press({ keys: 'return' }); //Set the export option checkboxes setPGPTExportOptions(); } main();
Dustin Harris @Dustin_Harris
Hi Davide. Try this. Note: I'm having a lot of issues with Sound Flow not finding elements when using elementWaitFor() (I'm in Big Sur )so this might be buggy, or it might be smooth.
Let me know how it goes
EDIT: Refined the script a bit
function setProToolsTextExportCheckboxes() { //Export Session As Text checkbox options const checkBoxes = [ { title: "Include File List", state: "Disable" }, { title: "Include Clip List", state: "Disable" }, { title: "Include Markers", state: "Disable" }, { title: "Include Plug-In List", state: "Disable" }, { title: "Include Track EDL's", state: "Enable" }, ]; try { checkBoxes.forEach( /** * @param {object} cb * @type {{ * group: string; * title: string; * state: "Enable" | "Disable" | "Toggle"; * }} */ cb => { let exportTextOptions = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportTextOptions.checkBoxes.whoseTitle.is(cb.title).first.checkboxSet({ targetValue: (cb.state), }); }); } catch (err) { throw `error in setCheckbox(): ${err}` } } function setBatchFadeOptions() { const checkBoxes = [ { group: "Fade In Operation", title: "Create new fade ins", state: "Disable" }, { group: "Fade In Operation", title: "Adjust existing fade in shape & slope", state: "Disable" }, { group: "Fade In Operation", title: "Adjust existing fade in length", state: "Disable" }, { group: "Crossfade Operation", title: "Create new crossfades", state: "Enable" }, { group: "Crossfade Operation", title: "Adjust existing crossfade shape & slope", state: "Disable" }, { group: "Crossfade Operation", title: "Adjust existing crossfade length", state: "Disable" }, { group: "Fade Out Operation", title: "Create new fade outs", state: "Disable" }, { group: "Fade Out Operation", title: "Adjust existing fade out shape & slope", state: "Disable" }, { group: "Fade Out Operation", title: "Adjust existing fade out length", state: "Disable" }, ]; try { sf.ui.proTools.menuClick({ menuPath: ["Edit", "Fades", 'Create...'], }, `Menu item "Edit", "Fades", 'Create...' not accessible or enabled`); const batchFadesWin = sf.ui.proTools.windows.whoseTitle.is("Batch Fades").first; batchFadesWin.elementWaitFor(); checkBoxes.forEach( /** * @param {object} cb * @type {{ * group: string; * title: string; * state: "Enable" | "Disable" | "Toggle"; * }} */ cb => { batchFadesWin.groups.whoseTitle.is(cb.group).first.checkBoxes.whoseTitle.is(cb.title).first.checkboxSet({ targetValue: cb.state, }); }) batchFadesWin.buttons.whoseTitle.is("OK").first.elementClick(); sf.wait({ intervalMs: 250 }) let adjustBounds = sf.ui.proTools.focusedWindow.buttons.whoseTitle.is("Adjust Bounds").first; if (adjustBounds.exists) adjustBounds.elementClick(); // batchFadesWin.elementWaitFor({ // waitType: "Disappear", // }); } catch (err) { throw `error in setBatchFadeOptions: ${err}` } } function setPGPTExportOptions() { try { let pgptSession = sf.ui.app("PGPTSession"); let exportOptionsCheckboxes = [ { "x": 42, "y": 145 }, //Export ADR format text files { "x": 42, "y": 169 }, //Export Master and Summary Excel files { "x": 42, "y": 193 }, //Export Master and Summary PDF files { "x": 42, "y": 220 }, //Export PDF Actor Files { "x": 42, "y": 243 }, //Export PDF Engineer Files { "x": 42, "y": 257 }, //Export Detailed Files { "x": 42, "y": 290 }, //Export MIDI Files { "x": 42, "y": 355 }, //Remove common suffixes { "x": 375, "y": 146 }, //Export Music format text files { "x": 375, "y": 170 }, //Export Music Excel file { "x": 375, "y": 320 }, //Show Scenes in detailed PDFs ]; //Export Options Button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 104 }, }, `could not click Export Options Button in PGPT`); sf.wait({ intervalMs: 250 }); let consent = confirm(`Do the checkboxes need to be set?\n\nClick 'OK' to change them or 'Cancel' to leave them`) if (consent) { try { exportOptionsCheckboxes.forEach(mousePosition => { let exportOptionsWin = sf.ui.app("PGPTSession").windows.whoseTitle.is("Export Options").first; exportOptionsWin.mouseClickElement({ relativePosition: mousePosition, }); }); } catch (err) { throw `error in exportOptionsCheckboxes.forEach: ${err}` } } pgptSession.windows.whoseTitle.is("Export Options").first.mouseClickElement({ relativePosition: { "x": 652, "y": 501 }, }); } catch (err) { throw `error in setPGPTExportOptions: ${err}` } } function main() { sf.ui.proTools.appActivateMainWindow(); let sessionRootFolder = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/'); let bouncedFilesFolder = sessionRootFolder + '/Bounced Files/' let defaultSaveName = sessionRootFolder.split('/').slice(-1)[0] + "_d@ve_CueSheet.txt" let pgptFile = sf.interaction.selectSaveFileName({ prompt: "Please Enter Name For EdiCue Text Document", defaultLocation: bouncedFilesFolder, defaultName: defaultSaveName, onCancel: "Continue", onError: "ThrowError" }); if (pgptFile.userCancelled === true) throw 0; let savePath = pgptFile.path; if (savePath.slice(savePath.lastIndexOf(".")) !== '.txt') savePath = savePath + ".txt"; sf.file.delete({path: savePath, onError: "Continue"}) let musicTrackNames = sf.ui.proTools.trackGetAllTracks().names.filter(name => name.match(/Music \d+/)); sf.ui.proTools.trackSelectByName({ names: musicTrackNames, deselectOthers: true }); sf.ui.proTools.menuClick({ menuPath: ["Edit", "Select All"], }); setBatchFadeOptions(); sf.ui.proTools.menuClick({ menuPath: ["File", "Export", "Session Info as Text..."], }); //shorten the UI element names let exportSessionTextWin = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportSessionTextWin.elementWaitFor({}), `Export Session window did not appear`; //this function sets the checkbox options setProToolsTextExportCheckboxes(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Selected Tracks Only').first.elementClick(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Combine Crossfaded Clips').first.elementClick(); exportSessionTextWin.buttons.whoseTitle.startsWith('OK').first.elementClick(); let saveWin = sf.ui.proTools.windows.whoseTitle.is('Save').first; saveWin.elementWaitFor(); //open the text path bar sf.keyboard.type({ text: "/", }); //wait for window animation to finish sf.wait({ intervalMs: 100 }); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor({}, `Pro Tools file path bar was not found`); saveWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementSetTextFieldWithAreaValue({ value: savePath, }, `could not enter savePath in filePath bar`); //OK buttons saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor({}, `GO button did not appear`); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementClick({}, `unable to click Go Button`); sf.ui.proTools.windows.whoseTitle.is("Save").first.buttons.whoseTitle.is("Save").first.elementWaitFor({}, `save button did not appear`); sf.ui.proTools.windows.whoseTitle.is("Save").first.buttons.whoseTitle.is("Save").first.elementClick(); //saveWin.buttons.whoseTitle.is("Save").first.elementClick({}, `could not click save button`); sf.wait({ intervalMs: 250 }) /* Beginning of PGPT Part of script */ let pgptSession = sf.ui.app("PGPTSession"); const pgptPath = '/Applications/PGPTSession.app'; if (!pgptSession.exists) { sf.app.launch({ path: pgptPath }, `could not launch PGPTSession`) } //wait for main window while (!pgptSession.mainWindow.exists) sf.wait({ intervalMs: 100 }); pgptSession.appActivateMainWindow({}, `could not activate PGPT Main window`); //open button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 45 }, }, `could not click PGPT Open Button`); //wait for file open dialog box to appear pgptSession.windows.whoseTitle.is("Open").first.elementWaitFor({}, 'PGPT Open window did not appear'); sf.ui.app("PGPTSession").windows.whoseTitle.is("Open").first.buttons.whoseTitle.is("Cancel").first.elementWaitFor(); //open the text path bar sf.keyboard.type({ text: "/", }); sf.wait({intervalMs: 500}); pgptSession.windows.whoseTitle.is("Open").first.sheets.first.comboBoxes.whoseValue.is("/").first.elementSetTextFieldWithAreaValue({ value: savePath, }); //click GO and Open buttons pgptSession.windows.whoseTitle.is("Open").first.sheets.first.buttons.whoseTitle.is("Go").first.elementClick(); pgptSession.windows.whoseTitle.is("Open").first.buttons.whoseTitle.is("Open").first.elementClick(); sf.keyboard.press({ keys: 'return' }); //Set the export option checkboxes setPGPTExportOptions(); sf.ui.app("PGPTSession").appActivateMainWindow(); //select all button sf.ui.app("PGPTSession").mainWindow.mouseClickElement({ relativePosition: { "x": 617, "y": 712 }, }); //export button sf.ui.app("PGPTSession").mainWindow.mouseClickElement({ relativePosition: { "x": 1009, "y": 80 }, }); } main();
Dav3 @D_av_3
Hi Guys, some time ago you helped me to write this code (which saves me a lot of time) but for a few days I have been having problems. The script crashes at line 77
and I can't figure out how to fix it. Could you give me a hand?
Below you will find the LOG code and the Code.LOG
21.10.2022 01:55:40.18 [Backend]: Logging error in action (01) ClickButtonAction: ClickButtonAction requires UIElement
21.10.2022 01:55:40.18 [Backend]: !! Command Error: 2024 pgpt [user:cl3vglqx70001s410znmoexl7:cl9hgjsyk000h67109y39pm50]:
Could not click preset button (2024 pgpt: Line 77)
ClickButtonAction requires UIElement
21.10.2022 01:55:40.18 [Backend]: << Command: 2024 pgpt [user:cl3vglqx70001s410znmoexl7:cl9hgjsyk000h67109y39pm50]sf.ui.proTools.invalidate(); //Select Music tracks and ungroup clips sf.ui.proTools.menuClick({menuPath: ["Window","Edit"]}); // UI bug - active edit window sf.ui.proTools.trackSelectByName({ deselectOthers: true, names: ['Music 1','Music 2','Music 3','Music 4','Music 5']}); sf.ui.proTools.menuClick({menuPath: ["Edit","Select All"]}); //Unlock Clips sf.ui.proTools.menuClick({menuPath: ["Window","Edit"]}); //FADES function addFadesToAllClipsOnSelectedTracks() { sf.ui.proTools.menuClick({ menuPath: ["Options", "Link Track and Edit Selection"], targetValue: 'Enable' }); sf.ui.proTools.menuClick({ menuPath: ["Edit", "Select All"], }); sf.ui.proTools.menuClick({ menuPath: ["Edit", "Fades", 'Create...'], }); const win = sf.ui.proTools.windows.whoseTitle.is("Batch Fades").first; win.elementWaitFor(); win.buttons.whoseTitle.is("OK").first.elementClick(); win.elementWaitFor({ waitType: "Disappear", }); } addFadesToAllClipsOnSelectedTracks(); function ensureGridIsOneFrame() { var nudgeValueField = sf.ui.proTools.mainWindow.gridNudgeCluster.nudgeControls.nudgeValue.invalidate(); var nudgeValue = (nudgeValueField.value.value + '').trim(); if (nudgeValue.indexOf('+') >= 0) { //Feet+Frames if (nudgeValue != '0+01.00') { nudgeValueField.elementClick(); sf.keyboard.press({ keys: '0, right, 1, right, 0, enter' }); } } else { //Assume timecode if (nudgeValue != '00:00:00:01.00') { sf.ui.proTools.nudgeSet({ value: '00:00:00:01.00' }); } } } function extendSelectionOnEitherSide(frameCount) { ensureGridIsOneFrame(); //You can remove this if it doesn't work for you; optimized for PT2019+ sf.keyboard.press({ keys: 'alt+shift+numpad minus', repetitions: frameCount }); sf.keyboard.press({ keys: 'cmd+shift+numpad plus', repetitions: frameCount }); } function trim() { sf.ui.proTools.getMenuItem('Edit', 'Trim Clip', 'To Selection').elementClick(); } function createFade(presetNum) { sf.ui.proTools.getMenuItem('Edit', 'Fades', 'Create...').elementClick(); var win = sf.ui.proTools.windows.whoseTitle.is('Batch Fades').first.elementWaitFor({ timeout: 500 }, 'Could not find Batch Fades window').element; win.buttons.whoseTitle.is('Fade Preset Toggle').allItems[presetNum - 3].elementClick({}, 'Could not click preset button'); win.buttons.whoseTitle.is('OK').first.elementClick(); } sf.ui.proTools.appActivateMainWindow(); extendSelectionOnEitherSide(2); //Change to 5 for 5 frames trim(); createFade(1); //Change to a different number for a different Batch Fades preset. function setProToolsTextExportCheckboxes() { //Export Session As Text checkbox options const checkBoxes = [ { Title: "Include File List", State: "Disable" }, { Title: "Include Clip List", State: "Disable" }, { Title: "Include Markers", State: "Disable" }, { Title: "Include Plug-In List", State: "Disable" }, { Title: "Include Track EDL's", State: "Enable" }, ]; function setCheckboxState(item) { try { let exportTextOptions = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportTextOptions.checkBoxes.whoseTitle.is(item.Title).first.checkboxSet({ targetValue: (item.State), }); } catch (err) { throw `error in setCheckbox(): ${err}` } } checkBoxes.forEach(setCheckboxState); } function setPGPTExportOptions() { try { let pgptSession = sf.ui.app("PGPTSession"); let exportOptionsCheckboxes = [ { "x": 42, "y": 145 }, //Export ADR format text files { "x": 42, "y": 169 }, //Export Master and Summary Excel files { "x": 42, "y": 193 }, //Export Master and Summary PDF files { "x": 42, "y": 220 }, //Export PDF Actor Files { "x": 42, "y": 243 }, //Export PDF Engineer Files { "x": 42, "y": 257 }, //Export Detailed Files { "x": 42, "y": 290 }, //Export MIDI Files { "x": 42, "y": 355 }, //Remove common suffixes { "x": 375, "y": 146 }, //Export Music format text files { "x": 375, "y": 170 }, //Export Music Excel file { "x": 375, "y": 320 }, //Show Scenes in detailed PDFs ]; //Export Options Button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 104 }, }, `could not click Export Options Button in PGPT`); sf.wait({ intervalMs: 250 }); function setPGPTCheckboxState(item) { let exportOptionsWin = sf.ui.app("PGPTSession").windows.whoseTitle.is("Export Options").first; try { exportOptionsWin.mouseClickElement({ relativePosition: item, }); } catch (err) { throw `error in setCheckbox(): ${err}` } } let consent = confirm(`Do the checkboxes need to be set?\n\nClick 'Yes' to change them or 'No' to leave them`) if (consent) exportOptionsCheckboxes.forEach(setPGPTCheckboxState); pgptSession.windows.whoseTitle.is("Export Options").first.mouseClickElement({ relativePosition: { "x": 652, "y": 501 }, }); } catch (err) { throw `error in setPGPTExportOptions: ${err}` } } function main() { sf.ui.proTools.appActivateMainWindow(); let sessionRootFolder = sf.ui.proTools.mainWindow.sessionPath.split('/').slice(0, -1).join('/'); let bouncedFilesFolder = sessionRootFolder + '/Bounced Files/' let defaultSaveName = sessionRootFolder.split('/').slice(-1)[0] + ".txt" let pgptFile = sf.interaction.selectSaveFileName({ prompt: "Please Enter Name For EdiCue Text Document", defaultLocation: bouncedFilesFolder, defaultName: defaultSaveName, onCancel: "Continue", onError: "ThrowError" }); if (pgptFile.userCancelled === true) throw 0; let savePath = pgptFile.path; if (savePath.slice(savePath.lastIndexOf(".")) !== '.txt') savePath = savePath + ".txt"; sf.ui.proTools.menuClick({ menuPath: ["File", "Export", "Session Info as Text..."], }); //shorten the UI element names let exportSessionTextWin = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportSessionTextWin.elementWaitFor({}), `Export Session window did not appear`; //this function sets the checkbox options setProToolsTextExportCheckboxes(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Selected Tracks Only').first.elementClick(); exportSessionTextWin.radioButtons.whoseTitle.startsWith('Combine Crossfaded Clips').first.elementClick(); exportSessionTextWin.buttons.whoseTitle.startsWith('OK').first.elementClick(); let saveWin = sf.ui.proTools.windows.whoseTitle.is('Save').first; saveWin.elementWaitFor(); //open the text path bar sf.keyboard.type({ text: "/", }); //wait for window animation to finish sf.wait({ intervalMs: 250 }); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor({}, `Pro Tools file path bar was not found`); saveWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementSetTextFieldWithAreaValue({ value: savePath, }, `could not enter savePath in filePath bar`); //OK buttons saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor({}, `GO button did not appear`); saveWin.sheets.first.buttons.whoseTitle.is("Go").first.elementClick({}, `unable to click Go Button`); saveWin.buttons.whoseTitle.is("Save").first.elementWaitFor({}, `save button did not appear`); saveWin.buttons.whoseTitle.is("Save").first.elementClick({}, `could not click save button`); sf.wait({ intervalMs: 250 }) //if a file already exists and replace was chosen buy the user, this will automatically click 'replace' let replaceButton = sf.ui.proTools.windows.whoseTitle.is("Save").first.sheets.first.buttons.whoseTitle.is("Replace").first; if (replaceButton.exists) replaceButton.elementClick({}, `unable to click replaceButton`); /* Beginning of PGPT Part of script */ let pgptSession = sf.ui.app("PGPTSession"); const pgptPath = '/Applications/PGPTSession.app'; if (!pgptSession.exists) { sf.app.launch({ path: pgptPath }, `could not launch PGPTSession`) } //wait for main window while (!pgptSession.mainWindow.exists) sf.wait({ intervalMs: 100 }); pgptSession.appActivateMainWindow({}, `could not activate PGPT Main window`); //open button pgptSession.mainWindow.mouseClickElement({ relativePosition: { "x": 1030, "y": 45 }, }, `could not click PGPT Open Button`); //wait for file open dialog box to appear pgptSession.windows.whoseTitle.is("Open").first.elementWaitFor({}, 'PGPT Open window did not appear'); //open the text path bar sf.keyboard.type({ text: "/", }); //wait for the text box to appear pgptSession.windows.whoseTitle.is("Open").first.sheets.first.buttons.whoseTitle.is("Go").first.elementWaitFor(); pgptSession.windows.whoseTitle.is("Open").first.sheets.first.comboBoxes.whoseValue.is("/").first.elementSetTextFieldWithAreaValue({ value: savePath, }); //click GO and Open buttons pgptSession.windows.whoseTitle.is("Open").first.sheets.first.buttons.whoseTitle.is("Go").first.elementClick(); pgptSession.windows.whoseTitle.is("Open").first.buttons.whoseTitle.is("Open").first.elementClick(); sf.keyboard.press({ keys: 'return' }); //Set the export option checkboxes setPGPTExportOptions(); } main();
- In reply tochrscheuer⬆:
Christian Scheuer @chrscheuer2021-05-11 17:24:54.987Z
Just a quick tip on why this is.
sf.keyboard.press with 'slash' means: Simulate a press on the key located where "slash" is on a US based keyboard. This mechanism is good for pressing a key based on its location, which is why it's usually used for special keys. For example the key located at "semicolon" is "Æ" in Danish, but they mean the same thing in Pro Tools. So for stuff like that we use sf.keyboard.press because it will press the key in that location, no matter what's printed on the key / what language is used.
sf.keyboard.type converts what you've written (here "/") using the actual virtual keyboard layout (for example Danish or Italian) and then figures out where on the localized keyboard the key is that has that printed value. For me, on a Danish keyboard, I can make a forward slash by holding down shift and hitting the 7 key. On an Italian keyboard it might be another combination. So in this case, because you want Finder to receive the textual value of a forward slash, you use sf.keyboard.type with the text "/", and SF will figure out which keys to press.
Conversely, as in the first example, when pressing semicolon / Æ to go down one track in PT, you should use sf.keyboard.press, because we're interested in the "location" of the key, not the contents.
Dav3 @D_av_3
thank you for the explanation ...
"SoundCity" the documentary comes to my
mind .. that scene in which Rupert Neve himself tries to explain to Dave Grohl how a preamp works ... ....and Dave doesn't seem to understand a damn thing ...here in this moment I feel like Dave ah ah ah ah
- In reply toD_av_3⬆:Dustin Harris @Dustin_Harris
This is a very useful idea! I won’t have time until the weekend but I might try writing a script for this!
Dav3 @D_av_3
Great !!! Thanks a lot ... in advance
- In reply toD_av_3⬆:Dav3 @D_av_3
IT WORKS!!!
It works damn well I would say !!
I have tested it about fifteen times ...
You were really Super !!!A couple of times it gave me this error ..
"Element was not found or removed after waiting 2000ms (Line 118)"
You were really Super !!!Dustin Harris @Dustin_Harris
Great! I know it isn’t perfect just yet, I’ll see if I can improve it :)
- PIn reply toD_av_3⬆:Pete Gates @Pete_Gates
Hi,
I'm Pete, the author of PGPTSession. Just came across this by accident (I wasn't Googling myself!!).Few points I'd like to make...
There's no need to do a Session Text import as my software will read PTX files directly (you can import Session Text files perfectly well if you really want/already have them, but there's no need otherwise).
It's still an active, in development application, no idea how anyone thought otherwise. There will be periods when I'm quiet as I'm a working dialogue editor/re-recording mixer. I'd never just drop the application, if circumstances dictated, I'd hand the reins over to someone.
Finally, unfortunately the UI elements aren't programmatically accessible as it's a custom drawn UI. Due to requests from Randall, I've provided shortcut keys for certain common functions though far from all.
Cheers,
PeteDustin Harris @Dustin_Harris
My apologies Pete, I don't recall where I read that the app might not be updated further, but I'll absolutely edit my above post :)
- PPete Gates @Pete_Gates
No worries!
- In reply toPete_Gates⬆:PPete Gates @Pete_Gates
I'm investigating what I can do about making the UI elements scriptable btw. I wouldn't expect anything very soon though.
Dustin Harris @Dustin_Harris
Sweet, I won't have time to do anything with it anytime soon anyway 🤣
- In reply toPete_Gates⬆:
Dav3 @D_av_3
Hi Pete..First of all, thank you so much for your softwares. I have been using them for years and they are really super!. The need to export the text file from protools is that I can export the files linked by crossfade as a single region in this way the file for the music cue sheet is almost ready.
if, on the other hand, I read the protools file directly, I will see a list of all the cuts on each audio file.