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 setting user data on tracks #983

Merged
merged 9 commits into from
Dec 2, 2023
Next Next commit
implement track user data
  • Loading branch information
topi314 committed Nov 26, 2023
commit 0ce1ed2a7bb1a5d41f792f40175b860b45442d60
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,24 @@ class PlayerRestHandler(
): ResponseEntity<Player> {
val context = socketContext(socketServer, sessionId)

val encodedTrack = playerUpdate.encodedTrack
if (encodedTrack is Omissible.Present && playerUpdate.identifier is Omissible.Present) {
val track = if (playerUpdate.track.isPresent()) {
playerUpdate.track
} else {
topi314 marked this conversation as resolved.
Show resolved Hide resolved
if (playerUpdate.encodedTrack is Omissible.Present || playerUpdate.identifier is Omissible.Present) {
PlayerUpdateTrack(
playerUpdate.encodedTrack,
playerUpdate.identifier
).toOmissible()
} else {
Omissible.Omitted()
}
}

val encodedTrack = track.ifPresent { it.encoded } ?: Omissible.Omitted()
val identifier = track.ifPresent { it.identifier } ?: Omissible.Omitted()
val userData = track.ifPresent { it.userData } ?: Omissible.Omitted()

if (encodedTrack is Omissible.Present && identifier is Omissible.Present) {
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot specify both encodedTrack and identifier")
}

Expand Down Expand Up @@ -112,25 +128,31 @@ class PlayerRestHandler(

// we handle pause differently for playing new tracks
val paused = playerUpdate.paused
paused.takeIfPresent { encodedTrack is Omissible.Omitted && playerUpdate.identifier is Omissible.Omitted }
paused.takeIfPresent { encodedTrack is Omissible.Omitted && identifier is Omissible.Omitted }
?.let {
player.setPause(it)
}

// we handle userData differently for playing new tracks
userData.takeIfPresent { encodedTrack is Omissible.Omitted && identifier is Omissible.Omitted }
?.let {
player.track?.userData = it
}

playerUpdate.volume.ifPresent {
player.setVolume(it)
}

// we handle position differently for playing new tracks
playerUpdate.position.takeIfPresent { encodedTrack is Omissible.Omitted && playerUpdate.identifier is Omissible.Omitted }
playerUpdate.position.takeIfPresent { encodedTrack is Omissible.Omitted && identifier is Omissible.Omitted }
?.let {
if (player.isPlaying) {
player.seekTo(it)
SocketServer.sendPlayerUpdate(context, player)
}
}

playerUpdate.endTime.takeIfPresent { encodedTrack is Omissible.Omitted && playerUpdate.identifier is Omissible.Omitted }
playerUpdate.endTime.takeIfPresent { encodedTrack is Omissible.Omitted && identifier is Omissible.Omitted }
?.let { endTime ->
val marker = TrackMarker(endTime, TrackEndMarkerHandler(player))
player.track?.setMarker(marker)
Expand All @@ -141,72 +163,77 @@ class PlayerRestHandler(
SocketServer.sendPlayerUpdate(context, player)
}

if (encodedTrack is Omissible.Present || playerUpdate.identifier is Omissible.Present) {
if (encodedTrack is Omissible.Present || identifier is Omissible.Present) {

if (noReplace && player.track != null) {
log.info("Skipping play request because of noReplace")
return ResponseEntity.ok(player.toPlayer(context, pluginInfoModifiers))
}
player.setPause(if (paused is Omissible.Present) paused.value else false)

val track: AudioTrack? = if (encodedTrack is Omissible.Present) {
val newTrack: AudioTrack? = if (encodedTrack is Omissible.Present) {
encodedTrack.value?.let {
decodeTrack(context.audioPlayerManager, it)
}
} else {
val trackFuture = CompletableFuture<AudioTrack>()
val identifier = playerUpdate.identifier as Omissible.Present
context.audioPlayerManager.loadItem(identifier.value, object : AudioLoadResultHandler {
override fun trackLoaded(track: AudioTrack) {
trackFuture.complete(track)
}

override fun playlistLoaded(playlist: AudioPlaylist) {
trackFuture.completeExceptionally(
ResponseStatusException(
HttpStatus.BAD_REQUEST,
"Cannot play a playlist or search result"
context.audioPlayerManager.loadItem(
(identifier as Omissible.Present).value,
object : AudioLoadResultHandler {
override fun trackLoaded(track: AudioTrack) {
trackFuture.complete(track)
}

override fun playlistLoaded(playlist: AudioPlaylist) {
trackFuture.completeExceptionally(
ResponseStatusException(
HttpStatus.BAD_REQUEST,
"Cannot play a playlist or search result"
)
)
)
}

override fun noMatches() {
trackFuture.completeExceptionally(
ResponseStatusException(
HttpStatus.BAD_REQUEST,
"No matches found for identifier"
}

override fun noMatches() {
trackFuture.completeExceptionally(
ResponseStatusException(
HttpStatus.BAD_REQUEST,
"No matches found for identifier"
)
)
)
}

override fun loadFailed(exception: FriendlyException) {
trackFuture.completeExceptionally(
ResponseStatusException(
HttpStatus.INTERNAL_SERVER_ERROR,
exception.message,
getRootCause(exception)
}

override fun loadFailed(exception: FriendlyException) {
trackFuture.completeExceptionally(
ResponseStatusException(
HttpStatus.INTERNAL_SERVER_ERROR,
exception.message,
getRootCause(exception)
)
)
)
}
})
}
})

trackFuture.exceptionally {
throw it
}.join()
}

track?.let {
newTrack?.let {
playerUpdate.position.ifPresent { position ->
track.position = position
newTrack.position = position
}

userData.ifPresent { userData ->
newTrack.userData = userData
}

playerUpdate.endTime.ifPresent { endTime ->
if (endTime != null) {
track.setMarker(TrackMarker(endTime, TrackEndMarkerHandler(player)))
newTrack.setMarker(TrackMarker(endTime, TrackEndMarkerHandler(player)))
}
}

player.play(track)
player.play(newTrack)
player.provideTo(context.getMediaConnection(player))
} ?: player.stop()
}
Expand Down
2 changes: 1 addition & 1 deletion LavalinkServer/src/main/java/lavalink/server/util/util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ fun AudioTrack.toTrack(encoded: String, pluginInfoModifiers: List<AudioPluginInf
acc + jsonObject
}

return Track(encoded, this.toInfo(), pluginInfo)
return Track(encoded, this.toInfo(), pluginInfo, this.userData as? JsonObject ?: JsonObject(emptyMap()))
}

private operator fun JsonObject.plus(other: JsonObject) = JsonObject(toMap() + other.toMap())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ data class Player(
data class Track(
val encoded: String,
val info: TrackInfo,
val pluginInfo: JsonObject
val pluginInfo: JsonObject,
val userData: JsonObject
) : LoadResult.Data

@Serializable
Expand Down Expand Up @@ -64,10 +65,20 @@ data class PlayerState(
val ping: Long
)

@Serializable
data class PlayerUpdateTrack(
val encoded: Omissible<String?> = Omissible.Omitted(),
val identifier: Omissible<String> = Omissible.Omitted(),
val userData: Omissible<JsonObject> = Omissible.Omitted()
)

@Serializable
data class PlayerUpdate(
@Deprecated("Use PlayerUpdateTrack#encoded instead", ReplaceWith("encoded"))
val encodedTrack: Omissible<String?> = Omissible.Omitted(),
@Deprecated("Use PlayerUpdateTrack#identifier instead")
val identifier: Omissible<String> = Omissible.Omitted(),
val track: Omissible<PlayerUpdateTrack> = Omissible.Omitted(),
val position: Omissible<Long> = Omissible.Omitted(),
val endTime: Omissible<Long?> = Omissible.Omitted(),
val volume: Omissible<Int> = Omissible.Omitted(),
Expand Down
Loading