package dev.jdtech.jellyfin.viewmodels import android.app.Application import android.net.Uri import android.os.Build import androidx.lifecycle.* import dagger.hilt.android.lifecycle.HiltViewModel import dev.jdtech.jellyfin.database.DownloadDatabaseDao import dev.jdtech.jellyfin.models.DownloadRequestItem import dev.jdtech.jellyfin.models.PlayerItem import dev.jdtech.jellyfin.repository.JellyfinRepository import dev.jdtech.jellyfin.utils.* import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import org.jellyfin.sdk.api.client.exception.ApiClientException import org.jellyfin.sdk.model.api.BaseItemDto import timber.log.Timber import java.text.DateFormat import java.time.ZoneOffset import java.util.Date import java.util.UUID import javax.inject.Inject @HiltViewModel class EpisodeBottomSheetViewModel @Inject constructor( private val application: Application, private val jellyfinRepository: JellyfinRepository, private val downloadDatabase: DownloadDatabaseDao ) : ViewModel() { private val uiState = MutableStateFlow(UiState.Loading) sealed class UiState { data class Normal( val episode: BaseItemDto, val runTime: String, val dateString: String, val played: Boolean, val favorite: Boolean, val canDownload: Boolean, val downloaded: Boolean, val downloadEpisode: Boolean, val available: Boolean, ) : UiState() object Loading : UiState() data class Error(val message: String?) : UiState() } fun onUiState(scope: LifecycleCoroutineScope, collector: (UiState) -> Unit) { scope.launch { uiState.collect { collector(it) } } } var item: BaseItemDto? = null private var runTime: String = "" private var dateString: String = "" var played: Boolean = false var favorite: Boolean = false private var canDownload = false private var downloaded: Boolean = false private var downloadEpisode: Boolean = false private var available: Boolean = true var playerItems: MutableList = mutableListOf() private lateinit var downloadRequestItem: DownloadRequestItem fun loadEpisode(episodeId: UUID) { viewModelScope.launch { uiState.emit(UiState.Loading) try { val tempItem = jellyfinRepository.getItem(episodeId) item = tempItem runTime = "${tempItem.runTimeTicks?.div(600000000)} min" dateString = getDateString(tempItem) played = tempItem.userData?.played == true favorite = tempItem.userData?.isFavorite == true canDownload = tempItem.canDownload == true downloaded = isItemDownloaded(downloadDatabase, episodeId) uiState.emit( UiState.Normal( tempItem, runTime, dateString, played, favorite, canDownload, downloaded, downloadEpisode, available, ) ) } catch (e: Exception) { uiState.emit(UiState.Error(e.message)) } } } fun loadEpisode(playerItem: PlayerItem) { viewModelScope.launch { uiState.emit(UiState.Loading) playerItems.add(playerItem) item = downloadMetadataToBaseItemDto(playerItem.item!!) available = isItemAvailable(playerItem.itemId) Timber.d("Available: $available") uiState.emit( UiState.Normal( item!!, runTime, dateString, played, favorite, canDownload, downloaded, downloadEpisode, available, ) ) } } fun markAsPlayed(itemId: UUID) { viewModelScope.launch { try { jellyfinRepository.markAsPlayed(itemId) } catch (e: ApiClientException) { Timber.d(e) } } played = true } fun markAsUnplayed(itemId: UUID) { viewModelScope.launch { try { jellyfinRepository.markAsUnplayed(itemId) } catch (e: ApiClientException) { Timber.d(e) } } played = false } fun markAsFavorite(itemId: UUID) { viewModelScope.launch { try { jellyfinRepository.markAsFavorite(itemId) } catch (e: ApiClientException) { Timber.d(e) } } favorite = true } fun unmarkAsFavorite(itemId: UUID) { viewModelScope.launch { try { jellyfinRepository.unmarkAsFavorite(itemId) } catch (e: ApiClientException) { Timber.d(e) } } favorite = false } fun loadDownloadRequestItem(itemId: UUID) { viewModelScope.launch { val episode = item val uri = jellyfinRepository.getStreamUrl(itemId, episode?.mediaSources?.get(0)?.id!!) val metadata = baseItemDtoToDownloadMetadata(episode) downloadRequestItem = DownloadRequestItem(uri, itemId, metadata) downloadEpisode = true requestDownload(downloadDatabase, Uri.parse(downloadRequestItem.uri), downloadRequestItem, application) } } fun deleteEpisode() { deleteDownloadedEpisode(downloadDatabase, playerItems[0].itemId) } private fun getDateString(item: BaseItemDto): String { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val instant = item.premiereDate?.toInstant(ZoneOffset.UTC) val date = Date.from(instant) DateFormat.getDateInstance(DateFormat.SHORT).format(date) } else { // TODO: Implement a way to get the year from LocalDateTime in Android < O item.premiereDate.toString() } } }