Title
My checkerboard script no longer works after ProTools update.
What do you expect to happen when you run the script/macro?
"Could not click popup menu item - Line 5" error? This script used to run perfectly and now it doesn't. I'm not much of a coder, so I can't figure out why. Please help.
Are you seeing an error?
Could not click popup menu item - Line 5
What happens when you run this script?
The window "WAIT! Have you batch renamed all clips to start with 2 digit scene numbers? (3 means Yes)" Does come up, bus as soon as I press Okay, I get the error, and nothing else happens.
How were you running this script?
I used a Stream Deck button
How important is this issue to you?
5
Details
{
"inputExpected": "\"Could not click popup menu item - Line 5\" error? This script used to run perfectly and now it doesn't. I'm not much of a coder, so I can't figure out why. Please help.",
"inputIsError": true,
"inputError": "Could not click popup menu item - Line 5",
"inputWhatHappens": "The window \"WAIT! Have you batch renamed all clips to start with 2 digit scene numbers? (3 means Yes)\" Does come up, bus as soon as I press Okay, I get the error, and nothing else happens.",
"inputHowRun": {
"key": "-MpfwmPg-2Sb-HxHQAff",
"title": "I used a Stream Deck button"
},
"inputImportance": 5,
"inputTitle": "My checkerboard script no longer works after ProTools update."
}
Source
/**
* @param {array} path - path menu path
*/
function clipListEnableMenu(path) {
sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is('Clip List').first.popupMenuSelect({
menuPath: path, targetValue: "Enable",
});
sf.ui.proTools.appActivateMainWindow();
};
// Enable menus from "clipListMenuItems" that are not yet enabled
function clipListEnableSubmenu({ menu, subMenu }) {
sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Clip List"], targetValue: "Enable" });
clipListEnableMenu(["Clear Find"])
const menuCheckedInShowMenu = sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is('Clip List').first.popupMenuFetchAllItems().menuItems.filter(x =>
!x.element.isMenuChecked && // Not MenuChecked
x.path[0] == menu).map(x => x.path[1]) // submenu item name
sf.ui.proTools.appActivateMainWindow()
// Get unchecked from "clipListMenuItemsShow list and create path
let menuToCheck = menuCheckedInShowMenu.filter(x => subMenu.includes(x)).map(x => [menu, x])
menuToCheck.forEach(clipListEnableMenu)
}
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
function goToStartOfSession() {
sf.ui.proTools.transportEnsureCluster();
const trannsportCluster = sf.ui.proTools.mainWindow.transportViewCluster;
const transportButtons = trannsportCluster.groups.whoseTitle.is("Normal Transport Buttons").first;
transportButtons.buttons.whoseTitle.is("Return to Zero").first.elementClick();
}
function selectedClipCount() {
const clipsTable = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first;
const selectedClipCount = clipsTable.getElements("AXSelectedRows").allItems.length;
return selectedClipCount
}
function askNumberChar() {
const chNumber = sf.interaction.displayDialog({
prompt: "WAIT! Have you batch renamed all clips to start with 2 digit scene numbers? (3 means Yes)",
defaultAnswer: "3" // Default number of characters here
}).text.match(/\d+/g)
return +chNumber
}
function duplicateSelectedTrack({ duplicateActivePlaylist: targetValue }) {
sf.ui.proTools.trackDuplicateSelected({
duplicateActivePlaylist: targetValue,
duplicateAlternatePlaylists: false,
duplicateAutomation: false,
duplicateInserts: false,
duplicateSends: false,
duplicateGroupAssignments: false,
insertAfterLastSelectedTrack: true,
onError: "ThrowError"
});
}
function timeCounter() {
// get current counter
const mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue
let originalTimeCounter
/// Bars Beats .
if (mainCounter.includes("|")) originalTimeCounter = "Bars|Beats"
// Min Secs .
else if (mainCounter.includes(":") && mainCounter.includes(".")) originalTimeCounter = "Min:Secs"
// Time code
else if (mainCounter.split(":").length == 4) originalTimeCounter = "Timecode"
// Feet+Frames
else if (mainCounter.includes("+")) originalTimeCounter = "Feet+Frames"
// Samples.
else originalTimeCounter = "Samples"
return originalTimeCounter
}
function createNewTrack(newTrackName) {
sf.ui.proTools.menuClick({ menuPath: ["Track", "New..."] });
const newTrackWin = sf.ui.proTools.windows.whoseTitle.is("New Tracks")
newTrackWin.first.elementWaitFor({ waitType: "Appear" });
newTrackWin.first.textFields.allItems[1].elementSetTextFieldWithAreaValue({
value: newTrackName,
});
newTrackWin.first.buttons.whoseTitle.is("Create").first.elementClick();
newTrackWin.first.elementWaitFor({ waitType: "Disappear" });
}
function getClipNames(chNumber) {
let selectedClips = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first.children.whoseRole.is("AXRow").allItems.map(row =>
row.children[1].children.first.value.value).filter(c => c.startsWith('Selected. ')).map(l =>
l.split("\n")[1]).map(n => n
.replace(/["\"]/g, '').trim().slice(2, chNumber))// Number of Characters to Grab
return selectedClips
}
function findTrackNameAndPaste(selectedClipsNames, processTrack) {
const trackByName = sf.ui.proTools.trackGetByName({ name: selectedClipsNames })
if (trackByName.track == null) {
createNewTrack(selectedClipsNames)
sf.ui.proTools.invalidate()
sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] });
} else if (trackByName.track != null) {
trackByName.track.trackSelect()
// Reset menu
sf.keyboard.press({ keys: "left" });
const selectionHasClip = sf.ui.proTools.getMenuItem("Edit", "Trim Clip").isEnabled
if (selectionHasClip) {
// Reset menu
sf.keyboard.press({ keys: "left" });
duplicateSelectedTrack({ duplicateActivePlaylist: false })
sf.ui.proTools.invalidate()
sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] });
} else {
sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] });
}
}
sf.ui.proTools.trackSelectByName({ names: processTrack })
}
function mainProcess(processTrack, originaSelectionEndToString, numberOfCharacters, originalSelectionStart) {
goToStartOfSession()
// one tab and select first clip
sf.keyboard.press({ keys: "tab" });
sf.keyboard.press({ keys: "shift+tab" })
//let selectionInfo
let selectedClipsNames
let selectedArray = []
let selectionEnd
let halfCrossFadeDuration
while (true) {
let currentSelectionEndNumber = +sf.ui.proTools.selectionGet().selectionEnd.replace(/[ :\+\|.]/g, '')
selectedClipsNames = getClipNames(numberOfCharacters)
if (selectedClipsNames[0] != null) {
selectedArray.push(selectedClipsNames[0])
}
if (selectedArray.filter(onlyUnique).length <= 1) {
selectionEnd = sf.ui.proTools.selectionGetInSamples().selectionEnd
if (sf.ui.proTools.selectionGetInfo().hasCrossFade) {
halfCrossFadeDuration = +sf.ui.proTools.selectionGetInSamples().selectionLength / 2
selectionEnd = +sf.ui.proTools.selectionGetInSamples().selectionEnd - halfCrossFadeDuration
}
}
if (selectedArray.filter(onlyUnique).length <= 1 &&
!sf.ui.proTools.selectionGetInfo().hasEndFade &&
currentSelectionEndNumber < originaSelectionEndToString) {
sf.keyboard.press({ keys: "ctrl+tab" });
} else {
sf.ui.proTools.selectionSetInSamples({
selectionStart: +originalSelectionStart,
selectionEnd: +selectionEnd
})
sf.ui.proTools.menuClick({ menuPath: ["Edit", "Cut"] });
sf.ui.proTools.selectionSetInSamples({
selectionStart: +originalSelectionStart,
selectionEnd: +selectionEnd
})
findTrackNameAndPaste(selectedArray[0], processTrack)
break;
}
//sf.wait({ intervalMs: 1000 })
}
}
function main() {
// Reset menu
sf.keyboard.press({ keys: "left" });
duplicateSelectedTrack({ duplicateActivePlaylist: true })
let processTrack = sf.ui.proTools.selectedTrackNames
sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" })
while (true) {
sf.ui.proTools.invalidate()
sf.ui.proTools.menuClick({ menuPath: ["Edit", "Select All"] });
let originaSelectionEndToString = +sf.ui.proTools.selectionGet().selectionEnd.replace(/[ :\+\|.]/g, '')
let originalSelectionStart = sf.ui.proTools.selectionGet().selectionStart
let selectedClipsNames = getClipNames(numberOfCharacters)
if (selectedClipsNames.length >= 1) {
mainProcess(processTrack, originaSelectionEndToString, numberOfCharacters, originalSelectionStart)
} else {
// goToStartOfSession()
// Option to delete empy track in the end
sf.ui.proTools.menuClick({ menuPath: ["Track", "Delete"] });
break
}
}
}
///////// MAIN /////////
///////// MAIN /////////
sf.ui.proTools.appActivateMainWindow()
// Ask how many characters to grab
const numberOfCharacters = askNumberChar()
if (numberOfCharacters == 0) {
alert("Numbers only, minimum is 1.")
}
// List of menus to enable
clipListEnableSubmenu({
menu: "Show",
subMenu: [
"Audio",
"Groups",
"Auto-Created",
]
})
let originalTimeCOunter = timeCounter()
sf.ui.proTools.selectedTrackHeaders.slice().map(track => {
sf.ui.proTools.invalidate()
track.trackSelect();
track.trackScrollToView();
main()
});
sf.ui.proTools.mainCounterSetValue({ targetValue: originalTimeCOunter })
alert(`Done!`)
Links
User UID: 5LUE5yrcgqbHGGoE1faKvuJFCi33
Feedback Key: sffeedback:5LUE5yrcgqbHGGoE1faKvuJFCi33:-O2_UFzfvJBUXXf_gBdB
Feedback ZIP: QUo2TfdpgaDdOR9wTThqWe4UGXzA+hFXJVTp0goOEz53gREJufvQIHMKaRkpyi6Jor7lOxUWtXrfsXZrQJS9x4CB+3UXsWXjDdhBGhGZkUCTrF+yYCNnwBJPywFOtiMrOwlgeKHy0++XloBQa5nkUh67KoyKHR6qrShAuwWXu7BghjfIQ21pNPVctzMoMIB9os57r5pEZx05KfBkpkNeDKLXDwBn3T8G7roHzA9YKrxtZoyI76YEFQSCLO6bAgLZEH23ThiRZNwoIw8AxrxDuY/sWPyW56uDozcm52MRlci1CvDa+djc2rAYoFdnrpqWuCCm8ISI/HgUhd8qWUuYfDI/fEnejGsKJBgHKP/5RLc=
Dustin Harris @Dustin_HarrisHeyya @Christopher_Cleator try this version, I haven't tested it completely but my adjustments worked in isolation :)
/** * @param {string} name - path menu path */ function clipListEnableMenu(name) { //sf.ui.proTools.mainWindow.popupButtons.whoseTitle.is('Clip List').first.popupMenuSelect({ sf.ui.proTools.mainWindow.buttons.whoseTitle.is("Plate").first.popupMenuSelect({ menuPath: [name], targetValue: "Enable", }); sf.ui.proTools.appActivateMainWindow(); }; /** * @param {{menuItemNames: string[]}} args - input object */ function clipListEnableSubmenu({ menuItemNames }) { sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Clip List"], targetValue: "Enable" }); menuItemNames.forEach(name => clipListEnableMenu(name)) } function onlyUnique(value, index, self) { return self.indexOf(value) === index; } function goToStartOfSession() { sf.ui.proTools.transportEnsureCluster(); const trannsportCluster = sf.ui.proTools.mainWindow.transportViewCluster; const transportButtons = trannsportCluster.groups.whoseTitle.is("Normal Transport Buttons").first; transportButtons.buttons.whoseTitle.is("Return to Zero").first.elementClick(); } function selectedClipCount() { const clipsTable = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first; const selectedClipCount = clipsTable.getElements("AXSelectedRows").allItems.length; return selectedClipCount } function askNumberChar() { const chNumber = sf.interaction.displayDialog({ prompt: "WAIT! Have you batch renamed all clips to start with 2 digit scene numbers? (3 means Yes)", defaultAnswer: "3" // Default number of characters here }).text.match(/\d+/g) return +chNumber } function duplicateSelectedTrack({ duplicateActivePlaylist: targetValue }) { sf.ui.proTools.trackDuplicateSelected({ duplicateActivePlaylist: targetValue, duplicateAlternatePlaylists: false, duplicateAutomation: false, duplicateInserts: false, duplicateSends: false, duplicateGroupAssignments: false, insertAfterLastSelectedTrack: true, onError: "ThrowError" }); } function timeCounter() { // get current counter const mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue let originalTimeCounter /// Bars Beats . if (mainCounter.includes("|")) originalTimeCounter = "Bars|Beats" // Min Secs . else if (mainCounter.includes(":") && mainCounter.includes(".")) originalTimeCounter = "Min:Secs" // Time code else if (mainCounter.split(":").length == 4) originalTimeCounter = "Timecode" // Feet+Frames else if (mainCounter.includes("+")) originalTimeCounter = "Feet+Frames" // Samples. else originalTimeCounter = "Samples" return originalTimeCounter } function createNewTrack(newTrackName) { sf.ui.proTools.menuClick({ menuPath: ["Track", "New..."] }); const newTrackWin = sf.ui.proTools.windows.whoseTitle.is("New Tracks") newTrackWin.first.elementWaitFor({ waitType: "Appear" }); newTrackWin.first.textFields.allItems[1].elementSetTextFieldWithAreaValue({ value: newTrackName, }); newTrackWin.first.buttons.whoseTitle.is("Create").first.elementClick(); newTrackWin.first.elementWaitFor({ waitType: "Disappear" }); } function getClipNames(chNumber) { let selectedClips = sf.ui.proTools.mainWindow.tables.whoseTitle.is('CLIPS').first.children.whoseRole.is("AXRow").allItems.map(row => row.children[1].children.first.value.value).filter(c => c.startsWith('Selected. ')).map(l => l.split("\n")[1]).map(n => n .replace(/["\"]/g, '').trim().slice(2, chNumber))// Number of Characters to Grab return selectedClips } function findTrackNameAndPaste(selectedClipsNames, processTrack) { const trackByName = sf.ui.proTools.trackGetByName({ name: selectedClipsNames }) if (trackByName.track == null) { createNewTrack(selectedClipsNames) sf.ui.proTools.invalidate() sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] }); } else if (trackByName.track != null) { trackByName.track.trackSelect() // Reset menu sf.keyboard.press({ keys: "left" }); const selectionHasClip = sf.ui.proTools.getMenuItem("Edit", "Trim Clip").isEnabled if (selectionHasClip) { // Reset menu sf.keyboard.press({ keys: "left" }); duplicateSelectedTrack({ duplicateActivePlaylist: false }) sf.ui.proTools.invalidate() sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] }); } else { sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste"] }); } } sf.ui.proTools.trackSelectByName({ names: processTrack }) } function mainProcess(processTrack, originaSelectionEndToString, numberOfCharacters, originalSelectionStart) { goToStartOfSession() // one tab and select first clip sf.keyboard.press({ keys: "tab" }); sf.keyboard.press({ keys: "shift+tab" }) //let selectionInfo let selectedClipsNames let selectedArray = [] let selectionEnd let halfCrossFadeDuration while (true) { let currentSelectionEndNumber = +sf.ui.proTools.selectionGet().selectionEnd.replace(/[ :\+\|.]/g, '') selectedClipsNames = getClipNames(numberOfCharacters) if (selectedClipsNames[0] != null) { selectedArray.push(selectedClipsNames[0]) } if (selectedArray.filter(onlyUnique).length <= 1) { selectionEnd = sf.ui.proTools.selectionGetInSamples().selectionEnd if (sf.ui.proTools.selectionGetInfo().hasCrossFade) { halfCrossFadeDuration = +sf.ui.proTools.selectionGetInSamples().selectionLength / 2 selectionEnd = +sf.ui.proTools.selectionGetInSamples().selectionEnd - halfCrossFadeDuration } } if (selectedArray.filter(onlyUnique).length <= 1 && !sf.ui.proTools.selectionGetInfo().hasEndFade && currentSelectionEndNumber < originaSelectionEndToString) { sf.keyboard.press({ keys: "ctrl+tab" }); } else { sf.ui.proTools.selectionSetInSamples({ selectionStart: +originalSelectionStart, selectionEnd: +selectionEnd }) sf.ui.proTools.menuClick({ menuPath: ["Edit", "Cut"] }); sf.ui.proTools.selectionSetInSamples({ selectionStart: +originalSelectionStart, selectionEnd: +selectionEnd }) findTrackNameAndPaste(selectedArray[0], processTrack) break; } //sf.wait({ intervalMs: 1000 }) } } function main() { // Reset menu sf.keyboard.press({ keys: "left" }); duplicateSelectedTrack({ duplicateActivePlaylist: true }) let processTrack = sf.ui.proTools.selectedTrackNames sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" }) while (true) { sf.ui.proTools.invalidate() sf.ui.proTools.menuClick({ menuPath: ["Edit", "Select All"] }); let originaSelectionEndToString = +sf.ui.proTools.selectionGet().selectionEnd.replace(/[ :\+\|.]/g, '') let originalSelectionStart = sf.ui.proTools.selectionGet().selectionStart let selectedClipsNames = getClipNames(numberOfCharacters) if (selectedClipsNames.length >= 1) { mainProcess(processTrack, originaSelectionEndToString, numberOfCharacters, originalSelectionStart) } else { // goToStartOfSession() // Option to delete empy track in the end sf.ui.proTools.menuClick({ menuPath: ["Track", "Delete"] }); break } } } ///////// MAIN ///////// ///////// MAIN ///////// sf.ui.proTools.appActivateMainWindow() // Ask how many characters to grab const numberOfCharacters = askNumberChar() if (numberOfCharacters == 0) { alert("Numbers only, minimum is 1.") } // List of menus to enable clipListEnableSubmenu({ menuItemNames: [ "Audio", "Groups", "Auto-Created", ] }) let originalTimeCOunter = timeCounter() sf.ui.proTools.selectedTrackHeaders.slice().map(track => { sf.ui.proTools.invalidate() track.trackSelect(); track.trackScrollToView(); main() }); sf.ui.proTools.mainCounterSetValue({ targetValue: originalTimeCOunter }) alert(`Done!`)- CIn reply toChristopher_Cleator⬆:Christopher Cleator @Christopher_Cleator
WOW!!! Thank you so much, @Dustin_Harris! Not only is it working perfectly again, but it's somehow going waaay faster than it used to. You're a genius! Thank you.