mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
parent
405e170750
commit
dc3193e2dc
17 changed files with 127 additions and 61 deletions
|
|
@ -21,6 +21,10 @@ This is not a fork of the [official client](https://github.com/jellyfin/jellyfin
|
||||||
- Subtly show playback position along the bottom of the screen while seeking w/ D-Pad
|
- Subtly show playback position along the bottom of the screen while seeking w/ D-Pad
|
||||||
- Force Continue Watching & Next Up TV episodes to use their Series posters
|
- Force Continue Watching & Next Up TV episodes to use their Series posters
|
||||||
|
|
||||||
|
### Roadmap
|
||||||
|
|
||||||
|
See [here for the roadmap](https://github.com/damontecres/Wholphin/wiki#roadmap)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Downloader Code: `8668671`
|
Downloader Code: `8668671`
|
||||||
|
|
|
||||||
|
|
@ -106,9 +106,21 @@ class ItemPlaybackRepository
|
||||||
saveItemPlayback(toSave)
|
saveItemPlayback(toSave)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the [ItemPlayback] into the database, returning the same object with the rowId updated if needed
|
||||||
|
*/
|
||||||
suspend fun saveItemPlayback(itemPlayback: ItemPlayback): ItemPlayback {
|
suspend fun saveItemPlayback(itemPlayback: ItemPlayback): ItemPlayback {
|
||||||
val rowId = itemPlaybackDao.saveItem(itemPlayback)
|
val toSave =
|
||||||
return itemPlayback.copy(rowId = rowId)
|
if (itemPlayback.userId < 0) {
|
||||||
|
val userRowId =
|
||||||
|
serverRepository.currentUser?.rowId?.takeIf { it >= 0 }
|
||||||
|
?: throw IllegalStateException("Trying to save an ItemPlayback without a user, but there is no current user")
|
||||||
|
itemPlayback.copy(userId = userRowId)
|
||||||
|
} else {
|
||||||
|
itemPlayback
|
||||||
|
}
|
||||||
|
val id = itemPlaybackDao.saveItem(toSave)
|
||||||
|
return toSave.copy(rowId = id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,10 +84,23 @@ interface JellyfinServerDao {
|
||||||
fun addOrUpdateUser(user: JellyfinUser) {
|
fun addOrUpdateUser(user: JellyfinUser) {
|
||||||
val result = addUser(user)
|
val result = addUser(user)
|
||||||
if (result == -1L) {
|
if (result == -1L) {
|
||||||
updateUser(user)
|
val toSave =
|
||||||
|
if (user.rowId <= 0) {
|
||||||
|
val temp = getUser(user.serverId, user.id)!!
|
||||||
|
user.copy(rowId = temp.rowId)
|
||||||
|
} else {
|
||||||
|
user
|
||||||
|
}
|
||||||
|
updateUser(toSave)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Query("SELECT * FROM users WHERE serverId = :serverId AND id = :userId")
|
||||||
|
fun getUser(
|
||||||
|
serverId: UUID,
|
||||||
|
userId: UUID,
|
||||||
|
): JellyfinUser?
|
||||||
|
|
||||||
@Query("DELETE FROM servers WHERE id = :serverId")
|
@Query("DELETE FROM servers WHERE id = :serverId")
|
||||||
fun deleteServer(serverId: UUID)
|
fun deleteServer(serverId: UUID)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,8 @@ class ServerRepository
|
||||||
id = userDto.id,
|
id = userDto.id,
|
||||||
name = userDto.name,
|
name = userDto.name,
|
||||||
)
|
)
|
||||||
|
if (updatedUser.rowId <= 0) {
|
||||||
|
}
|
||||||
serverDao.addOrUpdateServer(updatedServer)
|
serverDao.addOrUpdateServer(updatedServer)
|
||||||
serverDao.addOrUpdateUser(updatedUser)
|
serverDao.addOrUpdateUser(updatedUser)
|
||||||
userPreferencesDataStore.updateData {
|
userPreferencesDataStore.updateData {
|
||||||
|
|
@ -119,7 +121,6 @@ class ServerRepository
|
||||||
serverDao.getServer(serverId)
|
serverDao.getServer(serverId)
|
||||||
}
|
}
|
||||||
if (serverAndUsers != null) {
|
if (serverAndUsers != null) {
|
||||||
_currentServer = serverAndUsers.server
|
|
||||||
val user = serverAndUsers.users.firstOrNull { it.id == userId }
|
val user = serverAndUsers.users.firstOrNull { it.id == userId }
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
changeUser(serverAndUsers.server, user)
|
changeUser(serverAndUsers.server, user)
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,19 @@ val DefaultItemFields =
|
||||||
ItemFields.TRICKPLAY,
|
ItemFields.TRICKPLAY,
|
||||||
ItemFields.SORT_NAME,
|
ItemFields.SORT_NAME,
|
||||||
ItemFields.CHAPTERS,
|
ItemFields.CHAPTERS,
|
||||||
|
ItemFields.MEDIA_SOURCES,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [ItemFields] for higher level displays such as grids or rows
|
||||||
|
*/
|
||||||
|
val SlimItemFields =
|
||||||
|
listOf(
|
||||||
|
ItemFields.PRIMARY_IMAGE_ASPECT_RATIO,
|
||||||
|
ItemFields.SEASON_USER_DATA,
|
||||||
|
ItemFields.CHILD_COUNT,
|
||||||
|
ItemFields.OVERVIEW,
|
||||||
|
ItemFields.SORT_NAME,
|
||||||
)
|
)
|
||||||
|
|
||||||
val DefaultButtonPadding =
|
val DefaultButtonPadding =
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
|
||||||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
||||||
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
import com.github.damontecres.wholphin.ui.data.MovieSortOptions
|
import com.github.damontecres.wholphin.ui.data.MovieSortOptions
|
||||||
import com.github.damontecres.wholphin.ui.data.SeriesSortOptions
|
import com.github.damontecres.wholphin.ui.data.SeriesSortOptions
|
||||||
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||||
|
|
@ -135,7 +135,7 @@ class CollectionFolderViewModel
|
||||||
SortOrder.ASCENDING,
|
SortOrder.ASCENDING,
|
||||||
SortOrder.ASCENDING,
|
SortOrder.ASCENDING,
|
||||||
),
|
),
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val newPager =
|
val newPager =
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
||||||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
|
||||||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
||||||
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
import com.github.damontecres.wholphin.ui.cards.GridCard
|
import com.github.damontecres.wholphin.ui.cards.GridCard
|
||||||
import com.github.damontecres.wholphin.ui.detail.CardGrid
|
import com.github.damontecres.wholphin.ui.detail.CardGrid
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
|
|
@ -54,7 +54,7 @@ class GenreViewModel
|
||||||
val request =
|
val request =
|
||||||
GetGenresRequest(
|
GetGenresRequest(
|
||||||
parentId = itemId,
|
parentId = itemId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
)
|
)
|
||||||
val pager = ApiRequestPager(api, request, GetGenresRequestHandler, viewModelScope).init()
|
val pager = ApiRequestPager(api, request, GetGenresRequestHandler, viewModelScope).init()
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
|
||||||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
||||||
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
||||||
import com.github.damontecres.wholphin.ui.main.HomeRow
|
import com.github.damontecres.wholphin.ui.main.HomeRow
|
||||||
import com.github.damontecres.wholphin.ui.main.HomeSection
|
import com.github.damontecres.wholphin.ui.main.HomeSection
|
||||||
|
|
@ -52,7 +52,7 @@ class RecommendedMovieViewModel
|
||||||
val resumeItemsRequest =
|
val resumeItemsRequest =
|
||||||
GetResumeItemsRequest(
|
GetResumeItemsRequest(
|
||||||
parentId = parentId,
|
parentId = parentId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
includeItemTypes = listOf(BaseItemKind.MOVIE),
|
includeItemTypes = listOf(BaseItemKind.MOVIE),
|
||||||
enableUserData = true,
|
enableUserData = true,
|
||||||
)
|
)
|
||||||
|
|
@ -62,7 +62,7 @@ class RecommendedMovieViewModel
|
||||||
val recentlyReleasedRequest =
|
val recentlyReleasedRequest =
|
||||||
GetItemsRequest(
|
GetItemsRequest(
|
||||||
parentId = parentId,
|
parentId = parentId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
includeItemTypes = listOf(BaseItemKind.MOVIE),
|
includeItemTypes = listOf(BaseItemKind.MOVIE),
|
||||||
recursive = true,
|
recursive = true,
|
||||||
enableUserData = true,
|
enableUserData = true,
|
||||||
|
|
@ -75,7 +75,7 @@ class RecommendedMovieViewModel
|
||||||
val recentlyAddedRequest =
|
val recentlyAddedRequest =
|
||||||
GetItemsRequest(
|
GetItemsRequest(
|
||||||
parentId = parentId,
|
parentId = parentId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
includeItemTypes = listOf(BaseItemKind.MOVIE),
|
includeItemTypes = listOf(BaseItemKind.MOVIE),
|
||||||
recursive = true,
|
recursive = true,
|
||||||
enableUserData = true,
|
enableUserData = true,
|
||||||
|
|
@ -94,7 +94,7 @@ class RecommendedMovieViewModel
|
||||||
val unwatchedTopRatedRequest =
|
val unwatchedTopRatedRequest =
|
||||||
GetItemsRequest(
|
GetItemsRequest(
|
||||||
parentId = parentId,
|
parentId = parentId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
includeItemTypes = listOf(BaseItemKind.MOVIE),
|
includeItemTypes = listOf(BaseItemKind.MOVIE),
|
||||||
recursive = true,
|
recursive = true,
|
||||||
enableUserData = true,
|
enableUserData = true,
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
|
||||||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
||||||
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
||||||
import com.github.damontecres.wholphin.ui.main.HomeRow
|
import com.github.damontecres.wholphin.ui.main.HomeRow
|
||||||
import com.github.damontecres.wholphin.ui.main.HomeSection
|
import com.github.damontecres.wholphin.ui.main.HomeSection
|
||||||
|
|
@ -54,7 +54,7 @@ class RecommendedTvShowViewModel
|
||||||
val resumeItemsRequest =
|
val resumeItemsRequest =
|
||||||
GetResumeItemsRequest(
|
GetResumeItemsRequest(
|
||||||
parentId = parentId,
|
parentId = parentId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
includeItemTypes = listOf(BaseItemKind.EPISODE),
|
includeItemTypes = listOf(BaseItemKind.EPISODE),
|
||||||
enableUserData = true,
|
enableUserData = true,
|
||||||
)
|
)
|
||||||
|
|
@ -64,7 +64,7 @@ class RecommendedTvShowViewModel
|
||||||
val nextUpRequest =
|
val nextUpRequest =
|
||||||
GetNextUpRequest(
|
GetNextUpRequest(
|
||||||
parentId = parentId,
|
parentId = parentId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
enableUserData = true,
|
enableUserData = true,
|
||||||
)
|
)
|
||||||
val nextUpItems = ApiRequestPager(api, nextUpRequest, GetNextUpRequestHandler, viewModelScope, useSeriesForPrimary = true)
|
val nextUpItems = ApiRequestPager(api, nextUpRequest, GetNextUpRequestHandler, viewModelScope, useSeriesForPrimary = true)
|
||||||
|
|
@ -72,7 +72,7 @@ class RecommendedTvShowViewModel
|
||||||
val recentlyReleasedRequest =
|
val recentlyReleasedRequest =
|
||||||
GetItemsRequest(
|
GetItemsRequest(
|
||||||
parentId = parentId,
|
parentId = parentId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
includeItemTypes = listOf(BaseItemKind.EPISODE),
|
includeItemTypes = listOf(BaseItemKind.EPISODE),
|
||||||
recursive = true,
|
recursive = true,
|
||||||
enableUserData = true,
|
enableUserData = true,
|
||||||
|
|
@ -85,7 +85,7 @@ class RecommendedTvShowViewModel
|
||||||
val recentlyAddedRequest =
|
val recentlyAddedRequest =
|
||||||
GetItemsRequest(
|
GetItemsRequest(
|
||||||
parentId = parentId,
|
parentId = parentId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
includeItemTypes = listOf(BaseItemKind.EPISODE),
|
includeItemTypes = listOf(BaseItemKind.EPISODE),
|
||||||
recursive = true,
|
recursive = true,
|
||||||
enableUserData = true,
|
enableUserData = true,
|
||||||
|
|
@ -104,7 +104,7 @@ class RecommendedTvShowViewModel
|
||||||
val unwatchedTopRatedRequest =
|
val unwatchedTopRatedRequest =
|
||||||
GetItemsRequest(
|
GetItemsRequest(
|
||||||
parentId = parentId,
|
parentId = parentId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
includeItemTypes = listOf(BaseItemKind.SERIES),
|
includeItemTypes = listOf(BaseItemKind.SERIES),
|
||||||
recursive = true,
|
recursive = true,
|
||||||
enableUserData = true,
|
enableUserData = true,
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@ package com.github.damontecres.wholphin.ui.detail
|
||||||
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||||
import androidx.compose.material.icons.filled.ArrowForward
|
|
||||||
import androidx.compose.material.icons.filled.PlayArrow
|
import androidx.compose.material.icons.filled.PlayArrow
|
||||||
import androidx.compose.material.icons.filled.Settings
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
|
@ -87,8 +85,7 @@ fun buildMoreDialogItems(
|
||||||
add(
|
add(
|
||||||
DialogItem(
|
DialogItem(
|
||||||
"Choose Version",
|
"Choose Version",
|
||||||
Icons.Default.ArrowForward,
|
R.string.fa_file_video,
|
||||||
iconColor = Color.Green.copy(alpha = .8f),
|
|
||||||
) {
|
) {
|
||||||
onChooseVersion.invoke()
|
onChooseVersion.invoke()
|
||||||
},
|
},
|
||||||
|
|
@ -104,7 +101,7 @@ fun buildMoreDialogItems(
|
||||||
add(
|
add(
|
||||||
DialogItem(
|
DialogItem(
|
||||||
"Choose audio",
|
"Choose audio",
|
||||||
Icons.Default.Settings,
|
R.string.fa_volume_low,
|
||||||
) {
|
) {
|
||||||
onChooseTracks.invoke(MediaStreamType.AUDIO)
|
onChooseTracks.invoke(MediaStreamType.AUDIO)
|
||||||
},
|
},
|
||||||
|
|
@ -114,7 +111,7 @@ fun buildMoreDialogItems(
|
||||||
add(
|
add(
|
||||||
DialogItem(
|
DialogItem(
|
||||||
"Choose subtitles",
|
"Choose subtitles",
|
||||||
Icons.Default.Settings,
|
R.string.fa_closed_captioning,
|
||||||
) {
|
) {
|
||||||
onChooseTracks.invoke(MediaStreamType.SUBTITLE)
|
onChooseTracks.invoke(MediaStreamType.SUBTITLE)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import com.github.damontecres.wholphin.util.GetEpisodesRequestHandler
|
||||||
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
|
import com.github.damontecres.wholphin.util.profile.Codec
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
@ -135,7 +136,13 @@ class SeriesViewModel
|
||||||
val url =
|
val url =
|
||||||
api.universalAudioApi.getUniversalAudioStreamUrl(
|
api.universalAudioApi.getUniversalAudioStreamUrl(
|
||||||
theme.id,
|
theme.id,
|
||||||
container = listOf("opus", "mp3", "aaa", "flac"),
|
container =
|
||||||
|
listOf(
|
||||||
|
Codec.Audio.OPUS,
|
||||||
|
Codec.Audio.MP3,
|
||||||
|
Codec.Audio.AAC,
|
||||||
|
Codec.Audio.FLAC,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
Timber.Forest.v("Found theme song for series $seriesId")
|
Timber.Forest.v("Found theme song for series $seriesId")
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
import com.github.damontecres.wholphin.ui.nav.NavigationManager
|
import com.github.damontecres.wholphin.ui.nav.NavigationManager
|
||||||
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
|
|
@ -119,7 +119,7 @@ class HomeViewModel
|
||||||
val request =
|
val request =
|
||||||
GetResumeItemsRequest(
|
GetResumeItemsRequest(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
limit = limit,
|
limit = limit,
|
||||||
includeItemTypes = supportItemKinds,
|
includeItemTypes = supportItemKinds,
|
||||||
)
|
)
|
||||||
|
|
@ -140,7 +140,7 @@ class HomeViewModel
|
||||||
val request =
|
val request =
|
||||||
GetNextUpRequest(
|
GetNextUpRequest(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
imageTypeLimit = 1,
|
imageTypeLimit = 1,
|
||||||
parentId = null,
|
parentId = null,
|
||||||
limit = limit,
|
limit = limit,
|
||||||
|
|
@ -176,7 +176,7 @@ class HomeViewModel
|
||||||
view.name?.let { "Recently Added in $it" }
|
view.name?.let { "Recently Added in $it" }
|
||||||
val request =
|
val request =
|
||||||
GetLatestMediaRequest(
|
GetLatestMediaRequest(
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
imageTypeLimit = 1,
|
imageTypeLimit = 1,
|
||||||
parentId = view.id,
|
parentId = view.id,
|
||||||
groupItems = true,
|
groupItems = true,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import androidx.lifecycle.viewModelScope
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.ui.Cards
|
import com.github.damontecres.wholphin.ui.Cards
|
||||||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
import com.github.damontecres.wholphin.ui.cards.EpisodeCard
|
import com.github.damontecres.wholphin.ui.cards.EpisodeCard
|
||||||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||||
import com.github.damontecres.wholphin.ui.cards.SeasonCard
|
import com.github.damontecres.wholphin.ui.cards.SeasonCard
|
||||||
|
|
@ -78,7 +78,7 @@ class SearchViewModel
|
||||||
searchTerm = query,
|
searchTerm = query,
|
||||||
recursive = true,
|
recursive = true,
|
||||||
includeItemTypes = listOf(BaseItemKind.MOVIE),
|
includeItemTypes = listOf(BaseItemKind.MOVIE),
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
limit = 25,
|
limit = 25,
|
||||||
)
|
)
|
||||||
val pager = ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope)
|
val pager = ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope)
|
||||||
|
|
@ -93,7 +93,7 @@ class SearchViewModel
|
||||||
searchTerm = query,
|
searchTerm = query,
|
||||||
recursive = true,
|
recursive = true,
|
||||||
includeItemTypes = listOf(BaseItemKind.SERIES),
|
includeItemTypes = listOf(BaseItemKind.SERIES),
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
limit = 25,
|
limit = 25,
|
||||||
)
|
)
|
||||||
val pager = ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope)
|
val pager = ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope)
|
||||||
|
|
@ -108,7 +108,7 @@ class SearchViewModel
|
||||||
searchTerm = query,
|
searchTerm = query,
|
||||||
recursive = true,
|
recursive = true,
|
||||||
includeItemTypes = listOf(BaseItemKind.EPISODE),
|
includeItemTypes = listOf(BaseItemKind.EPISODE),
|
||||||
fields = DefaultItemFields,
|
fields = SlimItemFields,
|
||||||
limit = 25,
|
limit = 25,
|
||||||
)
|
)
|
||||||
val pager = ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope)
|
val pager = ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import androidx.media3.common.Tracks
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
import com.github.damontecres.wholphin.data.ItemPlaybackDao
|
import com.github.damontecres.wholphin.data.ItemPlaybackDao
|
||||||
|
import com.github.damontecres.wholphin.data.ItemPlaybackRepository
|
||||||
import com.github.damontecres.wholphin.data.ServerRepository
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.data.model.Chapter
|
import com.github.damontecres.wholphin.data.model.Chapter
|
||||||
|
|
@ -100,6 +101,7 @@ class PlaybackViewModel
|
||||||
val navigationManager: NavigationManager,
|
val navigationManager: NavigationManager,
|
||||||
val itemPlaybackDao: ItemPlaybackDao,
|
val itemPlaybackDao: ItemPlaybackDao,
|
||||||
val serverRepository: ServerRepository,
|
val serverRepository: ServerRepository,
|
||||||
|
val itemPlaybackRepository: ItemPlaybackRepository,
|
||||||
) : ViewModel(),
|
) : ViewModel(),
|
||||||
Player.Listener {
|
Player.Listener {
|
||||||
val player: ExoPlayer =
|
val player: ExoPlayer =
|
||||||
|
|
@ -461,10 +463,9 @@ class PlaybackViewModel
|
||||||
if (userInitiated) {
|
if (userInitiated) {
|
||||||
viewModelScope.launch(Dispatchers.IO + ExceptionHandler()) {
|
viewModelScope.launch(Dispatchers.IO + ExceptionHandler()) {
|
||||||
Timber.v("Saving user initiated item playback: %s", itemPlayback)
|
Timber.v("Saving user initiated item playback: %s", itemPlayback)
|
||||||
val rowId = itemPlaybackDao.saveItem(itemPlayback)
|
val updated = itemPlaybackRepository.saveItemPlayback(itemPlayback)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
this@PlaybackViewModel.currentItemPlayback.value =
|
this@PlaybackViewModel.currentItemPlayback.value = updated
|
||||||
itemPlayback.copy(rowId = rowId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,9 +125,7 @@ fun SwitchUserContent(
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.clearSwitchUserState()
|
viewModel.clearSwitchUserState()
|
||||||
if (useQuickConnect) {
|
if (useQuickConnect) {
|
||||||
viewModel.initiateQuickConnect(server) {
|
viewModel.initiateQuickConnect(server)
|
||||||
viewModel.navigationManager.goToHome()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BasicDialog(
|
BasicDialog(
|
||||||
|
|
@ -180,10 +178,18 @@ fun SwitchUserContent(
|
||||||
)
|
)
|
||||||
when (val s = userState) {
|
when (val s = userState) {
|
||||||
is LoadingState.Error -> {
|
is LoadingState.Error -> {
|
||||||
Text(
|
s.message?.let {
|
||||||
text = s.message ?: s.exception?.localizedMessage ?: "Error",
|
Text(
|
||||||
color = MaterialTheme.colorScheme.error,
|
text = it,
|
||||||
)
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
s.exception?.localizedMessage?.let {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
|
|
@ -208,6 +214,7 @@ fun SwitchUserContent(
|
||||||
autoCorrectEnabled = false,
|
autoCorrectEnabled = false,
|
||||||
keyboardType = KeyboardType.Text,
|
keyboardType = KeyboardType.Text,
|
||||||
),
|
),
|
||||||
|
isInputValid = { userState !is LoadingState.Error },
|
||||||
modifier = Modifier.focusRequester(focusRequester),
|
modifier = Modifier.focusRequester(focusRequester),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -235,6 +242,7 @@ fun SwitchUserContent(
|
||||||
KeyboardActions(
|
KeyboardActions(
|
||||||
onDone = { onSubmit.invoke() },
|
onDone = { onSubmit.invoke() },
|
||||||
),
|
),
|
||||||
|
isInputValid = { userState !is LoadingState.Error },
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Error switching user")
|
Timber.e(ex, "Error switching user")
|
||||||
setError(ex)
|
setError("Error switching user", ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -178,15 +178,19 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Error logging in user")
|
Timber.e(ex, "Error logging in user")
|
||||||
setError(ex)
|
if (ex is InvalidStatusException && ex.status == 401) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
switchUserState.value =
|
||||||
|
LoadingState.Error("Invalid username or password")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setError("Error during login", ex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initiateQuickConnect(
|
fun initiateQuickConnect(server: JellyfinServer) {
|
||||||
server: JellyfinServer,
|
|
||||||
onAuthenticated: () -> Unit,
|
|
||||||
) {
|
|
||||||
quickConnectJob?.cancel()
|
quickConnectJob?.cancel()
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
try {
|
try {
|
||||||
|
|
@ -218,19 +222,21 @@ class SwitchUserViewModel
|
||||||
)
|
)
|
||||||
serverRepository.changeUser(server.url, authenticationResult)
|
serverRepository.changeUser(server.url, authenticationResult)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
onAuthenticated.invoke()
|
navigationManager.goToHome()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Error during quick connect")
|
Timber.e(ex, "Error during quick connect")
|
||||||
if (ex is InvalidStatusException && ex.status == 401) {
|
if (ex is InvalidStatusException && ex.status == 401) {
|
||||||
quickConnectState.value = null
|
withContext(Dispatchers.Main) {
|
||||||
serverQuickConnect.value =
|
quickConnectState.value = null
|
||||||
serverQuickConnect.value!!.toMutableMap().apply {
|
serverQuickConnect.value =
|
||||||
put(server.id, false)
|
serverQuickConnect.value!!.toMutableMap().apply {
|
||||||
}
|
put(server.id, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setError(ex)
|
setError("Error with Quick Connect", ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -329,8 +335,10 @@ class SwitchUserViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun setError(ex: Exception) =
|
private suspend fun setError(
|
||||||
withContext(Dispatchers.Main) {
|
msg: String? = null,
|
||||||
switchUserState.value = LoadingState.Error(ex)
|
ex: Exception? = null,
|
||||||
}
|
) = withContext(Dispatchers.Main) {
|
||||||
|
switchUserState.value = LoadingState.Error(msg, ex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,6 @@
|
||||||
<string name="fa_shuffle" translatable="false"></string>
|
<string name="fa_shuffle" translatable="false"></string>
|
||||||
<string name="fa_open_folder" translatable="false"></string>
|
<string name="fa_open_folder" translatable="false"></string>
|
||||||
<string name="fa_list_ul" translatable="false"></string>
|
<string name="fa_list_ul" translatable="false"></string>
|
||||||
|
<string name="fa_file_video" translatable="false"></string>
|
||||||
|
<string name="fa_volume_low" translatable="false"></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue