This module aims to simplify the process of adding multiple music tracks to Foundry VTT, allowing for bulk importation of songs.
If you're like me, you probably enjoy amassing a large collection of songs to play for your players! However, importing your songs one at a time can be sluggish and time consuming. Playlist importer allows you to bulk import all of your songs!
I will only personally be maintaining the most recent version of foundry. In short, this means you may not get backwards compatibility for any breaking changes. Fortunately, you should be able to use releases to download a compatible version if it exists.
This is a module that enables drag and drop functionality for sound files too.
NOTE: This module is under maintenance, I have no plans to update or add features. However, I will try to fix any bugs as possible. Any contribution is welcome.
It's always better and easier to install modules through in in app browser.
To install this module manually:
- Inside the Foundry "Configuration and Setup" screen, click "Add-on Modules"
- Click "Install Module"
- In the "Manifest URL" field, paste the following url:
https://raw.githubusercontent.com/p4535992/foundryvtt-playlist-import/master/src/module.json - Click 'Install' and wait for installation to complete
- Don't forget to enable the module in game using the "Manage Module" button
- The dialog need some very adjustament, for now just ignore what you see, for lack of the time on developer side, anyone is welcome to open a PR with a loading dialog.
- Allows for quick importation of songs into FVTT
- Only adds songs that haven't been added already (can be disabled) . NOTE: This applies only for songs added by Playlist-importer
- Delete imported playlist
- Build playlist by following the hiearchy of folder or one paylist fr every folder
- Just drop one or more audio files into you playlist directory. Ensemble uploads them and creates a playlist.
Please read the following, as it may answer any questions as to unexpected behavior.
NOTE:
- Currently only .mp3, .mp4, .ogg, .wav, .m4a and .flac files are imported. All other types are excluded.
- Organization is force upon you! This means, that when you select your base directory in which to import, only folders within the base directory are checked, not the files. In otherwords, you must subdivide your music into folders inside the base directory.
- Songs added by playlist-importer will be excluded from being added again by the import function. This means, songs names should be unique! Make sure to avoid duplicate names across folders.
- Nested folders will result in unsuccessful importations. This will be addressed in future builds
- For general efficiency questions, refer to the "Efficiency" section below.
- Spaces in folder names should no longer cause issues. Please contact me if they cause trouble.
Note: The paths are vague, as you may have a different data path for your instance of FoundryVTT. If you have questions, feel free to message me.
- Download and install the mod, then enable it on Foundry.
- Inside of your "/FoundryVTT/Data/" folder, create a new folder called "music" (or any category you prefer).
- Inside of your module settings, navigate to Playlist import and select the desire base directory (music in this example).
- Note 1: If using S3, The Forge, or something similar be sure to select the proper source in the settings
- Note 2: If using S3 specifically, please name the bucket you are using within the "bucket" section of the playlist-importer settings. If you're not using S3, you can ignore this option.
- Inside of your "/FoundryVTT/Data/music" folder, you must create subfolders, perhaps with genres and types.
- Place your music files inside the corresponding folder names (Refer to structure below)
- Inside of FoundryVTT, select the playlist sidebar tab.
- Click "Playlist Import" to receive a conformation prompt.
- Select "Begin Import" to wait for imports to finish.
- A prompt will appear confirming task completion, confirm, and enjoy the music!
| /FoundryVTT/Data
|
|---> /music <----- This should be selected as the directory
| |
| |------> /battle_songs
| | |
| | |----> cool_battle.mp3
| | |
| |------> /epic_songs
| | |
| | |----> epic_battle.mp3
| | |----> last_stand.mp4
| |------> /tavern_songs
| | |
| | |----> gnarly_gnomes.mp3
| | |
| |------> /peaceful_songs
| | |
| | |----> safety.mp3
| | |----> just_kidding.mp4
// > BASE ARCHITECTURE <
MUSIQUE
|-- 1.mp3
|-- 2.mp3
|-- 3.mp3
|-- FA
| |-- FA1.mp3
| |-- FA2.mp3
|-- FB
| |-- FBA
| |-- FBA1.mp3
|-- FC (empty folder)
- Songs:
- Base music directory: Select a directory to serve as the base directory for music import",
- Select source: Options include ",
- S3 Bucket: If using an s3 bucket, enter in the name of the bucket here.",
- Set repeat for tracks: Should tracks be set to repeat by default?",
- Set stream for tracks: Should tracks be set to stream by default?",
- Set default volume: On a scale from 0.0 - 1.0",
- Set Fading Time: Put a fading time to imported songs (in ms, default is 0)
- Song Duplicate Checker: Checks during the importation process to see if duplicate songs exist, excluding them if true.",
- Reassign Regex: Adjust the regex to delete unnecessary based on personal preference. This is used in the first pass to remove things like '-' '_' and leading numbers.",
- Override playlist: If enabled if a playlist with the same name is founded during the import it will be override",
- Delete playlist before import: If enabled if a playlist has the flag of the 'playlist import module' it will be deleted before try the new import. This is useful for when you delete some folder on the disk and want to remove the old playlist on the game.",
- Maintain original folder name: Instead using the hierarchy naming this setting will force to create a playlist with the name of the current folder so instead 'parent_child' will be 'child'"
- Use the new folder structure creation method : This setting if enable change the import method and use the new "neoBeginPlaylistImport" import method that use a progress bar in a prompt and replicate the directory structure, creating foundryVTT folders and playlists instead of only playlists
Thanks to Ariphaos for their help with creating a more user friendly naming convention when importing playlists!
Thanks to Sciguymjm for all the suggestions for improvements to the importer! Thanks to users JJBocanegra, Jlanatta, and JMMarchant, and mikkerlo for assisting in the development of Playlist Importer.
Spanish: Thanks to Lozalojo for providing the Spanish translation.
Songs are added to a generalized hashtable that is checked each time a song is asked to be added. In this implementation, I use the name of the song as the key in which to add to the hashtable. This is the primary reason that unique song names should be used. When attempting to add a song, the hashtable is checked to see if an entry has already been added. Given the notion of hashtables, this should be constant time and at worse O(n) time (if identical song names are used) as it degrades to a list.
This leaves us with the remainder of the operations. Generally hand waiving the API call to add to the playlist as constant time, since it should be adding to the database of songs, we can assume that remaining operations are roughly O(N).
As such here is the plotted time complexity graph for up to 525 songs added at a time. You can see an .html file generated by plotly in images, if you're interested.
Note, because we've assumed that checking if a file exists is constant time, the running time is still approx O(N) as the operation to add scales off the number of files.
Additionally, you times should be faster! To test this, file operations were run on an old HDD approx ~6 years old. This likely means that you should experience better times.
The new import method "neoBeginPlaylistImport" work like this :
- first it get all the module settings in variables for later uses
- Then it initialize a FILO (first in, last out) stack by adding the setted "Base music directory" first
- then it start the main import loop, it pop an element (dad folder) from the stack and "work" on it
- It make the child folders of the dad folder, and put them in the stack as well for recursivity
- make the playlist for the dad folder (let call it dad playlist)
- import all audio files in the dad playlist
It also do a lot of other work in the main loop such as :
- tagging folders and playlists so we can delete only what the module imported
- creating and updating the progress bar prompt
- checking for duplicates, skipping empty folders, filtering to import only audio files, sanitize audio names...
Here some explain on how the progress bar work : all the creation is on the "_playlistStatusPrompt" method
- first it count with the helper method "_countTotalAudioFiles" all folders, playlists and audio files in the directories structure
- Then it create all DOM elements (the texts, the progress bar, a div that contain all)
- Make the FoundryVTT Dialog that will be the popup/prompt
- Finally return a big JS Object that contain all elements to update the progress bar in the prompt (the totals, the current values and the FoundryVtt Dialog object)
And for updating this big object we use another method that we call in the import loop "_incrementPlaylistStatusPromptProgressBar", we pass the big object "_playlistStatusPrompt" return to us and the increment we want to add to our counters and the method will update the values and the text of our progress Dialog/prompt.
This module have a DROP feature (as in Drag&Drop, you drag audio files and drop them) for audio files on the in game AUDIO tab of FoundryVTT three behaviours exist for now
PLAIN, FOLDER and PLAYLIST the behaviours is chosen depending on where you DROP the audio files
a good explanation with image is explained here : drag and drop issue
for PLAIN :
- If you drag and drop audio files into The "Plain" of the foundryVTT audio tab
- It will create a new "Dropped_Audios" playlist and import the files into it (quite simple)
- it will upload the audio files into the configured folder set in the module settings
for FOLDER :
- If you drop the audio files on a folder
- It will create again a new "Dropped_Audios" playlist in this specific folder and import the audio files in this created playlist
- it will also create the directory into the foundryVTT server system and upload the audio files inside for better management
for PLAYLIST :
- If you drag the audio files and drop them on a already existing playlist
- It will import the audio files into this specific playlist
- it will upload the audio files into the configured folder set in the module settings
it start on the hook for when the PLAYLIST/AUDIO tab appear on the in game screen
linked to this method : hookRenderPlaylistDirectory
the first step is to add the event listener
website for a quick tuto on event listener
to the directoriesList DOM Element, on the in game screen, it represent the big space in the TAB where are displayed all the playlists and audios
when this directoriesList Element receive a drop event we act.
come the difficult part, we parse all the elements that can be put inside this directoriesList father element
theres a lot of other DOM Element (text, div...) all theses element are different depending on if there is a Folder, or a playlists or an audio files...
so I parsed a lot manually with experimentation and the debug/dev mode of my navigator...
the two things we must receive from that big parsing and engineering are :
- The
targetTypewhich tell us if user DROP the files on a playlist, on the plain father element or a folder element, this element will tell us which behaviours to adopt / what to do next on the code. - And the name of which Playlist of Folder (
targettedPlaylistName/targettedFolderName) the user DROP the files on, this element will be used to know on which playlist/folder we must import the audio files and I got theses element by experimenting a lot too.
once this big step passed we can pass all the informations into another function that will manage the import and upload of the audio files.
the _DropImportAudioFiles of the PLIMP object.
we pass :
- the
targetType - the
targettedFolderName - the
targettedPlaylistName - and the
audioFiles
and now on the _DropImportAudioFiles method, we use same brick of code to adapt our behaviours based on the targetType, based of what said before on the drag and drop part of the document, we take a lot of infos and variables from the settings, then we create some folder or playlist, create a directory on the system for the folder part, and at last we can launch the upload of the audio files and then Import them once on the server.
npm installdev will let you develop you own code with hot reloading on the browser
npm run devbuild will build and set up a symlink between dist and your dataPath.
npm run buildbuild-watch will build and watch for changes, rebuilding automatically.
npm run build-watchprettier-format launch the prettier plugin based on the configuration here
npm run-script prettier-formatAny issues, bugs, or feature requests are always welcome to be reported directly to the Issue Tracker, or using the Bug Reporter Module.
This package is under an MIT license and the Foundry Virtual Tabletop Limited License Agreement for module development.
Bootstrapped with League of Extraordinary FoundryVTT Developers foundry-vtt-types.
Thanks to anyone who helps me with this code! I appreciate the user community's feedback on this project!




