Organize Music Files

This is a little jscript I wrote to help speed up the process of organizing music files. I like to keep my files organized by album artist and then by album name. Music files are received into a folder that is mixed together. First, I use Musicbrainz Picard to clean up the metadata and tie the tracks to their release. Having good metadata is key to this script working correctly, so do make sure you’ve tidied it up first. Then I select all the files, and run the below script which I keep tied to a button in a custom toolbar. This works great on large batches of files as well, just so long as they have metadata.

I had a massive folder of music that had gotten disorganized with tracks all over the place in the wrong subfolders. Flatting the view, then running this on it was a game changer.

Note: Review the comments in the script for some of the finer points of the script behavior and for how to customize it for your setup.

// ========================================================================
// Music Library Organizer for Directory Opus
// ========================================================================
// Created by: Berkeley Goodloe
// Last Updated: June 20, 2025
// Contact: oozy-lingo-switch@duck.com
// 
// This script was coded using Claude Sonnet 4 Think AI model
//
// DESCRIPTION:
// Automatically organizes music files into Artist\Album folder structure
// based on metadata. Works with selected files or folders containing music.
// Handles duplicates and cleans up empty source folders.
//
// NOTE: This script is designed to ignore non-music files in the source 
// folder and delete the folder anyways. This means files like folder.jpg
// will be casualties if not backed up elsewhere.
//
//
// CONFIGURATION:
// *** EDIT THIS LINE TO CHANGE YOUR MUSIC LIBRARY PATH ***
// Change "F:\\music" below to your desired music library location
// ========================================================================

function OnClick(clickData) {
    var cmd = clickData.func.command;
    var tab = clickData.func.sourcetab;
    var fsu = DOpus.FSUtil;
    
    // *** CHANGE THIS PATH TO YOUR MUSIC LIBRARY LOCATION ***
    var baseDir = "F:\\music";
    
    var sourceFolders = DOpus.Create.Vector();
    var processedFiles = 0;
    var movedFiles = 0;
    var deletedDuplicates = 0;
    var skippedFiles = 0;
    
    var filesToProcess = DOpus.Create.Vector();
    var processedPaths = DOpus.Create.Vector();
    
    // Process selected directories
    for (var e = new Enumerator(tab.selected_dirs); !e.atEnd(); e.moveNext()) {
        var folder = e.item();
        CollectMusicFilesWithDupeCheck(folder.realpath, filesToProcess, processedPaths);
    }
    
    // Process selected individual files
    for (var e = new Enumerator(tab.selected_files); !e.atEnd(); e.moveNext()) {
        var file = e.item();
        if (IsMusic(file.name)) {
            var filePath = String(file.realpath);
            if (!VectorContains(processedPaths, filePath)) {
                filesToProcess.push_back(file);
                processedPaths.push_back(filePath);
            }
        }
    }
    
    // Process all collected files
    for (var i = 0; i < filesToProcess.count; i++) {
        var file = filesToProcess(i);
        processedFiles++;
        
        try {
            var fileMeta = file.metadata;
            
            if (fileMeta != "none" && fileMeta.audio) {
                var audioMeta = fileMeta.audio;
                
                var albumArtist = audioMeta.mp3albumartist || audioMeta.mp3artist || "";
                var album = audioMeta.mp3album || "";
                
                albumArtist = CleanFolderName(String(albumArtist));
                album = CleanFolderName(String(album));
                
                if (albumArtist != "" && album != "") {
                    var artistDir = baseDir + "\\" + albumArtist;
                    var albumDir = artistDir + "\\" + album;
                    var destFile = albumDir + "\\" + file.name;
                    
                    if (!fsu.Exists(artistDir)) {
                        cmd.RunCommand("CreateFolder \"" + artistDir + "\"");
                    }
                    if (!fsu.Exists(albumDir)) {
                        cmd.RunCommand("CreateFolder \"" + albumDir + "\"");
                    }
                    
                    if (fsu.Exists(destFile)) {
                        cmd.RunCommand("Delete \"" + file.realpath + "\" QUIET");
                        DOpus.Output("Deleted duplicate: " + file.name + " (already exists in " + albumArtist + "\\" + album + ")");
                        deletedDuplicates++;
                    } else {
                        cmd.RunCommand("Copy MOVE \"" + file.realpath + "\" TO \"" + albumDir + "\"");
                        DOpus.Output("Moved: " + file.name + " -> " + albumArtist + "\\" + album);
                        movedFiles++;
                    }
                    
                    var sourceFolder = String(file.path);
                    if (!VectorContains(sourceFolders, sourceFolder)) {
                        sourceFolders.push_back(sourceFolder);
                    }
                    
                } else {
                    DOpus.Output("Skipped (missing metadata): " + file.name + " [Artist: '" + albumArtist + "', Album: '" + album + "']");
                    skippedFiles++;
                }
            } else {
                DOpus.Output("Skipped (no audio metadata): " + file.name);
                skippedFiles++;
            }
        } catch (e) {
            DOpus.Output("Error processing " + file.name + ": " + e.message);
            skippedFiles++;
        }
    }
    
    // Clean up empty source folders
    if (sourceFolders.count > 0) {
        DOpus.Output("--- Cleaning up source folders ---");
        for (var i = 0; i < sourceFolders.count; i++) {
            var sourceFolder = sourceFolders(i);
            try {
                if (!FolderHasAudioFiles(sourceFolder)) {
                    cmd.RunCommand("Delete \"" + sourceFolder + "\" QUIET");
                    DOpus.Output("Deleted source folder: " + sourceFolder);
                } else {
                    DOpus.Output("Source folder still contains audio files: " + sourceFolder);
                }
            } catch (e) {
                DOpus.Output("Error deleting source folder " + sourceFolder + ": " + e.message);
            }
        }
    }
    
    DOpus.Output("--- SUMMARY ---");
    DOpus.Output("Processed: " + processedFiles + " music files");
    DOpus.Output("Moved: " + movedFiles + " files");
    DOpus.Output("Deleted duplicates: " + deletedDuplicates + " files");
    DOpus.Output("Skipped: " + skippedFiles + " files");
    
    cmd.RunCommand("Go REFRESH");
}

function CollectMusicFilesWithDupeCheck(folderPath, fileVector, pathVector) {
    try {
        var folderEnum = DOpus.FSUtil.ReadDir(folderPath);
        
        while (!folderEnum.complete) {
            var item = folderEnum.next;
            if (item && !item.is_dir && IsMusic(item.name)) {
                var fullPath = folderPath + "\\" + item.name;
                var fileItem = DOpus.FSUtil.GetItem(fullPath);
                if (fileItem) {
                    var filePath = String(fileItem.realpath);
                    if (!VectorContains(pathVector, filePath)) {
                        fileVector.push_back(fileItem);
                        pathVector.push_back(filePath);
                    }
                }
            }
        }
        
        folderEnum.Close();
    } catch (e) {
        DOpus.Output("Error collecting files from folder " + folderPath + ": " + e.message);
    }
}

function VectorContains(vector, value) {
    for (var i = 0; i < vector.count; i++) {
        if (vector(i) == value) {
            return true;
        }
    }
    return false;
}

function FolderHasAudioFiles(folderPath) {
    try {
        var folderEnum = DOpus.FSUtil.ReadDir(folderPath, false);
        
        while (!folderEnum.complete) {
            var folderItem = folderEnum.next;
            if (!folderItem.is_dir && IsMusic(folderItem.name)) {
                return true;
            }
        }
        
        return false;
        
    } catch (e) {
        DOpus.Output("Error checking folder contents: " + e.message);
        return true;
    }
}

function IsMusic(filename) {
    var ext = filename.substr(filename.lastIndexOf('.') + 1).toLowerCase();
    return (ext == "mp3" || ext == "flac" || ext == "m4a" || ext == "wma" || ext == "ogg" || ext == "wav");
}

function CleanFolderName(name) {
    if (!name || name == "undefined") return "";
    
    name = name.replace(/[<>:"/\\|?*]/g, "_");
    name = name.replace(/^[\s.]+|[\s.]+$/g, "");
    
    return name;
}

2 Likes