diff --git a/code/__DEFINES/jukebox.dm b/code/__DEFINES/jukebox.dm new file mode 100644 index 00000000000..29b4cdea7c9 --- /dev/null +++ b/code/__DEFINES/jukebox.dm @@ -0,0 +1,4 @@ +#define CONFIG_JUKEBOX_SOUNDS "[global.config.directory]/jukebox_music/sounds/" + +#define JUKEBOX_NAME 1 +#define JUKEBOX_BEATS 2 diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm index a990e41cda1..fc2fca91a2d 100644 --- a/code/__HELPERS/files.dm +++ b/code/__HELPERS/files.dm @@ -175,7 +175,7 @@ GLOBAL_VAR_INIT(fileaccess_timer, 0) var/extstart = findlasttext("[file]", ".") if(!extstart) return "[file]" - var/ext = copytext("[file]", extstart) + var/ext = copytext("[file]", extstart + 1) if(ext in file_types) return copytext("[file]", 1, extstart) return "[file]" diff --git a/code/datums/components/jukebox.dm b/code/datums/components/jukebox.dm index 36c3b9d311b..07004dd05b3 100644 --- a/code/datums/components/jukebox.dm +++ b/code/datums/components/jukebox.dm @@ -95,16 +95,26 @@ var/static/list/config_songs if(isnull(config_songs)) config_songs = list() - var/list/tracks = flist("[global.config.directory]/jukebox_music/sounds/") + var/list/tracks = flist(CONFIG_JUKEBOX_SOUNDS) for(var/track_file in tracks) var/datum/track/new_track = new() - new_track.song_path = file("[global.config.directory]/jukebox_music/sounds/[track_file]") + new_track.song_path = file("[CONFIG_JUKEBOX_SOUNDS][track_file]") var/list/track_data = splittext(track_file, "+") - if(length(track_data) < 3 || !IS_SOUND_FILE(new_track.song_path)) + if(!length(track_data) || !IS_SOUND_FILE_SAFE(new_track.song_path)) continue - new_track.song_name = track_data[1] - new_track.song_length = text2num(track_data[2]) - new_track.song_beat = text2num(track_data[3]) + var/track_name = track_data[JUKEBOX_NAME] + track_name = strip_filepath_extension(track_name, SSsounds.safe_formats) + new_track.song_name = track_name + new_track.song_length = SSsounds.get_sound_length(new_track.song_path) + if(track_data.len >= 3) // Bandaid for legacy tracks to not use the length for the bpm rather then the actual beats. + var/static/logged_to_admins = FALSE + log_game("[new_track.song_path] track data seems to be using the legacy format; we will attempt to make it work.") + if(!logged_to_admins) + message_admins("The jukebox has tracks uploaded in a legacy format. Length is now fetched programmatically, with title and beats being the only required fields.") + logged_to_admins = TRUE + new_track.song_beat_deciseconds = text2num(track_data[3]) + else if(track_data.len >= 2) + new_track.song_beat_deciseconds = text2num(track_data[JUKEBOX_BEATS]) config_songs[new_track.song_name] = new_track if(!length(config_songs)) @@ -128,7 +138,7 @@ UNTYPED_LIST_ADD(songs_data, list( \ "name" = song_name, \ "length" = DisplayTimeText(one_song.song_length), \ - "beat" = one_song.song_beat, \ + "beat" = one_song.song_beat_deciseconds || "Unknown", \ )) data["active"] = !!active_song_sound @@ -399,11 +409,12 @@ var/song_length = 0 /// How long is a beat of the song in decisconds /// Used to determine time between effects when played - var/song_beat = 0 + /// Do note this is NOT BPM. + var/song_beat_deciseconds = 0 // Default track supplied for testing and also because it's a banger /datum/track/default song_path = 'sound/music/lobby_music/title3.ogg' song_name = "Tintin on the Moon" song_length = 3 MINUTES + 52 SECONDS - song_beat = 1 SECONDS + song_beat_deciseconds = 1 SECONDS diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm index 29b2ea7cf98..dadc83331b4 100644 --- a/code/game/machinery/dance_machine.dm +++ b/code/game/machinery/dance_machine.dm @@ -337,7 +337,7 @@ glow.even_cycle = !glow.even_cycle if(prob(2)) // Unique effects for the dance floor that show up randomly to mix things up INVOKE_ASYNC(src, PROC_REF(hierofunk)) - sleep(music_player.selection.song_beat) + sleep(music_player.selection.song_beat_deciseconds || 1 SECONDS) if(QDELETED(src)) return diff --git a/code/modules/admin/verbs/jukebox_moderation.dm b/code/modules/admin/verbs/jukebox_moderation.dm new file mode 100644 index 00000000000..4b7bf35594c --- /dev/null +++ b/code/modules/admin/verbs/jukebox_moderation.dm @@ -0,0 +1,57 @@ +ADMIN_VERB(upload_jukebox_music, R_SERVER, "Jukebox Upload Music", "Upload a valid .ogg file to be accessed via the jukebox.", ADMIN_CATEGORY_SERVER) + var/file = input(user, "Select a .ogg file to upload to the jukebox.") as sound|null + if(!file) + return + + // we could theorticly support other sound types but OGG is the better format from what I am aware and I am 100% sure its length is properly fetched. + if(!IS_OGG_FILE(file)) + tgui_alert(user, "Invalid file type. Please select an OGG file.", "Loading error", list("Ok")) + return + + var/list/track_data = splittext(file, "+") + if(track_data.len < 2) + if(tgui_alert(user, "Your song currently does not have a beat in deciseconds added to its title, e.g: SS13+5.ogg. Continue?", "Confirmation", list("Yes", "No")) != "Yes") + return + if(track_data.len > 2) + tgui_alert(user, "Titles should only have its title and beat in deciseconds, e.g: SS13+5.ogg", "Loading error", list("Ok")) + return + + + var/clean_name = SANITIZE_FILENAME("[file]") + var/save_path = "[CONFIG_JUKEBOX_SOUNDS][clean_name]" + + // Copy uploaded file to the server + fcopy(file, save_path) + + message_admins("[key_name_admin(user)] uploaded [clean_name] to the jukebox!") + to_chat(user, span_notice("Successfully uploaded [clean_name]!")) + +ADMIN_VERB(browse_jukebox_music, R_SERVER, "Jukebox Browse Music", "Browse music files for moderation.", ADMIN_CATEGORY_SERVER) + var/list/files = flist(CONFIG_JUKEBOX_SOUNDS) + // Filter out things that are not sound files, like the exclude + for(var/thing in files) + if(!IS_SOUND_FILE(thing)) + files -= thing + if(!files.len) + to_chat(user, span_warning("No uploaded tracks found.")) + return + + var/choice = tgui_input_list(user, "Select a track:", "Select Jukebox Music", files) + if(!choice) + return + + var/path = "[CONFIG_JUKEBOX_SOUNDS][choice]" + + switch(tgui_alert(user, "Play, Delete, or Download?", choice, list("Play", "Delete", "Download"))) + if ("Play") + SEND_SOUND(user, sound(path)) + if ("Delete") + fdel(path) + var/msg = "[key_name_admin(user)] deleted [choice] from the jukebox!" + message_admins(msg) + log_admin(msg) + SSblackbox.record_feedback("associative", "jukebox_deletion", 1, list("round_id" = "[GLOB.round_id]", "deletor" = "[key_name_admin(user)]", "deleted" = "[choice]")) + if ("Download") + user << ftp(file(path)) + else + return diff --git a/config/jukebox_music/README.txt b/config/jukebox_music/README.txt index 97027c657da..a89c1162d02 100644 --- a/config/jukebox_music/README.txt +++ b/config/jukebox_music/README.txt @@ -1,5 +1,5 @@ The enclosed /sounds folder holds the sound files used for player selectable songs for an ingame jukebox. -OGG is the recommended sound format but see code/controllers/subsystem/sounds.dm for the rest of the supported ones. +OGG, WAV, and MP3 are supported. (I recommend verifying this is up to date with the list of `IS_SOUND_FILE_SAFE`) Using unnecessarily huge sounds can cause client side lag and should be avoided. @@ -11,6 +11,8 @@ Naming Conventions: Every sound you add must have a unique name. Avoid using the plus sign "+" and the period "." in names, as these are used internally to classify sounds. -Sound names must be in the format of [song name]+[length in deciseconds]+[beat in deciseconds].ogg +Sound names must be in the format of [song name]+[beat in deciseconds].ogg -A three minute song title "SS13" that lasted 3 minutes would have a file name SS13+1800+5.ogg +beat is recommended but the code does not require it as its only used in the disco jukebox and UI + +A song title "SS13" would have a file name SS13+5.ogg or SS13.ogg diff --git a/tgstation.dme b/tgstation.dme index 98717f175e2..0af5f8389a1 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -131,6 +131,7 @@ #include "code\__DEFINES\ipintel.dm" #include "code\__DEFINES\is_helpers.dm" #include "code\__DEFINES\jobs.dm" +#include "code\__DEFINES\jukebox.dm" #include "code\__DEFINES\keybinding.dm" #include "code\__DEFINES\lag_switch.dm" #include "code\__DEFINES\language.dm" @@ -3171,6 +3172,7 @@ #include "code\modules\admin\verbs\hiddenprints.dm" #include "code\modules\admin\verbs\highlander_datum.dm" #include "code\modules\admin\verbs\individual_logging.dm" +#include "code\modules\admin\verbs\jukebox_moderation.dm" #include "code\modules\admin\verbs\lawpanel.dm" #include "code\modules\admin\verbs\light_debug.dm" #include "code\modules\admin\verbs\list_exposer.dm"