Browse Source

Use Moshi library for JSON

merge-requests/2174/head
Robert Stone 1 year ago
committed by Nicolas Pomepuy
parent
commit
e11cd7d65e
  1. 1
      application/webserver/build.gradle
  2. 135
      application/webserver/src/main/java/org/videolan/vlc/webserver/RemoteAccessRouting.kt
  3. 45
      application/webserver/src/main/java/org/videolan/vlc/webserver/RemoteAccessServer.kt
  4. 5
      application/webserver/src/main/java/org/videolan/vlc/webserver/TranslationMapping.kt
  5. 21
      application/webserver/src/main/java/org/videolan/vlc/webserver/websockets/RemoteAccessWebSockets.kt

1
application/webserver/build.gradle

@ -84,7 +84,6 @@ dependencies {
implementation project(':application:vlc-android')
implementation "io.ktor:ktor:$rootProject.ext.ktorVersion"
implementation "io.ktor:ktor-server-netty:$rootProject.ext.ktorVersion"
implementation "io.ktor:ktor-gson:1.6.8"
implementation "io.ktor:ktor-server-websockets:$rootProject.ext.ktorVersion"
implementation "io.ktor:ktor-server-caching-headers:$rootProject.ext.ktorVersion"
implementation "io.ktor:ktor-server-cors:$rootProject.ext.ktorVersion"

135
application/webserver/src/main/java/org/videolan/vlc/webserver/RemoteAccessRouting.kt

@ -33,7 +33,11 @@ import android.util.Log
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.lifecycle.LiveData
import com.google.gson.Gson
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import io.ktor.http.ContentDisposition
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
@ -64,8 +68,6 @@ import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import org.json.JSONArray
import org.json.JSONObject
import org.videolan.medialibrary.MLServiceLocator
import org.videolan.medialibrary.interfaces.Medialibrary
import org.videolan.medialibrary.interfaces.media.Album
@ -146,15 +148,9 @@ import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStreamWriter
import java.text.DateFormat
import java.util.Date
import java.util.Locale
private val format by lazy {
object : ThreadLocal<DateFormat>() {
override fun initialValue() = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.getDefault())
}
}
/**
* Setup the server routing
*
@ -255,21 +251,13 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
call.respond(HttpStatusCode.Forbidden)
return@get
}
val logs = getLogsFiles().sortedBy { File(it.path).lastModified() }.reversed()
val jsonArray = JSONArray()
for (log in logs) {
val json = JSONObject()
json.put("path", log.path)
json.put("date", format.get()?.format(File(log.path).lastModified()))
json.put("type", log.type)
jsonArray.put(json)
}
call.respondJson(jsonArray.toString())
val logs = getLogsFiles().sortedByDescending { it.date }
call.respondJson(convertToJson(logs))
}
// Get the translation string list
get("/translation") {
call.respondJson(TranslationMapping.generateTranslations(appContext.getContextWithLocale(AppContextProvider.locale)))
call.respondJson(convertToJson(TranslationMapping.generateTranslations(appContext.getContextWithLocale(AppContextProvider.locale))))
}
get("/secure-url") {
call.respondText(RemoteAccessServer.getInstance(appContext).getSecureUrl(call))
@ -442,7 +430,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
}
val result = RemoteAccessServer.VideoListResult(list, groupTitle)
call.respondJson(Gson().toJson(result))
call.respondJson(convertToJson(result))
}
get("/longpolling") {
//Empty the queue if needed
@ -450,27 +438,27 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
val queue = mutableListOf<RemoteAccessServer.WSMessage>().apply {
RemoteAccessWebSockets.messageQueue.drainTo(this)
}
call.respondJson(Gson().toJson(queue))
call.respondJson(convertToJson(queue))
return@get
}
//block the request until a message is received
// The 3 second timeout is to avoid blocking forever
try {
val message = withTimeout(3000) { RemoteAccessWebSockets.onPlaybackEventChannel.receive() }
if (message.contains("\"type\":\"browser-description\"")) {
call.respondJson("[$message]")
if (message.type == RemoteAccessServer.WSMessageType.BROWSER_DESCRIPTION) {
call.respondJson(convertToJson(listOf(message)))
return@get
}
} catch (e: TimeoutCancellationException) {
// Fall through to the next block of code
}
val remoteAccessServer = RemoteAccessServer.getInstance(appContext)
val messages = arrayListOf<String>()
remoteAccessServer.generatePlayQueue()?.let { playQueue -> messages.add(Gson().toJson(playQueue)) }
val isPlaying = PlaylistManager.showAudioPlayer.value ?: false
messages.add(Gson().toJson(PlayerStatus(isPlaying)))
remoteAccessServer.generateNowPlaying()?.let { it1 -> messages.add(Gson().toJson(it1)) }
call.respondJson("[${messages.joinToString(",")}]")
val messages = listOfNotNull(
remoteAccessServer.generatePlayQueue(),
PlayerStatus(PlaylistManager.showAudioPlayer.value ?: false),
remoteAccessServer.generateNowPlaying()
)
call.respondJson(convertToJson(messages))
}
// Manage playback events
get("/playback-event") {
@ -499,7 +487,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
albums.forEach { album ->
list.add(album.toPlayQueueItem())
}
call.respondJson(Gson().toJson(list))
call.respondJson(convertToJson(list))
}
// List of all the artists
get("/artist-list") {
@ -514,7 +502,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
artists.forEach { artist ->
list.add(artist.toPlayQueueItem(appContext))
}
call.respondJson(Gson().toJson(list))
call.respondJson(convertToJson(list))
}
// List of all the audio tracks
get("/track-list") {
@ -529,7 +517,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
tracks.forEach { track ->
list.add(track.toPlayQueueItem(defaultArtist = appContext.getString(R.string.unknown_artist)))
}
call.respondJson(Gson().toJson(list))
call.respondJson(convertToJson(list))
}
// List of all the audio genres
get("/genre-list") {
@ -544,7 +532,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
genres.forEach { genre ->
list.add(genre.toPlayQueueItem(appContext))
}
call.respondJson(Gson().toJson(list))
call.respondJson(convertToJson(list))
}
// Get an album details
get("/album") {
@ -562,7 +550,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
list.add(track.toPlayQueueItem(album.albumArtist))
}
val result = RemoteAccessServer.AlbumResult(list, album.title)
call.respondJson(Gson().toJson(result))
call.respondJson(convertToJson(result))
}
// Get a genre details
get("/genre") {
@ -580,7 +568,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
list.add(track.toPlayQueueItem())
}
val result = RemoteAccessServer.AlbumResult(list, genre.title)
call.respondJson(Gson().toJson(result))
call.respondJson(convertToJson(result))
}
// Get an playlist details
get("/playlist") {
@ -600,7 +588,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
})
}
val result = RemoteAccessServer.PlaylistResult(list, playlist.title)
call.respondJson(Gson().toJson(result))
call.respondJson(convertToJson(result))
}
// Create a new playlist
post("/playlist-create") {
@ -700,7 +688,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
list.add(album.toPlayQueueItem())
}
val result = RemoteAccessServer.ArtistResult(list, listOf(), artist.title)
call.respondJson(Gson().toJson(result))
call.respondJson(convertToJson(result))
}
// List of all the playlists
get("/playlist-list") {
@ -715,7 +703,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
playlists.forEach { playlist ->
list.add(playlist.toPlayQueueItem(appContext))
}
call.respondJson(Gson().toJson(list))
call.respondJson(convertToJson(list))
}
// Search media
get("/search") {
@ -742,11 +730,11 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
result.tracks?.filterNotNull()?.map { it.toPlayQueueItem() }
?: listOf(),
)
call.respondJson(Gson().toJson(results))
call.respondJson(convertToJson(results))
}
}
call.respondJson(Gson().toJson(RemoteAccessServer.SearchResults(listOf(), listOf(), listOf(), listOf(), listOf(), listOf())))
call.respondJson(convertToJson(RemoteAccessServer.SearchResults(listOf(), listOf(), listOf(), listOf(), listOf(), listOf())))
}
// List of all the file storages
get("/storage-list") {
@ -769,7 +757,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
call.respond(HttpStatusCode.InternalServerError)
return@get
}
call.respondJson(Gson().toJson(list))
call.respondJson(convertToJson(list))
}
// List of all the file favorites
get("/favorite-list") {
@ -789,7 +777,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
call.respond(HttpStatusCode.InternalServerError)
return@get
}
call.respondJson(Gson().toJson(list))
call.respondJson(convertToJson(list))
}
get("/history") {
verifyLogin(settings)
@ -811,7 +799,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
call.respond(HttpStatusCode.InternalServerError)
return@get
}
call.respondJson(Gson().toJson(list))
call.respondJson(convertToJson(list))
}
// List of all the network shares
get("/network-list") {
@ -826,7 +814,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
list.add(RemoteAccessServer.PlayQueueItem(3000L + index, mediaLibraryItem.title, " ", 0, mediaLibraryItem.artworkMrl
?: "", false, "", (mediaLibraryItem as MediaWrapper).uri.toString(), true, favorite = mediaLibraryItem.isFavorite))
}
call.respondJson(Gson().toJson(list))
call.respondJson(convertToJson(list))
}
get("/stream-list") {
verifyLogin(settings)
@ -838,7 +826,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
list.add(RemoteAccessServer.PlayQueueItem(3000L + index, mediaLibraryItem.title, " ", 0, mediaLibraryItem.artworkMrl
?: "", false, "", (mediaLibraryItem as MediaWrapper).uri.toString(), true, favorite = mediaLibraryItem.isFavorite))
}
call.respondJson(Gson().toJson(list))
call.respondJson(convertToJson(list))
}
//list of folders and files in a path
get("/browse-list") {
@ -912,7 +900,7 @@ fun Route.setupRouting(appContext: Context, scope: CoroutineScope) {
}
val result = RemoteAccessServer.BrowsingResult(list, breadcrumbItems)
call.respondJson(Gson().toJson(result))
call.respondJson(convertToJson(result))
}
// Resume playback
get("/resume-playback") {
@ -1377,21 +1365,22 @@ private suspend fun getLogsFiles(): List<LogFile> = withContext(Dispatchers.IO)
val folder = File(AndroidDevices.EXTERNAL_PUBLIC_DIRECTORY)
val files = folder.listFiles()
files.forEach {
if (it.isFile && it.name.startsWith("vlc_logcat_")) result.add(LogFile(it.path, if (it.name.startsWith("vlc_logcat_remote_access")) "web" else "device"))
if (it.isFile && it.name.startsWith("vlc_logcat_"))
result.add(LogFile(it.path, if (it.name.startsWith("vlc_logcat_remote_access")) "web" else "device", Date(it.lastModified())))
}
val crashFolder = File(AppContextProvider.appContext.getExternalFilesDir(null)!!.absolutePath )
val crashFiles = crashFolder.listFiles()
crashFiles.forEach {
if (it.isFile && it.name.startsWith("vlc_crash")) result.add(LogFile(it.path, "crash"))
if (it.isFile && it.name.startsWith("vlc_crash"))
result.add(LogFile(it.path, "crash", Date(it.lastModified())))
}
return@withContext result
}
data class LogFile(val path:String, val type:String)
data class LogFile(val path: String, val type: String, val date: Date)
private suspend fun getMediaFromProvider(provider: BrowserProvider, dataset: LiveDataset<MediaLibraryItem>): Pair<List<MediaLibraryItem>, ArrayList<Pair<Int, String>>> {
dataset.await()
@ -1505,6 +1494,46 @@ private suspend fun getProviderContent(context:Context, provider: BrowserProvide
return list
}
fun convertToJson(data: Any?): String {
if (data == null) return "{}"
val moshi = Moshi.Builder().build()
val adapter = moshi.adapter<Any>(data::class.java)
return adapter.toJson(data)
}
inline fun <reified K, reified V> convertToJson(data: Map<K,V>?): String {
val moshi = Moshi.Builder().build()
val type = Types.newParameterizedType(MutableMap::class.java, K::class.java, V::class.java)
val adapter = moshi.adapter<Map<K,V>>(type).nullSafe()
return adapter.toJson(data)
}
inline fun <reified T> convertToJson(data: List<T>?): String {
val moshi = Moshi.Builder()
.add(Date::class.java, FormattedDateJsonAdapter().nullSafe())
.build()
val type = Types.newParameterizedType(MutableList::class.java, Any::class.java)
val adapter = moshi.adapter<List<T>>(type).nullSafe()
return adapter.toJson(data)
}
private val format by lazy {
object : ThreadLocal<DateFormat>() {
override fun initialValue() = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.getDefault())
}
}
class FormattedDateJsonAdapter : JsonAdapter<Date>() {
override fun fromJson(reader: JsonReader): Date? {
val string = reader.nextString()
return format.get().parse(string)
}
override fun toJson(writer: JsonWriter, value: Date?) {
val string = format.get().format(value)
writer.value(string)
}
}
private suspend fun ApplicationCall.respondJson(text: String, status: HttpStatusCode? = null, configure: OutgoingContent.() -> Unit = {}) {
respond(TextContent(text, ContentType.Application.Json, status).apply(configure))
}

45
application/webserver/src/main/java/org/videolan/vlc/webserver/RemoteAccessServer.kt

@ -32,6 +32,7 @@ import android.net.Uri
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.squareup.moshi.Json
import io.ktor.http.CacheControl
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
@ -800,26 +801,26 @@ class RemoteAccessServer(private val context: Context) : PlaybackService.Callbac
return list
}
abstract class WSMessage(val type: String)
abstract class WSMessage(val type: WSMessageType)
data class NowPlaying(val title: String, val artist: String, val playing: Boolean, val isVideoPlaying: Boolean, val progress: Long,
val duration: Long, val id: Long, val artworkURL: String, val uri: String, val volume: Int, val speed: Float,
val sleepTimer: Long, val waitForMediaEnd:Boolean, val resetOnInteraction:Boolean, val shuffle: Boolean, val repeat: Int,
val shouldShow: Boolean = PlaylistManager.playingState.value ?: false,
val bookmarks:List<WSBookmark> = listOf(), val chapters:List<WSChapter> = listOf()) : WSMessage("now-playing")
val bookmarks: List<WSBookmark> = listOf(), val chapters: List<WSChapter> = listOf()) : WSMessage(WSMessageType.NOW_PLAYING)
data class WSBookmark(val id:Long, val title: String, val time: Long)
data class WSChapter(val title: String, val time: Long)
data class PlayQueue(val medias: List<PlayQueueItem>) : WSMessage("play-queue")
data class PlayQueue(val medias: List<PlayQueueItem>) : WSMessage(WSMessageType.PLAY_QUEUE)
data class PlayQueueItem(val id: Long, val title: String, val artist: String, val duration: Long, val artworkURL: String, val playing: Boolean, val resolution: String = "", val path: String = "", val isFolder: Boolean = false, val progress: Long = 0L, val played: Boolean = false, var fileType: String = "", val favorite: Boolean = false)
data class WebSocketAuthorization(val status:String, val initialMessage:String) : WSMessage("auth")
data class Volume(val volume: Int) : WSMessage("volume")
data class PlayerStatus(val playing: Boolean) : WSMessage("player-status")
data class LoginNeeded(val dialogOpened: Boolean) : WSMessage("login-needed")
data class ResumeConfirmationNeeded(val mediaTitle: String?, val consumed:Boolean) : WSMessage("resume-confirmation")
data class MLRefreshNeeded(val refreshNeeded: Boolean = true) : WSMessage("ml-refresh-needed")
data class BrowserDescription(val path: String, val description:String) : WSMessage("browser-description")
data class PlaybackControlForbidden(val forbidden: Boolean = true): WSMessage("playback-control-forbidden")
data class WebSocketAuthorization(val status:String, val initialMessage:String) : WSMessage(WSMessageType.AUTH)
data class Volume(val volume: Int) : WSMessage(WSMessageType.VOLUME)
data class PlayerStatus(val playing: Boolean) : WSMessage(WSMessageType.PLAYER_STATUS)
data class LoginNeeded(val dialogOpened: Boolean) : WSMessage(WSMessageType.LOGIN_NEEDED)
data class ResumeConfirmationNeeded(val mediaTitle: String?, val consumed: Boolean) : WSMessage(WSMessageType.RESUME_CONFIRMATION)
data class MLRefreshNeeded(val refreshNeeded: Boolean = true) : WSMessage(WSMessageType.ML_REFRESH_NEEDED)
data class BrowserDescription(val path: String, val description: String) : WSMessage(WSMessageType.BROWSER_DESCRIPTION)
data class PlaybackControlForbidden(val forbidden: Boolean = true): WSMessage(WSMessageType.PLAYBACK_CONTROL_FORBIDDEN)
data class SearchResults(val albums: List<PlayQueueItem>, val artists: List<PlayQueueItem>, val genres: List<PlayQueueItem>, val playlists: List<PlayQueueItem>, val videos: List<PlayQueueItem>, val tracks: List<PlayQueueItem>)
data class BreadcrumbItem(val title: String, val path: String)
data class BrowsingResult(val content: List<PlayQueueItem>, val breadcrumb: List<BreadcrumbItem>)
@ -846,4 +847,26 @@ class RemoteAccessServer(private val context: Context) : PlaybackService.Callbac
data class RemoteAccessConnection(val ip: String)
enum class WSMessageType {
@Json(name = "now-playing")
NOW_PLAYING,
@Json(name = "play-queue")
PLAY_QUEUE,
@Json(name = "auth")
AUTH,
@Json(name = "volume")
VOLUME,
@Json(name = "player-status")
PLAYER_STATUS,
@Json(name = "login-needed")
LOGIN_NEEDED,
@Json(name = "resume-confirmation")
RESUME_CONFIRMATION,
@Json(name = "ml-refresh-needed")
ML_REFRESH_NEEDED,
@Json(name = "browser-description")
BROWSER_DESCRIPTION,
@Json(name = "playback-control-forbidden")
PLAYBACK_CONTROL_FORBIDDEN
}
}

5
application/webserver/src/main/java/org/videolan/vlc/webserver/TranslationMapping.kt

@ -27,11 +27,10 @@ package org.videolan.vlc.webserver
import android.content.Context
import android.os.Build
import androidx.annotation.StringRes
import org.json.JSONObject
import org.videolan.vlc.BuildConfig
object TranslationMapping {
fun generateTranslations(context: Context): String {
fun generateTranslations(context: Context): Map<String, String> {
val map = HashMap<String, String>()
StringMapping.values().forEach {
map[it.name] = context.getString(it.id).replace("%s", "{msg}")
@ -39,7 +38,7 @@ object TranslationMapping {
map["PORT"] = "android"
map["DEVICE_NAME"] = "${Build.MANUFACTURER} - ${Build.MODEL}"
map["APP_VERSION"] = BuildConfig.VLC_VERSION_NAME
return JSONObject(map.toMap()).toString()
return map
}
enum class StringMapping(@StringRes val id:Int) {

21
application/webserver/src/main/java/org/videolan/vlc/webserver/websockets/RemoteAccessWebSockets.kt

@ -29,7 +29,7 @@ import android.content.SharedPreferences
import android.media.AudioManager
import android.support.v4.media.session.PlaybackStateCompat
import android.util.Log
import com.google.gson.Gson
import com.squareup.moshi.Moshi
import io.ktor.server.routing.Routing
import io.ktor.server.websocket.WebSocketServerSession
import io.ktor.server.websocket.webSocket
@ -48,6 +48,7 @@ import org.videolan.vlc.R
import org.videolan.vlc.gui.video.VideoPlayerActivity
import org.videolan.vlc.webserver.BuildConfig
import org.videolan.vlc.webserver.RemoteAccessServer
import org.videolan.vlc.webserver.convertToJson
import org.videolan.vlc.webserver.ssl.SecretGenerator
import org.videolan.vlc.webserver.websockets.IncomingMessageType.*
import java.util.Calendar
@ -59,7 +60,7 @@ import java.util.concurrent.atomic.AtomicInteger
object RemoteAccessWebSockets {
private const val TAG = "VLC/HttpSharingServerWS"
val onPlaybackEventChannel = Channel<String>()
val onPlaybackEventChannel = Channel<RemoteAccessServer.WSMessage>()
val messageQueue: LinkedBlockingQueue<RemoteAccessServer.WSMessage> = LinkedBlockingQueue()
private val webSocketSessions: MutableMap<Int, WebSocketServerSession> = ConcurrentHashMap()
private val tickets: MutableList<WSAuthTicket> = Collections.synchronizedList(mutableListOf())
@ -71,16 +72,16 @@ object RemoteAccessWebSockets {
try {
webSocketSessions[sessionId] = this
if (BuildConfig.DEBUG) Log.d(TAG, "WebSockets: Started session: $sessionId")
val moshi = Moshi.Builder().build().adapter(WSIncomingMessage::class.java)
// Handle a WebSocket session
for (frame in incoming) {
try {
frame as? Frame.Text ?: continue
val message = frame.readText()
val gson = Gson()
val incomingMessage = gson.fromJson(message, WSIncomingMessage::class.java)
val incomingMessage = moshi.fromJson(message) ?: continue
if (BuildConfig.DEBUG) Log.i(TAG, "WebSockets: Received message '$message'")
if (!verifyWebsocketAuth(incomingMessage)) {
send(Frame.Text(Gson().toJson(RemoteAccessServer.WebSocketAuthorization("forbidden", initialMessage = message))))
send(Frame.Text(convertToJson(RemoteAccessServer.WebSocketAuthorization("forbidden", initialMessage = message))))
} else {
val service = RemoteAccessServer.getInstance(context).service
manageIncomingMessages(incomingMessage, settings, service, context)
@ -287,7 +288,7 @@ object RemoteAccessWebSockets {
private fun playbackControlAllowedOrSend(settings: SharedPreferences): Boolean {
val allowed = settings.getBoolean(REMOTE_ACCESS_PLAYBACK_CONTROL, true)
if (!allowed) {
val message = Gson().toJson(RemoteAccessServer.PlaybackControlForbidden())
val message = convertToJson(RemoteAccessServer.PlaybackControlForbidden())
AppScope.launch { webSocketSessions.forEach { (_, session) -> session.send(Frame.Text(message)) } }
}
return allowed
@ -310,7 +311,7 @@ object RemoteAccessWebSockets {
?: 0
}
}
return Gson().toJson(RemoteAccessServer.Volume(volume))
return convertToJson(RemoteAccessServer.Volume(volume))
}
fun createTicket(): String {
@ -320,9 +321,9 @@ object RemoteAccessWebSockets {
}
suspend fun sendToAll(messageObj: RemoteAccessServer.WSMessage) {
val message = Gson().toJson(messageObj)
val message = convertToJson(messageObj)
addToQueue(messageObj)
onPlaybackEventChannel.trySend(message)
onPlaybackEventChannel.trySend(messageObj)
if (BuildConfig.DEBUG) Log.d(TAG, "WebSockets: sendToAll called on ${webSocketSessions.size} sessions with message '$message'")
webSocketSessions.forEach { (sessionId, session) ->
try {
@ -342,7 +343,7 @@ object RemoteAccessWebSockets {
private fun addToQueue(wsMessage: RemoteAccessServer.WSMessage) {
when (wsMessage.type) {
// Duplicate browser description messages are OK
"browser-description" -> {}
RemoteAccessServer.WSMessageType.BROWSER_DESCRIPTION -> {}
else -> messageQueue.removeIf { it.type == wsMessage.type }
}
messageQueue.add(wsMessage)

Loading…
Cancel
Save