Try to fix the mess :)
This commit is contained in:
parent
827654c0c1
commit
707339b88b
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue