No internet connection
  1. Home
  2. How to

Setup multiple variables for a file renaming script

By Rob Sannen @Rob_Sannen
    2024-06-13 13:39:47.556Z

    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

    • 18 replies

    There are 18 replies. Estimated reading time: 29 minutes

    1. 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.
      1. R
        In reply toRob_Sannen:
        Rob Sannen @Rob_Sannen
          2024-06-13 15:29:27.460Z

          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

          1. R
            In reply toRob_Sannen:
            Rob Sannen @Rob_Sannen
              2024-06-19 10:31:28.079Z

              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:

              1. festival name > this is the same for all to be renamed audio files, preferably filled in via a popup window
              2. festival year > this is the same for all to be renamed audio files, preferably filled in via a popup window
              3. artist name > this is the same for all to be renamed audio files, preferably filled in via a popup window
              4. used DAW/workstation > this is the same for all to be renamed audio files, preferably filled in via a popup window
              5. recording date (YYYYMMDD) > this is the same for all to be renamed audio files, preferably filled in via a popup window
              6. track number > to be copied from the Protools session's currently selected track's "track name"
              7. instrument name > to be copied from the Protools session's currently selected track's "track comment"
              8. 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

              1. 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.

                1. Oh, which version of Pro Tools are you using?
                  Will you be using this version consistently or will you be using different versions?

                  1. 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 know

                    1. One last question - is it safe to assume that there will only be one audio file / clip per track?

                      1. In reply toChris_Shaw:
                        RRob Sannen @Rob_Sannen
                          2024-06-20 10:55:07.813Z

                          totally agree on make 3-digit numbers

                        • In reply toChris_Shaw:
                          RRob Sannen @Rob_Sannen
                            2024-06-20 11:03:36.839Z

                            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
                              2024-06-20 10:54:46.970Z

                              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

                          • 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!" })
                            
                            1. RRob Sannen @Rob_Sannen
                                2024-06-20 11:07:20.173Z

                                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

                                1. 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 track

                                  1. 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

                              • R
                                In reply toRob_Sannen:
                                Rob Sannen @Rob_Sannen
                                  2024-06-24 11:21:47.356Z

                                  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 script

                                  I 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/_Fse08Bg4p9FYER

                                  Thanks again,

                                  Rob

                                  1. 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

                                    1. 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.

                                  2. R
                                    In reply toRob_Sannen:
                                    Rob Sannen @Rob_Sannen
                                      2024-08-14 10:31:11.011Z

                                      Hi Chris, wanted to pick this up again. The don't use button would also work fine I think.
                                      Bests, Rob