Several UI/UX minor changes (#1623)

## Description
Gathering some smaller UI/UX changes:
- Default to the user's preferred subtitle language when
searching/downloading subtitles and if unset, fall back to the UI's
language
- Only show >0s runtime on quick details & don't show negative ends at
- On the OS channel row for recently added, only include movies &
episodes
- Only show movies & episodes to "Recently Added" Android OS channel
- Fix context menu for adding songs & albums to the music queue

### Related issues
Related to #1339 & #1484
Fixes #1617
Fixes
https://github.com/damontecres/Wholphin/issues/1329#issuecomment-4440684328

### Testing
Just on the emulator

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Damontecres 2026-06-26 12:43:56 -04:00 • committed by GitHub
parent e5887dfa15
commit f5ce9745ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 41 additions and 5 deletions

View file

@ -160,6 +160,7 @@ data class BaseItem(
data.runTimeTicks
?.ticks
?.roundMinutes
?.takeIf { it > Duration.ZERO }
?.let { add(it.toString()) }
data.timeRemaining
?.roundMinutes

View file

@ -228,6 +228,8 @@ class MusicService
start()
}
}
} else {
Timber.w("Tried to add non-audio type %s: %s", item.type, item.id)
}
}

View file

@ -264,6 +264,7 @@ class TvProviderWorker
.getLatestMedia(
GetLatestMediaRequest(
fields = SlimItemFields,
includeItemTypes = listOf(BaseItemKind.MOVIE, BaseItemKind.EPISODE),
imageTypeLimit = 1,
parentId = null,
groupItems = true,

View file

@ -244,7 +244,7 @@ val BaseItemDto.timeRemaining: Duration?
get() =
userData?.playbackPositionTicks?.let {
if (it > 0) {
runTimeTicks?.minus(it)?.ticks
runTimeTicks?.minus(it)?.ticks?.takeIf { it > Duration.ZERO }
} else {
null
}

View file

@ -339,7 +339,8 @@ private fun buildContextMenuItems(
onClickPlayWith: () -> Unit,
): List<DialogItem> =
buildList {
if (showGoTo) {
// Songs should not show Go to
if (showGoTo && item.type != BaseItemKind.AUDIO) {
add(
DialogItem(
resources.getString(R.string.go_to),
@ -466,7 +467,7 @@ private fun buildContextMenuItems(
}
}
}
if (item.type == BaseItemKind.MUSIC_ALBUM) {
if (item.type == BaseItemKind.MUSIC_ALBUM || item.type == BaseItemKind.AUDIO) {
add(
DialogItem(
resources.getString(R.string.add_to_queue),

View file

@ -91,6 +91,7 @@ class ContextMenuUtils(
onClearChosenStreams = {
// Not supported on this page
},
onClickAddToQueue = playlistViewModel::addToQueue,
)
}
val contextMenu =

View file

@ -5,14 +5,19 @@ import android.widget.Toast
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.services.MusicService
import com.github.damontecres.wholphin.services.PlaylistCreator
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
import com.github.damontecres.wholphin.ui.detail.music.addToQueue
import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.util.ExceptionHandler
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.model.api.MediaType
import timber.log.Timber
import java.util.UUID
@ -27,7 +32,9 @@ class AddPlaylistViewModel
@Inject
constructor(
@param:ApplicationContext private val context: Context,
private val api: ApiClient,
private val playlistCreator: PlaylistCreator,
private val musicService: MusicService,
) : ViewModel() {
val playlistState = MutableStateFlow<PlaylistLoadingState>(PlaylistLoadingState.Pending)
@ -72,4 +79,10 @@ class AddPlaylistViewModel
}
}
}
fun addToQueue(item: BaseItem) {
viewModelScope.launchDefault {
addToQueue(api, musicService, item, -1)
}
}
}

View file

@ -147,3 +147,11 @@ fun ViewModel.addToQueue(
}
}
}
suspend fun addToQueue(
api: ApiClient,
musicService: MusicService,
item: BaseItem,
index: Int,
) {
}

View file

@ -139,7 +139,7 @@ class PlaybackViewModel
val navigationManager: NavigationManager,
private val playlistCreator: PlaylistCreator,
private val itemPlaybackDao: ItemPlaybackDao,
private val serverRepository: ServerRepository,
internal val serverRepository: ServerRepository,
private val itemPlaybackRepository: ItemPlaybackRepository,
private val playerFactory: PlayerFactory,
private val datePlayedService: DatePlayedService,

View file

@ -7,6 +7,7 @@ import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.PlaylistItem
import com.github.damontecres.wholphin.data.model.TrackIndex
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.onMain
import com.github.damontecres.wholphin.ui.showToast
@ -41,7 +42,15 @@ sealed interface SubtitleSearchStatus {
/**
* Trigger a search for subtitles in the given language for the currently playing media
*/
fun PlaybackViewModel.searchForSubtitles(language: String = Locale.current.language) {
fun PlaybackViewModel.searchForSubtitles(
language: String =
serverRepository
.currentUserDto
?.configuration
?.subtitleLanguagePreference
?.takeIf { it.isNotNullOrBlank() }
?: Locale.current.language,
) {
subtitleSearchState.update {
it.copy(
status = SubtitleSearchStatus.Searching,