Upgrade to jellyfin-sdk 1.3.0 (#122)

Uses BaseItemKind to specify the item type
This commit is contained in:
Jarne Demeulemeester 2022-06-11 21:39:08 +02:00 committed by GitHub
parent d1dcf9b343
commit 8c039a3c81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 80 additions and 103 deletions

View file

@ -95,7 +95,7 @@ dependencies {
implementation("androidx.preference:preference-ktx:$preferenceVersion") implementation("androidx.preference:preference-ktx:$preferenceVersion")
// Jellyfin // Jellyfin
val jellyfinVersion = "1.2.0" val jellyfinVersion = "1.3.0"
implementation("org.jellyfin.sdk:jellyfin-core:$jellyfinVersion") implementation("org.jellyfin.sdk:jellyfin-core:$jellyfinVersion")
// Glide // Glide

View file

@ -13,6 +13,7 @@ import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
import dev.jdtech.jellyfin.api.JellyfinApi import dev.jdtech.jellyfin.api.JellyfinApi
import dev.jdtech.jellyfin.database.Server import dev.jdtech.jellyfin.database.Server
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.BaseItemPerson import org.jellyfin.sdk.model.api.BaseItemPerson
import org.jellyfin.sdk.model.api.ImageType import org.jellyfin.sdk.model.api.ImageType
import java.util.UUID import java.util.UUID
@ -32,7 +33,7 @@ fun bindItems(recyclerView: RecyclerView, data: List<BaseItemDto>?) {
@BindingAdapter("itemImage") @BindingAdapter("itemImage")
fun bindItemImage(imageView: ImageView, item: BaseItemDto) { fun bindItemImage(imageView: ImageView, item: BaseItemDto) {
val itemId = val itemId =
if (item.type == "Episode" || item.type == "Season" && item.imageTags.isNullOrEmpty()) item.seriesId else item.id if (item.type == BaseItemKind.EPISODE || item.type == BaseItemKind.SEASON && item.imageTags.isNullOrEmpty()) item.seriesId else item.id
imageView imageView
.loadImage("/items/$itemId/Images/${ImageType.PRIMARY}") .loadImage("/items/$itemId/Images/${ImageType.PRIMARY}")
@ -75,7 +76,7 @@ fun bindBaseItemImage(imageView: ImageView, episode: BaseItemDto?) {
if (!episode.imageTags.isNullOrEmpty()) { //TODO: Downloadmetadata currently does not store imagetags, so it always uses the backdrop if (!episode.imageTags.isNullOrEmpty()) { //TODO: Downloadmetadata currently does not store imagetags, so it always uses the backdrop
when (episode.type) { when (episode.type) {
"Movie" -> { BaseItemKind.MOVIE -> {
if (!episode.backdropImageTags.isNullOrEmpty()) { if (!episode.backdropImageTags.isNullOrEmpty()) {
imageType = ImageType.BACKDROP imageType = ImageType.BACKDROP
} }
@ -87,7 +88,7 @@ fun bindBaseItemImage(imageView: ImageView, episode: BaseItemDto?) {
} }
} }
} else { } else {
if (episode.type == "Episode") { if (episode.type == BaseItemKind.EPISODE) {
imageItemId = episode.seriesId!! imageItemId = episode.seriesId!!
imageType = ImageType.BACKDROP imageType = ImageType.BACKDROP
} }

View file

@ -9,6 +9,7 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dev.jdtech.jellyfin.databinding.HomeEpisodeItemBinding import dev.jdtech.jellyfin.databinding.HomeEpisodeItemBinding
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
class HomeEpisodeListAdapter(private val onClickListener: OnClickListener) : ListAdapter<BaseItemDto, HomeEpisodeListAdapter.EpisodeViewHolder>(DiffCallback) { class HomeEpisodeListAdapter(private val onClickListener: OnClickListener) : ListAdapter<BaseItemDto, HomeEpisodeListAdapter.EpisodeViewHolder>(DiffCallback) {
class EpisodeViewHolder(private var binding: HomeEpisodeItemBinding) : class EpisodeViewHolder(private var binding: HomeEpisodeItemBinding) :
@ -21,10 +22,10 @@ class HomeEpisodeListAdapter(private val onClickListener: OnClickListener) : Lis
binding.progressBar.visibility = View.VISIBLE binding.progressBar.visibility = View.VISIBLE
} }
if (episode.type == "Movie") { if (episode.type == BaseItemKind.MOVIE) {
binding.primaryName.text = episode.name binding.primaryName.text = episode.name
binding.secondaryName.visibility = View.GONE binding.secondaryName.visibility = View.GONE
} else if (episode.type == "Episode") { } else if (episode.type == BaseItemKind.EPISODE) {
binding.primaryName.text = episode.seriesName binding.primaryName.text = episode.seriesName
} }

View file

@ -9,6 +9,7 @@ import androidx.recyclerview.widget.RecyclerView
import dev.jdtech.jellyfin.R import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.databinding.BaseItemBinding import dev.jdtech.jellyfin.databinding.BaseItemBinding
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
class ViewItemListAdapter( class ViewItemListAdapter(
private val onClickListener: OnClickListener, private val onClickListener: OnClickListener,
@ -20,7 +21,7 @@ class ViewItemListAdapter(
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
fun bind(item: BaseItemDto, fixedWidth: Boolean) { fun bind(item: BaseItemDto, fixedWidth: Boolean) {
binding.item = item binding.item = item
binding.itemName.text = if (item.type == "Episode") item.seriesName else item.name binding.itemName.text = if (item.type == BaseItemKind.EPISODE) item.seriesName else item.name
binding.itemCount.visibility = binding.itemCount.visibility =
if (item.userData?.unplayedItemCount != null && item.userData?.unplayedItemCount!! > 0) View.VISIBLE else View.GONE if (item.userData?.unplayedItemCount != null && item.userData?.unplayedItemCount!! > 0) View.VISIBLE else View.GONE
if (fixedWidth) { if (fixedWidth) {

View file

@ -99,7 +99,7 @@ class DownloadFragment : Fragment() {
DownloadFragmentDirections.actionDownloadFragmentToMediaInfoFragment( DownloadFragmentDirections.actionDownloadFragmentToMediaInfoFragment(
UUID.randomUUID(), UUID.randomUUID(),
item.name, item.name,
item.item?.type?.type ?: "Unkown", item.item!!.type,
item, item,
isOffline = true isOffline = true
) )

View file

@ -98,7 +98,7 @@ class FavoriteFragment : Fragment() {
FavoriteFragmentDirections.actionFavoriteFragmentToMediaInfoFragment( FavoriteFragmentDirections.actionFavoriteFragmentToMediaInfoFragment(
item.id, item.id,
item.name, item.name,
item.type ?: "Unknown" item.type
) )
) )
} }

View file

@ -23,15 +23,11 @@ import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
import dev.jdtech.jellyfin.adapters.ViewListAdapter import dev.jdtech.jellyfin.adapters.ViewListAdapter
import dev.jdtech.jellyfin.databinding.FragmentHomeBinding import dev.jdtech.jellyfin.databinding.FragmentHomeBinding
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
import dev.jdtech.jellyfin.models.ContentType
import dev.jdtech.jellyfin.models.ContentType.EPISODE
import dev.jdtech.jellyfin.models.ContentType.MOVIE
import dev.jdtech.jellyfin.models.ContentType.TVSHOW
import dev.jdtech.jellyfin.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.checkIfLoginRequired
import dev.jdtech.jellyfin.utils.contentType
import dev.jdtech.jellyfin.viewmodels.HomeViewModel import dev.jdtech.jellyfin.viewmodels.HomeViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
import timber.log.Timber import timber.log.Timber
@AndroidEntryPoint @AndroidEntryPoint
@ -91,9 +87,9 @@ class HomeFragment : Fragment() {
navigateToMediaInfoFragment(it) navigateToMediaInfoFragment(it)
}, },
onNextUpClickListener = HomeEpisodeListAdapter.OnClickListener { item -> onNextUpClickListener = HomeEpisodeListAdapter.OnClickListener { item ->
when (item.contentType()) { when (item.type) {
EPISODE -> navigateToEpisodeBottomSheetFragment(item) BaseItemKind.EPISODE -> navigateToEpisodeBottomSheetFragment(item)
MOVIE -> navigateToMediaInfoFragment(item) BaseItemKind.MOVIE -> navigateToMediaInfoFragment(item)
else -> Toast.makeText(requireContext(), R.string.unknown_error, LENGTH_LONG) else -> Toast.makeText(requireContext(), R.string.unknown_error, LENGTH_LONG)
.show() .show()
} }
@ -158,12 +154,12 @@ class HomeFragment : Fragment() {
} }
private fun navigateToMediaInfoFragment(item: BaseItemDto) { private fun navigateToMediaInfoFragment(item: BaseItemDto) {
if (item.contentType() == EPISODE) { if (item.type == BaseItemKind.EPISODE) {
findNavController().navigate( findNavController().navigate(
HomeFragmentDirections.actionNavigationHomeToMediaInfoFragment( HomeFragmentDirections.actionNavigationHomeToMediaInfoFragment(
item.seriesId!!, item.seriesId!!,
item.seriesName, item.seriesName,
TVSHOW.type BaseItemKind.SERIES
) )
) )
} else { } else {
@ -171,7 +167,7 @@ class HomeFragment : Fragment() {
HomeFragmentDirections.actionNavigationHomeToMediaInfoFragment( HomeFragmentDirections.actionNavigationHomeToMediaInfoFragment(
item.id, item.id,
item.name, item.name,
item.type ?: ContentType.UNKNOWN.type item.type
) )
) )
} }

View file

@ -144,7 +144,7 @@ class LibraryFragment : Fragment() {
LibraryFragmentDirections.actionLibraryFragmentToMediaInfoFragment( LibraryFragmentDirections.actionLibraryFragmentToMediaInfoFragment(
item.id, item.id,
item.name, item.name,
item.type ?: "Unknown" item.type
) )
) )
} }

View file

@ -7,7 +7,6 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -34,7 +33,7 @@ import dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.serializer.toUUID import org.jellyfin.sdk.model.api.BaseItemKind
import timber.log.Timber import timber.log.Timber
import java.util.UUID import java.util.UUID
@ -78,7 +77,7 @@ class MediaInfoFragment : Fragment() {
} }
} }
if (args.itemType != "Movie") { if (args.itemType != BaseItemKind.MOVIE) {
binding.downloadButton.visibility = View.GONE binding.downloadButton.visibility = View.GONE
} }
@ -111,16 +110,7 @@ class MediaInfoFragment : Fragment() {
navigateToSeasonFragment(season) navigateToSeasonFragment(season)
}, fixedWidth = true) }, fixedWidth = true)
binding.peopleRecyclerView.adapter = PersonListAdapter { person -> binding.peopleRecyclerView.adapter = PersonListAdapter { person ->
val uuid = person.id?.toUUID() navigateToPersonDetail(person.id)
if (uuid != null) {
navigateToPersonDetail(uuid)
} else {
Toast.makeText(
requireContext(),
R.string.error_getting_person_id,
Toast.LENGTH_SHORT
).show()
}
} }
binding.playButton.setOnClickListener { binding.playButton.setOnClickListener {

View file

@ -137,7 +137,7 @@ internal class PersonDetailFragment : Fragment() {
PersonDetailFragmentDirections.actionPersonDetailFragmentToMediaInfoFragment( PersonDetailFragmentDirections.actionPersonDetailFragmentToMediaInfoFragment(
itemId = item.id, itemId = item.id,
itemName = item.name, itemName = item.name,
itemType = item.type ?: "Unknown" itemType = item.type
) )
) )
} }

View file

@ -102,7 +102,7 @@ class SearchResultFragment : Fragment() {
FavoriteFragmentDirections.actionFavoriteFragmentToMediaInfoFragment( FavoriteFragmentDirections.actionFavoriteFragmentToMediaInfoFragment(
item.id, item.id,
item.name, item.name,
item.type ?: "Unknown" item.type
) )
) )
} }

View file

@ -1,8 +0,0 @@
package dev.jdtech.jellyfin.models
enum class ContentType(val type: String) {
MOVIE("Movie"),
TVSHOW("Series"),
EPISODE("Episode"),
UNKNOWN("")
}

View file

@ -4,6 +4,7 @@ import android.os.Parcelable
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.jellyfin.sdk.model.api.BaseItemKind
import java.util.* import java.util.*
@Parcelize @Parcelize
@ -11,7 +12,7 @@ import java.util.*
data class DownloadItem( data class DownloadItem(
@PrimaryKey @PrimaryKey
val id: UUID, val id: UUID,
val type: ContentType, val type: BaseItemKind,
val name: String, val name: String,
val played: Boolean, val played: Boolean,
val overview: String? = null, val overview: String? = null,

View file

@ -1,12 +1,8 @@
package dev.jdtech.jellyfin.repository package dev.jdtech.jellyfin.repository
import dev.jdtech.jellyfin.models.ContentType
import dev.jdtech.jellyfin.utils.SortBy import dev.jdtech.jellyfin.utils.SortBy
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.*
import org.jellyfin.sdk.model.api.ItemFields
import org.jellyfin.sdk.model.api.MediaSourceInfo
import org.jellyfin.sdk.model.api.SortOrder
import java.util.* import java.util.*
interface JellyfinRepository { interface JellyfinRepository {
@ -16,7 +12,7 @@ interface JellyfinRepository {
suspend fun getItems( suspend fun getItems(
parentId: UUID? = null, parentId: UUID? = null,
includeTypes: List<String>? = null, includeTypes: List<BaseItemKind>? = null,
recursive: Boolean = false, recursive: Boolean = false,
sortBy: SortBy = SortBy.defaultValue, sortBy: SortBy = SortBy.defaultValue,
sortOrder: SortOrder = SortOrder.ASCENDING sortOrder: SortOrder = SortOrder.ASCENDING
@ -24,7 +20,7 @@ interface JellyfinRepository {
suspend fun getPersonItems( suspend fun getPersonItems(
personIds: List<UUID>, personIds: List<UUID>,
includeTypes: List<ContentType>? = null, includeTypes: List<BaseItemKind>? = null,
recursive: Boolean = true recursive: Boolean = true
): List<BaseItemDto> ): List<BaseItemDto>

View file

@ -1,7 +1,6 @@
package dev.jdtech.jellyfin.repository package dev.jdtech.jellyfin.repository
import dev.jdtech.jellyfin.api.JellyfinApi import dev.jdtech.jellyfin.api.JellyfinApi
import dev.jdtech.jellyfin.models.ContentType
import dev.jdtech.jellyfin.utils.SortBy import dev.jdtech.jellyfin.utils.SortBy
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -20,7 +19,7 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep
override suspend fun getItems( override suspend fun getItems(
parentId: UUID?, parentId: UUID?,
includeTypes: List<String>?, includeTypes: List<BaseItemKind>?,
recursive: Boolean, recursive: Boolean,
sortBy: SortBy, sortBy: SortBy,
sortOrder: SortOrder sortOrder: SortOrder
@ -37,13 +36,13 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep
override suspend fun getPersonItems( override suspend fun getPersonItems(
personIds: List<UUID>, personIds: List<UUID>,
includeTypes: List<ContentType>?, includeTypes: List<BaseItemKind>?,
recursive: Boolean recursive: Boolean
): List<BaseItemDto> = withContext(Dispatchers.IO) { ): List<BaseItemDto> = withContext(Dispatchers.IO) {
jellyfinApi.itemsApi.getItems( jellyfinApi.itemsApi.getItems(
jellyfinApi.userId!!, jellyfinApi.userId!!,
personIds = personIds, personIds = personIds,
includeItemTypes = includeTypes?.map { it.type }, includeItemTypes = includeTypes,
recursive = recursive recursive = recursive
).content.items.orEmpty() ).content.items.orEmpty()
} }
@ -52,7 +51,7 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep
jellyfinApi.itemsApi.getItems( jellyfinApi.itemsApi.getItems(
jellyfinApi.userId!!, jellyfinApi.userId!!,
filters = listOf(ItemFilter.IS_FAVORITE), filters = listOf(ItemFilter.IS_FAVORITE),
includeItemTypes = listOf("Movie", "Series", "Episode"), includeItemTypes = listOf(BaseItemKind.MOVIE, BaseItemKind.SERIES, BaseItemKind.EPISODE),
recursive = true recursive = true
).content.items.orEmpty() ).content.items.orEmpty()
} }
@ -62,7 +61,7 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep
jellyfinApi.itemsApi.getItems( jellyfinApi.itemsApi.getItems(
jellyfinApi.userId!!, jellyfinApi.userId!!,
searchTerm = searchQuery, searchTerm = searchQuery,
includeItemTypes = listOf("Movie", "Series", "Episode"), includeItemTypes = listOf(BaseItemKind.MOVIE, BaseItemKind.SERIES, BaseItemKind.EPISODE),
recursive = true recursive = true
).content.items.orEmpty() ).content.items.orEmpty()
} }
@ -70,7 +69,7 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep
override suspend fun getResumeItems(): List<BaseItemDto> = withContext(Dispatchers.IO) { override suspend fun getResumeItems(): List<BaseItemDto> = withContext(Dispatchers.IO) {
jellyfinApi.itemsApi.getResumeItems( jellyfinApi.itemsApi.getResumeItems(
jellyfinApi.userId!!, jellyfinApi.userId!!,
includeItemTypes = listOf("Movie", "Episode"), includeItemTypes = listOf(BaseItemKind.MOVIE, BaseItemKind.EPISODE),
).content.items.orEmpty() ).content.items.orEmpty()
} }
@ -128,6 +127,9 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep
), ),
transcodingProfiles = emptyList(), transcodingProfiles = emptyList(),
responseProfiles = emptyList(), responseProfiles = emptyList(),
subtitleProfiles = emptyList(),
xmlRootAttributes = emptyList(),
supportedMediaTypes = "",
enableAlbumArtInDidl = false, enableAlbumArtInDidl = false,
enableMsMediaReceiverRegistrar = false, enableMsMediaReceiverRegistrar = false,
enableSingleAlbumArtLimit = false, enableSingleAlbumArtLimit = false,
@ -144,7 +146,7 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep
subtitleStreamIndex = null, subtitleStreamIndex = null,
maxStreamingBitrate = 1_000_000_000, maxStreamingBitrate = 1_000_000_000,
) )
).content.mediaSources.orEmpty() ).content.mediaSources
} }
override suspend fun getStreamUrl(itemId: UUID, mediaSourceId: String): String = override suspend fun getStreamUrl(itemId: UUID, mediaSourceId: String): String =

View file

@ -109,7 +109,7 @@ internal class HomeFragment : BrowseSupportFragment() {
HomeFragmentDirections.actionHomeFragmentToMediaDetailFragment( HomeFragmentDirections.actionHomeFragmentToMediaDetailFragment(
item.id, item.id,
item.seriesName ?: item.name, item.seriesName ?: item.name,
item.type ?: "Unknown" item.type
) )
) )
} }

View file

@ -12,6 +12,7 @@ import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.databinding.BaseItemBinding import dev.jdtech.jellyfin.databinding.BaseItemBinding
import dev.jdtech.jellyfin.databinding.HomeEpisodeItemBinding import dev.jdtech.jellyfin.databinding.HomeEpisodeItemBinding
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
class MediaItemPresenter(private val onClick: (BaseItemDto) -> Unit) : Presenter() { class MediaItemPresenter(private val onClick: (BaseItemDto) -> Unit) : Presenter() {
@ -27,7 +28,7 @@ class MediaItemPresenter(private val onClick: (BaseItemDto) -> Unit) : Presenter
if (item is BaseItemDto) { if (item is BaseItemDto) {
DataBindingUtil.getBinding<BaseItemBinding>(viewHolder.view)?.apply { DataBindingUtil.getBinding<BaseItemBinding>(viewHolder.view)?.apply {
this.item = item this.item = item
this.itemName.text = if (item.type == "Episode") item.seriesName else item.name this.itemName.text = if (item.type == BaseItemKind.EPISODE) item.seriesName else item.name
this.itemCount.visibility = this.itemCount.visibility =
if (item.userData?.unplayedItemCount != null && item.userData?.unplayedItemCount!! > 0) View.VISIBLE else View.GONE if (item.userData?.unplayedItemCount != null && item.userData?.unplayedItemCount!! > 0) View.VISIBLE else View.GONE
this.itemLayout.layoutParams.width = this.itemLayout.layoutParams.width =
@ -62,10 +63,10 @@ class DynamicMediaItemPresenter(private val onClick: (BaseItemDto) -> Unit) : Pr
progressBar.isVisible = true progressBar.isVisible = true
} }
if (item.type == "Movie") { if (item.type == BaseItemKind.MOVIE) {
primaryName.text = item.name primaryName.text = item.name
secondaryName.visibility = View.GONE secondaryName.visibility = View.GONE
} else if (item.type == "Episode") { } else if (item.type == BaseItemKind.EPISODE) {
primaryName.text = item.seriesName primaryName.text = item.seriesName
} }

View file

@ -13,6 +13,7 @@ import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
import dev.jdtech.jellyfin.models.PlayerItem import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.repository.JellyfinRepository import dev.jdtech.jellyfin.repository.JellyfinRepository
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.UserItemDataDto import org.jellyfin.sdk.model.api.UserItemDataDto
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
@ -149,7 +150,7 @@ fun downloadMetadataToBaseItemDto(item: DownloadItem): BaseItemDto {
return BaseItemDto( return BaseItemDto(
id = item.id, id = item.id,
type = item.type.type, type = item.type,
seriesName = item.seriesName, seriesName = item.seriesName,
name = item.name, name = item.name,
parentIndexNumber = item.parentIndexNumber, parentIndexNumber = item.parentIndexNumber,
@ -163,7 +164,7 @@ fun downloadMetadataToBaseItemDto(item: DownloadItem): BaseItemDto {
fun baseItemDtoToDownloadMetadata(item: BaseItemDto): DownloadItem { fun baseItemDtoToDownloadMetadata(item: BaseItemDto): DownloadItem {
return DownloadItem( return DownloadItem(
id = item.id, id = item.id,
type = item.contentType(), type = item.type,
name = item.name.orEmpty(), name = item.name.orEmpty(),
played = item.userData?.played ?: false, played = item.userData?.played ?: false,
seriesId = item.seriesId, seriesId = item.seriesId,
@ -188,7 +189,7 @@ fun downloadSeriesMetadataToBaseItemDto(metadata: DownloadSeriesMetadata): BaseI
return BaseItemDto( return BaseItemDto(
id = metadata.itemId, id = metadata.itemId,
type = "Series", type = BaseItemKind.SERIES,
name = metadata.name, name = metadata.name,
userData = userData userData = userData
) )

View file

@ -12,7 +12,6 @@ import androidx.annotation.StringRes
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import dev.jdtech.jellyfin.AppNavigationDirections import dev.jdtech.jellyfin.AppNavigationDirections
import dev.jdtech.jellyfin.models.ContentType
import dev.jdtech.jellyfin.models.View import dev.jdtech.jellyfin.models.View
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import timber.log.Timber import timber.log.Timber
@ -25,13 +24,6 @@ fun BaseItemDto.toView(): View {
) )
} }
fun BaseItemDto.contentType() = when (type) {
"Movie" -> ContentType.MOVIE
"Series" -> ContentType.TVSHOW
"Episode" -> ContentType.EPISODE
else -> ContentType.UNKNOWN
}
fun Fragment.checkIfLoginRequired(error: String?) { fun Fragment.checkIfLoginRequired(error: String?) {
if (error != null) { if (error != null) {
if (error.contains("401")) { if (error.contains("401")) {

View file

@ -3,7 +3,6 @@ package dev.jdtech.jellyfin.viewmodels
import androidx.lifecycle.* import androidx.lifecycle.*
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.database.DownloadDatabaseDao import dev.jdtech.jellyfin.database.DownloadDatabaseDao
import dev.jdtech.jellyfin.models.ContentType
import dev.jdtech.jellyfin.models.DownloadSection import dev.jdtech.jellyfin.models.DownloadSection
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
import dev.jdtech.jellyfin.models.PlayerItem import dev.jdtech.jellyfin.models.PlayerItem
@ -11,6 +10,7 @@ import dev.jdtech.jellyfin.utils.loadDownloadedEpisodes
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.BaseItemKind
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -43,7 +43,7 @@ constructor(
val items = loadDownloadedEpisodes(downloadDatabase) val items = loadDownloadedEpisodes(downloadDatabase)
val showsMap = mutableMapOf<UUID, MutableList<PlayerItem>>() val showsMap = mutableMapOf<UUID, MutableList<PlayerItem>>()
items.filter { it.item?.type == ContentType.EPISODE }.forEach { items.filter { it.item?.type == BaseItemKind.EPISODE }.forEach {
showsMap.computeIfAbsent(it.item!!.seriesId!!) { mutableListOf() } += it showsMap.computeIfAbsent(it.item!!.seriesId!!) { mutableListOf() } += it
} }
val shows = showsMap.map { DownloadSeriesMetadata(it.key, it.value[0].item!!.seriesName, it.value) } val shows = showsMap.map { DownloadSeriesMetadata(it.key, it.value[0].item!!.seriesName, it.value) }
@ -53,7 +53,7 @@ constructor(
DownloadSection( DownloadSection(
UUID.randomUUID(), UUID.randomUUID(),
"Movies", "Movies",
items.filter { it.item?.type == ContentType.MOVIE } items.filter { it.item?.type == BaseItemKind.MOVIE }
).let { ).let {
if (it.items!!.isNotEmpty()) downloadSections.add( if (it.items!!.isNotEmpty()) downloadSections.add(
it it

View file

@ -10,6 +10,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.model.api.BaseItemKind
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -52,7 +53,7 @@ constructor(
FavoriteSection( FavoriteSection(
UUID.randomUUID(), UUID.randomUUID(),
"Movies", "Movies",
items.filter { it.type == "Movie" }).let { items.filter { it.type == BaseItemKind.MOVIE }).let {
if (it.items.isNotEmpty()) favoriteSections.add( if (it.items.isNotEmpty()) favoriteSections.add(
it it
) )
@ -60,7 +61,7 @@ constructor(
FavoriteSection( FavoriteSection(
UUID.randomUUID(), UUID.randomUUID(),
"Shows", "Shows",
items.filter { it.type == "Series" }).let { items.filter { it.type == BaseItemKind.SERIES }).let {
if (it.items.isNotEmpty()) favoriteSections.add( if (it.items.isNotEmpty()) favoriteSections.add(
it it
) )
@ -68,7 +69,7 @@ constructor(
FavoriteSection( FavoriteSection(
UUID.randomUUID(), UUID.randomUUID(),
"Episodes", "Episodes",
items.filter { it.type == "Episode" }).let { items.filter { it.type == BaseItemKind.EPISODE }).let {
if (it.items.isNotEmpty()) favoriteSections.add( if (it.items.isNotEmpty()) favoriteSections.add(
it it
) )

View file

@ -7,6 +7,7 @@ import dev.jdtech.jellyfin.utils.SortBy
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.SortOrder import org.jellyfin.sdk.model.api.SortOrder
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
@ -38,8 +39,8 @@ constructor(
) { ) {
Timber.d("$libraryType") Timber.d("$libraryType")
val itemType = when (libraryType) { val itemType = when (libraryType) {
"movies" -> "Movie" "movies" -> BaseItemKind.MOVIE
"tvshows" -> "Series" "tvshows" -> BaseItemKind.SERIES
else -> null else -> null
} }
viewModelScope.launch { viewModelScope.launch {

View file

@ -17,6 +17,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.api.client.exception.ApiClientException import org.jellyfin.sdk.api.client.exception.ApiClientException
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.BaseItemPerson import org.jellyfin.sdk.model.api.BaseItemPerson
import timber.log.Timber import timber.log.Timber
import java.util.UUID import java.util.UUID
@ -80,7 +81,7 @@ constructor(
lateinit var playerItem: PlayerItem lateinit var playerItem: PlayerItem
fun loadData(itemId: UUID, itemType: String) { fun loadData(itemId: UUID, itemType: BaseItemKind) {
viewModelScope.launch { viewModelScope.launch {
uiState.emit(UiState.Loading) uiState.emit(UiState.Loading)
try { try {
@ -97,7 +98,7 @@ constructor(
favorite = tempItem.userData?.isFavorite ?: false favorite = tempItem.userData?.isFavorite ?: false
canDownload = tempItem.canDownload == true canDownload = tempItem.canDownload == true
downloaded = isItemDownloaded(downloadDatabase, itemId) downloaded = isItemDownloaded(downloadDatabase, itemId)
if (itemType == "Series") { if (itemType == BaseItemKind.SERIES) {
nextUp = getNextUp(itemId) nextUp = getNextUp(itemId)
seasons = jellyfinRepository.getSeasons(itemId) seasons = jellyfinRepository.getSeasons(itemId)
} }

View file

@ -4,13 +4,11 @@ import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.models.ContentType.MOVIE
import dev.jdtech.jellyfin.models.ContentType.TVSHOW
import dev.jdtech.jellyfin.repository.JellyfinRepository import dev.jdtech.jellyfin.repository.JellyfinRepository
import dev.jdtech.jellyfin.utils.contentType
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
import java.util.UUID import java.util.UUID
import javax.inject.Inject import javax.inject.Inject
@ -45,12 +43,12 @@ internal class PersonDetailViewModel @Inject internal constructor(
val items = jellyfinRepository.getPersonItems( val items = jellyfinRepository.getPersonItems(
personIds = listOf(personId), personIds = listOf(personId),
includeTypes = listOf(MOVIE, TVSHOW), includeTypes = listOf(BaseItemKind.MOVIE, BaseItemKind.SERIES),
recursive = true recursive = true
) )
val movies = items.filter { it.contentType() == MOVIE } val movies = items.filter { it.type == BaseItemKind.MOVIE }
val shows = items.filter { it.contentType() == TVSHOW } val shows = items.filter { it.type == BaseItemKind.SERIES }
val starredIn = StarredIn(movies, shows) val starredIn = StarredIn(movies, shows)

View file

@ -13,6 +13,7 @@ import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.ItemFields import org.jellyfin.sdk.model.api.ItemFields
import org.jellyfin.sdk.model.api.LocationType.VIRTUAL import org.jellyfin.sdk.model.api.LocationType.VIRTUAL
import org.jellyfin.sdk.model.api.MediaProtocol import org.jellyfin.sdk.model.api.MediaProtocol
@ -98,9 +99,9 @@ class PlayerViewModel @Inject internal constructor(
playbackPosition: Long, playbackPosition: Long,
mediaSourceIndex: Int mediaSourceIndex: Int
): List<PlayerItem> = when (item.type) { ): List<PlayerItem> = when (item.type) {
"Movie" -> itemToMoviePlayerItems(item, playbackPosition, mediaSourceIndex) BaseItemKind.MOVIE -> itemToMoviePlayerItems(item, playbackPosition, mediaSourceIndex)
"Series" -> seriesToPlayerItems(item, playbackPosition, mediaSourceIndex) BaseItemKind.SERIES -> seriesToPlayerItems(item, playbackPosition, mediaSourceIndex)
"Episode" -> episodeToPlayerItems(item, playbackPosition, mediaSourceIndex) BaseItemKind.EPISODE -> episodeToPlayerItems(item, playbackPosition, mediaSourceIndex)
else -> emptyList() else -> emptyList()
} }

View file

@ -10,6 +10,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jellyfin.sdk.model.api.BaseItemKind
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -48,7 +49,7 @@ constructor(
FavoriteSection( FavoriteSection(
UUID.randomUUID(), UUID.randomUUID(),
"Movies", "Movies",
items.filter { it.type == "Movie" }).let { items.filter { it.type == BaseItemKind.MOVIE }).let {
if (it.items.isNotEmpty()) sections.add( if (it.items.isNotEmpty()) sections.add(
it it
) )
@ -56,7 +57,7 @@ constructor(
FavoriteSection( FavoriteSection(
UUID.randomUUID(), UUID.randomUUID(),
"Shows", "Shows",
items.filter { it.type == "Series" }).let { items.filter { it.type == BaseItemKind.SERIES }).let {
if (it.items.isNotEmpty()) sections.add( if (it.items.isNotEmpty()) sections.add(
it it
) )
@ -64,7 +65,7 @@ constructor(
FavoriteSection( FavoriteSection(
UUID.randomUUID(), UUID.randomUUID(),
"Episodes", "Episodes",
items.filter { it.type == "Episode" }).let { items.filter { it.type == BaseItemKind.EPISODE }).let {
if (it.items.isNotEmpty()) sections.add( if (it.items.isNotEmpty()) sections.add(
it it
) )

View file

@ -6,7 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.api.JellyfinApi import dev.jdtech.jellyfin.api.JellyfinApi
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.DeviceOptions import org.jellyfin.sdk.model.api.DeviceOptionsDto
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
@ -17,7 +17,7 @@ internal class SettingsDeviceViewModel @Inject internal constructor(
fun updateDeviceName(name: String) { fun updateDeviceName(name: String) {
api.jellyfin.deviceInfo?.id?.let { id -> api.jellyfin.deviceInfo?.id?.let { id ->
viewModelScope.launch(IO) { viewModelScope.launch(IO) {
api.devicesApi.updateDeviceOptions(id, DeviceOptions(name)) api.devicesApi.updateDeviceOptions(id, DeviceOptionsDto(0, customName = name))
} }
} }
} }

View file

@ -114,8 +114,8 @@
app:nullable="true" /> app:nullable="true" />
<argument <argument
android:name="itemType" android:name="itemType"
android:defaultValue="unknown" app:argType="org.jellyfin.sdk.model.api.BaseItemKind"
app:argType="string" /> android:defaultValue="MOVIE" />
<argument <argument
android:name="playerItem" android:name="playerItem"
android:defaultValue="@null" android:defaultValue="@null"

View file

@ -45,8 +45,8 @@
app:nullable="true" /> app:nullable="true" />
<argument <argument
android:name="itemType" android:name="itemType"
android:defaultValue="unknown" app:argType="org.jellyfin.sdk.model.api.BaseItemKind"
app:argType="string" /> android:defaultValue="MOVIE" />
<action <action
android:id="@+id/action_mediaDetailFragment_to_playerActivity" android:id="@+id/action_mediaDetailFragment_to_playerActivity"