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

Adds Play Internet Sound #28183

Merged
merged 19 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions code/__HELPERS/shell.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//Runs the command in the system's shell, returns a list of (error code, stdout, stderr)

#define SHELLEO_NAME "data/shelleo."
#define SHELLEO_ERR ".err"
#define SHELLEO_OUT ".out"
/world/proc/shelleo(command)
var/static/list/shelleo_ids = list()
var/stdout = ""
var/stderr = ""
var/errorcode = 1
var/shelleo_id
var/out_file = ""
var/err_file = ""
var/static/list/interpreters = list("[MS_WINDOWS]" = "cmd /c", "[UNIX]" = "sh -c")
var/interpreter = interpreters["[world.system_type]"]
if(interpreter)
for(var/seo_id in shelleo_ids)
if(!shelleo_ids[seo_id])
shelleo_ids[seo_id] = TRUE
shelleo_id = "[seo_id]"
break
if(!shelleo_id)
shelleo_id = "[shelleo_ids.len + 1]"
shelleo_ids += shelleo_id
shelleo_ids[shelleo_id] = TRUE
out_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_OUT]"
err_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_ERR]"
errorcode = shell("[interpreter] \"[command]\" > [out_file] 2> [err_file]")
if(fexists(out_file))
stdout = file2text(out_file)
fdel(out_file)
if(fexists(err_file))
stderr = file2text(err_file)
fdel(err_file)
shelleo_ids[shelleo_id] = FALSE
else
CRASH("Operating System: [world.system_type] not supported") // If you encounter this error, you are encouraged to update this proc with support for the new operating system
. = list(errorcode, stdout, stderr)
#undef SHELLEO_NAME
#undef SHELLEO_ERR
#undef SHELLEO_OUT

/proc/shell_url_scrub(url)
var/static/regex/bad_chars_regex = regex("\[^#%&./:=?\\w]*", "g")
var/scrubbed_url = ""
var/bad_match = ""
var/last_good = 1
var/bad_chars = 1
do
bad_chars = bad_chars_regex.Find(url)
scrubbed_url += copytext(url, last_good, bad_chars)
if(bad_chars)
bad_match = url_encode(bad_chars_regex.match)
scrubbed_url += bad_match
last_good = bad_chars + length(bad_match)
while(bad_chars)
. = scrubbed_url
1 change: 1 addition & 0 deletions code/_globalvars/_regexes.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
GLOBAL_DATUM_INIT(filename_forbidden_chars, /regex, regex(@{""|[\\\n\t/?%*:|<>]|\.\."}, "g"))
GLOBAL_DATUM_INIT(is_color, /regex, regex("^#\[0-9a-fA-F]{6}$"))
GLOBAL_DATUM_INIT(regex_rgb_text, /regex, regex(@"^#?(([0-9a-fA-F]{8})|([0-9a-fA-F]{6})|([0-9a-fA-F]{3}))$"))
GLOBAL_DATUM_INIT(is_http_protocol, /regex, regex("^https?://"))
GLOBAL_PROTECT(filename_forbidden_chars)
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
var/list/region_map = list()
/// Send a system toast on init completion?
var/toast_on_init_complete = FALSE
/// The URL for a ss13-yt-wrap server (https://github.com/Absolucy/ss13-yt-wrap) to use.
var/ytdlp_url = null

/datum/configuration_section/system_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
Expand All @@ -50,6 +52,7 @@
CONFIG_LOAD_STR(internal_ip, data["internal_ip"])

CONFIG_LOAD_STR(override_map, data["override_map"])
CONFIG_LOAD_STR(ytdlp_url, data["ytdlp_url"])

// Load region overrides
if(islist(data["regional_servers"]))
Expand Down
3 changes: 2 additions & 1 deletion code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ GLOBAL_LIST_INIT(admin_verbs_sounds, list(
/client/proc/play_sound,
/client/proc/play_server_sound,
/client/proc/play_intercomm_sound,
/client/proc/stop_global_admin_sounds
/client/proc/stop_global_admin_sounds,
/client/proc/play_web_sound
))
GLOBAL_LIST_INIT(admin_verbs_event, list(
/client/proc/object_talk,
Expand Down
101 changes: 101 additions & 0 deletions code/modules/admin/verbs/playsound.dm
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ GLOBAL_LIST_EMPTY(sounds_cache)
message_admins("[key_name_admin(src)] stopped admin sounds.", 1)
for(var/mob/M in GLOB.player_list)
M << awful_sound
var/client/C = M.client
C?.tgui_panel?.stop_music()

/client/proc/play_sound(S as sound)
set category = "Event"
Expand Down Expand Up @@ -113,3 +115,102 @@ GLOBAL_LIST_EMPTY(sounds_cache)
if(!I.on && !ignore_power)
continue
playsound(I, melody, cvol)

/client/proc/play_web_sound()
set category = "Event"
set name = "Play Internet Sound"
if(!check_rights(R_SOUNDS))
return

if(!GLOB.configuration.system.ytdlp_url)
to_chat(src, "<span class='boldwarning'>yt-dlp was not configured, action unavailable</span>") //Check config
return


var/web_sound_input = tgui_input_text(src, "Enter content URL (supported sites only, leave blank to stop playing)", "Play Internet Sound", null)
if(istext(web_sound_input))
var/web_sound_url = ""
var/stop_web_sounds = FALSE
var/list/music_extra_data = list()
if(length(web_sound_input))
web_sound_input = trim(web_sound_input)
if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol))
to_chat(src, "<span class='boldwarning'>Non-http(s) URIs are not allowed.</span>")
to_chat(src, "<span class='warning'>For yt-dlp shortcuts like ytsearch: please use the appropriate full url from the website.</span>")
return

// Prepare the body
var/list/request_body = list("url" = web_sound_input)

// Send the request off
var/datum/http_request/media_poll_request = new()
// The fact we are using GET with a body offends me
media_poll_request.prepare(RUSTG_HTTP_METHOD_GET, GLOB.configuration.system.ytdlp_url, json_encode(request_body))
// Start it off and wait
media_poll_request.begin_async()
UNTIL(media_poll_request.is_complete())
var/datum/http_response/media_poll_response = media_poll_request.into_response()

if(media_poll_response.status_code == 200)
var/list/data
try
data = json_decode(media_poll_response.body)
catch(var/exception/e)
to_chat(src, "<span class='boldwarning'>yt-dlp JSON parsing FAILED:</span>")
to_chat(src, "<span class='warning'>[e]: [media_poll_response.body]</span>")
return

if(data["sound_url"])
web_sound_url = data["sound_url"]
var/title = "[data["title"]]"
var/webpage_url = title
if(data["webpage_url"])
webpage_url = "<a href=\"[data["webpage_url"]]\">[title]</a>"
music_extra_data["start"] = data["start"]
music_extra_data["end"] = data["end"]
music_extra_data["link"] = data["webpage_url"]
music_extra_data["title"] = data["title"]

var/res = tgui_alert(src, "Show the title of and link to this song to the players?\n[title]", "Show Info?", list("Yes", "No", "Cancel"))
switch(res)
if("Yes")
to_chat(world, "<span class='boldannounceooc'>[src.ckey] played: [webpage_url]</span>")
if("No")
music_extra_data["link"] = "Song Link Hidden"
music_extra_data["title"] = "Song Title Hidden"
music_extra_data["artist"] = "Song Artist Hidden"
to_chat(world, "<span class='boldannounceooc'>[src.ckey] played an internet sound</span>")
if("Cancel")
return

SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Internet Sound")
log_admin("[key_name(src)] played web sound: [web_sound_input]")
message_admins("[key_name(src)] played web sound: [web_sound_input]")

else
to_chat(src, "<span class='boldwarning'>yt-dlp URL retrieval FAILED:</span>")
to_chat(src, "<span class='warning'>[media_poll_response.body]</span>")

else //pressed ok with blank
log_admin("[key_name(src)] stopped web sound")
message_admins("[key_name(src)] stopped web sound")
web_sound_url = null
stop_web_sounds = TRUE

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

if(web_sound_url || stop_web_sounds)
for(var/mob/M in GLOB.player_list)
var/client/C = M.client
var/this_uid = M.client.UID()
if(C.prefs.toggles & SOUND_MIDI)
if(ckey in M.client.prefs.admin_sound_ckey_ignore)
return
if(!stop_web_sounds)
C.tgui_panel?.play_music(web_sound_url, music_extra_data)
to_chat(M, "(<a href='byond://?src=[this_uid];action=silenceSound'>SILENCE</a>) (<a href='byond://?src=[this_uid];action=muteAdmin&a=[ckey]'>ALWAYS SILENCE THIS ADMIN</a>)</span>")
else
C.tgui_panel?.stop_music()
2 changes: 2 additions & 0 deletions code/modules/client/client_procs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,12 @@

if("silenceSound")
usr.stop_sound_channel(CHANNEL_ADMIN)
tgui_panel?.stop_music()
return

if("muteAdmin")
usr.stop_sound_channel(CHANNEL_ADMIN)
tgui_panel?.stop_music()
prefs.admin_sound_ckey_ignore |= href_list["a"]
to_chat(usr, "You will no longer hear admin playsounds from <code>[href_list["a"]]</code>. To remove them, go to Preferences --&gt; <code>Manage Admin Sound Mutes</code>.")
prefs.save_preferences(src)
Expand Down
1 change: 1 addition & 0 deletions code/modules/client/preference/preferences_toggles.dm
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@
set category = "Special Verbs"
set desc = "Silence the current admin midi playing"
usr.stop_sound_channel(CHANNEL_ADMIN)
tgui_panel?.stop_music()
to_chat(src, "The current admin midi has been silenced")

/datum/preference_toggle/toggle_runechat
Expand Down
2 changes: 2 additions & 0 deletions code/modules/tgui/tgui_panel/audio.dm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
/datum/tgui_panel/proc/play_music(url, extra_data)
if(!is_ready())
return
if(!findtext(url, GLOB.is_http_protocol))
return
var/list/payload = list()
if(length(extra_data) > 0)
for(var/key in extra_data)
Expand Down
2 changes: 2 additions & 0 deletions config/example/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,8 @@ regional_servers = [
]
# Send a toast on server init completion. You probably dont need this on in production
toast_on_init_complete = true
# The URL for a ss13-yt-wrap server (https://github.com/Absolucy/ss13-yt-wrap) to use.
#ytdlp_url = "http://ytdlpserverurlhere"


################################################################
Expand Down
5 changes: 3 additions & 2 deletions paradise.dme
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
#include "code\__HELPERS\radiation_helpers.dm"
#include "code\__HELPERS\sanitize_values.dm"
#include "code\__HELPERS\screen_objs.dm"
#include "code\__HELPERS\shell.dm"
#include "code\__HELPERS\stacktrace.dm"
#include "code\__HELPERS\string_assoc_lists.dm"
#include "code\__HELPERS\text.dm"
Expand Down Expand Up @@ -453,8 +454,8 @@
#include "code\datums\components\footstep.dm"
#include "code\datums\components\forces_doors_open.dm"
#include "code\datums\components\fullauto.dm"
#include "code\datums\components\jetpack_component.dm"
#include "code\datums\components\ghost_direct_control.dm"
#include "code\datums\components\jetpack_component.dm"
#include "code\datums\components\label.dm"
#include "code\datums\components\largeobjecttransparency.dm"
#include "code\datums\components\material_container.dm"
Expand Down Expand Up @@ -1532,8 +1533,8 @@
#include "code\modules\antagonists\_common\antag_hud.dm"
#include "code\modules\antagonists\_common\antag_spawner.dm"
#include "code\modules\antagonists\_common\antag_team.dm"
#include "code\modules\antagonists\abductor\team_abductor.dm"
#include "code\modules\antagonists\abductor\datum_abductor.dm"
#include "code\modules\antagonists\abductor\team_abductor.dm"
#include "code\modules\antagonists\antag_org\antag_org_datum.dm"
#include "code\modules\antagonists\antag_org\antag_org_syndicate.dm"
#include "code\modules\antagonists\changeling\changeling_power.dm"
Expand Down