Skip to content

Commit

Permalink
Partially add radio undo, various fixes and refactors
Browse files Browse the repository at this point in the history
Partially implement radio undo for addToQueue() (part of #55, untested)
Split and organise several files into packages
Several UI fixes
  • Loading branch information
toasterofbread committed Jul 3, 2023
1 parent 29c2333 commit 32e5655
Show file tree
Hide file tree
Showing 19 changed files with 1,122 additions and 1,035 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ actual open class MediaPlayerService {

// Undo
private var current_action: MutableList<UndoRedoAction>? = null
private var current_action_is_further: Boolean = false
private val action_list: MutableList<List<UndoRedoAction>> = mutableListOf()
private var action_head: Int = 0

Expand Down Expand Up @@ -200,39 +201,74 @@ actual open class MediaPlayerService {
listeners.remove(listener)
}

protected actual open fun onSongMoved(from: Int, to: Int) {}

actual fun undoableAction(action: MediaPlayerService.() -> Unit) {
undoableActionWithCustom {
action()
actual fun undoableAction(action: MediaPlayerService.(furtherAction: (MediaPlayerService.() -> Unit) -> Unit) -> Unit) {
customUndoableAction { furtherAction ->
action {
furtherAction(it)
}
null
}
}

actual fun undoableActionWithCustom(action: MediaPlayerService.() -> UndoRedoAction?) {
synchronized(action_list) {
assert(current_action == null)
current_action = mutableListOf()

actual fun customUndoableAction(action: MediaPlayerService.(furtherAction: (MediaPlayerService.() -> UndoRedoAction?) -> Unit) -> UndoRedoAction?) {
if (current_action != null) {
val custom_action = action(this)
if (custom_action != null) {
performAction(custom_action)
}
return
}

for (i in 0 until redo_count) {
action_list.removeLast()
synchronized(action_list) {
val c_action = mutableListOf()
current_action = c_action

val custom_action = action(this) { further ->
synchronized(action_list) {
current_action_is_further = true
current_action = c_action

val custom_action = further()
if (custom_action != null) {
performAction(custom_action)
}

current_action = null
current_action_is_further = false
}
}
if (custom_action != null) {
performAction(custom_action)
}
action_list.add(current_action!!)
action_head++

commitActionList(c_action)
current_action = null
listeners.forEach { it.onUndoStateChanged() }
}
}

private fun commitActionList(actions: List<UndoRedoAction>) {
for (i in 0 until redo_count) {
action_list.removeLast()
}
action_list.add(actions)
action_head++

listeners.forEach { it.onUndoStateChanged() }
}

private fun performAction(action: UndoRedoAction) {
action.redo()
current_action?.add(action)
synchronized(action_list) {
action.redo()

val current = current_action
if (current != null) {
current.add(action)
}
else if (!current_action_is_further) {
// If not being performed as part of an undoableAction, commit as a single aciton
commitActionList(listOf(action))
}
}
}

actual fun redo() {
Expand Down Expand Up @@ -297,12 +333,10 @@ actual open class MediaPlayerService {
override fun redo() {
player.moveMediaItem(from, to)
listeners.forEach { it.onSongMoved(from, to) }
onSongMoved(from, to)
}
override fun undo() {
player.moveMediaItem(to, from)
listeners.forEach { it.onSongMoved(to, from) }
onSongMoved(to, from)
}
}
private inner class RemoveAction(val index: Int): Action() {
Expand All @@ -317,23 +351,6 @@ actual open class MediaPlayerService {
listeners.forEach { it.onSongAdded(index, item.getSong()) }
}
}
private inner class ClearAction : Action() {
private var items: List<ExoMediaItem>? = null
override fun redo() {
if (items == null && is_undoable) {
items = List(player.mediaItemCount) {
player.getMediaItemAt(it)
}
}
player.clearMediaItems()
}
override fun undo() {
assert(items != null && player.mediaItemCount == 0)
for (item in items!!) {
player.addMediaItem(item)
}
}
}

private fun release() {
player.release()
Expand Down
121 changes: 77 additions & 44 deletions shared/src/commonMain/kotlin/com/toasterofbread/spmp/PlayerService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,13 @@ class PlayerService : MediaPlayerService() {
radio.cancelRadio()
}

for (i in song_count - 1 downTo from) {
if (keep_current && i == current_song_index) {
continue
undoableAction {
for (i in song_count - 1 downTo from) {
if (keep_current && i == current_song_index) {
continue
}
removeFromQueue(i, save = false)
}
removeFromQueue(i, save = false)
}

if (save) {
Expand All @@ -158,19 +160,22 @@ class PlayerService : MediaPlayerService() {
shuffleQueue(range)
}

fun shuffleQueueAndIndices(indices: List<Int>) {
for (i in indices.withIndex()) {
val swap_index = Random.nextInt(indices.size)
swapQueuePositions(i.value, indices[swap_index], false)
// indices.swap(i.index, swap_index)
fun shuffleQueue(range: IntRange) {
undoableAction {
for (i in range) {
val swap = Random.nextInt(range)
swapQueuePositions(i, swap, false)
}
}
savePersistentQueue()
}

fun shuffleQueue(range: IntRange) {
for (i in range) {
val swap = Random.nextInt(range)
swapQueuePositions(i, swap, false)
fun shuffleQueueIndices(indices: List<Int>) {
undoableAction {
for (i in indices.withIndex()) {
val swap_index = Random.nextInt(indices.size)
swapQueuePositions(i.value, indices[swap_index], false)
}
}
savePersistentQueue()
}
Expand All @@ -184,58 +189,78 @@ class PlayerService : MediaPlayerService() {
assert(b in 0 until song_count)

val offset_b = b + (if (b > a) -1 else 1)
moveSong(a, b)
moveSong(offset_b, a)

undoableAction {
moveSong(a, b)
moveSong(offset_b, a)
}

if (save) {
savePersistentQueue()
}
}

fun addToQueue(song: Song, index: Int? = null, is_active_queue: Boolean = false, start_radio: Boolean = false, save: Boolean = true): Int {
val added_index: Int
val add_to_index: Int
if (index == null) {
addSong(song)
added_index = song_count - 1
add_to_index = song_count - 1
}
else {
addSong(song, index)
added_index = if (index < song_count) index else song_count - 1
add_to_index = if (index < song_count) index else song_count - 1
}

if (is_active_queue) {
active_queue_index = added_index
active_queue_index = add_to_index
}

if (start_radio) {
clearQueue(added_index + 1, save = false)
customUndoableAction { furtherAction ->
addSong(song, add_to_index)
if (start_radio) {
clearQueue(add_to_index + 1, save = false)

synchronized(radio) {
radio.playMediaItem(song, added_index)
radio.loadContinuation { result ->
if (result.isFailure) {
SpMp.error_manager.onError("addToQueue", result.exceptionOrNull()!!)
if (save) {
savePersistentQueue()
val radio_state: RadioState
val previous_radio_state: RadioState

synchronized(radio) {
previous_radio_state = radio.playMediaItem(song, add_to_index)
radio_state = radio.state

radio.loadContinuation { result ->
if (result.isFailure) {
SpMp.error_manager.onError("addToQueue", result.exceptionOrNull()!!)
if (save) {
savePersistentQueue()
}
}
}
else {
withContext(Dispatchers.Main) {
addMultipleToQueue(result.getOrThrowHere(), added_index + 1, save = save, skip_existing = true)
else {
withContext(Dispatchers.Main) {
furtherAction {
addMultipleToQueue(result.getOrThrowHere(), add_to_index + 1, save = save, skip_existing = true)
}
}
}
}
}

return@customUndoableAction object : UndoRedoAction {
override fun redo() {
radio.setRadioState(radio_state)
}
override fun undo() {
radio.setRadioState(previous_radio_state)
}
}
}
else if (save) {
savePersistentQueue()
return@customUndoableAction null
}
}
else if (save) {
savePersistentQueue()
}

return added_index
return add_to_index
}

fun addMultipleToQueue(songs: List<Song>, index: Int = 0, skip_first: Boolean = false, save: Boolean = true, is_active_queue: Boolean = false, skip_existing: Boolean = false) {

val to_add: List<Song> =
if (!skip_existing) {
songs
Expand Down Expand Up @@ -328,10 +353,6 @@ class PlayerService : MediaPlayerService() {
}
}

override fun onSongMoved(from: Int, to: Int) {
radio.onSongMoved(from, to)
}

private val prefs_listener = object : ProjectPreferences.Listener {
override fun onChanged(prefs: ProjectPreferences, key: String) {
when (key) {
Expand Down Expand Up @@ -376,6 +397,17 @@ class PlayerService : MediaPlayerService() {
checkRadioContinuation()
updateDiscordStatus(song)
}

override fun onSongMoved(from: Int, to: Int) {
checkRadioContinuation()
radio.onSongMoved(from, to)
}

override fun onSongRemoved(index: Int) {
checkRadioContinuation()
radio.onSongRemoved(index)
}

override fun onStateChanged(state: MediaPlayerState) {
if (state == MediaPlayerState.ENDED) {
onSongEnded()
Expand Down Expand Up @@ -421,6 +453,7 @@ class PlayerService : MediaPlayerService() {
updateDiscordStatus(null)
}

@Synchronized
private fun updateDiscordStatus(song: Song?) {
discord_status_update_job?.cancel()
discord_rpc?.apply {
Expand All @@ -432,7 +465,7 @@ class PlayerService : MediaPlayerService() {

check(song.artist?.title != null)

discord_status_update_job = Api.scope.launch {
discord_status_update_job = coroutine_scope.launch {
fun formatText(text: String): String {
return text
.replace("\$artist", song.artist!!.title!!)
Expand Down
Loading

0 comments on commit 32e5655

Please sign in to comment.