mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 16:11:20 +02:00
Add option to delete media (#1014)
## Description Adds a setting to enable "Media management" which adds a context menu item to delete media. This is accessible using either the More button or long clicking. You can delete episodes, seasons, series and movies/videos. If order for the option to appear the current user must have server-side settings for "Allow media deletion from" enabled for the library and the in-app setting under Settings->Advanced Settings->"Show media management options". ### Related issues Closes #104 ### Testing Emulator ## Screenshots ### Context menu  ### Confirmation  ## AI or LLM usage None
This commit is contained in:
parent
0b73b4cf64
commit
8935067c5a
19 changed files with 597 additions and 53 deletions
|
|
@ -0,0 +1,104 @@
|
|||
package com.github.damontecres.wholphin.services
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.showToast
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.libraryApi
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class MediaManagementService
|
||||
@Inject
|
||||
constructor(
|
||||
@param:ApplicationContext private val context: Context,
|
||||
private val api: ApiClient,
|
||||
private val serverRepository: ServerRepository,
|
||||
private val userPreferencesService: UserPreferencesService,
|
||||
) {
|
||||
private val _deletedItemFlow =
|
||||
MutableSharedFlow<DeletedItem>(
|
||||
replay = 1,
|
||||
extraBufferCapacity = 0,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
|
||||
/**
|
||||
* Listen for recently deleted items. Useful for ViewModels to react and refresh data
|
||||
*/
|
||||
val deletedItemFlow: SharedFlow<DeletedItem> = _deletedItemFlow
|
||||
|
||||
suspend fun canDelete(item: BaseItem): Boolean {
|
||||
Timber.v("canDelete %s: %s", item.id, item.canDelete)
|
||||
val enabled =
|
||||
userPreferencesService
|
||||
.getCurrent()
|
||||
.appPreferences.interfacePreferences.enableMediaManagement
|
||||
return enabled &&
|
||||
item.canDelete &&
|
||||
if (item.type == BaseItemKind.RECORDING) {
|
||||
serverRepository.currentUserDto.value
|
||||
?.policy
|
||||
?.enableLiveTvManagement == true
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteItem(item: BaseItem): DeleteResult {
|
||||
try {
|
||||
Timber.i("Deleting %s", item.id)
|
||||
// TODO enable
|
||||
api.libraryApi.deleteItem(item.id)
|
||||
_deletedItemFlow.emit(DeletedItem(item))
|
||||
return DeleteResult.Success
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Error deleting %s", item.id)
|
||||
return DeleteResult.Error(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class DeletedItem(
|
||||
val item: BaseItem,
|
||||
)
|
||||
|
||||
sealed interface DeleteResult {
|
||||
data object Success : DeleteResult
|
||||
|
||||
data class Error(
|
||||
val ex: Exception,
|
||||
) : DeleteResult
|
||||
}
|
||||
|
||||
fun ViewModel.deleteItem(
|
||||
context: Context,
|
||||
mediaManagementService: MediaManagementService,
|
||||
item: BaseItem,
|
||||
onSuccess: () -> Unit = {},
|
||||
) = viewModelScope.launchIO {
|
||||
when (val r = mediaManagementService.deleteItem(item)) {
|
||||
is DeleteResult.Error -> {
|
||||
showToast(
|
||||
context,
|
||||
"Error deleting item: ${r.ex.localizedMessage}",
|
||||
)
|
||||
}
|
||||
|
||||
DeleteResult.Success -> {
|
||||
showToast(context, "Deleted")
|
||||
onSuccess.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import com.github.damontecres.wholphin.ui.showToast
|
|||
import com.github.damontecres.wholphin.util.supportedCollectionTypes
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
|
|
@ -22,6 +23,7 @@ import kotlinx.coroutines.flow.combine
|
|||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.liveTvApi
|
||||
import org.jellyfin.sdk.api.client.extensions.userViewsApi
|
||||
|
|
@ -149,7 +151,9 @@ class NavDrawerService
|
|||
val allItems = builtins + libraries
|
||||
|
||||
val navDrawerPins =
|
||||
serverPreferencesDao.getNavDrawerPinnedItems(user).associateBy { it.itemId }
|
||||
withContext(Dispatchers.IO) {
|
||||
serverPreferencesDao.getNavDrawerPinnedItems(user).associateBy { it.itemId }
|
||||
}
|
||||
|
||||
val items = mutableListOf<NavDrawerItem>()
|
||||
val moreItems = mutableListOf<NavDrawerItem>()
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ object AppModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
@DefaultCoroutineScope
|
||||
fun defaultCoroutineScope(): CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
fun defaultCoroutineScope(): CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue