Duplicate a highlighted clip and its track, rename track with B, C, D, etc
I have this code to copy a clip on a track, dupe the track below the existing one, paste that clip and rename the track, alphabetically sequencing., Works great for the first track. But if I already have a "B" track and then go back to the original track to make another copy, ostensibly a “C”, it doesn’t know there is already a “B” track, stops the macro and says there is already a track by that name with a "B" suffix.
Can anyone helpt to adjust the script so it choses the next available letter ?
//This script duplicates a selected track (eg.Track 1), then renames the duplicate "Track 4B" then "Track 4C" etc.
//Note: This should be assigned to a keyboard shortcut or Stream Deck button.
sf.keyboard.press({
keys: "b, cmd+c, cmd+m",
});
function getTrackName() {
var selectedTrackName = sf.ui.proTools.selectedTrackNames[0];1
}
function renameTrack() {
sf.ui.proTools.selectedTrack.trackOpenRenameDialog();
}
function duplicateSelectedTrack() {
sf.ui.proTools.trackDuplicateSelected({
duplicateActivePlaylist: false,
duplicateAlternatePlaylists: true,
duplicateAutomation: true,
});
}
function main() {
if (sf.ui.proTools.selectedTrackCount === 1) {
//Get track name of selected track
var originalTrackName = getTrackName();
//Duplicate the selected track
duplicateSelectedTrack();
sf.ui.proTools.selectedTrack.trackRename({
renameFunction: function (name) {
var letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
//This function gets called after the Rename track dialog is open
//the argument 'name' holds the existing name, eg. "Audio B.dup1"
//We're supposed to eventually return the target name we want from this function.
var grps = name.match(/^(.*)([ \d])([a-zA-Z])(\.dup.*|)$/);
//This regular expression matches a string containing of the following groups (each group is in parentheses)
//Group 1 - syntax: (.*) - meaning: Match any number (*) of any characters (.) This will match "Audio"
//Group 2 - syntax: ( ) - meaning: Match 1 space character
//Group 3 - syntax: ([a-zA-Z]) - meaning: Match 1 character that is a letter. This will match "B"
//After last group- syntax: \.dup.* - meaning: Match a dot (\.), the text "dup", and then ANY character (.), any number of times (*)
//Save out the groups, baseName, space, numName
var baseName, space, numName;
if (grps) {
baseName = grps[1];
space = grps[2];
numName = grps[3];
} else {
//It didn't match our format. Try again without the requirement of a suffix
if (name.indexOf('.dup') >= 0)
baseName = name.substring(0, name.indexOf('.dup')); //Get the base name before any .dup
else
baseName = name; 1
space = " "; //default space
numName = "";
}
//If you didn't have a number, by default we want a space ' ' before our new #2 track
if (numName == "") space = "";
//Create the new number. If there wasn't one, we'll default to 2, if there was one, convert our string to a number, and add 1 to that
var newIndex = 1; //Start with 2nd index (1 because indices are always 0-based)
if (numName) {
var idx = letters.indexOf(numName.toUpperCase()); //Find the index (0-based)
if (idx >= 0) {
newIndex = idx + 1; //Set to the found index + 1
}
//If not found, default to 2nd index (1) which is already set
}
if (newIndex < 0 || newIndex >= letters.length)
throw 'New index out of bounds'; //We reached Z or some other error
var letter = letters[newIndex];
if (numName && numName.toLowerCase() === numName)
letter = letter.toLowerCase(); //Make letter lowercase if the input was lowercase
//Return the new name, a combination of the base name "Audio", a space or not " ", and the new letter
return baseName + space + letter;
}
});
} else {
log('More than one track is selected');
//throw (0);
}
}
main();
sf.keyboard.press({
keys: "cmd+v",
});
/////
- Raphael Sepulveda @raphaelsepulveda2022-01-20 08:26:50.158Z2022-01-21 05:50:37.127Z
Hey @Jonathan_Grossman, nice script!
Give this a try. Cleaned it up a bit but kept your commentary:
//This script duplicates a selected track (eg.Track 1), then renames the duplicate "Track 4B" then "Track 4C" etc. //Note: This should be assigned to a keyboard shortcut or Stream Deck button. function duplicateSelectedTrack() { sf.ui.proTools.trackDuplicateSelected({ duplicateActivePlaylist: false, duplicateAlternatePlaylists: true, duplicateAutomation: true, }); } function separateClip() { sf.ui.proTools.menuClick({ menuPath: ['Edit', 'Separate Clip', 'At Selection'] }); } function copyClip() { sf.ui.proTools.menuClick({ menuPath: ['Edit', 'Copy'] }); } function pasteClip() { sf.ui.proTools.menuClick({ menuPath: ['Edit', 'Paste'] }); } function muteClip() { sf.ui.proTools.menuClick({ menuPath: ['Edit', 'Mute Clips'] }); } function unmuteClip() { sf.ui.proTools.menuClick({ menuPath: ['Edit', 'Unmute Clips'] }); } function renameFunction(trackName) { //This function gets called after the Rename track dialog is open //the argument 'trackName' holds the existing name, eg. "Audio B.dup1" //We're supposed to eventually return the target name we want from this function. /** @param {{ name?: string, letter?: string, oldName?: string }} args */ function getNewName({ name, letter, oldName }) { let newLetter; if (oldName) { name = oldName.slice(0, -1); newLetter = getNewLetter({ oldLetter: oldName.slice(-1) }); } else { newLetter = getNewLetter({ oldLetter: letter }); } const newName = name + newLetter; return validateName(newName); } function getNewLetter({ oldLetter }) { const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; if (!oldLetter) return letters[1]; let oldLetterIndex = letters.indexOf(oldLetter.toUpperCase()); //Find the index (0-based) let newLetterIndex = ++oldLetterIndex; //Set to the found index + 1 if (newLetterIndex >= letters.length) { throw 'All letters have been used.'; //We reached Z or some other error } let newLetter = letters[newLetterIndex]; if (oldLetter.toLowerCase() === oldLetter) { newLetter = newLetter.toLowerCase(); //Make letter lowercase if the input was lowercase } return newLetter; } const validateName = function () { const namesOfAllTracksInSession = sf.ui.proTools.tracks.names; return (name) => { function isThereATrackThatStartsWith(name) { // Prevents conflicts with playlist return namesOfAllTracksInSession.some(trackName => trackName.startsWith(name)) } if (isThereATrackThatStartsWith(name)) { return getNewName({ oldName: name }); } else { return name; } } }(); var grps = trackName.match(/^(.*)([ \d])([a-zA-Z])(\.dup.*)$/); //This regular expression matches a string containing of the following groups (each group is in parentheses) //Group 1 - syntax: (.*) - meaning: Match any number (*) of any characters (.) This will match "Audio" //Group 2 - syntax: ([ \d]) - meaning: Match a space ( ) or a number (\d) //Group 3 - syntax: ([a-zA-Z]) - meaning: Match 1 character that is a letter. This will match "B" //After last group- syntax: \.dup.* - meaning: Match a dot (\.), the text "dup", and then ANY character (.), any number of times (*) let baseName, spaceOrNumber, letter; if (grps) { // Match [, baseName, spaceOrNumber, letter] = grps; // Destructuring relevant group matches } else { // No Match baseName = trackName.substring(0, trackName.indexOf('.dup')).trim(); //Get the base name before any .dup spaceOrNumber = baseName.match(/.* \d+/) ? "" : " "; // If baseName ends in a space and a number return empty string, else return space. } const newName = getNewName({ name: baseName + spaceOrNumber, letter }); return newName; } function duplicateAndRename() { duplicateSelectedTrack(); sf.ui.proTools.selectedTrack.trackRename({ renameFunction }); } function main() { sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.mainWindow.invalidate(); if (sf.ui.proTools.selectedTrackCount !== 1) { throw 'Make sure that one track is selected.'; } const isAClipSelected = sf.ui.proTools.getMenuItem('Edit', 'Trim Clip').isEnabled; const isSelectedClipMuted = sf.ui.proTools.getMenuItem('Edit', 'Unmute Clips').isEnabled; if (isAClipSelected) { isSelectedClipMuted ? unmuteClip() : separateClip(); copyClip(); muteClip(); } duplicateAndRename(); if (isAClipSelected) pasteClip(); } main();
- JIn reply toJonathan_Grossman⬆:Jonathan Grossman @Jonathan_Grossman
Thank you! This works almost perfectly! A tweak if I may ask....it fails if I reselect the now muted clip on the original track, in order to make another copy of the same thing (for alternate mix options).
- JIn reply toJonathan_Grossman⬆:Jonathan Grossman @Jonathan_Grossman
Also just noticed that if I attempt this with a track that has a duplicated playlist with ".01" in the suffix, it fails as well.
- In reply toJonathan_Grossman⬆:Raphael Sepulveda @raphaelsepulveda2022-01-20 23:47:45.073Z
@Jonathan_Grossman, got em! Updated the script above.
Give it another go! - JIn reply toJonathan_Grossman⬆:Jonathan Grossman @Jonathan_Grossman
BAMMO!!! Thanks Raphael. Works perfectly now.
Raphael Sepulveda @raphaelsepulveda2022-01-21 23:55:15.951Z
Awesome!