2021-07-12 10:14:26 +02:00
|
|
|
package audio.funkwhale.ffa.playback
|
2020-05-30 21:16:28 +02:00
|
|
|
|
|
|
|
import android.content.Context
|
2021-07-12 10:14:26 +02:00
|
|
|
import audio.funkwhale.ffa.R
|
2021-08-22 09:48:33 +02:00
|
|
|
import audio.funkwhale.ffa.model.Radio
|
|
|
|
import audio.funkwhale.ffa.model.Track
|
2021-07-12 10:14:26 +02:00
|
|
|
import audio.funkwhale.ffa.repositories.FavoritedRepository
|
|
|
|
import audio.funkwhale.ffa.repositories.Repository
|
2021-08-09 06:50:46 +02:00
|
|
|
import audio.funkwhale.ffa.utils.*
|
2020-05-30 21:16:28 +02:00
|
|
|
import com.github.kittinunf.fuel.Fuel
|
2020-06-21 13:36:42 +02:00
|
|
|
import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult
|
2020-05-30 21:16:28 +02:00
|
|
|
import com.github.kittinunf.fuel.coroutines.awaitObjectResult
|
|
|
|
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
|
|
|
|
import com.google.gson.Gson
|
2020-06-25 01:26:15 +02:00
|
|
|
import kotlinx.coroutines.CoroutineScope
|
2020-05-30 21:16:28 +02:00
|
|
|
import kotlinx.coroutines.Dispatchers.IO
|
|
|
|
import kotlinx.coroutines.Dispatchers.Main
|
2020-05-31 02:39:32 +02:00
|
|
|
import kotlinx.coroutines.flow.map
|
|
|
|
import kotlinx.coroutines.flow.toList
|
2020-05-30 21:16:28 +02:00
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
import kotlinx.coroutines.sync.Semaphore
|
|
|
|
import kotlinx.coroutines.withContext
|
2021-08-09 06:50:46 +02:00
|
|
|
import org.koin.java.KoinJavaComponent.inject
|
|
|
|
|
|
|
|
data class RadioSessionBody(
|
|
|
|
val radio_type: String,
|
|
|
|
var custom_radio: Int? = null,
|
|
|
|
var related_object_id: String? = null
|
|
|
|
)
|
2020-05-30 21:16:28 +02:00
|
|
|
|
|
|
|
data class RadioSession(val id: Int)
|
|
|
|
data class RadioTrackBody(val session: Int)
|
|
|
|
data class RadioTrack(val position: Int, val track: RadioTrackID)
|
|
|
|
data class RadioTrackID(val id: Int)
|
|
|
|
|
2020-06-25 01:26:15 +02:00
|
|
|
class RadioPlayer(val context: Context, val scope: CoroutineScope) {
|
2020-05-30 21:16:28 +02:00
|
|
|
val lock = Semaphore(1)
|
|
|
|
|
2021-08-09 06:50:46 +02:00
|
|
|
private val oAuth: OAuth by inject(OAuth::class.java)
|
2020-05-30 21:16:28 +02:00
|
|
|
private var currentRadio: Radio? = null
|
|
|
|
private var session: Int? = null
|
2020-06-21 13:36:42 +02:00
|
|
|
private var cookie: String? = null
|
2020-05-30 21:16:28 +02:00
|
|
|
|
2020-05-31 02:39:32 +02:00
|
|
|
private val favoritedRepository = FavoritedRepository(context)
|
|
|
|
|
|
|
|
init {
|
2021-08-09 06:50:46 +02:00
|
|
|
FFACache.get(context, "radio_type")?.readLine()?.let { radio_type ->
|
|
|
|
FFACache.get(context, "radio_id")?.readLine()?.toInt()?.let { radio_id ->
|
|
|
|
FFACache.get(context, "radio_session")?.readLine()?.toInt()?.let { radio_session ->
|
|
|
|
val cachedCookie = FFACache.get(context, "radio_cookie")?.readLine()
|
2020-07-10 18:46:49 +02:00
|
|
|
|
|
|
|
currentRadio = Radio(radio_id, radio_type, "", "")
|
|
|
|
session = radio_session
|
|
|
|
cookie = cachedCookie
|
2020-06-01 14:38:50 +02:00
|
|
|
}
|
2020-05-31 02:39:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-30 21:16:28 +02:00
|
|
|
fun play(radio: Radio) {
|
|
|
|
currentRadio = radio
|
|
|
|
session = null
|
|
|
|
|
2020-06-25 01:26:15 +02:00
|
|
|
scope.launch(IO) {
|
2020-05-30 21:16:28 +02:00
|
|
|
createSession()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun stop() {
|
|
|
|
currentRadio = null
|
|
|
|
session = null
|
2020-05-31 02:39:32 +02:00
|
|
|
|
2021-08-09 06:50:46 +02:00
|
|
|
FFACache.delete(context, "radio_type")
|
|
|
|
FFACache.delete(context, "radio_id")
|
|
|
|
FFACache.delete(context, "radio_session")
|
|
|
|
FFACache.delete(context, "radio_cookie")
|
2020-05-30 21:16:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fun isActive() = currentRadio != null && session != null
|
|
|
|
|
|
|
|
private suspend fun createSession() {
|
2020-06-01 14:38:50 +02:00
|
|
|
currentRadio?.let { radio ->
|
|
|
|
try {
|
2021-08-09 06:50:46 +02:00
|
|
|
val request =
|
|
|
|
RadioSessionBody(radio.radio_type, related_object_id = radio.related_object_id).apply {
|
|
|
|
if (radio_type == "custom") {
|
|
|
|
custom_radio = radio.id
|
|
|
|
}
|
2020-05-30 21:16:28 +02:00
|
|
|
}
|
2020-06-01 14:38:50 +02:00
|
|
|
|
|
|
|
val body = Gson().toJson(request)
|
2020-06-21 13:36:42 +02:00
|
|
|
val (_, response, result) = Fuel.post(mustNormalizeUrl("/api/v1/radios/sessions/"))
|
2021-08-09 06:50:46 +02:00
|
|
|
.authorize(context, oAuth)
|
2020-06-01 14:38:50 +02:00
|
|
|
.header("Content-Type", "application/json")
|
|
|
|
.body(body)
|
2020-06-21 13:36:42 +02:00
|
|
|
.awaitObjectResponseResult(gsonDeserializerOf(RadioSession::class.java))
|
2020-06-01 14:38:50 +02:00
|
|
|
|
|
|
|
session = result.get().id
|
2020-06-21 13:36:42 +02:00
|
|
|
cookie = response.header("set-cookie").joinToString(";")
|
2020-06-01 14:38:50 +02:00
|
|
|
|
2021-08-09 06:50:46 +02:00
|
|
|
FFACache.set(context, "radio_type", radio.radio_type.toByteArray())
|
|
|
|
FFACache.set(context, "radio_id", radio.id.toString().toByteArray())
|
|
|
|
FFACache.set(context, "radio_session", session.toString().toByteArray())
|
|
|
|
FFACache.set(context, "radio_cookie", cookie.toString().toByteArray())
|
2020-06-01 14:38:50 +02:00
|
|
|
|
|
|
|
prepareNextTrack(true)
|
|
|
|
} catch (e: Exception) {
|
2021-09-08 09:51:49 +02:00
|
|
|
e.logError()
|
2020-06-01 14:38:50 +02:00
|
|
|
withContext(Main) {
|
|
|
|
context.toast(context.getString(R.string.radio_playback_error))
|
|
|
|
}
|
2020-05-30 21:16:28 +02:00
|
|
|
}
|
2020-06-01 14:38:50 +02:00
|
|
|
}
|
2020-05-30 21:16:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
suspend fun prepareNextTrack(first: Boolean = false) {
|
|
|
|
session?.let { session ->
|
|
|
|
try {
|
|
|
|
val body = Gson().toJson(RadioTrackBody(session))
|
|
|
|
val result = Fuel.post(mustNormalizeUrl("/api/v1/radios/tracks/"))
|
2021-08-09 06:50:46 +02:00
|
|
|
.authorize(context, oAuth)
|
2020-05-30 21:16:28 +02:00
|
|
|
.header("Content-Type", "application/json")
|
2020-06-21 13:36:42 +02:00
|
|
|
.apply {
|
|
|
|
cookie?.let {
|
|
|
|
header("cookie", it)
|
|
|
|
}
|
|
|
|
}
|
2020-05-30 21:16:28 +02:00
|
|
|
.body(body)
|
|
|
|
.awaitObjectResult(gsonDeserializerOf(RadioTrack::class.java))
|
|
|
|
|
2020-05-31 02:39:32 +02:00
|
|
|
val trackResponse = Fuel.get(mustNormalizeUrl("/api/v1/tracks/${result.get().track.id}/"))
|
2021-08-09 06:50:46 +02:00
|
|
|
.authorize(context, oAuth)
|
2020-05-30 21:16:28 +02:00
|
|
|
.awaitObjectResult(gsonDeserializerOf(Track::class.java))
|
|
|
|
|
2020-07-10 15:08:08 +02:00
|
|
|
val favorites = favoritedRepository.fetch(Repository.Origin.Cache.origin)
|
2020-05-31 02:39:32 +02:00
|
|
|
.map { it.data }
|
|
|
|
.toList()
|
|
|
|
.flatten()
|
|
|
|
|
|
|
|
val track = trackResponse.get().apply {
|
|
|
|
favorite = favorites.contains(id)
|
|
|
|
}
|
|
|
|
|
2020-05-30 21:16:28 +02:00
|
|
|
if (first) {
|
2020-05-31 02:39:32 +02:00
|
|
|
CommandBus.send(Command.ReplaceQueue(listOf(track), true))
|
2020-05-30 21:16:28 +02:00
|
|
|
} else {
|
2020-05-31 02:39:32 +02:00
|
|
|
CommandBus.send(Command.AddToQueue(listOf(track)))
|
2020-05-30 21:16:28 +02:00
|
|
|
}
|
|
|
|
} catch (e: Exception) {
|
|
|
|
withContext(Main) {
|
|
|
|
context.toast(context.getString(R.string.radio_playback_error))
|
|
|
|
}
|
2020-06-02 18:50:46 +02:00
|
|
|
} finally {
|
|
|
|
EventBus.send(Event.RadioStarted)
|
2020-05-30 21:16:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-02 13:55:49 +02:00
|
|
|
}
|