Try to fix the mess :)

This commit is contained in:
tzugen 2022-04-22 21:03:57 +02:00
parent 827654c0c1
commit 707339b88b
No known key found for this signature in database
GPG Key ID: 61E9C34BC10EC930
5 changed files with 64 additions and 35 deletions

View File

@ -77,7 +77,7 @@ class LegacyPlaylistManager : KoinComponent {
// Public facing playlist (immutable) // Public facing playlist (immutable)
val playlist: List<DownloadFile> val playlist: List<DownloadFile>
get() = _playlist.toList() get() = _playlist
@get:Synchronized @get:Synchronized
val playlistDuration: Long val playlistDuration: Long

View File

@ -150,18 +150,20 @@ class Downloader(
// Store the result in a flag to know if changes have occurred // Store the result in a flag to know if changes have occurred
var listChanged = cleanupActiveDownloads() var listChanged = cleanupActiveDownloads()
val playlist = legacyPlaylistManager.playlist
// Check if need to preload more from playlist // Check if need to preload more from playlist
val preloadCount = Settings.preloadCount val preloadCount = Settings.preloadCount
// Start preloading at the current playing song // Start preloading at the current playing song
var start = mediaController.currentMediaItemIndex var start = mediaController.currentMediaItemIndex
if (start == -1) start = 0 if (start == -1 || start > playlist.size) start = 0
val end = (start + preloadCount).coerceAtMost(mediaController.mediaItemCount) val end = (start + preloadCount).coerceAtMost(playlist.size)
for (i in start until end) { for (i in start until end) {
val download = legacyPlaylistManager.playlist[i] val download = playlist[i]
// Set correct priority (the lower the number, the higher the priority) // Set correct priority (the lower the number, the higher the priority)
download.priority = i download.priority = i

View File

@ -18,6 +18,9 @@ import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken import androidx.media3.session.SessionToken
import com.google.common.util.concurrent.MoreExecutors import com.google.common.util.concurrent.MoreExecutors
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.app.UApp
@ -61,6 +64,8 @@ class MediaPlayerController(
private val rxBusSubscription: CompositeDisposable = CompositeDisposable() private val rxBusSubscription: CompositeDisposable = CompositeDisposable()
private var mainScope = CoroutineScope(Dispatchers.Main)
private var sessionToken = private var sessionToken =
SessionToken(context, ComponentName(context, PlaybackService::class.java)) SessionToken(context, ComponentName(context, PlaybackService::class.java))
@ -94,10 +99,10 @@ class MediaPlayerController(
/* /*
* This will be called everytime the playlist has changed. * This will be called everytime the playlist has changed.
* We run the event through RxBus in order to throttle them
*/ */
override fun onTimelineChanged(timeline: Timeline, reason: Int) { override fun onTimelineChanged(timeline: Timeline, reason: Int) {
legacyPlaylistManager.rebuildPlaylist(controller!!) legacyPlaylistManager.rebuildPlaylist(controller!!)
serializeCurrentSession()
} }
override fun onPlaybackStateChanged(playbackState: Int) { override fun onPlaybackStateChanged(playbackState: Int) {
@ -143,6 +148,20 @@ class MediaPlayerController(
isJukeboxEnabled = activeServerProvider.getActiveServer().jukeboxByDefault isJukeboxEnabled = activeServerProvider.getActiveServer().jukeboxByDefault
} }
rxBusSubscription += RxBus.throttledPlaylistObservable.subscribe {
// Even though Rx should launch on the main thread it doesn't always :(
mainScope.launch {
serializeCurrentSession()
}
}
rxBusSubscription += RxBus.throttledPlayerStateObservable.subscribe {
// Even though Rx should launch on the main thread it doesn't always :(
mainScope.launch {
serializeCurrentSession()
}
}
created = true created = true
Timber.i("MediaPlayerController started") Timber.i("MediaPlayerController started")
} }
@ -162,9 +181,6 @@ class MediaPlayerController(
} }
} }
// Save playback state
serializeCurrentSession()
// Update widget // Update widget
if (currentPlaying != null) { if (currentPlaying != null) {
updateWidget(currentPlaying.track) updateWidget(currentPlaying.track)
@ -367,8 +383,6 @@ class MediaPlayerController(
if (songs == null) return if (songs == null) return
val filteredSongs = songs.filterNotNull() val filteredSongs = songs.filterNotNull()
downloader.downloadBackground(filteredSongs, save) downloader.downloadBackground(filteredSongs, save)
serializeCurrentSession()
} }
fun stopJukeboxService() { fun stopJukeboxService() {

View File

@ -9,8 +9,6 @@ package org.moire.ultrasonic.service
import android.content.Context import android.content.Context
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@ -30,9 +28,6 @@ class PlaybackStateSerializer : KoinComponent {
private val context by inject<Context>() private val context by inject<Context>()
private val lock: Lock = ReentrantLock()
private val setup = AtomicBoolean(false)
private val ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) private val mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
@ -41,25 +36,24 @@ class PlaybackStateSerializer : KoinComponent {
currentPlayingIndex: Int, currentPlayingIndex: Int,
currentPlayingPosition: Int currentPlayingPosition: Int
) { ) {
if (!setup.get()) return if (isSerializing.get() || !isSetup.get()) return
if (lock.tryLock()) { isSerializing.set(true)
ioScope.launch {
try { ioScope.launch {
serializeNow(songs, currentPlayingIndex, currentPlayingPosition) serializeNow(songs, currentPlayingIndex, currentPlayingPosition)
} finally { }.invokeOnCompletion {
lock.unlock() isSerializing.set(false)
}
}
} }
} }
fun serializeNow( fun serializeNow(
songs: Iterable<DownloadFile>, referencedList: Iterable<DownloadFile>,
currentPlayingIndex: Int, currentPlayingIndex: Int,
currentPlayingPosition: Int currentPlayingPosition: Int
) { ) {
val state = State() val state = State()
val songs = referencedList.toList()
for (downloadFile in songs) { for (downloadFile in songs) {
state.songs.add(downloadFile.track) state.songs.add(downloadFile.track)
@ -78,16 +72,15 @@ class PlaybackStateSerializer : KoinComponent {
} }
fun deserialize(afterDeserialized: (State?) -> Unit?) { fun deserialize(afterDeserialized: (State?) -> Unit?) {
if (isDeserializing.get()) return
ioScope.launch { ioScope.launch {
try { try {
lock.lock()
deserializeNow(afterDeserialized) deserializeNow(afterDeserialized)
setup.set(true) isSetup.set(true)
} catch (all: Exception) { } catch (all: Exception) {
Timber.e(all, "Had a problem deserializing:") Timber.e(all, "Had a problem deserializing:")
} finally { } finally {
lock.unlock() isDeserializing.set(false)
} }
} }
} }
@ -108,4 +101,10 @@ class PlaybackStateSerializer : KoinComponent {
afterDeserialized(state) afterDeserialized(state)
} }
} }
companion object {
private val isSetup = AtomicBoolean(false)
private val isSerializing = AtomicBoolean(false)
private val isDeserializing = AtomicBoolean(false)
}
} }

View File

@ -1,5 +1,6 @@
package org.moire.ultrasonic.service package org.moire.ultrasonic.service
import android.os.Looper
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -8,42 +9,55 @@ import io.reactivex.rxjava3.subjects.PublishSubject
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class RxBus { class RxBus {
companion object { companion object {
private fun mainThread() = AndroidSchedulers.from(Looper.getMainLooper())
var activeServerChangePublisher: PublishSubject<Int> = var activeServerChangePublisher: PublishSubject<Int> =
PublishSubject.create() PublishSubject.create()
var activeServerChangeObservable: Observable<Int> = var activeServerChangeObservable: Observable<Int> =
activeServerChangePublisher.observeOn(AndroidSchedulers.mainThread()) activeServerChangePublisher.observeOn(mainThread())
val themeChangedEventPublisher: PublishSubject<Unit> = val themeChangedEventPublisher: PublishSubject<Unit> =
PublishSubject.create() PublishSubject.create()
val themeChangedEventObservable: Observable<Unit> = val themeChangedEventObservable: Observable<Unit> =
themeChangedEventPublisher.observeOn(AndroidSchedulers.mainThread()) themeChangedEventPublisher.observeOn(mainThread())
val musicFolderChangedEventPublisher: PublishSubject<String> = val musicFolderChangedEventPublisher: PublishSubject<String> =
PublishSubject.create() PublishSubject.create()
val musicFolderChangedEventObservable: Observable<String> = val musicFolderChangedEventObservable: Observable<String> =
musicFolderChangedEventPublisher.observeOn(AndroidSchedulers.mainThread()) musicFolderChangedEventPublisher.observeOn(mainThread())
val playerStatePublisher: PublishSubject<StateWithTrack> = val playerStatePublisher: PublishSubject<StateWithTrack> =
PublishSubject.create() PublishSubject.create()
val playerStateObservable: Observable<StateWithTrack> = val playerStateObservable: Observable<StateWithTrack> =
playerStatePublisher.observeOn(AndroidSchedulers.mainThread()) playerStatePublisher.observeOn(mainThread())
.replay(1) .replay(1)
.autoConnect(0) .autoConnect(0)
val throttledPlayerStateObservable: Observable<StateWithTrack> =
playerStatePublisher.observeOn(mainThread())
.replay(1)
.autoConnect(0)
.throttleLatest(300, TimeUnit.MILLISECONDS)
val playlistPublisher: PublishSubject<List<DownloadFile>> = val playlistPublisher: PublishSubject<List<DownloadFile>> =
PublishSubject.create() PublishSubject.create()
val playlistObservable: Observable<List<DownloadFile>> = val playlistObservable: Observable<List<DownloadFile>> =
playlistPublisher.observeOn(AndroidSchedulers.mainThread()) playlistPublisher.observeOn(mainThread())
.replay(1) .replay(1)
.autoConnect(0) .autoConnect(0)
val throttledPlaylistObservable: Observable<List<DownloadFile>> =
playlistPublisher.observeOn(mainThread())
.replay(1)
.autoConnect(0)
.throttleLatest(300, TimeUnit.MILLISECONDS)
// Commands // Commands
val dismissNowPlayingCommandPublisher: PublishSubject<Unit> = val dismissNowPlayingCommandPublisher: PublishSubject<Unit> =
PublishSubject.create() PublishSubject.create()
val dismissNowPlayingCommandObservable: Observable<Unit> = val dismissNowPlayingCommandObservable: Observable<Unit> =
dismissNowPlayingCommandPublisher.observeOn(AndroidSchedulers.mainThread()) dismissNowPlayingCommandPublisher.observeOn(mainThread())
} }
data class StateWithTrack( data class StateWithTrack(