Setup multiple variables for a file renaming script
Hi there.
I'm in the live audio recording industry & want to setup a workflow to rename all recorded audio files using a certain naming convention. For example:
festival_name_year-band_name-tracknumber-instrumentname-samplerate-dawname-bitdepth-yymmdd.wav
Everything except for the "tracknumber" and "instrumentname" are "fixed variables" for that specific renaming workflow.
I'm hope to get a pop-up window to set these variables (including a repeat value based on the amount of recorded tracks) and the use the variables for the rest of the workflow.
Thanks ! Rob
- Chris Shaw @Chris_Shaw2024-06-13 15:19:37.906Z
A little more info about the workflow you want to achieve is needed.
- Are you looking to rename files that reside in a folder or are you looking to rename audio / tracks within a pro Tools session? The script would be different depending on the scenario. Renaming files in a folder would be easiest.
- If you're renaming files in a folder what naming convention is being used for the original file names? If you could supply a couple examples that would be very helpful.
- RIn reply toRob_Sannen⬆:Rob Sannen @Rob_Sannen
Hi Chris,
Thanks for reaching out so quickly.
I’m looking to rename files in the audio files folder of a Protools session.
Track number would be the track name in Protools
Instrument name would be the comment fiels of the specific track.Thanks a lot
Rob
- RIn reply toRob_Sannen⬆:Rob Sannen @Rob_Sannen
Hi Chris,
I'll try to describe an example.
We would typically record 128 audio tracks during a live show. There is normally no time to rename all of the tracks & fill in the instrument name in the comments before we go into record (and usually channel layout is not entirely clear before the show starts anyway).
We would rename tracks & fill in comments fields during the recording, or otherwise after the recording is finished.
This results in the audio files just being named Audio 1 > Audio 128.Once all of the information is final, I would like to run the workflow.
We would have the following variables:- festival name > this is the same for all to be renamed audio files, preferably filled in via a popup window
- festival year > this is the same for all to be renamed audio files, preferably filled in via a popup window
- artist name > this is the same for all to be renamed audio files, preferably filled in via a popup window
- used DAW/workstation > this is the same for all to be renamed audio files, preferably filled in via a popup window
- recording date (YYYYMMDD) > this is the same for all to be renamed audio files, preferably filled in via a popup window
- track number > to be copied from the Protools session's currently selected track's "track name"
- instrument name > to be copied from the Protools session's currently selected track's "track comment"
- audio sample rate & bit depth > this is the same for all to be renamed audio files, preferably filled in via a popup window
In addition to that, the popup window would need a field where I can fill in how many times the workflow needs to be repeated (in this case 128 times since we have 128 audio files to relabel)
The workflow would rename the audiofile called
"audio 01.wav"
into
"wacken_2024-iron_maiden-20230807-rm14_main-track_01-kick-48k24b.wav"
and then move on to the next track in Protools.I would think the variables pop-up window would have to be visible once to fill in variables 1, 2, 3, 4, 5 & 8. Variables 6 & 7 can be copied from the specific track's name & comment field as the workflow progresses.
Hopefully this makes sense.
Bests,
Rob
Chris Shaw @Chris_Shaw2024-06-19 16:33:29.388Z
Thanks for all of the info, it helps a lot.
A couple of questions:- The example name "wacken_2024-iron_maiden-20230807-rm14_main-track_01-kick-48k24b.wav" - seems to be formatted as festival_year-bandName-recordDate-Daw(+"_main-track"?)_trackNumber-instrumentName - sampleRate&bitDepth). This is slightly different than the numbered list you provided above.
Should I follow the numbered list format above or the example name below it?
Should all of the instrument names be proceeded by "main-track"?
I'm assuming in the example DAW/Workstation name in the example is "rm14" - correct?
I'll get started on this - it'll be easy to change the formatting once you've confirmed everything.Chris Shaw @Chris_Shaw2024-06-19 16:38:42.840Z
Oh, which version of Pro Tools are you using?
Will you be using this version consistently or will you be using different versions?Chris Shaw @Chris_Shaw2024-06-19 17:52:08.967Z
I think it would be a good idea to format the track number with three digits (001, 055, 128, etc). This way when the files are sorted alphabetically in a computer window or imported into another DAW the tracks will be sorted / arranged the same way they were in the original PT session, saving a lot of time reorganizing track in a DAW.
Let me knowChris Shaw @Chris_Shaw2024-06-19 17:52:56.048Z
One last question - is it safe to assume that there will only be one audio file / clip per track?
- In reply toChris_Shaw⬆:RRob Sannen @Rob_Sannen
totally agree on make 3-digit numbers
- In reply toChris_Shaw⬆:RRob Sannen @Rob_Sannen
23.3.0.89
since we only use it for recording audio, we won't be needing all the new features included in updates. as long as this runs stable, we'll probably stick with this for a while
- In reply toChris_Shaw⬆:RRob Sannen @Rob_Sannen
Hi Chris,
we should stick to the numbered following order, i just mistyped the example, sorry :-)
DAW/workstation is rm14_main
we always record on 2 DAW simultaneously, "main" and "backup". the machines have numbers as well. these numbers don't make sense for the client receiving the files, but to make sense for us, so that's why i combine them in "daw/workstation"Rob
- The example name "wacken_2024-iron_maiden-20230807-rm14_main-track_01-kick-48k24b.wav" - seems to be formatted as festival_year-bandName-recordDate-Daw(+"_main-track"?)_trackNumber-instrumentName - sampleRate&bitDepth). This is slightly different than the numbered list you provided above.
- In reply toRob_Sannen⬆:Chris Shaw @Chris_Shaw2024-06-20 00:12:12.421Z2024-06-20 04:25:23.556Z
Here's my first try.
A few things to note:- This script assumes that every track has only a single clip that is a complete file.
- If you're running PT 2023.6 or higher it should be pretty quick. If an earlier version of PT is used it will still do its thing but renaming the clips will take longer.
- there's no need to enter the number of tracks that are to be renamed, just selected the tracks and the script will take care of the rest.
- You'll be asked for the needed values via dialog boxes. The sample rate and bit depth will be automatically detected from the session setup window.
- after entering the needed info, the info will be stored to disk (on the first run there will be generic values for the default answers). The next time you run the script the default values in the dialog boxes will use the values stored to disk. I figured it'd be handy if you're doing this after every show with the same band.
- if you make a mistake with one of the values fill out the rest of them and click 'Cancel' at the first confirmation box and start again. When you run the script again you'll only have to change the entries that were incorrect.
- I formatted the track number to be three digits long for the reason I asked in my previous posts
I would try this script on a test session first to confirm it works.
Let me know if there are any changes you need.function formatExtractedNumber(str) { // Extract the first number found in the string const match = str.match(/\d+/); if (!match) { throw new Error("No number found in the string"); } const num = match[0]; // Convert the extracted number to a string and pad it return num.toString().padStart(3, '0'); } function getPTVersion() { const versNumberArray = sf.ui.proTools.appVersion.split(".") let [year, month, increment] = versNumberArray month = "0" + month.slice(-2) increment = "0" + increment.slice(-2) let versionNumber = Number(year + month + increment) return versionNumber } const PACKAGE_DIRECTORY = sf.soundflow.thisPackage.getDirectory().path; const previousValuesPath = `${PACKAGE_DIRECTORY}/Previous_Entries.json` // //////////////// // ///// MAIN ///// // //////////////// sf.ui.proTools.appActivateMainWindow() // Check for and load previous entries let prevFestivalName, prevFestivalYear, prevArtist, prevdawName, prevRecordingDate; if (sf.file.exists({ path: previousValuesPath }).exists) { let previousEntries = sf.file.readJson({ path: previousValuesPath }).json; ({ prevFestivalName, prevFestivalYear, prevArtist, prevdawName, prevRecordingDate } = previousEntries) } // Instruct user to select tracks to be renamed sf.interaction.displayDialog({ prompt: "Select tracks in sessionto be renamed and click OK", buttons: ["Cancel", "OK"], defaultButton: "OK" }).text // Get Festival name from user let festivalName = sf.interaction.displayDialog({ prompt: "Enter festival name:", defaultAnswer: prevFestivalName ? prevFestivalName : "<festival name>", buttons: ["Cancel", "OK"], defaultButton: "OK" }).text // get festival year from user const festivalYear = sf.interaction.displayDialog({ prompt: "Enter festival year:", defaultAnswer: prevFestivalYear ? prevFestivalYear : "<2024>", buttons: ["Cancel", "OK"], defaultButton: "OK" }).text // get artist name from user const artist = sf.interaction.displayDialog({ prompt: "Enter artist name:", defaultAnswer: prevArtist ? prevArtist : "<Band Name>", buttons: ["Cancel", "OK"], defaultButton: "OK" }).text // get DAW name from user const dawName = sf.interaction.displayDialog({ prompt: "Enter DAW / workstation name:", defaultAnswer: prevdawName ? prevdawName : "Pro-Tools", buttons: ["Cancel", "OK"], defaultButton: "OK" }).text // Get recording date from user const recordingDate = sf.interaction.displayDialog({ prompt: "Enter recording date:", defaultAnswer: prevRecordingDate ? prevRecordingDate : "<YYYYMMDD>", buttons: ["Cancel", "OK"], defaultButton: "OK" }).text // Get sample rate and bit depth from Session Setup window // Open Session Setup if neccessary const sessionSetupWin = sf.ui.proTools.windows.whoseTitle.is("Session Setup").first if (!sessionSetupWin.exists) { sf.ui.proTools.menuClick({ menuPath: ["Setup", "Session"] }) sessionSetupWin.elementWaitFor() } // Get sample rate const sampleRate = sessionSetupWin.groups.whoseTitle.is("Session Format").first .children.whoseRole.is("AXStaticText").allItems[2].value.invalidate().value .split(" k")[0] + "k" // Get bit depth const bitDepth = sessionSetupWin.groups.whoseTitle.is("Session Format").first. popupButtons.allItems[1].value.invalidate().value.split(" B")[0] + "b"; //Close Session Info window sessionSetupWin.windowClose() sessionSetupWin.elementWaitFor({ waitType: "Disappear" }) //Construct first part of new track name const trackNamePart1 = `${festivalName}_${festivalYear}-${artist}-${recordingDate}-${dawName}_main-track_` //Save entries to disk to reuse on next run const previousEntries = { prevFestivalName: festivalName, prevFestivalYear: festivalYear, prevArtist: artist, prevdawName: dawName, prevRecordingDate: recordingDate, } sf.file.writeJson({ json: previousEntries, path: previousValuesPath }) // Have user confirm entries sf.interaction.displayDialog({ title: "Confirm Rename Tracks", prompt: `Confirm entries before proceeding. (Cancel to start over) Festival Name: ${festivalName} Festival Year: ${festivalYear} Artist: ${artist} DAW: ${dawName} Recording Date: ${recordingDate} Sample Rate: ${sampleRate} Bit Depth: ${bitDepth} ----------- Sample Track Name = ${trackNamePart1}015-kick-${sampleRate}${bitDepth}`, buttons: ["Cancel", "OK"], defaultButton: "OK" }); // Ensure time line and edit selection are linked const linkeTimeLineAndEditBtn = sf.ui.proTools.mainWindow.cursorToolCluster.buttons .whoseTitle.is("Link Track and Edit Selection").first if (linkeTimeLineAndEditBtn.value.invalidate().value !== "Selected") linkeTimeLineAndEditBtn.elementClick(); // Show only selected tracks const trackListPopupBtn = sf.ui.proTools.mainWindow.trackListPopupButton trackListPopupBtn.popupMenuSelect({ menuPath: ["Show Only Selected Tracks"] }); // Get visible track headers let selectedTrackHeaders = sf.ui.proTools.visibleTrackHeaders; // Ensure comments are visible const commentsMenuItem = sf.ui.proTools.getMenuItem("View", "Edit Window Views", "Comments") if (!commentsMenuItem.isMenuChecked) commentsMenuItem.elementClick() //Scroll to view first track sf.ui.proTools.trackSelectByName({ names: [selectedTrackHeaders[0].normalizedTrackName] }) sf.ui.proTools.selectedTrack.trackScrollToView() // Open rename window selectedTrackHeaders[0].popupButtons.first.mouseClickElement({ clickCount: 2 }) // Wait for rename window sf.ui.proTools.windows.whoseTitle.is(selectedTrackHeaders[0].popupButtons.first.value.invalidate().value).first.elementWaitFor(); //Get the elements of text fields and buttons from rename window const [trackName, comments] = sf.ui.proTools.focusedWindow.invalidate().textFields.map(x => x) const [ok, cncl, pr, next] = sf.ui.proTools.focusedWindow.buttons.map(x => x) //Rename Tracks selectedTrackHeaders.forEach(track => { //Check if comments field is not empty before proceeding if (comments.value.invalidate().value.trim().length !== 0) { // Construct track name const trackNumber = formatExtractedNumber(trackName.value.invalidate().value); const instrumentName = comments.value.invalidate().value; const fulltrackName = `${trackNamePart1}_${trackNumber}-${instrumentName}-${sampleRate}${bitDepth}` // Set track name from comments if (comments.value.invalidate().value.trim().length !== 0) { // Set new track name while (true) { if (trackName.value.invalidate().value != fulltrackName) { trackName.elementSetTextFieldWithAreaValue({ value: fulltrackName, }); sf.wait({ intervalMs: 5 }) } else { break; } }; } }; // If it's the last track press ok, else press next if (selectedTrackHeaders.indexOf(track) === selectedTrackHeaders.length - 1) { ok.elementClick() } else { next.elementClick() }; }) // ask user to confirm new track names before proceeding const confirmTrackNames = sf.interaction.displayDialog({ title: "Confirm Rename Audio Files", prompt: `Please review and confirm track names before proceeding to renaming audio files. (You may need to make the track list wider to see full track names.) You can revert to the last saved version of the session if you would like to start again. You may also manually rename any mislabeled tracks before clicking "Proceed"`, buttons: ["Cancel", "Revert", "Proceed"] }).button; // Revert to saved if requested if (confirmTrackNames == "Revert") { sf.ui.proTools.menuClick({ menuPath: ["File", "Revert"], looseMatch: true }) throw 0 } // Rename Audio Files sf.ui.proTools.mainWindow.invalidate(); // refresh track headers selectedTrackHeaders = sf.ui.proTools.visibleTrackHeaders if (getPTVersion() < 230600) { // If running older version of proTools run the following: const refreshMenus = () => sf.keyboard.press({ keys: "n", repetitions: 2, fast: true }) selectedTrackHeaders.forEach(track => { sf.ui.proTools.trackSelectByName({ names: [track.normalizedTrackName] }) refreshMenus() sf.ui.proTools.menuClick({ menuPath: ["Edit", "Select All"] }) refreshMenus() sf.ui.proTools.menuClick({ menuPath: ["Clip", "Rename..."] }) const clipNameWindow = sf.ui.proTools.windows.whoseTitle.is("Name").first clipNameWindow.elementWaitFor() //Define clip rename window elements const nameGroup = clipNameWindow.groups.whoseTitle.is("Name").first const clipNameField = nameGroup.textFields.first; const clipAndDiskBtn = nameGroup.radioButtons.whoseTitle.is("name clip and disk file").first; const nameOkBtn = clipNameWindow.buttons.whoseTitle.is("OK").first while (true) { // log(clipNameField.value.invalidate().value) if (clipNameField.value.invalidate().value !== track.normalizedTrackName) { clipNameField.elementClick() sf.keyboard.press({ keys: "backspace", repetitions: 2 }); sf.wait({ intervalMs: 25 }) clipNameField.elementSetTextFieldWithAreaValue({ value: track.normalizedTrackName, useMouseKeyboard: true }) sf.wait({ intervalMs: 300 }) clipAndDiskBtn.elementClick() // log(clipNameField.value.invalidate().value) } else { break; } } nameOkBtn.elementClick() }) } else { // If new version of PT run this selectedTrackHeaders.forEach(track => { sf.app.proTools.selectAllClipsOnTrack({ trackName: track.normalizedTrackName }); sf.app.proTools.renameSelectedClip({ newName: track.normalizedTrackName, renameFile: true }) }) sf.app.proTools.refreshAllModifiedAudioFiles() } // Show previously selected tracks trackListPopupBtn.popupMenuSelect({ menuPath: ["Restore Previously Shown Tracks"] }); sf.interaction.notify({ title: "Rename track and clips complete!" })
- RRob Sannen @Rob_Sannen
Hi Chris,
Awesome ! I will give this a run over the weekend !Good question concerning the 1 or multiple clip per track. we will probably have occasions where we would record the soundcheck & show in the same protools session.
Would it be possible to use the marker-name for this and paste this name in between variable 5 & 6 ?I was also thinking: maybe it's a good idea to add more variables (like 10 in total) we could use in the future, and maybe be able to use variables during the workflow based on a checkbox that's ticked or not in the popup-window ?
Bests, Rob
Chris Shaw @Chris_Shaw2024-06-20 12:34:46.792Z
Thanks for the replies.
The script above will only work if there just one unedited clip per track.
I’ll have to rework it for multiple clips per trackChris Shaw @Chris_Shaw2024-06-20 20:27:14.879Z
After thinking about how you'd like this script to work, the easiest way to code this would be to have a single text entry dialog where you could fill out the entire track name up to the track number, name, and sr/bit depth - something like this:
wacken_2024-iron_maiden-20230807-rm14_main
The script will take care of the rest: (-track_001-kick-48k24b.wav
).
This prefix would then be stored to disk.
To make things easier, the next time you enter a new name prefix you'll be presented with a searchable list of all prefixes you've entered in the past. You then pick or search the prefix that most closely matches what you need then edit it.
Here's a video explaining how this would work:
https://www.dropbox.com/scl/fi/w68i93xrh6grn24wmb0jq/track-prefix-search.mp4?rlkey=cj956phyzvz0w66zv5nu23yph&dl=0
- RIn reply toRob_Sannen⬆:Rob Sannen @Rob_Sannen
Hi Chris,
although I like the idea of what you showed me in the video, I think the list would get way to long and therefore more confusing then helpful during festival days. There is so much lastminute stuff going on, that I think the way you have it now makes more sense. The popups show you what you've filled in before, that's great to avoid typo's ect.
Also of the option to be able to select which tracks you need to rename !
Is it easy to pack this into 1 popup dialog and do the checkbox idea for which variables you wish to use or not ?
I can think of occasions where we're only recording 1 show of 1 artist, so there's no need for festival name ect, but we could still use the same scriptI gave the script a test run:
- could we do 1 underscore between "track" and the actual number ?
- i see "main" twice, not sure where this comes from
It also seems stuck on renaming the first audio-file. I made a screenrecording on how it looks on my side:
https://backups.robsannen.nl/_Fse08Bg4p9FYERThanks again,
Rob
Chris Shaw @Chris_Shaw2024-06-25 16:23:30.897Z2024-06-25 17:51:50.027Z
Unfortunately there's no easy way to combine everything into one dialog window - it's a shortcoming of SF's back-end. Creating a single window (a surface) would take LOADS of scripting and can only be done as a developer. Further more it would have to be a paid app through the store. If it's something you really need you can contact the SF team and get the details for getting a custom app made for you.
I'm locked into a heavy mix session for the next few days - if a get a spare moment I'll tweak the script to get rid of the extra "main" and add the extra underscore
Chris Shaw @Chris_Shaw2024-06-25 16:26:33.751Z
I'm thinking instead of checkboxes, each dialog will have an additional "Don't use" option. I'll see if I can also have the default button option on each dialog be the same as the last selection of that dialog box (much like the previous text.
- RIn reply toRob_Sannen⬆:Rob Sannen @Rob_Sannen
Hi Chris, wanted to pick this up again. The don't use button would also work fine I think.
Bests, Rob