Script Keeps Throwing Error - Can't Figure it Out
hi everyone
so many people here helped me w this script! it creates an aux and adds an insert and routes it all nicely blah blah blah. this script works perfectly after pro tools restarts for anywhere up to about 1 - 2 hours of use. Then it crashes and i have to restart pro tools again to make it work. restarting soundflow doesn't seem to help (or running soundflow invalidate macro).
this is the error message i keep getting:
26.01.2022 16:33:15.05 [Backend]: !! Command Error: Create Aux and Add Insert MACRO [user:default:ckvjqw5s40000kh102j0b0425]:
Error running command package:cktsrvilz00021o10qn3eid79 (Create Aux and Add Insert MACRO: Line 10)
TypeError: Cannot read property 'toLowerCase' of undefined
(Create Aux + Add Insert line 244)
this is the script (sorry, it's a long one!)
/**
* @param {string} sendAssignment
*/
function assignToNewTrack(sendAssignment) {
const selectedTracks = sf.ui.proTools.selectedTrackNames
sf.ui.proTools.trackGetByName({ name: selectedTracks[selectedTracks.length - 1] }).track.groups.whoseTitle.is(getGroup("Sends", sendAssignment)).first.buttons.whoseTitle.is(`Send Assignment ${sendAssignment}`).first.popupMenuSelect({
isShift: true,
isOption: true,
menuPath: ["new track..."],
});
}
function moveSendWinOutOfSlots() {
sf.ui.proTools.invalidate()
const sendWin = sf.ui.proTools.mainTrackOutputWindow.elementWaitFor().element
const sendWinPos = sendWin.position.x
const sendSlots = sf.ui.proTools.selectedTrack.groups.whoseTitle.is("Inserts A-E").first
const sendSlotsPos = sendSlots.position.x
if (sendWinPos - sendSlotsPos < 450 && sendSlotsPos - sendWinPos < 170) {
sendWin.windowMove({
position: {
y: sendWin.position.y,
x: sendSlotsPos + 450
}
})
}
}
function getFirstFreeSend() {
const selectedTracks = sf.ui.proTools.selectedTrackHeaders
const sendLetters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
let sendAssignmentList = []
for (let i = 0; i < selectedTracks.length; i++) {
let trackInfo = sf.ui.proTools.trackGetByName({ name: selectedTracks[i].normalizedTrackName })
.track.sendButtons.filter(x => x.value.invalidate().value != "unassigned").map(x => x.title.invalidate().value.split(" ")[2])
sendAssignmentList.push(...trackInfo)
}
const firstFreeSend = sendLetters.filter(letter => sendAssignmentList.indexOf(letter) === -1)[0]
if (firstFreeSend) {
return firstFreeSend
} else {
alert("Must have at least one free send, that is the same for all tracks.")
throw 0
};
};
/**
* @param {'Insert'|'Send'} slotType
* @param {array} [defaultUsedSlots] = [] - If parameter is not used, default is []
*/
function getFirstFreeInsertOrSend(slotType, defaultUsedSlots) {
let buttons
slotType == "Insert" ? buttons = "insertButtons" : buttons = "sendButtons"
const selectedTracks = sf.ui.proTools.selectedTrackHeaders
const sendLetters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
let sendAssignmentList = defaultUsedSlots || []
try {
for (let i = 0; i < selectedTracks.length; i++) {
let trackInfo = sf.ui.proTools.trackGetByName({ name: selectedTracks[i].normalizedTrackName })
// .insertButtons or .sendButtons
.track[buttons].filter(x => x.value.invalidate().value != "unassigned").map(x => x.title.invalidate().value.split(" ")[2])
sendAssignmentList.push(...trackInfo)
}
} catch (err) { }
const firstFreeSend = sendLetters.filter(letter => sendAssignmentList.indexOf(letter) === -1)[0]
if (firstFreeSend) {
return firstFreeSend
} else {
if (slotType == "Insert") { alert(`Must have at least one free ${slotType} slot.`) }
else if (slotType == "Send") { alert(`Must have at least one free ${slotType} slot, that is the same for selected tracks.`) }
throw 0
};
};
/**
* @param {string} sendLetter
* @param {'Inserts'|'Sends'} slotType
*/
function getGroup(slotType, sendLetter) {
let group
sendLetter.toLowerCase().charCodeAt(0) - 97 <= 4 ? group = `${slotType} A-E` : group = `${slotType} F-J`
return group
}
function newTrackSettings() {
const newTrackWin = sf.ui.proTools.windows.whoseTitle.is("New Track").first.elementWaitFor().element
const format = newTrackWin.popupButtons.whoseTitle.is("Stereo").first
const type = newTrackWin.popupButtons.whoseTitle.is("Aux Input").first
const timeBase = newTrackWin.popupButtons.whoseTitle.is("Samples").first
if (format.value.invalidate().value != "Stereo") { format.popupMenuSelect({ menuPath: ["Stereo"] }); }
if (type.value.invalidate().value != "Aux Input") { type.popupMenuSelect({ menuPath: ["Aux Input"] }); }
if (timeBase.value.invalidate().value != "Samples") { timeBase.popupMenuSelect({ menuPath: ["Samples"] }); }
newTrackWin.checkBoxes.whoseTitle.is("Create next to current track").first.checkboxSet({ targetValue: "Enable" });
// Wait to name track and press ok
newTrackWin.elementWaitFor({ waitType: "Disappear", timeout: -1 })
sf.ui.proTools.invalidate()
}
// function setWindowViews() {
// const views = ["Inserts A-E", "Inserts F-J", "Sends A-E", "Sends F-J"]
// views.forEach(view => {
// sf.ui.proTools.menuClick({ menuPath: ["View", "Edit Window Views", view], targetValue: "Enable" })
// sf.ui.proTools.menuClick({ menuPath: ["View", "Mix Window Views", view], targetValue: "Enable" })
// })
// }
/**
* @param {'Enable'|'Disable'} targetValue
* @param {array} [viewsNames] = [] - If parameter is not used, default is set inside
*/
function setWindowViews(targetValue, viewsNames) {
let views = viewsNames || ["Inserts A-E", "Inserts F-J", "Sends A-E", "Sends F-J"]
views.forEach(view => {
sf.ui.proTools.menuClick({ menuPath: ["View", "Edit Window Views", view], targetValue: targetValue })
// sf.ui.proTools.menuClick({ menuPath: ["View", "Mix Window Views", view], targetValue: targetValue })
})
}
// function closeWindowViews(views) {
// views.forEach(view => {
// sf.ui.proTools.menuClick({ menuPath: ["View", "Edit Window Views", view], targetValue: "Disable" })
// sf.ui.proTools.menuClick({ menuPath: ["View", "Mix Window Views", view], targetValue: "Disable" })
// })
// }
const getSelectedTrackOut = (trackName) => sf.ui.proTools.trackGetByName({ name: trackName }).track.outputPathButton.title.invalidate().value.split('\n')[1].trim();
let insertSlot
let firstFreeSend
function actions() {
const originalSelectedTracks = sf.ui.proTools.selectedTrackNames
// Save first selected track output on variable
const firstSelectedOut = getSelectedTrackOut(originalSelectedTracks[0])
// Get free sends
firstFreeSend = getFirstFreeInsertOrSend("Send")
// Assign new send to new track
assignToNewTrack(firstFreeSend)
// New track setting and wait for track name
newTrackSettings()
// If new send win is blocking the inserts or sends, move win away
moveSendWinOutOfSlots()
// Expand created send
sf.ui.proTools.menuClick({ menuPath: ["View", "Expanded Sends", `Send ${firstFreeSend}`], targetValue: "Enable" })
// Solo safe new Aux
const selectedTracks = sf.ui.proTools.selectedTrackNames
const auxTrack = sf.ui.proTools.trackGetByName({ name: selectedTracks[selectedTracks.length - 1] }).track
auxTrack.trackSelect()
auxTrack.trackScrollToView()
auxTrack.buttons.whoseTitle.is("Solo").first.mouseClickElement({ isCommand: true })
// set new aux output
if (getSelectedTrackOut(auxTrack.normalizedTrackName) != firstSelectedOut) {
auxTrack.trackOutputSelect({ outputSelector: items => items.filter(item => item.path[1] === firstSelectedOut)[0] })
}
// Get free insert slot after A
insertSlot = getFirstFreeInsertOrSend("Insert", ["A"])
// Open search...
auxTrack.groups.whoseTitle.is(getGroup("Inserts", insertSlot)).first
.buttons.whoseTitle.is(`Insert Assignment ${insertSlot}`)
.first.popupMenuSelect({
menuPath: ["search..."],
});
// Wait to open new plug
while (true) {
const plugIsOpen = auxTrack.groups.whoseTitle.is(getGroup("Inserts", insertSlot)).first
.buttons.whoseTitle.is(`Insert Assignment ${insertSlot}`).first
.value.invalidate().value != "unassigned"
if (!plugIsOpen) {
sf.wait({ intervalMs: 1000 })
} else {
break;
}
}
sf.ui.proTools.trackSelectByName({ names: originalSelectedTracks })
}
function main() {
sf.ui.proTools.appActivateMainWindow();
sf.ui.proTools.invalidate();
// const isActiveInsGroupFMix = sf.ui.proTools.getMenuItem("View", "Mix Window Views", "Inserts F-J").isMenuChecked
const isActiveInsGroupFEdit = sf.ui.proTools.getMenuItem("View", "Edit Window Views", "Inserts F-J").isMenuChecked
// const isActiveSendGroupFMix = sf.ui.proTools.getMenuItem("View", "Mix Window Views", "Sends F-J").isMenuChecked
const isActiveSendGroupFEdit = sf.ui.proTools.getMenuItem("View", "Edit Window Views", "Sends F-J").isMenuChecked
const isEditWin = sf.ui.proTools.focusedWindow.title.value.startsWith("Edit")
if (!isEditWin) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Edit"] }) }
// Close expand created sends
sf.ui.proTools.menuClick({ menuPath: ["View", "Expanded Sends", "None"], targetValue: "Enable" })
setWindowViews("Enable")
try {
actions()
} catch (err) { throw err }
finally {
// Go to mix if needed
if (!isEditWin) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Mix"] }) }
// Close Inserts Views if not needed
if (insertSlot.toLowerCase().charCodeAt(0) - 97 <= 4 && (/* !isActiveInsGroupFMix || */ !isActiveInsGroupFEdit)) {
// if (!isActiveInsGroupFMix) { setWindowViews("Disable", ["Inserts F-J"]) }
if (!isActiveInsGroupFEdit) { setWindowViews("Disable", ["Inserts F-J"]) }
}
// Close Sends F-J if not needed
if (firstFreeSend.toLowerCase().charCodeAt(0) - 97 <= 4 && (/* !isActiveSendGroupFMix || */ !isActiveSendGroupFEdit)) {
// if (!isActiveSendGroupFMix) { setWindowViews("Disable", ["Sends F-J"]) }
if (!isActiveSendGroupFEdit) { setWindowViews("Disable", ["Sends F-J"]) }
}
}
}
main()
- samuel henriques @samuel_henriques
Hello Philip,
This was my code.try this, if you are still getting errors send a screen recording so I can try to figure out better.
/** * @param {string} sendAssignment */ function assignToNewTrack(sendAssignment) { const selectedTracks = sf.ui.proTools.selectedTrackNames sf.ui.proTools.trackGetByName({ name: selectedTracks[selectedTracks.length - 1] }).track.groups.whoseTitle.is(getGroup("Sends", sendAssignment)).first.buttons.whoseTitle.is(`Send Assignment ${sendAssignment}`).first.popupMenuSelect({ isShift: true, isOption: true, menuPath: ["new track..."], }); } function moveSendWinOutOfSlots() { sf.ui.proTools.invalidate() const sendWin = sf.ui.proTools.mainTrackOutputWindow.elementWaitFor().element const sendWinPos = sendWin.position.x const sendSlots = sf.ui.proTools.selectedTrack.groups.whoseTitle.is("Inserts A-E").first const sendSlotsPos = sendSlots.position.x if (sendWinPos - sendSlotsPos < 450 && sendSlotsPos - sendWinPos < 170) { sendWin.windowMove({ position: { y: sendWin.position.y, x: sendSlotsPos + 450 } }) } } function getFirstFreeSend() { const selectedTracks = sf.ui.proTools.selectedTrackHeaders const sendLetters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"] let sendAssignmentList = [] for (let i = 0; i < selectedTracks.length; i++) { let trackInfo = sf.ui.proTools.trackGetByName({ name: selectedTracks[i].normalizedTrackName }) .track.sendButtons.filter(x => x.value.invalidate().value != "unassigned").map(x => x.title.invalidate().value.split(" ")[2]) sendAssignmentList.push(...trackInfo) } const firstFreeSend = sendLetters.filter(letter => sendAssignmentList.indexOf(letter) === -1)[0] if (firstFreeSend) { return firstFreeSend } else { alert("Must have at least one free send, that is the same for all tracks.") throw 0 }; }; /** * @param {'Insert'|'Send'} slotType * @param {array} [defaultUsedSlots] = [] - If parameter is not used, default is [] */ function getFirstFreeInsertOrSend(slotType, defaultUsedSlots) { let buttons slotType == "Insert" ? buttons = "insertButtons" : buttons = "sendButtons" const selectedTracks = sf.ui.proTools.selectedTrackHeaders const sendLetters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"] let sendAssignmentList = defaultUsedSlots || [] try { for (let i = 0; i < selectedTracks.length; i++) { let trackInfo = sf.ui.proTools.trackGetByName({ name: selectedTracks[i].normalizedTrackName }) // .insertButtons or .sendButtons .track[buttons].filter(x => x.value.invalidate().value != "unassigned").map(x => x.title.invalidate().value.split(" ")[2]) sendAssignmentList.push(...trackInfo) } } catch (err) { } const firstFreeSend = sendLetters.filter(letter => sendAssignmentList.indexOf(letter) === -1)[0] if (firstFreeSend) { return firstFreeSend } else { if (slotType == "Insert") { alert(`Must have at least one free ${slotType} slot.`) } else if (slotType == "Send") { alert(`Must have at least one free ${slotType} slot, that is the same for selected tracks.`) } throw 0 }; }; /** * @param {string} sendLetter * @param {'Inserts'|'Sends'} slotType */ function getGroup(slotType, sendLetter) { let group sendLetter.toLowerCase().charCodeAt(0) - 97 <= 4 ? group = `${slotType} A-E` : group = `${slotType} F-J` return group } function newTrackSettings() { const newTrackWin = sf.ui.proTools.windows.whoseTitle.is("New Track").first.elementWaitFor().element /// Popup butons to variables const [type, timeBase, format] = newTrackWin.popupButtons.map(x => x) if (format.title.invalidate().value != "Stereo") { format.popupMenuSelect({ menuPath: ["Stereo"] }); } if (type.title.invalidate().value != "Aux Input") { type.popupMenuSelect({ menuPath: ["Aux Input"] }); } if (timeBase.title.invalidate().value != "Samples") { timeBase.popupMenuSelect({ menuPath: ["Samples"] }); } newTrackWin.checkBoxes.whoseTitle.is("Create next to current track").first.checkboxSet({ targetValue: "Enable" }); // Wait to name track and press ok newTrackWin.elementWaitFor({ waitType: "Disappear", timeout: -1 }) sf.ui.proTools.invalidate() } /** * @param {'Enable'|'Disable'} targetValue * @param {array} [viewsNames] = [] - If parameter is not used, default is set inside */ function setWindowViews(targetValue, viewsNames) { let views = viewsNames || ["Inserts A-E", "Inserts F-J", "Sends A-E", "Sends F-J"] views.forEach(view => { sf.ui.proTools.menuClick({ menuPath: ["View", "Edit Window Views", view], targetValue: targetValue }) // sf.ui.proTools.menuClick({ menuPath: ["View", "Mix Window Views", view], targetValue: targetValue }) }) } const getSelectedTrackOut = (trackName) => sf.ui.proTools.trackGetByName({ name: trackName }).track.outputPathButton.title.invalidate().value.split('\n')[1].trim(); let insertSlot let firstFreeSend function actions() { const originalSelectedTracks = sf.ui.proTools.selectedTrackNames // Save first selected track output on variable const firstSelectedOut = getSelectedTrackOut(originalSelectedTracks[0]) // Get free sends firstFreeSend = getFirstFreeInsertOrSend("Send") // Assign new send to new track assignToNewTrack(firstFreeSend) // New track setting and wait for track name newTrackSettings() // If new send win is blocking the inserts or sends, move win away moveSendWinOutOfSlots() // Expand created send sf.ui.proTools.menuClick({ menuPath: ["View", "Expanded Sends", `Send ${firstFreeSend}`], targetValue: "Enable" }) // Solo safe new Aux const selectedTracks = sf.ui.proTools.selectedTrackNames const auxTrack = sf.ui.proTools.trackGetByName({ name: selectedTracks[selectedTracks.length - 1] }).track auxTrack.trackSelect() auxTrack.trackScrollToView() auxTrack.buttons.whoseTitle.is("Solo").first.mouseClickElement({ isCommand: true }) // set new aux output if (getSelectedTrackOut(auxTrack.normalizedTrackName) != firstSelectedOut) { auxTrack.trackOutputSelect({ outputSelector: items => items.filter(item => item.element.title.value.includes(firstSelectedOut))[0] }) } // Get free insert slot after A insertSlot = getFirstFreeInsertOrSend("Insert", ["A"]) // Open search... auxTrack.groups.whoseTitle.is(getGroup("Inserts", insertSlot)).first .buttons.whoseTitle.is(`Insert Assignment ${insertSlot}`) .first.popupMenuSelect({ menuPath: ["search..."], }); // Wait to open new plug while (true) { const plugIsOpen = auxTrack.groups.whoseTitle.is(getGroup("Inserts", insertSlot)).first .buttons.whoseTitle.is(`Insert Assignment ${insertSlot}`).first .value.invalidate().value != "unassigned" if (!plugIsOpen) { sf.wait({ intervalMs: 1000 }) } else { break; } } sf.ui.proTools.trackSelectByName({ names: originalSelectedTracks }) } function main() { sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.invalidate(); const isActiveInsGroupFEdit = sf.ui.proTools.getMenuItem("View", "Edit Window Views", "Inserts F-J").isMenuChecked const isActiveSendGroupFEdit = sf.ui.proTools.getMenuItem("View", "Edit Window Views", "Sends F-J").isMenuChecked const isEditWin = sf.ui.proTools.focusedWindow.title.value.startsWith("Edit") if (!isEditWin) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Edit"] }) } // Close expand created sends sf.ui.proTools.menuClick({ menuPath: ["View", "Expanded Sends", "None"], targetValue: "Enable" }) setWindowViews("Enable") try { actions() } catch (err) { } finally { // Go to mix if needed if (!isEditWin) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Mix"] }) } if (insertSlot) { // Close Inserts Views if not needed if (insertSlot.toLowerCase().charCodeAt(0) - 97 <= 4 && (!isActiveInsGroupFEdit)) { if (!isActiveInsGroupFEdit) { setWindowViews("Disable", ["Inserts F-J"]) } } } if (firstFreeSend) { // Close Sends F-J if not needed if (firstFreeSend.toLowerCase().charCodeAt(0) - 97 <= 4 && (!isActiveSendGroupFEdit)) { if (!isActiveSendGroupFEdit) { setWindowViews("Disable", ["Sends F-J"]) } } } } } main()