Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow playing uploaded sounds through the music player w/ Webroot #4934

Merged
merged 11 commits into from
Nov 22, 2023
2 changes: 1 addition & 1 deletion code/__DEFINES/__game.dm
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ block( \
#define SOUND_MIDI (1<<1)
#define SOUND_AMBIENCE (1<<2)
#define SOUND_LOBBY (1<<3)
#define SOUND_INTERNET (1<<4)
#define SOUND_INTERNET (1<<4) // Unused currently. Kept for default prefs compat only
#define SOUND_REBOOT (1<<5)
#define SOUND_ADMIN_MEME (1<<6)
#define SOUND_ADMIN_ATMOSPHERIC (1<<7)
Expand Down
2 changes: 1 addition & 1 deletion code/__DEFINES/sounds.dm
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#define SOUND_CHANNEL_AMBIENCE 1019
#define SOUND_CHANNEL_WALKMAN 1020
#define SOUND_CHANNEL_SOUNDSCAPE 1021
#define SOUND_CHANNEL_ADMIN_MIDI 1022
//#define SOUND_CHANNEL_ADMIN_MIDI 1022
#define SOUND_CHANNEL_LOBBY 1023
#define SOUND_CHANNEL_Z 1024

Expand Down
5 changes: 0 additions & 5 deletions code/datums/soundOutput.dm
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,6 @@
adjust_volume_prefs(VOLUME_AMB, "Set the volume for ambience and soundscapes", 0)
soundOutput.update_ambience(null, null, TRUE)

/client/verb/adjust_volume_admin_music()
set name = "Adjust Volume Admin MIDIs"
set category = "Preferences.Sound"
adjust_volume_prefs(VOLUME_ADM, "Set the volume for admin MIDIs", SOUND_CHANNEL_ADMIN_MIDI)

/client/verb/adjust_volume_lobby_music()
set name = "Adjust Volume LobbyMusic"
set category = "Preferences.Sound"
Expand Down
6 changes: 2 additions & 4 deletions code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,8 @@ var/list/admin_verbs_ban = list(
)

var/list/admin_verbs_sounds = list(
/client/proc/play_web_sound,
/client/proc/play_sound,
/client/proc/stop_web_sound,
/client/proc/stop_sound,
/client/proc/play_admin_sound,
/client/proc/stop_admin_sound,
/client/proc/cmd_admin_vox_panel
)

Expand Down
226 changes: 100 additions & 126 deletions code/modules/admin/verbs/playsound.dm
Original file line number Diff line number Diff line change
@@ -1,45 +1,83 @@
/client/proc/play_web_sound()
/client/proc/play_admin_sound()
set category = "Admin.Fun"
set name = "Play Internet Sound"
set name = "Play Admin Sound"
if(!check_rights(R_SOUNDS))
return

var/ytdl = CONFIG_GET(string/invoke_youtubedl)
if(!ytdl)
to_chat(src, SPAN_BOLDWARNING("Youtube-dl was not configured, action unavailable"), confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value
var/sound_mode = tgui_input_list(src, "Play a sound from which source?", "Select Source", list("Web", "Upload"))
if(!sound_mode)
return

var/web_sound_input = input("Enter content URL (supported sites only)", "Play Internet Sound via youtube-dl") as text|null
if(!istext(web_sound_input) || !length(web_sound_input))
return
var/list/data = list()
var/log_title = TRUE
var/web_sound_input
var/asset_name
var/must_send_assets = FALSE
var/announce_title = TRUE

if(sound_mode == "Web")
var/ytdl = CONFIG_GET(string/invoke_youtubedl)
if(!ytdl)
to_chat(src, SPAN_BOLDWARNING("Youtube-dl was not configured, action unavailable"), confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value
return

web_sound_input = trim(web_sound_input)
web_sound_input = input("Enter content URL (supported sites only)", "Play Internet Sound via youtube-dl") as text|null
if(!istext(web_sound_input) || !length(web_sound_input))
return

if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol))
to_chat(src, SPAN_WARNING("Non-http(s) URIs are not allowed."))
to_chat(src, SPAN_WARNING("For youtube-dl shortcuts like ytsearch: please use the appropriate full url from the website."))
return
web_sound_input = trim(web_sound_input)

var/web_sound_url = ""
var/list/music_extra_data = list()
var/title
if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol))
to_chat(src, SPAN_WARNING("Non-http(s) URIs are not allowed."))
to_chat(src, SPAN_WARNING("For youtube-dl shortcuts like ytsearch: please use the appropriate full url from the website."))
return

var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height<=360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist -- \"[shell_url_scrub(web_sound_input)]\"")
var/errorlevel = output[SHELLEO_ERRORLEVEL]
var/stdout = output[SHELLEO_STDOUT]
var/stderr = output[SHELLEO_STDERR]
var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height<=360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist -- \"[shell_url_scrub(web_sound_input)]\"")
var/errorlevel = output[SHELLEO_ERRORLEVEL]
var/stdout = output[SHELLEO_STDOUT]
var/stderr = output[SHELLEO_STDERR]

if(errorlevel)
to_chat(src, SPAN_WARNING("Youtube-dl URL retrieval FAILED: [stderr]"))
return
if(errorlevel)
to_chat(src, SPAN_WARNING("Youtube-dl URL retrieval FAILED: [stderr]"))
return

var/list/data = list()
try
data = json_decode(stdout)
catch(var/exception/e)
to_chat(src, SPAN_WARNING("Youtube-dl JSON parsing FAILED: [e]: [stdout]"))
return
try
data = json_decode(stdout)
catch(var/exception/e)
to_chat(src, SPAN_WARNING("Youtube-dl JSON parsing FAILED: [e]: [stdout]"))
return

else if(sound_mode == "Upload")
var/current_transport = CONFIG_GET(string/asset_transport)
if(!current_transport || current_transport == "simple")
if(tgui_alert(usr, "WARNING: Your server is using simple asset transport. Sounds will have to be sent directly to players, which may freeze the game for long durations. Are you SURE?", "Really play direct sound?", list("Yes", "No")) != "Yes")
return
must_send_assets = TRUE

var/soundfile = input(usr, "Choose a sound file to play", "Upload Sound") as null|file
if(!soundfile)
return

var/static/regex/only_extension = regex(@{"^.*\.([a-z0-9]{1,5})$"}, "gi")
var/extension = only_extension.Replace("[soundfile]", "$1")
if(!length(extension))
to_chat(src, SPAN_WARNING("Invalid filename extension."))
return

var/static/playsound_notch = 1
asset_name = "admin_sound_[playsound_notch++].[extension]"
SSassets.transport.register_asset(asset_name, soundfile)
message_admins("[key_name_admin(src)] uploaded admin sound '[soundfile]' to asset transport.")

var/static/regex/remove_extension = regex(@{"\.[a-z0-9]+$"}, "gi")
data["title"] = remove_extension.Replace("[soundfile]", "")
data["url"] = SSassets.transport.get_asset_url(asset_name)
web_sound_input = "[soundfile]"
log_title = FALSE

var/title
var/web_sound_url = ""
var/list/music_extra_data = list()
if(data["url"])
music_extra_data["link"] = data["url"]
music_extra_data["title"] = data["title"]
Expand All @@ -48,19 +86,28 @@
music_extra_data["start"] = data["start_time"]
music_extra_data["end"] = data["end_time"]

if(web_sound_url && !findtext(web_sound_url, GLOB.is_http_protocol))
if(!must_send_assets && web_sound_url && !findtext(web_sound_url, GLOB.is_http_protocol))
to_chat(src, SPAN_BOLDWARNING("BLOCKED: Content URL not using http(s) protocol"), confidential = TRUE)
to_chat(src, SPAN_WARNING("The media provider returned a content URL that isn't using the HTTP or HTTPS protocol"), confidential = TRUE)
return

switch(tgui_alert(src, "Show the name of this sound to the players?", "Sound Name", list("Yes","No","Cancel")))
if("No")
music_extra_data["title"] = "Admin sound"
announce_title = FALSE
if("Cancel")
return

var/list/targets = list()
var/list/sound_type_list = list(
"Meme" = SOUND_ADMIN_MEME,
"Atmospheric" = SOUND_ADMIN_ATMOSPHERIC
)
var/style = tgui_input_list(src, "Who do you want to play this to?", "Select Listeners", list("Globally", "Xenos", "Marines", "Ghosts", "All Inview", "Single Inview"))

var/style = tgui_input_list(src, "Who do you want to play this to?", "Select Listeners", list("Globally", "Xenos", "Marines", "Ghosts", "All In View Range", "Single Mob"))
var/sound_type = tgui_input_list(src, "What kind of sound is this?", "Select Sound Type", sound_type_list)
sound_type = sound_type_list[sound_type]

switch(style)
if("Globally")
targets = GLOB.mob_list
Expand All @@ -70,30 +117,40 @@
targets = GLOB.human_mob_list + GLOB.dead_mob_list
if("Ghosts")
targets = GLOB.observer_list + GLOB.dead_mob_list
if("All Inview")
targets = viewers(usr.client.view, src)
if("Single Inview")
var/mob/choice = tgui_input_list(src, "Select the mob to play to:","Select Mob", sortmobs())
if("All In View Range")
var/list/atom/ranged_atoms = urange(usr.client.view, get_turf(usr))
for(var/mob/receiver in ranged_atoms)
targets += receiver
if("Single Mob")
var/list/mob/all_mobs = sortmobs()
var/list/mob/all_client_mobs = list()
for(var/mob/mob in all_mobs)
if(mob.client)
all_client_mobs += mob
var/mob/choice = tgui_input_list(src, "Select the mob to play to:","Select Mob", all_client_mobs)
if(QDELETED(choice))
return
targets.Add(choice)
else
return

for(var/i in targets)
var/mob/M = i
var/client/client = M?.client
if((client?.prefs.toggles_sound & SOUND_INTERNET) && (client?.prefs.toggles_sound & sound_type))
for(var/mob/mob as anything in targets)
var/client/client = mob?.client
if((client?.prefs?.toggles_sound & SOUND_MIDI) && (client?.prefs?.toggles_sound & sound_type))
if(must_send_assets)
SSassets.transport.send_assets(client, asset_name)
client?.tgui_panel?.play_music(web_sound_url, music_extra_data)
if(announce_title)
to_chat(client, SPAN_BOLDANNOUNCE("An admin played: [music_extra_data["title"]]"), confidential = TRUE)
else
client?.tgui_panel?.stop_music()

log_admin("[key_name(src)] played web sound: [web_sound_input] - [title] - [style]")
message_admins("[key_name_admin(src)] played web sound: [web_sound_input] - [title] - [style]")
log_admin("[key_name(src)] played admin sound: [web_sound_input] -[log_title ? " [title] -" : ""] [style]")
message_admins("[key_name_admin(src)] played admin sound: [web_sound_input] -[log_title ? " [title] -" : ""] [style]")

/client/proc/stop_web_sound()
/client/proc/stop_admin_sound()
set category = "Admin.Fun"
set name = "Stop Internet Sounds"
set name = "Stop Admin Sounds"

if(!check_rights(R_SOUNDS))
return
Expand All @@ -105,86 +162,3 @@
log_admin("[key_name(src)] stopped the currently playing web sounds.")
message_admins("[key_name_admin(src)] stopped the currently playing web sounds.")

/client/proc/play_sound(S as sound)
set category = "Admin.Fun"
set name = "Play Midi Sound"
if(!check_rights(R_SOUNDS))
return

var/freq = 1
var/vol = tgui_input_number(src, "What volume would you like the sound to play at?", "Volume", 25, 100, 1)
if(!vol)
return
vol = clamp(vol, 1, 100)

var/sound/admin_sound = new()
admin_sound.file = S
admin_sound.priority = 250
admin_sound.channel = SOUND_CHANNEL_ADMIN_MIDI
admin_sound.frequency = freq
admin_sound.wait = 1
admin_sound.repeat = FALSE
admin_sound.status = SOUND_STREAM
admin_sound.volume = vol

var/showtitle = FALSE
var/res = alert(src, "Show the title of this song to the players?",, "Yes","No", "Cancel")
switch(res)
if("Yes")
showtitle = TRUE
if("Cancel")
return

var/list/targets = list()
var/list/sound_type_list = list(
"Meme" = SOUND_ADMIN_MEME,
"Atmospheric" = SOUND_ADMIN_ATMOSPHERIC
)
var/style = tgui_input_list(src, "Who do you want to play this to?", "Select Listeners", list("Globally", "Xenos", "Marines", "Ghosts", "All Inview", "Single Inview"))
var/sound_type = tgui_input_list(src, "What kind of sound is this?", "Select Sound Type", sound_type_list)
sound_type = sound_type_list[sound_type]
switch(style)
if("Globally")
targets = GLOB.mob_list
if("Xenos")
targets = GLOB.xeno_mob_list + GLOB.dead_mob_list
if("Marines")
targets = GLOB.human_mob_list + GLOB.dead_mob_list
if("Ghosts")
targets = GLOB.observer_list + GLOB.dead_mob_list
if("All Inview")
targets = viewers(usr.client.view, src)
if("Single Inview")
var/mob/choice = tgui_input_list(src, "Select the mob to play to:","Select Mob", sortmobs())
if(QDELETED(choice))
return
targets.Add(choice)
else
return

for(var/items in targets)
var/mob/Mob = items
var/client/client = Mob?.client
if((client?.prefs.toggles_sound & SOUND_INTERNET) && (client?.prefs.toggles_sound & sound_type))
admin_sound.volume = vol * client?.admin_music_volume
SEND_SOUND(Mob, admin_sound)
admin_sound.volume = vol
if(showtitle)
to_chat(client, SPAN_BOLDANNOUNCE("An admin played: [S]"), confidential = TRUE)

log_admin("[key_name(src)] played midi sound [S] - [style]")
message_admins("[key_name_admin(src)] played midi sound [S] - [style]")

/client/proc/stop_sound()
set category = "Admin.Fun"
set name = "Stop Midi Sounds"

if(!check_rights(R_SOUNDS))
return

for(var/mob/M in GLOB.player_list)
if(M.client)
SEND_SOUND(M, sound(null))

log_admin("[key_name(src)] stopped midi sounds.")
message_admins("[key_name_admin(src)] stopped midi sounds.")
10 changes: 4 additions & 6 deletions code/modules/client/preferences.dm
Original file line number Diff line number Diff line change
Expand Up @@ -586,8 +586,7 @@ var/const/MAX_SAVE_SLOTS = 10
dat += "<b>Tooltips:</b> <a href='?_src_=prefs;preference=tooltips'><b>[tooltips ? "Enabled" : "Disabled"]</b></a><br>"
dat += "<b>tgui Window Mode:</b> <a href='?_src_=prefs;preference=tgui_fancy'><b>[(tgui_fancy) ? "Fancy (default)" : "Compatible (slower)"]</b></a><br>"
dat += "<b>tgui Window Placement:</b> <a href='?_src_=prefs;preference=tgui_lock'><b>[(tgui_lock) ? "Primary monitor" : "Free (default)"]</b></a><br>"
dat += "<b>Play Admin Midis:</b> <a href='?_src_=prefs;preference=hear_midis'><b>[(toggles_sound & SOUND_MIDI) ? "Yes" : "No"]</b></a><br>"
dat += "<b>Play Admin Internet Sounds:</b> <a href='?_src_=prefs;preference=hear_internet'><b>[(toggles_sound & SOUND_INTERNET) ? "Yes" : "No"]</b></a><br>"
dat += "<b>Play Admin Sounds:</b> <a href='?_src_=prefs;preference=hear_admin_sounds'><b>[(toggles_sound & SOUND_MIDI) ? "Yes" : "No"]</b></a><br>"
dat += "<b>Toggle Meme or Atmospheric Sounds:</b> <a href='?src=\ref[src];action=proccall;procpath=/client/proc/toggle_admin_sound_types'>Toggle</a><br>"
dat += "<b>Set Eye Blur Type:</b> <a href='?src=\ref[src];action=proccall;procpath=/client/proc/set_eye_blur_type'>Set</a><br>"
dat += "<b>Play Lobby Music:</b> <a href='?_src_=prefs;preference=lobby_music'><b>[(toggles_sound & SOUND_LOBBY) ? "Yes" : "No"]</b></a><br>"
Expand Down Expand Up @@ -1807,11 +1806,10 @@ var/const/MAX_SAVE_SLOTS = 10
if("rand_body")
be_random_body = !be_random_body

if("hear_midis")
if("hear_admin_sounds")
toggles_sound ^= SOUND_MIDI

if("hear_internet")
toggles_sound ^= SOUND_INTERNET
if(!(toggles_sound & SOUND_MIDI))
user?.client?.tgui_panel?.stop_music()

if("lobby_music")
toggles_sound ^= SOUND_LOBBY
Expand Down
21 changes: 3 additions & 18 deletions code/modules/client/preferences_toggles.dm
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,10 @@
to_chat(src, "You will [(prefs.toggles_sound & SOUND_REBOOT) ? "now" : "no longer"] hear server reboot sounds.")

/client/verb/togglemidis()
set name = "Silence Current Midi"
set name = "Silence Current Admin Sound"
set category = "Preferences.Sound"
set desc = "Toggles hearing sounds uploaded by admins"
// prefs.toggles_sound ^= SOUND_MIDI // Toggle on/off
// prefs.save_preferences() // We won't save the change - it'll be a temporary switch instead of permanent, but they can still make it permanent in character setup.
if(prefs.toggles_sound & SOUND_MIDI) // Not using && midi_playing here - since we can't tell how long an admin midi is, the user should always be able to turn it off at any time.
to_chat(src, SPAN_BOLDNOTICE("The currently playing midi has been silenced."))
var/sound/break_sound = sound(null, repeat = 0, wait = 0, channel = SOUND_CHANNEL_ADMIN_MIDI)
break_sound.priority = 250
src << break_sound //breaks the client's sound output on SOUND_CHANNEL_ADMIN_MIDI
if(src.mob.client.midi_silenced) return
if(midi_playing)
total_silenced++
message_admins("A player has silenced the currently playing midi. Total: [total_silenced] player(s).", 1)
src.mob.client.midi_silenced = 1
spawn(30 SECONDS) // Prevents message_admins() spam. Should match with the midi_playing_timer spawn() in playsound.dm
src.mob.client.midi_silenced = 0
else
to_chat(src, SPAN_BOLDNOTICE("You have 'Play Admin Midis' disabled in your Character Setup, so this verb is useless to you."))
set desc = "Stops the current admin sound. You can also use the STOP icon in the player above tgchat."
tgui_panel?.stop_music()

/client/verb/togglechat()
set name = "Toggle Abovehead Chat"
Expand Down
Loading