Skip to content

Commit

Permalink
Ports virgo's Jukebox interface / overhauls the old one, portable mus…
Browse files Browse the repository at this point in the history
…ic players and more jukebox songs / some fixes (Oh my!) (#5996)

<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

## About The Pull Request

<!-- Describe The Pull Request. Please be sure every change is
documented or this can delay review and even discourage maintainers from
merging your PR! -->

This ports over and makes the jukebox media player into a subsystem wile
also overhauling / porting over virgo's tgui interface for jukeboxes.

This PR also brings over the Podzu music player and makes it available
to all players in cargo, loadouts, and venders.

It also has a function that is not at the moment working where you can
add more music as an admin. But if someone is willing to fix the admin
system I can see about making that function work instead of leaving it
commented out.

I will in another PR go over more of the broken songs and eliminate or
fix them. (I listen to music heavily when playing here)
## Why It's Good For The Game

<!-- Argue for the merits of your changes and how they benefit the game,
especially if they are controversial and/or far reaching. If you can't
actually explain WHY what you are doing will improve the game, then it
probably isn't good for the game in the first place. -->

The current Jukebox GUI is horrible and the current media player system
is not modular what so ever and is clunky to use, but the port of
virgo's jukebox UI is not really perfect, however it is leagues better
than Nano. (PLEASE I SUFFERED TO PORT IT)

Portable music players are really nice and sharing tunes with fellow
players is sweet.
## Changelog

<!-- If your PR modifies aspects of the game that can be concretely
observed by players or admins you should add a changelog. If your change
does NOT meet this description, remove this section. Be sure to properly
mark your PRs to prevent unnecessary GBP loss. You can read up on GBP
and it's effects on PRs in the tgstation guides for contributors. Please
note that maintainers freely reserve the right to remove and add tags
should they deem it appropriate. You can attempt to finagle the system
all you want, but it's best to shoot for clear communication right off
the bat. -->

:cl:
add: Portable music players!, and improved Jukebox UI
tweak: jukebox.json
qol: Jukebox ui is not as clunky
fix: Jukebox ui
soundadd: Jukebox music?
code: media players, jukebox ui, and portable music player ui
refactor: refactored media players (they are their own subsystem now!)
config: Added more songs and also configured changes to the jukebox.json
to work with the changes
admin: some jukebox code (idk if what I did caused anything)
/:cl:

<!-- Both :cl:'s are required for the changelog to work! You can put
your name to the right of the first :cl: if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->
  • Loading branch information
pixelkitty286 authored Sep 29, 2023
1 parent 2a71046 commit ad0df33
Show file tree
Hide file tree
Showing 14 changed files with 1,068 additions and 461 deletions.
2 changes: 2 additions & 0 deletions citadel.dme
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@
#include "code\controllers\subsystem\lobby.dm"
#include "code\controllers\subsystem\machines.dm"
#include "code\controllers\subsystem\materials.dm"
#include "code\controllers\subsystem\media_tracks.dm"
#include "code\controllers\subsystem\minimaps.dm"
#include "code\controllers\subsystem\mobs.dm"
#include "code\controllers\subsystem\nanoui.dm"
Expand Down Expand Up @@ -3200,6 +3201,7 @@
#include "code\modules\media\media_player_wmp.dm"
#include "code\modules\media\media_tracks.dm"
#include "code\modules\media\mediamanager.dm"
#include "code\modules\media\walkpod.dm"
#include "code\modules\metric\activity.dm"
#include "code\modules\metric\count.dm"
#include "code\modules\metric\department.dm"
Expand Down
1 change: 1 addition & 0 deletions code/__DEFINES/controllers/_subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ DEFINE_BITFIELD(runlevels, list(
#define INIT_ORDER_SERVER_MAINT 65
#define INIT_ORDER_INSTRUMENTS 50
#define INIT_ORDER_EARLY_ASSETS 48
#define INIT_ORDER_MEDIA_TRACKS 38
#define INIT_ORDER_CHEMISTRY 35
#define INIT_ORDER_MATERIALS 34
#define INIT_ORDER_PHOTOGRAPHY 27
Expand Down
4 changes: 4 additions & 0 deletions code/__HELPERS/sorts/comparators.dm
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ GLOBAL_VAR_INIT(cmp_field, "name")
/proc/cmp_holiday_priority(datum/holiday/A, datum/holiday/B)
return A.priority - B.priority

///Tracks are sorted by genre then by title inside that.
/proc/cmp_media_track_asc(datum/media_track/A, datum/media_track/B)
var/genre_sort = sorttext(B.genre || "Uncategorized", A.genre || "Uncategorized")
return genre_sort || sorttext(B.title, A.title)

//! Line Profiling

Expand Down
195 changes: 195 additions & 0 deletions code/controllers/subsystem/media_tracks.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
SUBSYSTEM_DEF(media_tracks)
name = "Media Tracks"
subsystem_flags = SS_NO_FIRE
init_order = INIT_ORDER_MEDIA_TRACKS

/// Every track, including secret
var/list/all_tracks = list()
/// Non-secret jukebox tracks
var/list/jukebox_tracks = list()
/// Lobby music tracks
var/list/lobby_tracks = list()

/datum/controller/subsystem/media_tracks/Initialize(timeofday)
load_tracks()
sort_tracks()
return ..()

/datum/controller/subsystem/media_tracks/proc/load_tracks()
var/jukebox_track_file = "config_static/jukebox.json"
report_progress("Loading jukebox track: [jukebox_track_file]")

if(!fexists(jukebox_track_file))
error("File not found: [jukebox_track_file]")
return

var/list/jsonData = json_decode(file2text(jukebox_track_file))

if(!istype(jsonData))
error("Failed to read tracks from [jukebox_track_file], json_decode failed.")
return

for(var/entry in jsonData)

// Critical problems that will prevent the track from working
if(!istext(entry["url"]))
error("Jukebox entry in [jukebox_track_file]: bad or missing 'url'. Tracks must have a URL.")
continue
if(!istext(entry["title"]))
error("Jukebox entry in [jukebox_track_file]: bad or missing 'title'. Tracks must have a title.")
continue
if(!isnum(entry["duration"]))
error("Jukebox entry in [jukebox_track_file]: bad or missing 'duration'. Tracks must have a duration (in deciseconds).")
continue

// Noncritical problems, we can keep going anyway, but warn so it can be fixed
if(!istext(entry["artist"]))
warning("Jukebox entry in [jukebox_track_file], [entry["title"]]: bad or missing 'artist'. Please consider crediting the artist.")
if(!istext(entry["genre"]))
warning("Jukebox entry in [jukebox_track_file], [entry["title"]]: bad or missing 'genre'. Please consider adding a genre.")

var/datum/media_track/T = new(entry["url"], entry["title"], entry["duration"], entry["artist"], entry["genre"])

T.secret = entry["secret"] ? 1 : 0
T.lobby = entry["lobby"] ? 1 : 0

all_tracks += T


/datum/controller/subsystem/media_tracks/proc/sort_tracks()
report_progress("Sorting media tracks...")
tim_sort(all_tracks, GLOBAL_PROC_REF(cmp_media_track_asc))

jukebox_tracks.Cut()
lobby_tracks.Cut()

for(var/datum/media_track/T in all_tracks)
if(!T.secret)
jukebox_tracks += T
if(T.lobby)
lobby_tracks += T

//TODO: Needs to work with our bo bo admin system
/*
/datum/controller/subsystem/media_tracks/proc/manual_track_add()
var/client/C = usr.client
if(!check_rights(R_DEBUG|R_FUN))
return
// Required
var/url = tgui_input_text(C, "REQUIRED: Provide URL for track, or paste JSON if you know what you're doing. See code comments.", "Track URL", multiline = TRUE)
if(!url)
return
var/list/json
try
json = json_decode(url)
catch
/**
* Alternatively to using a series of inputs, you can use json and paste it in.
* The json base element needs to be an array, even if it's only one song, so wrap it in []
* The songs are json object literals inside the base array and use these keys:
* "url": the url for the song (REQUIRED) (text)
* "title": the title of the song (REQUIRED) (text)
* "duration": duration of song in 1/10ths of a second (seconds * 10) (REQUIRED) (number)
* "artist": artist of the song (text)
* "genre": artist of the song, REALLY try to match an existing one (text)
* "secret": only on hacked jukeboxes (true/false)
* "lobby": plays in the lobby (true/false)
*/
if(islist(json))
for(var/song in json)
if(!islist(song))
to_chat(C, "<span class='warning'>Song appears to be malformed.</span>")
continue
var/list/songdata = song
if(!songdata["url"] || !songdata["title"] || !songdata["duration"])
to_chat(C, "<span class='warning'>URL, Title, or Duration was missing from a song. Skipping.</span>")
continue
var/datum/media_track/T = new(songdata["url"], songdata["title"], songdata["duration"], songdata["artist"], songdata["genre"], songdata["secret"], songdata["lobby"])
all_tracks += T
report_progress("New media track added by [C]: [T.title]")
sort_tracks()
return
var/title = tgui_input_text(C, "REQUIRED: Provide title for track", "Track Title")
if(!title)
return
var/duration = tgui_input_number(C, "REQUIRED: Provide duration for track (in deciseconds, aka seconds*10)", "Track Duration")
if(!duration)
return
// Optional
var/artist = tgui_input_text(C, "Optional: Provide artist for track", "Track Artist")
if(isnull(artist)) // Cancel rather than empty string
return
var/genre = tgui_input_text(C, "Optional: Provide genre for track (try to match an existing one)", "Track Genre")
if(isnull(genre)) // Cancel rather than empty string
return
var/secret = tgui_alert(C, "Optional: Mark track as secret?", "Track Secret", list("Yes", "Cancel", "No"))
if(secret == "Cancel")
return
else if(secret == "Yes")
secret = TRUE
else
secret = FALSE
var/lobby = tgui_alert(C, "Optional: Mark track as lobby music?", "Track Lobby", list("Yes", "Cancel", "No"))
if(lobby == "Cancel")
return
else if(secret == "Yes")
secret = TRUE
else
secret = FALSE
var/datum/media_track/T = new(url, title, duration, artist, genre)
T.secret = secret
T.lobby = lobby
all_tracks += T
report_progress("New media track added by [C]: [title]")
sort_tracks()
/datum/controller/subsystem/media_tracks/proc/manual_track_remove()
var/client/C = usr.client
if(!check_rights(R_DEBUG|R_FUN))
return
var/track = tgui_input_text(C, "Input track title or URL to remove (must be exact)", "Remove Track")
if(!track)
return
for(var/datum/media_track/T in all_tracks)
if(T.title == track || T.url == track)
all_tracks -= T
qdel(T)
report_progress("Media track removed by [C]: [track]")
sort_tracks()
return
to_chat(C, "<span class='warning>Couldn't find a track matching the specified parameters.</span>")
/datum/controller/subsystem/media_tracks/vv_get_dropdown()
. = ..()
VV_DROPDOWN_OPTION("", "---")
VV_DROPDOWN_OPTION("add_track", "Add New Track")
VV_DROPDOWN_OPTION("remove_track", "Remove Track")
/datum/controller/subsystem/media_tracks/vv_do_topic(list/href_list)
. = ..()
IF_VV_OPTION("add_track")
manual_track_add()
href_list["datumrefresh"] = "\ref[src]"
IF_VV_OPTION("remove_track")
manual_track_remove()
href_list["datumrefresh"] = "\ref[src]"
*/
Loading

0 comments on commit ad0df33

Please sign in to comment.