2021-07-12 10:14:26 +02:00
|
|
|
package audio.funkwhale.ffa.playback
|
2019-08-19 16:50:33 +02:00
|
|
|
|
|
|
|
import android.content.Context
|
|
|
|
import android.net.Uri
|
2021-08-22 09:48:33 +02:00
|
|
|
import audio.funkwhale.ffa.model.QueueCache
|
|
|
|
import audio.funkwhale.ffa.model.Track
|
2021-09-09 09:56:15 +02:00
|
|
|
import audio.funkwhale.ffa.utils.Command
|
|
|
|
import audio.funkwhale.ffa.utils.CommandBus
|
|
|
|
import audio.funkwhale.ffa.utils.Event
|
|
|
|
import audio.funkwhale.ffa.utils.EventBus
|
|
|
|
import audio.funkwhale.ffa.utils.FFACache
|
|
|
|
import audio.funkwhale.ffa.utils.log
|
|
|
|
import audio.funkwhale.ffa.utils.mustNormalizeUrl
|
2019-08-19 16:50:33 +02:00
|
|
|
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
|
2022-08-27 09:21:03 +02:00
|
|
|
import com.google.android.exoplayer2.MediaItem
|
2019-08-19 16:50:33 +02:00
|
|
|
import com.google.android.exoplayer2.source.ConcatenatingMediaSource
|
|
|
|
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
|
|
|
import com.google.gson.Gson
|
2021-08-09 06:50:46 +02:00
|
|
|
import org.koin.java.KoinJavaComponent.inject
|
|
|
|
|
|
|
|
class QueueManager(val context: Context) {
|
2020-06-20 15:42:10 +02:00
|
|
|
|
2021-08-09 06:50:46 +02:00
|
|
|
private val cacheDataSourceFactoryProvider: CacheDataSourceFactoryProvider by inject(
|
|
|
|
CacheDataSourceFactoryProvider::class.java
|
|
|
|
)
|
2021-07-30 10:57:49 +02:00
|
|
|
|
2021-08-09 06:50:46 +02:00
|
|
|
var metadata: MutableList<Track> = mutableListOf()
|
|
|
|
val dataSources = ConcatenatingMediaSource()
|
|
|
|
var current = -1
|
2019-08-19 16:50:33 +02:00
|
|
|
|
2020-06-10 16:25:20 +02:00
|
|
|
init {
|
2022-06-12 14:48:32 +02:00
|
|
|
FFACache.getLine(context, "queue")?.let { json ->
|
2022-07-14 13:12:30 +02:00
|
|
|
gsonDeserializerOf(QueueCache::class.java).deserialize(json.reader())?.let { cache ->
|
2019-08-19 16:50:33 +02:00
|
|
|
metadata = cache.data.toMutableList()
|
|
|
|
|
2021-08-09 06:50:46 +02:00
|
|
|
val factory = cacheDataSourceFactoryProvider.create(context)
|
2019-08-19 16:50:33 +02:00
|
|
|
|
2021-09-09 09:56:15 +02:00
|
|
|
dataSources.addMediaSources(
|
|
|
|
metadata.map { track ->
|
|
|
|
val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "")
|
2019-08-19 16:50:33 +02:00
|
|
|
|
2022-08-27 09:21:03 +02:00
|
|
|
val mediaItem = MediaItem.fromUri(Uri.parse(url)).buildUpon().setTag(track.title).build()
|
|
|
|
ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem)
|
2021-09-09 09:56:15 +02:00
|
|
|
}
|
|
|
|
)
|
2019-08-19 16:50:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-12 14:48:32 +02:00
|
|
|
FFACache.getLine(context, "current")?.let {
|
|
|
|
current = it.toInt()
|
2019-08-19 16:50:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun persist() {
|
2021-08-09 06:50:46 +02:00
|
|
|
FFACache.set(
|
2019-08-19 16:50:33 +02:00
|
|
|
context,
|
|
|
|
"queue",
|
2022-06-12 14:48:32 +02:00
|
|
|
Gson().toJson(QueueCache(metadata)).toString()
|
2019-08-19 16:50:33 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun replace(tracks: List<Track>) {
|
2021-08-13 14:54:42 +02:00
|
|
|
tracks.map { it.formatted }.log("Replacing queue with ${tracks.size} tracks")
|
2021-08-09 06:50:46 +02:00
|
|
|
val factory = cacheDataSourceFactoryProvider.create(context)
|
2019-08-19 16:50:33 +02:00
|
|
|
val sources = tracks.map { track ->
|
2019-10-22 21:56:33 +02:00
|
|
|
val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "")
|
2022-08-27 09:21:03 +02:00
|
|
|
val mediaItem = MediaItem.fromUri(Uri.parse(url)).buildUpon().setTag(track.title).build()
|
|
|
|
ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem)
|
2019-08-19 16:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
metadata = tracks.toMutableList()
|
2021-08-09 06:50:46 +02:00
|
|
|
dataSources.clear()
|
|
|
|
dataSources.addMediaSources(sources)
|
2019-08-19 16:50:33 +02:00
|
|
|
|
|
|
|
persist()
|
|
|
|
|
|
|
|
EventBus.send(Event.QueueChanged)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun append(tracks: List<Track>) {
|
2021-08-13 14:54:42 +02:00
|
|
|
tracks.map { it.formatted }.log("Appending ${tracks.size} tracks")
|
2021-08-09 06:50:46 +02:00
|
|
|
val factory = cacheDataSourceFactoryProvider.create(context)
|
2020-06-01 21:25:16 +02:00
|
|
|
val missingTracks = tracks.filter { metadata.indexOf(it) == -1 }
|
2019-08-19 16:50:33 +02:00
|
|
|
|
2020-06-01 21:25:16 +02:00
|
|
|
val sources = missingTracks.map { track ->
|
2019-10-22 21:56:33 +02:00
|
|
|
val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "")
|
2019-08-19 16:50:33 +02:00
|
|
|
|
2022-08-27 09:21:03 +02:00
|
|
|
val mediaItem = MediaItem.fromUri(Uri.parse(url)).buildUpon().setTag(track.title).build()
|
|
|
|
ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem)
|
2019-08-19 16:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
metadata.addAll(tracks)
|
2021-08-09 06:50:46 +02:00
|
|
|
dataSources.addMediaSources(sources)
|
2019-08-19 16:50:33 +02:00
|
|
|
|
|
|
|
persist()
|
|
|
|
|
|
|
|
EventBus.send(Event.QueueChanged)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun insertNext(track: Track) {
|
2021-08-13 15:04:15 +02:00
|
|
|
track.formatted.log("Next track")
|
2021-08-09 06:50:46 +02:00
|
|
|
val factory = cacheDataSourceFactoryProvider.create(context)
|
2019-10-22 21:56:33 +02:00
|
|
|
val url = mustNormalizeUrl(track.bestUpload()?.listen_url ?: "")
|
2019-08-19 16:50:33 +02:00
|
|
|
|
|
|
|
if (metadata.indexOf(track) == -1) {
|
2022-08-27 09:21:03 +02:00
|
|
|
val mediaItem = MediaItem.fromUri(Uri.parse(url)).buildUpon().setTag(track.title).build()
|
|
|
|
ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem).let {
|
2021-08-09 06:50:46 +02:00
|
|
|
dataSources.addMediaSource(current + 1, it)
|
2019-08-19 16:50:33 +02:00
|
|
|
metadata.add(current + 1, track)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
move(metadata.indexOf(track), current + 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
persist()
|
|
|
|
|
|
|
|
EventBus.send(Event.QueueChanged)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun remove(track: Track) {
|
2021-08-13 14:54:42 +02:00
|
|
|
track.formatted.log("Removing track")
|
2019-08-19 16:50:33 +02:00
|
|
|
metadata.indexOf(track).let {
|
2020-06-20 16:32:14 +02:00
|
|
|
if (it < 0) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-09 06:50:46 +02:00
|
|
|
dataSources.removeMediaSource(it)
|
2019-08-19 16:50:33 +02:00
|
|
|
metadata.removeAt(it)
|
2020-06-20 16:32:14 +02:00
|
|
|
|
|
|
|
if (it == current) {
|
|
|
|
CommandBus.send(Command.NextTrack)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (it < current) {
|
|
|
|
current--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (metadata.isEmpty()) {
|
|
|
|
current = -1
|
2019-08-19 16:50:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
persist()
|
|
|
|
|
|
|
|
EventBus.send(Event.QueueChanged)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun move(oldPosition: Int, newPosition: Int) {
|
2021-08-09 06:50:46 +02:00
|
|
|
dataSources.moveMediaSource(oldPosition, newPosition)
|
2019-08-19 16:50:33 +02:00
|
|
|
metadata.add(newPosition, metadata.removeAt(oldPosition))
|
|
|
|
|
|
|
|
persist()
|
|
|
|
}
|
|
|
|
|
|
|
|
fun get() = metadata.mapIndexed { index, track ->
|
|
|
|
track.current = index == current
|
|
|
|
track
|
|
|
|
}
|
|
|
|
|
|
|
|
fun get(index: Int): Track = metadata[index]
|
|
|
|
|
|
|
|
fun current(): Track? {
|
|
|
|
if (current == -1) {
|
|
|
|
return metadata.getOrNull(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
return metadata.getOrNull(current)
|
|
|
|
}
|
2019-10-30 22:06:57 +01:00
|
|
|
|
|
|
|
fun clear() {
|
|
|
|
metadata = mutableListOf()
|
2021-08-09 06:50:46 +02:00
|
|
|
dataSources.clear()
|
2019-10-30 22:06:57 +01:00
|
|
|
current = -1
|
2020-09-02 12:04:42 +02:00
|
|
|
|
|
|
|
persist()
|
2019-10-30 22:06:57 +01:00
|
|
|
}
|
2020-09-01 21:06:00 +02:00
|
|
|
|
|
|
|
fun shuffle() {
|
2020-09-02 12:04:42 +02:00
|
|
|
if (metadata.size < 2) return
|
2020-09-01 21:06:00 +02:00
|
|
|
|
|
|
|
if (current == -1) {
|
|
|
|
replace(metadata.shuffled())
|
|
|
|
} else {
|
2020-09-02 12:04:42 +02:00
|
|
|
move(current, 0)
|
|
|
|
current = 0
|
|
|
|
|
|
|
|
val shuffled =
|
|
|
|
metadata
|
|
|
|
.drop(1)
|
|
|
|
.shuffled()
|
2020-09-01 21:06:00 +02:00
|
|
|
|
2020-09-02 12:04:42 +02:00
|
|
|
while (metadata.size > 1) {
|
2021-08-09 06:50:46 +02:00
|
|
|
dataSources.removeMediaSource(metadata.size - 1)
|
2020-09-02 12:04:42 +02:00
|
|
|
metadata.removeAt(metadata.size - 1)
|
2020-09-01 21:06:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
append(shuffled)
|
|
|
|
}
|
|
|
|
|
2020-09-02 12:04:42 +02:00
|
|
|
persist()
|
|
|
|
|
2020-09-01 21:06:00 +02:00
|
|
|
EventBus.send(Event.QueueChanged)
|
|
|
|
}
|
2021-07-02 13:55:49 +02:00
|
|
|
}
|