From 4d6165b2180bd3487aff1770029a5d368be87cd5 Mon Sep 17 00:00:00 2001 From: RemixDev <RemixDev64@gmail.com> Date: Tue, 13 Apr 2021 18:40:34 +0200 Subject: [PATCH] Apply settings to track and generate filename & filepath --- deemix/downloader.js | 55 ++++++-- deemix/settings.js | 105 ++++++++++++++ deemix/types/Album.js | 4 +- deemix/types/Artist.js | 2 +- deemix/types/Track.js | 85 +++++++++++- deemix/utils/pathtemplates.js | 253 ++++++++++++++++++++++++++++++++++ 6 files changed, 485 insertions(+), 19 deletions(-) create mode 100644 deemix/settings.js create mode 100644 deemix/utils/pathtemplates.js diff --git a/deemix/downloader.js b/deemix/downloader.js index 43fb7dc..0f3741a 100644 --- a/deemix/downloader.js +++ b/deemix/downloader.js @@ -2,9 +2,22 @@ const { Track, AlbumDoesntExists } = require('./types/Track.js') const { streamTrack, generateStreamURL } = require('./decryption.js') const { TrackFormats } = require('deezer-js') const { USER_AGENT_HEADER } = require('./utils/index.js') +const { DEFAULTS } = require('./settings.js') +const { generatePath } = require('./utils/pathtemplates.js') const got = require('got') const fs = require('fs') +const extensions = { + [TrackFormats.FLAC]: '.flac', + [TrackFormats.LOCAL]: '.mp3', + [TrackFormats.MP3_320]: '.mp3', + [TrackFormats.MP3_128]: '.mp3', + [TrackFormats.DEFAULT]: '.mp3', + [TrackFormats.MP4_RA3]: '.mp4', + [TrackFormats.MP4_RA2]: '.mp4', + [TrackFormats.MP4_RA1]: '.mp4' +} + async function getPreferredBitrate(track, bitrate, shouldFallback, uuid, listener){ bitrate = parseInt(bitrate) if (track.localTrack) { return TrackFormats.LOCAL } @@ -12,17 +25,17 @@ async function getPreferredBitrate(track, bitrate, shouldFallback, uuid, listene let falledBack = false const formats_non_360 = { - "FLAC": TrackFormats.FLAC, - "MP3_320": TrackFormats.MP3_320, - "MP3_128": TrackFormats.MP3_128 + [TrackFormats.FLAC]: "FLAC", + [TrackFormats.MP3_320]: "MP3_320", + [TrackFormats.MP3_128]: "MP3_128" } const formats_360 = { - "MP4_RA3": TrackFormats.MP4_RA3, - "MP4_RA2": TrackFormats.MP4_RA2, - "MP4_RA1": TrackFormats.MP4_RA1 + [TrackFormats.MP4_RA3]: "MP4_RA3", + [TrackFormats.MP4_RA2]: "MP4_RA2", + [TrackFormats.MP4_RA1]: "MP4_RA1" } - const is360Format = Object.values(formats_360).indexOf(bitrate) != -1 + const is360Format = Object.keys(formats_360).indexOf(bitrate) != -1 let formats if (!shouldFallback){ formats = {...formats_360, ...formats_non_360} @@ -33,8 +46,8 @@ async function getPreferredBitrate(track, bitrate, shouldFallback, uuid, listene } for (let i = 0; i < Object.keys(formats).length; i++){ - let formatName = Object.keys(formats)[i] - let formatNumber = formats[formatName] + let formatNumber = Object.keys(formats)[i] + let formatName = formats[formatNumber] if (formatNumber > bitrate) { continue } if (Object.keys(track.filesizes).indexOf(`FILESIZE_${formatName}`) != -1){ @@ -88,7 +101,7 @@ class Downloader { constructor(dz, downloadObject, settings, listener){ this.dz = dz this.downloadObject = downloadObject - this.settings = settings + this.settings = settings || DEFAULTS this.bitrate = downloadObject.bitrate this.listener = listener @@ -165,12 +178,26 @@ class Downloader { track.bitrate = selectedFormat track.album.bitrate = selectedFormat - // Generate covers URLs // Apply Settings + track.applySettings(this.settings) + // Generate filename and filepath from metadata - // Remove Subfolders from filename and add it to filepath + let { + filename, + filepath, + artistPath, + coverPath, + extrasPath + } = generatePath(track, this.downloadObject, this.settings) + // Make sure the filepath exsists + fs.mkdirSync(filepath, { recursive: true }) + let writepath = `${filepath}/${filename}${extensions[track.bitrate]}` + // Save extrasPath + if (extrasPath && !this.extrasPath) this.extrasPath = extrasPath + + // Generate covers URLs // Download and cache the coverart // Save local album art // Save artist art @@ -181,9 +208,9 @@ class Downloader { // Download the track console.log("Downloading") track.downloadURL = generateStreamURL(track.id, track.MD5, track.mediaVersion, track.bitrate) - let stream = fs.createWriteStream('./writepath') + let stream = fs.createWriteStream(writepath) await streamTrack(stream, track, 0, this.downloadObject, this.listener) - console.log("done") + console.log(filename) // Adding tags } diff --git a/deemix/settings.js b/deemix/settings.js new file mode 100644 index 0000000..d95fd77 --- /dev/null +++ b/deemix/settings.js @@ -0,0 +1,105 @@ +const { TrackFormats } = require('deezer-js') + +// Should the lib overwrite files? +const OverwriteOption = { + OVERWRITE: 'y', // Yes, overwrite the file + DONT_OVERWRITE: 'n', // No, don't overwrite the file + DONT_CHECK_EXT: 'e', // No, and don't check for extensions + KEEP_BOTH: 'b', // No, and keep both files + ONLY_TAGS: 't' // Overwrite only the tags +} + +// What should I do with featured artists? +const FeaturesOption = { + NO_CHANGE: "0", // Do nothing + REMOVE_TITLE: "1", // Remove from track title + REMOVE_TITLE_ALBUM: "3", // Remove from track title and album title + MOVE_TITLE: "2" // Move to track title +} + +const DEFAULTS = { + downloadLocation: "", + tracknameTemplate: "%artist% - %title%", + albumTracknameTemplate: "%tracknumber% - %title%", + playlistTracknameTemplate: "%position% - %artist% - %title%", + createPlaylistFolder: true, + playlistNameTemplate: "%playlist%", + createArtistFolder: false, + artistNameTemplate: "%artist%", + createAlbumFolder: true, + albumNameTemplate: "%artist% - %album%", + createCDFolder: true, + createStructurePlaylist: false, + createSingleFolder: false, + padTracks: true, + paddingSize: "0", + illegalCharacterReplacer: "_", + queueConcurrency: 3, + maxBitrate: String(TrackFormats.MP3_320), + fallbackBitrate: true, + fallbackSearch: false, + logErrors: true, + logSearched: false, + saveDownloadQueue: false, + overwriteFile: OverwriteOption.DONT_OVERWRITE, + createM3U8File: false, + playlistFilenameTemplate: "playlist", + syncedLyrics: false, + embeddedArtworkSize: 800, + embeddedArtworkPNG: false, + localArtworkSize: 1400, + localArtworkFormat: "jpg", + saveArtwork: true, + coverImageTemplate: "cover", + saveArtworkArtist: false, + artistImageTemplate: "folder", + jpegImageQuality: 80, + dateFormat: "Y-M-D", + albumVariousArtists: true, + removeAlbumVersion: false, + removeDuplicateArtists: false, + tagsLanguage: "", + featuredToTitle: FeaturesOption.NO_CHANGE, + titleCasing: "nothing", + artistCasing: "nothing", + executeCommand: "", + tags: { + title: true, + artist: true, + album: true, + cover: true, + trackNumber: true, + trackTotal: false, + discNumber: true, + discTotal: false, + albumArtist: true, + genre: true, + year: true, + date: true, + explicit: false, + isrc: true, + length: true, + barcode: true, + bpm: true, + replayGain: false, + label: true, + lyrics: false, + syncedLyrics: false, + copyright: false, + composer: false, + involvedPeople: false, + source: false, + savePlaylistAsCompilation: false, + useNullSeparator: false, + saveID3v1: true, + multiArtistSeparator: "default", + singleAlbumArtist: false, + coverDescriptionUTF8: false + } +} + +module.exports = { + OverwriteOption, + FeaturesOption, + DEFAULTS +} diff --git a/deemix/types/Album.js b/deemix/types/Album.js index b106d9c..bd291d6 100644 --- a/deemix/types/Album.js +++ b/deemix/types/Album.js @@ -14,7 +14,7 @@ class Album { this.artist = {"Main": []} this.artists = [] this.mainArtist = null - this.date = new Date() + this.date = null this.dateString = "" this.trackTotal = "0" this.discTotal = "0" @@ -84,6 +84,7 @@ class Album { this.label = albumAPI.label || this.label this.explicit = Boolean(albumAPI.explicit_lyrics || false) if (albumAPI.release_date){ + this.date = new Date() this.date.year = albumAPI.release_date.substring(0,4) this.date.month = albumAPI.release_date.substring(5,7) this.date.day = albumAPI.release_date.substring(8,10) @@ -126,6 +127,7 @@ class Album { this.pic.md5 = albumAPI_gw.ALB_PICTURE } if (albumAPI_gw.PHYSICAL_RELEASE_DATE){ + this.date = new Date() this.date.year = albumAPI_gw.PHYSICAL_RELEASE_DATE.substring(0,4) this.date.month = albumAPI_gw.PHYSICAL_RELEASE_DATE.substring(5,7) this.date.day = albumAPI_gw.PHYSICAL_RELEASE_DATE.substring(8,10) diff --git a/deemix/types/Artist.js b/deemix/types/Artist.js index e739867..4570189 100644 --- a/deemix/types/Artist.js +++ b/deemix/types/Artist.js @@ -10,7 +10,7 @@ class Artist { this.save = true } - ifVariousArtist(){ + isVariousArtists(){ return this.id == VARIOUS_ARTISTS } } diff --git a/deemix/types/Track.js b/deemix/types/Track.js index 07db178..227bde4 100644 --- a/deemix/types/Track.js +++ b/deemix/types/Track.js @@ -5,6 +5,8 @@ const { Playlist } = require('./Playlist.js') const { Picture } = require('./Picture.js') const { Lyrics } = require('./Lyrics.js') const { VARIOUS_ARTISTS } = require('./index.js') +const { changeCase } = require('../utils/index.js') +const { FeaturesOption } = require('../settings.js') const { generateReplayGainString, @@ -41,7 +43,6 @@ class Track { this.position = null this.searched = false this.bitrate = 0 - this.singleDownload = false this.dateString = "" this.artistsString = "" this.mainArtistsString = "" @@ -277,8 +278,86 @@ class Track { } applySettings(settings){ - // TODO: Applay settings - settings; + // Check if should save the playlist as a compilation + if (settings.tags.savePlaylistAsCompilation && this.playlist){ + this.trackNumber = this.position + this.discNumber = "1" + this.album.makePlaylistCompilation(this.playlist) + } else { + if (this.album.date) this.date = this.album.date + } + this.dateString = this.date.format(settings.dateFormat) + this.album.dateString = this.album.date.format(settings.dateFormat) + if (this.playlist) this.playlist.dateString = this.playlist.date.format(settings.dateFormat) + + // Check various artist option + if (settings.albumVariousArtists && this.album.variousArtists){ + let artist = this.album.variousArtists + let isMainArtist = artist.role === "Main" + + if (this.album.artists.indexOf(artist.name) == -1) + this.album.artists.push(artist.name) + + if (isMainArtist || !this.album.artsit.Main.contains(artist.name) && !isMainArtist){ + if (!this.album.artist[artist.role]) + this.album.artist[artist.role] = [] + this.album.artist[artist.role].push(artist.name) + } + } + this.album.mainArtist.save = (!this.album.mainArtist.isVariousArtists() || settings.albumVariousArtists && this.album.mainArtist.isVariousArtists()) + + // Check removeDuplicateArtists + if (settings.removeDuplicateArtists) this.removeDuplicateArtists() + + // Check if user wants the feat in the title + if (settings.featuredToTitle == FeaturesOption.REMOVE_TITLE){ + this.title = this.getCleanTitle() + }else if (settings.featuredToTitle == FeaturesOption.MOVE_TITLE){ + this.title = this.getFeatTitle() + }else if (settings.featuredToTitle == FeaturesOption.REMOVE_TITLE_ALBUM){ + this.title = this.getCleanTitle() + this.album.title = this.album.getCleanTitle() + } + + // Remove (Album Version) from tracks that have that + if (settings.removeAlbumVersion && this.title.indexOf("Album Version") != -1){ + this.title = this.title.replace(/ ?\(Album Version\)/g, '').trim() + } + + // Change title and artist casing if needed + if (settings.titleCasing != "nothing"){ + this.title = changeCase(this.title, settings.titleCasing) + } else if (settings.artistCasing != "nothing"){ + this.mainArtist.name = changeCase(this.mainArtist.name, settings.artistCasing) + this.artists.forEach((artist, i) => { + this.artists[i] = changeCase(artist, settings.artistCasing) + }) + Object.keys(this.artist).forEach((art_type) => { + this.artist[art_type].forEach((artist, i) => { + this.artist[art_type][i] = changeCase(artist, settings.artistCasing) + }) + }) + } + + // Generate artist tag + if (settings.tags.multiArtistSeparator == "default"){ + if (settings.featuredToTitle == FeaturesOption.MOVE_TITLE){ + this.artistsString = this.artist.Main.join(", ") + } else { + this.artistString = this.artists.join(", ") + } + } else if (settings.tags.multiArtistSeparator == "andFeat"){ + this.artsitsString = this.mainArtistsString + if (this.featArtistsString && settings.featuredToTitle != FeaturesOption.MOVE_TITLE) + this.artistsString += ` ${this.featArtistsString}` + } else { + let separator = settings.tags.multiArtistSeparator + if (settings.featuredToTitle == FeaturesOption.MOVE_TITLE){ + this.artsitsString = this.artsit.Main.join(separator) + } else { + this.artsitsString = this.artists.join(separator) + } + } } } diff --git a/deemix/utils/pathtemplates.js b/deemix/utils/pathtemplates.js new file mode 100644 index 0000000..0162cdc --- /dev/null +++ b/deemix/utils/pathtemplates.js @@ -0,0 +1,253 @@ +const { TrackFormats } = require('deezer-js') + +const bitrateLabels = { + [TrackFormats.MP4_RA3]: "360 HQ", + [TrackFormats.MP4_RA2]: "360 MQ", + [TrackFormats.MP4_RA1]: "360 LQ", + [TrackFormats.FLAC] : "FLAC", + [TrackFormats.MP3_320]: "320", + [TrackFormats.MP3_128]: "128", + [TrackFormats.DEFAULT]: "128", + [TrackFormats.LOCAL] : "MP3" +} + +function fixName(txt, char='_'){ + txt = txt+"" + txt = txt.replace(/[\0/\\:*?"<>|]/g, char) + return txt.normalize('NFC') +} + +function fixLongName(name){ + if (name.indexOf('/') != -1){ + let sepName = name.split('/') + name = "" + sepName.forEach((txt) => { + txt = fixLongName(txt) + name += `${txt}/` + }) + name = name.slice(0, -1) + } else { + name = name.substring(0, 200) + } + return name +} + +function antiDot(str){ + while(str[str.length-1] == "." || str[str.length-1] == " " || str[str.length-1] == "\n"){ + str = str.substring(0,str.length-1); + } + if(str.length < 1){ + str = "dot"; + } + return str; +} + +function pad(num, max_val, settings) { + let paddingSize; + if (parseInt(settings.paddingSize) == 0) { + paddingSize = (max_val+"").length + } else{ + paddingSize = ((10 ** (parseInt(settings.paddingSize) - 1))+"").length + } + if (paddingSize == 1) paddingSize = 2 + + if (settings.padTracks) return (num+"").padStart(paddingSize, "0") + return (num+"") +} + +function generatePath(track, downloadObject, settings){ + + let filenameTemplate = "%artist% - %title%"; + let singleTrack = false + if (downloadObject.type === "track"){ + if (settings.createSingleFolder) filenameTemplate = settings.albumTracknameTemplate + else filenameTemplate = settings.tracknameTemplate + singleTrack = true + } else if (downloadObject.type === "album") { + filenameTemplate = settings.albumTracknameTemplate + } else { + filenameTemplate = settings.playlistTracknameTemplate + } + + let filename = generateTrackName(filenameTemplate, track, settings) + let filepath, artistPath, coverPath, extrasPath + + filepath = settings.downloadLocation || "." + + if (settings.createPlaylistFolder && track.playlist && !settings.tags.savePlaylistAsCompilation) + filepath += `/${generatePlaylistName(settings.playlistNameTemplate, track.playlist, settings)}` + + if (track.playlist && !settings.tags.savePlaylistAsCompilation) + extrasPath = filepath + + if ( + (settings.createArtistFolder && !track.playlist) || + (settings.createArtistFolder && track.playlist && settings.tags.savePlaylistAsCompilation) || + (settings.createArtistFolder && track.playlist && settings.createStructurePlaylist) + ){ + filepath += `/${generateArtistName(settings.artistNameTemplate, track.album.mainAritst, settings, track.album.rootArtist)}` + artistPath = filepath + } + + + if (settings.createAlbumFolder && + (!singleTrack || singleTrack && settings.createSingleFolder) && + (!track.playlist || + (track.playlist && settings.tags.savePlaylistAsCompilation) || + (track.playlist && settings.createStructurePlaylist) + ) + ){ + filepath += `/${generateAlbumName(settings.albumNameTemplate, track.album, settings, track.playlist)}` + coverPath = filepath + } + + if (!extrasPath) extrasPath = filepath + + if ( + parseInt(track.album.discTotal) > 1 && ( + (settings.createAlbumFolder && settings.createCDFolder) && + (!singleTrack || (singleTrack && settings.createSingleFolder)) && + (!track.playlist || + (track.playlist && settings.tags.savePlaylistAsCompilation)) || + (track.playlist && settings.createStructurePlaylist) + ) + ) + filepath += `/CD${track.discNumber}` + + // Remove Subfolders from filename and add it to filepath + if (filename.indexOf('/') != -1){ + let tempPath = filename.substring(0, filename.indexOf('/')) + filepath += `/${tempPath}` + filename = filename.substring(tempPath.length+1) + } + + return { + filename, + filepath, + artistPath, + coverPath, + extrasPath + } +} + +function generateTrackName(filename, track, settings){ + let c = settings.illegalCharacterReplacer + filename = filename.replaceAll("%title%", fixName(track.title, c)) + filename = filename.replaceAll("%artist%", fixName(track.mainArtist.name, c)) + filename = filename.replaceAll("%artists%", fixName(track.artists.join(", "), c)) + filename = filename.replaceAll("%allartists%", fixName(track.artistsString, c)) + filename = filename.replaceAll("%mainartists%", fixName(track.mainArtistsString, c)) + if (track.featArtistsString) filename = filename.replaceAll("%featartists%", fixName('('+track.featArtistsString+')', c)) + else filename = filename.replaceAll("%featartists%", '') + filename = filename.replaceAll("%album%", fixName(track.album.title, c)) + filename = filename.replaceAll("%albumartist%", fixName(track.album.mainArtist.name, c)) + filename = filename.replaceAll("%tracknumber%", pad(track.trackNumber, track.album.trackTotal, settings)) + filename = filename.replaceAll("%tracktotal%", track.album.trackTotal) + filename = filename.replaceAll("%discnumber%", track.discNumber) + filename = filename.replaceAll("%disctotal%", track.album.discTotal) + if (track.album.genre.length) filename = filename.replaceAll("%genre%", fixName(track.album.genre[0], c)) + else filename = filename.replaceAll("%genre%", "Unknown") + filename = filename.replaceAll("%year%", track.date.year) + filename = filename.replaceAll("%date%", track.dateString) + filename = filename.replaceAll("%bpm%", track.bpm) + filename = filename.replaceAll("%label%", fixName(track.album.label, c)) + filename = filename.replaceAll("%isrc%", track.ISRC) + filename = filename.replaceAll("%upc%", track.album.barcode) + filename = filename.replaceAll("%explicit%", track.explicit ? "(Explicit)" : "") + + filename = filename.replaceAll("%track_id%", track.id) + filename = filename.replaceAll("%album_id%", track.album.id) + filename = filename.replaceAll("%artist_id%", track.mainArtist.id) + if (track.playlist){ + filename = filename.replaceAll("%playlist_id%", track.playlist.playlistID) + filename = filename.replaceAll("%position%", pad(track.position, track.playlist.trackTotal, settings)) + } else { + filename = filename.replaceAll("%playlist_id%", '') + filename = filename.replaceAll("%position%", pad(track.position, track.album.trackTotal, settings)) + } + filename = filename.replaceAll('\\', '/') + return antiDot(fixLongName(filename)) +} + +function generateAlbumName(foldername, album, settings, playlist){ + let c = settings.illegalCharacterReplacer + if (playlist && settings.tags.savePlaylistAsCompilation){ + foldername = foldername.replaceAll("%album_id%", "pl_" + playlist.playlistID) + foldername = foldername.replaceAll("%genre%", "Compile") + } else { + foldername = foldername.replaceAll("%album_id%", album.id) + if (album.genre.length) foldername = foldername.replaceAll("%genre%", fixName(album.genre[0], c)) + else foldername = foldername.replaceAll("%genre%", "Unknown") + } + foldername = foldername.replaceAll("%album%", fixName(album.title, c)) + foldername = foldername.replaceAll("%artist%", fixName(album.mainArtist.name, c)) + foldername = foldername.replaceAll("%artist_id%", album.mainArtist.id) + if (album.rootArtist){ + foldername = foldername.replaceAll("%root_artist%", fixName(album.rootArtist.name, c)) + foldername = foldername.replaceAll("%root_artist_id%", album.rootArtist.id) + } else { + foldername = foldername.replaceAll("%root_artist%", fixName(album.mainArtist.name, c)) + foldername = foldername.replaceAll("%root_artist_id%", album.mainArtist.id) + } + foldername = foldername.replaceAll("%tracktotal%", album.trackTotal) + foldername = foldername.replaceAll("%disctotal%", album.discTotal) + foldername = foldername.replaceAll("%type%", fixName(album.recordType.capitalize(), c)) + foldername = foldername.replaceAll("%upc%", album.barcode) + foldername = foldername.replaceAll("%explicit%", album.explicit ? "(Explicit)" : "") + foldername = foldername.replaceAll("%label%", fixName(album.label, c)) + foldername = foldername.replaceAll("%year%", album.date.year) + foldername = foldername.replaceAll("%date%", album.dateString) + foldername = foldername.replaceAll("%bitrate%", bitrateLabels[parseInt(album.bitrate)]) + + foldername = foldername.replaceAll('\\', '/') + return antiDot(fixLongName(foldername)) +} + +function generateArtistName(foldername, artist, settings, rootArtist){ + let c = settings['illegalCharacterReplacer'] + foldername = foldername.replaceAll("%artist%", fixName(artist.name, c)) + foldername = foldername.replaceAll("%artist_id%", artist.id) + if (rootArtist){ + foldername = foldername.replaceAll("%root_artist%", fixName(rootArtist.name, c)) + foldername = foldername.replaceAll("%root_artist_id%", rootArtist.id) + } else { + foldername = foldername.replaceAll("%root_artist%", fixName(artist.name, c)) + foldername = foldername.replaceAll("%root_artist_id%", artist.id) + } + foldername = foldername.replaceAll('\\', '/') + return antiDot(fixLongName(foldername)) +} + +function generatePlaylistName(foldername, playlist, settings){ + let c = settings['illegalCharacterReplacer'] + foldername = foldername.replaceAll("%playlist%", fixName(playlist.title, c)) + foldername = foldername.replaceAll("%playlist_id%", fixName(playlist.playlistID, c)) + foldername = foldername.replaceAll("%owner%", fixName(playlist.owner['name'], c)) + foldername = foldername.replaceAll("%owner_id%", playlist.owner['id']) + foldername = foldername.replaceAll("%year%", playlist.date.year) + foldername = foldername.replaceAll("%date%", playlist.dateString) + foldername = foldername.replaceAll("%explicit%", playlist.explicit ? "(Explicit)" : "") + foldername = foldername.replaceAll('\\', '/') + return antiDot(fixLongName(foldername)) +} + +function generateDownloadItemName(foldername, queueItem, settings){ + let c = settings['illegalCharacterReplacer'] + foldername = foldername.replaceAll("%title%", fixName(queueItem.title, c)) + foldername = foldername.replaceAll("%artist%", fixName(queueItem.artist, c)) + foldername = foldername.replaceAll("%size%", queueItem.size) + foldername = foldername.replaceAll("%type%", fixName(queueItem.type, c)) + foldername = foldername.replaceAll("%id%", fixName(queueItem.id, c)) + foldername = foldername.replaceAll("%bitrate%", bitrateLabels[parseInt(queueItem.bitrate)]) + foldername = foldername.replaceAll('\\', '/').replace('/', c) + return antiDot(fixLongName(foldername)) +} + +module.exports = { + generatePath, + generateTrackName, + generateAlbumName, + generateArtistName, + generatePlaylistName, + generateDownloadItemName +}