No internet connection
  1. Home
  2. How to
  3. Pro Tools

Sharing a script - move tracks into existing routing folders

By Dion Stewart @Dion_Stewart
    2026-02-25 17:03:25.550Z2026-02-25 17:13:32.823Z

    I’m sharing this script with the hope that others may find it useful. I use it during mix session prep to move tracks into existing routing folders. I’m using Michael Brauer’s template which has a large number of routing folders and this script saves me a ton of time.

    The script is based on a workflow Chad Wahlbrink explains in this thread. In that workflow, you start by putting tracks into groups and then you execute commands for every group. That approach allows for different commands to be run for every group.

    In my case, I needed to run what would essentially be the same command for every group. So, the script does a simple move matching on the group name. Tracks in the “KICK PREP” get moved to “KICK”, tracks in “SN PREP” get moved to “SN” etc.

    The script moves tracks into existing routing folders using code from the Raphael Sepulveda Utilities package. The script could easily be modified to move tracks into new routing folders, bus tracks to aux tracks, etc.

    @Kitch and @Chad made significant contributions to this script. Thanks, guys.

    /**
     * @fileoverview  - Move tracks into existing routing folders based on group names
     *
     * Provides the following functionality:
     * - Moves audio files in prep groups into existing folders
     * - Follows a standard naming convention, groups ending with " PREP" 
     *   will be moved into a group matching the name with " PREP" stripped
     *   off. "KICK PREP" tracks get moved into "KICK".
     *   "SN PREP" tracks get moved into "SN", etc.
     * - Groups with names ending with " PREP" are deleted after their 
     *   associated audio files are moved into their corresponding folders.
     *
     * @author Dion Stewart, with a lot of help from Kitch Membery and Chad Wahlbrink.
     *         Script uses Raphael Sepulveda's "RS_Move To Existing Folder" command.
     * @version 1.1.1
     */
    
    if (sf.ui.useSfx) sf.ui.useSfx();
    
    const PREP_GROUP_SUFFIX = " PREP";
    const errorMessages = [];
    
    function getGroupRows() {
        sf.ui.proTools.mainWindow.groupListView.invalidate();
    
        return sf.ui.proTools.mainWindow.groupListView.childrenByRole("AXRow").map(r => r).slice(1);
    }
    
    // returns an array for each group containing a button cell and the name of the group,
    // which is what's needed to select the tracks in the group and map the tracks in the
    // group to pre-existing folders
    function mapRow(row) {
        const groupCell = row.childrenByRole('AXCell');
    
        groupCell.first.buttons.first.value.invalidate();
    
        const buttonCell = groupCell.first.buttons.first;
    
        // Handle cases where "-" might appear in the group name more than once
        const rawText = groupCell.allItems[1].buttons.first.value.invalidate().value
        const separator = " - ";
        const i = rawText.indexOf(separator);
        const name = i === -1 ? "" : rawText.slice(i + separator.length);
    
        return { buttonCell, name };
    }
    
    // Calling command "RS_Move To Existing Folder" from package "Raphael Sepulveda Utilities" 
    // (installed from user/pkg/version "wumCaKnhA0T91I79tL1NbXLQmH03/ckijqv49o000b4z10qr66346e/cm1wokvjq0000sw10fn0uynab")
    function moveSelectedTracksToExistingFolder(folderName) {
        sf.soundflow.runCommand({
            commandId: 'user:cmjgblmhm0001mn69vjgn706z:ckl1ba8vc0005oa10zi9r0q36',
            props: {
                mode: "Static",
                folderTrack: sf.ui.proTools.trackGetByName({ name: folderName, makeVisible: true }).track,
                isRouteToFolder: true,
                inheritColor: true,
            }
        });
    }
    
    function deleteGroups(groups) {
        groups.forEach(group => {
            try {
                sf.ui.proTools.groupsDelete({ groupName: group.name });
            } catch (err) {
                var message = err && err.message ? err.message : err;
                errorMessages.push(`Error occurred while deleting ${group.name} : ` + message);
            }
        });
    }
    
    
    function main() {
    
        const groupsToDelete = [];
    
        getGroupRows().forEach(row => {
    
            let targetFolderName = null;
    
            let group = null;
    
            try {
    
                group = mapRow(row);
    
                // Skip groups that don't end with the PREP_GROUP_SUFFIX
                if (!group.name.endsWith(PREP_GROUP_SUFFIX)) {
                    return;
                }
    
                // target folder is prep group name without the suffix, 
                // e.g., the target for "KICK PREP" is "KICK" if the suffix is " PREP"
                targetFolderName = group.name.slice(0, -PREP_GROUP_SUFFIX.length);
    
                // Ensure the target folder exists. End processing for the group if it doesn't exist.
                const track = sf.ui.proTools.trackGetByName({ name: targetFolderName }).track;
                if (track === null) {
                    errorMessages.push(`Could not move tracks from "${group.name}" to "${targetFolderName}" because "${targetFolderName}" does not exist.`);
                    return;
                };
    
                // select the tracks in the group
                group.buttonCell.popupMenuSelect({ menuPath: ['Select Tracks In Group'], isRightClick: true });
    
                // Call Raphael Sepulveda's Move to Existing Folder command
                moveSelectedTracksToExistingFolder(targetFolderName);
    
                // close the folder
                sf.app.proTools.setTrackOpenState({ trackNames: [targetFolderName], enabled: false, });
    
                // add the group to the array of groups that will be deleted after moves have been completed
                groupsToDelete.push(group);
            }
            catch (err) {
                errorMessages.push(`Error moving tracks from group "${group.name}"` +
                    (targetFolderName ? ` to folder "${targetFolderName}"` : "") +
                    `: ${(err && err.message) ? err.message : err}`);
            }
        });
    
        deleteGroups(groupsToDelete);
        errorMessages.forEach(message => log(message));
    }
    
    
    main();
    
    • 2 replies
    1. J
      Jonathan Johnson @Jonathan_Johnson
        2026-03-13 18:17:39.436Z

        How about a video of this set up prep and in action for the weak of mind?

        1. DDion Stewart @Dion_Stewart
            2026-03-14 17:33:09.552Z2026-03-14 22:19:24.054Z

            Hi Jonathan,

            @Chad's video in the thread referenced above goes through the workflow. The only difference is in the script that's run after tracks have been put into prep groups.

            Let me know if you still have questions after watching that video.

            Dion