Merge branch 'main' into develop/jellyseerr

This commit is contained in:
Damontecres 2026-01-02 17:07:42 -05:00
commit cf73239c46
No known key found for this signature in database
40 changed files with 1333 additions and 502 deletions

View file

@ -23,8 +23,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.datastore.core.DataStore import androidx.datastore.core.DataStore
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.LifecycleStartEffect import androidx.lifecycle.compose.LifecycleEventEffect
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
@ -46,6 +47,7 @@ import com.github.damontecres.wholphin.services.ImageUrlService
import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.services.PlaybackLifecycleObserver import com.github.damontecres.wholphin.services.PlaybackLifecycleObserver
import com.github.damontecres.wholphin.services.RefreshRateService import com.github.damontecres.wholphin.services.RefreshRateService
import com.github.damontecres.wholphin.services.ServerEventListener
import com.github.damontecres.wholphin.services.SetupDestination import com.github.damontecres.wholphin.services.SetupDestination
import com.github.damontecres.wholphin.services.SetupNavigationManager import com.github.damontecres.wholphin.services.SetupNavigationManager
import com.github.damontecres.wholphin.services.UpdateChecker import com.github.damontecres.wholphin.services.UpdateChecker
@ -113,6 +115,14 @@ class MainActivity : AppCompatActivity() {
@Inject @Inject
lateinit var tvProviderSchedulerService: TvProviderSchedulerService lateinit var tvProviderSchedulerService: TvProviderSchedulerService
// Note: unused but injected to ensure it is created
@Inject
lateinit var serverEventListener: ServerEventListener
// Note: unused but injected to ensure it is created
@Inject
lateinit var datePlayedInvalidationService: DatePlayedInvalidationService
private var signInAuto = true private var signInAuto = true
@OptIn(ExperimentalTvMaterial3Api::class) @OptIn(ExperimentalTvMaterial3Api::class)
@ -232,13 +242,12 @@ class MainActivity : AppCompatActivity() {
var showContent by remember { var showContent by remember {
mutableStateOf(true) mutableStateOf(true)
} }
LifecycleEventEffect(Lifecycle.Event.ON_STOP) {
if (!preferences.appPreferences.signInAutomatically) { if (!preferences.appPreferences.signInAutomatically) {
LifecycleStartEffect(Unit) {
onStopOrDispose {
showContent = false showContent = false
} }
} }
}
if (showContent) { if (showContent) {
ApplicationContent( ApplicationContent(
user = current.user, user = current.user,

View file

@ -14,4 +14,5 @@ data class LocalTrailer(
data class RemoteTrailer( data class RemoteTrailer(
override val name: String, override val name: String,
val url: String, val url: String,
val subtitle: String?,
) : Trailer ) : Trailer

View file

@ -20,6 +20,10 @@ class PlaybackLifecycleObserver
private var wasPlaying: Boolean? = null private var wasPlaying: Boolean? = null
override fun onStart(owner: LifecycleOwner) { override fun onStart(owner: LifecycleOwner) {
val lastDest = navigationManager.backStack.lastOrNull()
if (lastDest is Destination.Playback || lastDest is Destination.PlaybackList) {
navigationManager.goBack()
}
wasPlaying = null wasPlaying = null
} }
@ -40,9 +44,6 @@ class PlaybackLifecycleObserver
} }
override fun onStop(owner: LifecycleOwner) { override fun onStop(owner: LifecycleOwner) {
if (navigationManager.backStack.lastOrNull() is Destination.Playback) {
navigationManager.goBack()
}
themeSongPlayer.stop() themeSongPlayer.stop()
} }
} }

View file

@ -95,13 +95,13 @@ class RefreshRateService
} catch (ex: InterruptedException) { } catch (ex: InterruptedException) {
Timber.w(ex, "Exception waiting for refresh rate switch") Timber.w(ex, "Exception waiting for refresh rate switch")
} }
val targetRate = (targetMode.refreshRate * 100).roundToInt() val targetRate = (targetMode.refreshRate * 1000).roundToInt()
val isSeamless = val isSeamless =
targetRate == (display.mode.refreshRate * 100).roundToInt() || targetRate == (currentDisplayMode.refreshRate * 1000).roundToInt() ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
display.mode.alternativeRefreshRates currentDisplayMode.alternativeRefreshRates
.map { (it * 100).roundToInt() } .map { (it * 1000).roundToInt() }
.any { targetRate % it == 0 } .any { it % targetRate == 0 }
} else { } else {
false false
} }

View file

@ -104,7 +104,9 @@ class ThemeSongPlayer
} }
fun stop() { fun stop() {
if (player.isPlaying) {
Timber.v("Stopping theme song") Timber.v("Stopping theme song")
player.stop() player.stop()
} }
} }
}

View file

@ -22,8 +22,7 @@ class TrailerService
@param:ApplicationContext private val context: Context, @param:ApplicationContext private val context: Context,
private val api: ApiClient, private val api: ApiClient,
) { ) {
suspend fun getTrailers(item: BaseItem): List<Trailer> { fun getRemoteTrailers(item: BaseItem): List<Trailer> =
val remoteTrailers =
item.data.remoteTrailers item.data.remoteTrailers
?.mapNotNull { t -> ?.mapNotNull { t ->
t.url?.let { url -> t.url?.let { url ->
@ -33,20 +32,43 @@ class TrailerService
// ?.replace(item.name ?: "", "") // ?.replace(item.name ?: "", "")
// ?.removePrefix(" - ") // ?.removePrefix(" - ")
?: context.getString(R.string.trailer) ?: context.getString(R.string.trailer)
RemoteTrailer(name, url) val subtitle =
when (url.toUri().host) {
"youtube.com", "www.youtube.com" -> "YouTube"
else -> null
}
RemoteTrailer(name, url, subtitle)
} }
}.orEmpty() }.orEmpty()
.sortedBy { it.name } .sortedWith(
val localTrailerCount = item.data.localTrailerCount ?: 0 compareBy(
{
// Try to show official trailers first & teasers last
when {
it.name.contains("Official Trailer", true) -> 0
it.name.contains("Official Theatrical Trailer", true) -> 0
it.name.contains("Teaser", true) -> 10
it.name.contains("Trailer", true) -> 1
else -> 5
}
},
{
it.name
},
),
)
suspend fun getLocalTrailers(item: BaseItem): List<Trailer> {
val localTrailerCount = item.data.localTrailerCount ?: return emptyList()
val localTrailers = val localTrailers =
if (localTrailerCount > 0) { if (localTrailerCount > 0) {
api.userLibraryApi.getLocalTrailers(item.id).content.map { api.userLibraryApi.getLocalTrailers(item.id).content.map {
LocalTrailer(BaseItem.Companion.from(it, api)) LocalTrailer(BaseItem.from(it, api))
} }
} else { } else {
listOf() listOf()
} }
return localTrailers + remoteTrailers return localTrailers
} }
companion object { companion object {

View file

@ -28,9 +28,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.compose.LifecycleEventEffect
import androidx.media3.common.Player import androidx.media3.common.Player
import coil3.request.ErrorResult import coil3.request.ErrorResult
import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.BaseItem
@ -182,10 +180,10 @@ fun RequestOrRestoreFocus(
debugKey?.let { Timber.v("RequestOrRestoreFocus: %s", it) } debugKey?.let { Timber.v("RequestOrRestoreFocus: %s", it) }
focusRequester.tryRequestFocus() focusRequester.tryRequestFocus()
} }
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { // LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
debugKey?.let { Timber.v("RequestOrRestoreFocus onResume: %s", it) } // debugKey?.let { Timber.v("RequestOrRestoreFocus onResume: %s", it) }
focusRequester.tryRequestFocus() // focusRequester.tryRequestFocus()
} // }
} }
} }

View file

@ -169,6 +169,7 @@ class CollectionFolderViewModel
context.getString(R.string.error_loading_collection, itemId), context.getString(R.string.error_loading_collection, itemId),
) + Dispatchers.IO, ) + Dispatchers.IO,
) { ) {
super.itemId = itemId
itemId.toUUIDOrNull()?.let { itemId.toUUIDOrNull()?.let {
fetchItem(it) fetchItem(it)
} }
@ -206,7 +207,7 @@ class CollectionFolderViewModel
) { ) {
if (collectionFilter.useSavedLibraryDisplayInfo) { if (collectionFilter.useSavedLibraryDisplayInfo) {
serverRepository.currentUser.value?.let { user -> serverRepository.currentUser.value?.let { user ->
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launchIO {
val libraryDisplayInfo = val libraryDisplayInfo =
LibraryDisplayInfo( LibraryDisplayInfo(
userId = user.rowId, userId = user.rowId,

View file

@ -9,6 +9,7 @@ import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@ -239,24 +240,24 @@ fun DialogPopupContent(
) { ) {
val elevatedContainerColor = val elevatedContainerColor =
MaterialTheme.colorScheme.surfaceColorAtElevation(elevation) MaterialTheme.colorScheme.surfaceColorAtElevation(elevation)
LazyColumn( Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier =
modifier modifier
// .widthIn(min = 520.dp, max = 300.dp)
// .dialogFocusable()
.graphicsLayer { .graphicsLayer {
this.clip = true this.clip = true
this.shape = RoundedCornerShape(28.0.dp) this.shape = RoundedCornerShape(28.0.dp)
}.drawBehind { drawRect(color = elevatedContainerColor) } }.drawBehind { drawRect(color = elevatedContainerColor) }
.padding(PaddingValues(24.dp)), .padding(PaddingValues(24.dp)),
) { ) {
stickyHeader {
Text( Text(
text = title, text = title,
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
) )
} LazyColumn(
modifier = Modifier,
) {
items(dialogItems) { items(dialogItems) {
when (it) { when (it) {
is DialogItemDivider -> { is DialogItemDivider -> {
@ -285,6 +286,7 @@ fun DialogPopupContent(
} }
} }
} }
}
@Composable @Composable
fun DialogPopup( fun DialogPopup(

View file

@ -21,7 +21,10 @@ import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Refresh
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
@ -40,6 +43,7 @@ import androidx.tv.material3.Icon
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.Trailer
import com.github.damontecres.wholphin.ui.FontAwesome import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.PreviewTvSpec import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.data.SortAndDirection import com.github.damontecres.wholphin.ui.data.SortAndDirection
@ -59,10 +63,12 @@ fun ExpandablePlayButtons(
resumePosition: Duration, resumePosition: Duration,
watched: Boolean, watched: Boolean,
favorite: Boolean, favorite: Boolean,
trailers: List<Trailer>?,
playOnClick: (position: Duration) -> Unit, playOnClick: (position: Duration) -> Unit,
watchOnClick: () -> Unit, watchOnClick: () -> Unit,
favoriteOnClick: () -> Unit, favoriteOnClick: () -> Unit,
moreOnClick: () -> Unit, moreOnClick: () -> Unit,
trailerOnClick: (Trailer) -> Unit,
buttonOnFocusChanged: (FocusState) -> Unit, buttonOnFocusChanged: (FocusState) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
@ -134,6 +140,16 @@ fun ExpandablePlayButtons(
) )
} }
if (trailers != null) {
item("trailers") {
TrailerButton(
trailers = trailers,
trailerOnClick = trailerOnClick,
modifier = Modifier.onFocusChanged(buttonOnFocusChanged),
)
}
}
// More button // More button
item("more") { item("more") {
ExpandablePlayButton( ExpandablePlayButton(
@ -229,6 +245,7 @@ fun ExpandableFaButton(
), ),
contentPadding = DefaultButtonPadding, contentPadding = DefaultButtonPadding,
interactionSource = interactionSource, interactionSource = interactionSource,
enabled = enabled,
) { ) {
Box( Box(
modifier = modifier =
@ -255,6 +272,42 @@ fun ExpandableFaButton(
} }
} }
@Composable
fun TrailerButton(
trailers: List<Trailer>,
trailerOnClick: (Trailer) -> Unit,
modifier: Modifier = Modifier,
) {
var showDialog by remember { mutableStateOf(false) }
ExpandableFaButton(
title =
if (trailers.isEmpty()) {
R.string.no_trailers
} else if (trailers.size == 1) {
R.string.play_trailer
} else {
R.string.trailers
},
iconStringRes = R.string.fa_film,
enabled = trailers.isNotEmpty(),
onClick = {
if (trailers.size == 1) {
trailerOnClick.invoke(trailers.first())
} else {
showDialog = true
}
},
modifier = modifier,
)
if (showDialog) {
TrailerDialog(
onDismissRequest = { showDialog = false },
trailers = trailers,
onClick = trailerOnClick,
)
}
}
@PreviewTvSpec @PreviewTvSpec
@Composable @Composable
private fun ExpandablePlayButtonsPreview() { private fun ExpandablePlayButtonsPreview() {
@ -268,6 +321,8 @@ private fun ExpandablePlayButtonsPreview() {
favoriteOnClick = {}, favoriteOnClick = {},
moreOnClick = {}, moreOnClick = {},
buttonOnFocusChanged = {}, buttonOnFocusChanged = {},
trailers = listOf(),
trailerOnClick = {},
modifier = Modifier, modifier = Modifier,
) )
} }

View file

@ -0,0 +1,51 @@
package com.github.damontecres.wholphin.ui.components
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.LocalTrailer
import com.github.damontecres.wholphin.data.model.RemoteTrailer
import com.github.damontecres.wholphin.data.model.Trailer
@Composable
fun TrailerDialog(
onDismissRequest: () -> Unit,
trailers: List<Trailer>,
onClick: (Trailer) -> Unit,
) {
val trailersStr = stringResource(R.string.play_trailer)
val localStr = stringResource(R.string.local)
val externalStr = stringResource(R.string.external_track)
val params =
remember(trailers) {
DialogParams(
fromLongClick = false,
title = trailersStr,
items =
trailers.map { trailer ->
DialogItem(
headlineContent = {
Text(trailer.name)
},
supportingContent = {
val subtitle =
when (trailer) {
is LocalTrailer -> localStr
is RemoteTrailer -> trailer.subtitle ?: externalStr
}
Text(
text = subtitle,
)
},
onClick = { onClick.invoke(trailer) },
)
},
)
}
DialogPopup(
params = params,
onDismissRequest = onDismissRequest,
)
}

View file

@ -196,6 +196,19 @@ fun <T : CardGridItem> CardGrid(
} }
} }
if (pager.isEmpty()) {
Box(
contentAlignment = Alignment.Center,
modifier = modifier.fillMaxSize(),
) {
Text(
text = stringResource(R.string.no_results),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSurface,
textAlign = TextAlign.Center,
)
}
} else {
var longPressing by remember { mutableStateOf(false) } var longPressing by remember { mutableStateOf(false) }
Row( Row(
// horizontalArrangement = Arrangement.spacedBy(8.dp), // horizontalArrangement = Arrangement.spacedBy(8.dp),
@ -403,6 +416,7 @@ fun <T : CardGridItem> CardGrid(
} }
} }
} }
}
@Composable @Composable
fun JumpButtons( fun JumpButtons(

View file

@ -27,7 +27,7 @@ abstract class ItemViewModel(
) : ViewModel() { ) : ViewModel() {
val item = MutableLiveData<BaseItem?>(null) val item = MutableLiveData<BaseItem?>(null)
lateinit var itemId: String lateinit var itemId: String
lateinit var itemUuid: UUID var itemUuid: UUID? = null
suspend fun fetchItem(itemId: UUID): BaseItem = suspend fun fetchItem(itemId: UUID): BaseItem =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {

View file

@ -155,8 +155,10 @@ class PersonViewModel
fun setFavorite(favorite: Boolean) { fun setFavorite(favorite: Boolean) {
viewModelScope.launchIO { viewModelScope.launchIO {
favoriteWatchManager.setFavorite(itemUuid, favorite) itemUuid?.let {
fetchAndSetItem(itemUuid) favoriteWatchManager.setFavorite(it, favorite)
fetchAndSetItem(it)
}
} }
} }
} }

View file

@ -25,7 +25,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.LifecycleResumeEffect import androidx.lifecycle.compose.LifecycleResumeEffect
import androidx.lifecycle.compose.LifecycleStartEffect
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.ChosenStreams import com.github.damontecres.wholphin.data.ChosenStreams
import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.BaseItem
@ -112,12 +111,12 @@ fun EpisodeDetails(
LoadingState.Success -> { LoadingState.Success -> {
item?.let { ep -> item?.let { ep ->
LifecycleStartEffect(destination.itemId) { LifecycleResumeEffect(destination.itemId) {
viewModel.maybePlayThemeSong( viewModel.maybePlayThemeSong(
destination.itemId, destination.itemId,
preferences.appPreferences.interfacePreferences.playThemeSongs, preferences.appPreferences.interfacePreferences.playThemeSongs,
) )
onStopOrDispose { onPauseOrDispose {
viewModel.release() viewModel.release()
} }
} }
@ -335,6 +334,8 @@ fun EpisodeDetailsContent(
} }
} }
}, },
trailers = null,
trailerOnClick = {},
modifier = modifier =
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()

View file

@ -8,9 +8,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.relocation.BringIntoViewRequester import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -23,25 +20,18 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.LifecycleResumeEffect import androidx.lifecycle.compose.LifecycleResumeEffect
import androidx.lifecycle.compose.LifecycleStartEffect
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.ChosenStreams import com.github.damontecres.wholphin.data.ChosenStreams
import com.github.damontecres.wholphin.data.ExtrasItem import com.github.damontecres.wholphin.data.ExtrasItem
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
import com.github.damontecres.wholphin.data.model.LocalTrailer
import com.github.damontecres.wholphin.data.model.Person import com.github.damontecres.wholphin.data.model.Person
import com.github.damontecres.wholphin.data.model.RemoteTrailer
import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.data.model.Trailer
import com.github.damontecres.wholphin.data.model.aspectRatioFloat import com.github.damontecres.wholphin.data.model.aspectRatioFloat
import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.preferences.UserPreferences
@ -143,12 +133,12 @@ fun MovieDetails(
LoadingState.Success -> { LoadingState.Success -> {
item?.let { movie -> item?.let { movie ->
LifecycleStartEffect(destination.itemId) { LifecycleResumeEffect(destination.itemId) {
viewModel.maybePlayThemeSong( viewModel.maybePlayThemeSong(
destination.itemId, destination.itemId,
preferences.appPreferences.interfacePreferences.playThemeSongs, preferences.appPreferences.interfacePreferences.playThemeSongs,
) )
onStopOrDispose { onPauseOrDispose {
viewModel.release() viewModel.release()
} }
} }
@ -437,6 +427,11 @@ fun MovieDetailsContent(
} }
} }
}, },
trailers = trailers,
trailerOnClick = {
position = TRAILER_ROW
trailerOnClick.invoke(it)
},
modifier = modifier =
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
@ -464,21 +459,6 @@ fun MovieDetailsContent(
) )
} }
} }
if (trailers.isNotEmpty()) {
item {
TrailerRow(
trailers = trailers,
onClickTrailer = {
position = TRAILER_ROW
trailerOnClick.invoke(it)
},
modifier =
Modifier
.fillMaxWidth()
.focusRequester(focusRequesters[TRAILER_ROW]),
)
}
}
if (chapters.isNotEmpty()) { if (chapters.isNotEmpty()) {
item { item {
ChapterRow( ChapterRow(
@ -546,78 +526,3 @@ fun MovieDetailsContent(
} }
} }
} }
@Composable
fun TrailerRow(
trailers: List<Trailer>,
onClickTrailer: (Trailer) -> Unit,
modifier: Modifier = Modifier,
) {
val state = rememberLazyListState()
val firstFocus = remember { FocusRequester() }
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier,
) {
Text(
text = stringResource(R.string.trailers),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onBackground,
)
LazyRow(
state = state,
horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 8.dp),
modifier =
Modifier
.fillMaxWidth()
.focusRestorer(firstFocus),
) {
itemsIndexed(trailers) { index, item ->
val cardModifier =
if (index == 0) {
Modifier.focusRequester(firstFocus)
} else {
Modifier
}
when (item) {
is LocalTrailer -> {
SeasonCard(
item = item.baseItem,
onClick = { onClickTrailer.invoke(item) },
onLongClick = {},
imageHeight = Cards.height2x3,
imageWidth = Dp.Unspecified,
showImageOverlay = false,
modifier = cardModifier,
)
}
is RemoteTrailer -> {
val subtitle =
when (item.url.toUri().host) {
"youtube.com", "www.youtube.com" -> "YouTube"
else -> null
}
SeasonCard(
title = item.name,
subtitle = subtitle,
name = item.name,
imageUrl = null,
isFavorite = false,
isPlayed = false,
unplayedItemCount = 0,
playedPercentage = 0.0,
onClick = { onClickTrailer.invoke(item) },
onLongClick = {},
modifier = cardModifier,
showImageOverlay = false,
imageHeight = Cards.height2x3,
imageWidth = Dp.Unspecified,
)
}
}
}
}
}
}

View file

@ -25,6 +25,7 @@ import com.github.damontecres.wholphin.services.TrailerService
import com.github.damontecres.wholphin.services.UserPreferencesService import com.github.damontecres.wholphin.services.UserPreferencesService
import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.SlimItemFields
import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.ui.setValueOnMain
import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.ExceptionHandler
@ -116,16 +117,19 @@ class MovieViewModel
item, item,
userPreferencesService.getCurrent(), userPreferencesService.getCurrent(),
) )
val remoteTrailers = trailerService.getRemoteTrailers(item)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
this@MovieViewModel.item.value = item this@MovieViewModel.item.value = item
chosenStreams.value = result chosenStreams.value = result
this@MovieViewModel.trailers.value = remoteTrailers
loading.value = LoadingState.Success loading.value = LoadingState.Success
backdropService.submit(item) backdropService.submit(item)
} }
viewModelScope.launchIO { viewModelScope.launchIO {
val trailers = trailerService.getTrailers(item) trailerService.getLocalTrailers(item).letNotEmpty { localTrailers ->
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
this@MovieViewModel.trailers.value = trailers this@MovieViewModel.trailers.value = localTrailers + remoteTrailers
}
} }
} }
viewModelScope.launchIO { viewModelScope.launchIO {

View file

@ -45,6 +45,8 @@ fun FocusedEpisodeFooter(
watchOnClick = watchOnClick, watchOnClick = watchOnClick,
favoriteOnClick = favoriteOnClick, favoriteOnClick = favoriteOnClick,
buttonOnFocusChanged = buttonOnFocusChanged, buttonOnFocusChanged = buttonOnFocusChanged,
trailers = null,
trailerOnClick = {},
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) )
} }

View file

@ -34,7 +34,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.LifecycleStartEffect import androidx.lifecycle.compose.LifecycleResumeEffect
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
@ -62,6 +62,7 @@ import com.github.damontecres.wholphin.ui.components.LoadingPage
import com.github.damontecres.wholphin.ui.components.Optional import com.github.damontecres.wholphin.ui.components.Optional
import com.github.damontecres.wholphin.ui.components.OverviewText import com.github.damontecres.wholphin.ui.components.OverviewText
import com.github.damontecres.wholphin.ui.components.SeriesQuickDetails import com.github.damontecres.wholphin.ui.components.SeriesQuickDetails
import com.github.damontecres.wholphin.ui.components.TrailerButton
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo
@ -70,7 +71,6 @@ import com.github.damontecres.wholphin.ui.detail.PlaylistDialog
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForPerson import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForPerson
import com.github.damontecres.wholphin.ui.detail.movie.TrailerRow
import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.rememberInt import com.github.damontecres.wholphin.ui.rememberInt
@ -124,12 +124,12 @@ fun SeriesDetails(
LoadingState.Success -> { LoadingState.Success -> {
item?.let { item -> item?.let { item ->
LifecycleStartEffect(destination.itemId) { LifecycleResumeEffect(destination.itemId) {
viewModel.maybePlayThemeSong( viewModel.maybePlayThemeSong(
destination.itemId, destination.itemId,
preferences.appPreferences.interfacePreferences.playThemeSongs, preferences.appPreferences.interfacePreferences.playThemeSongs,
) )
onStopOrDispose { onPauseOrDispose {
viewModel.release() viewModel.release()
} }
} }
@ -411,6 +411,18 @@ fun SeriesDetailsContent(
} }
}, },
) )
TrailerButton(
trailers = trailers,
trailerOnClick = trailerOnClick,
modifier =
Modifier.onFocusChanged {
if (it.isFocused) {
scope.launch(ExceptionHandler()) {
bringIntoViewRequester.bringIntoView()
}
}
},
)
} }
} }
item { item {
@ -472,21 +484,6 @@ fun SeriesDetailsContent(
) )
} }
} }
if (trailers.isNotEmpty()) {
item {
TrailerRow(
trailers = trailers,
onClickTrailer = {
position = TRAILER_ROW
trailerOnClick.invoke(it)
},
modifier =
Modifier
.fillMaxWidth()
.focusRequester(focusRequesters[TRAILER_ROW]),
)
}
}
if (extras.isNotEmpty()) { if (extras.isNotEmpty()) {
item { item {
ExtrasRow( ExtrasRow(

View file

@ -16,11 +16,12 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.LifecycleStartEffect import androidx.lifecycle.compose.LifecycleResumeEffect
import androidx.lifecycle.map import androidx.lifecycle.map
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
import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
import com.github.damontecres.wholphin.ui.components.DialogParams import com.github.damontecres.wholphin.ui.components.DialogParams
import com.github.damontecres.wholphin.ui.components.DialogPopup import com.github.damontecres.wholphin.ui.components.DialogPopup
import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.ErrorMessage
@ -37,7 +38,6 @@ import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItems
import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.rememberInt import com.github.damontecres.wholphin.ui.rememberInt
import com.github.damontecres.wholphin.ui.seasonEpisode import com.github.damontecres.wholphin.ui.seasonEpisode
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.LoadingState
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -158,19 +158,21 @@ fun SeriesOverview(
LoadingState.Success -> { LoadingState.Success -> {
series?.let { series -> series?.let { series ->
LaunchedEffect(Unit) { RequestOrRestoreFocus(
when (rowFocused) { when (rowFocused) {
EPISODE_ROW -> episodeRowFocusRequester.tryRequestFocus() EPISODE_ROW -> episodeRowFocusRequester
CAST_AND_CREW_ROW -> castCrewRowFocusRequester.tryRequestFocus() CAST_AND_CREW_ROW -> castCrewRowFocusRequester
GUEST_STAR_ROW -> guestStarRowFocusRequester.tryRequestFocus() GUEST_STAR_ROW -> guestStarRowFocusRequester
} else -> episodeRowFocusRequester
} },
LifecycleStartEffect(destination.itemId) { "series_overview",
)
LifecycleResumeEffect(destination.itemId) {
viewModel.maybePlayThemeSong( viewModel.maybePlayThemeSong(
destination.itemId, destination.itemId,
preferences.appPreferences.interfacePreferences.playThemeSongs, preferences.appPreferences.interfacePreferences.playThemeSongs,
) )
onStopOrDispose { onPauseOrDispose {
viewModel.release() viewModel.release()
} }
} }

View file

@ -49,7 +49,6 @@ import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.Person import com.github.damontecres.wholphin.data.model.Person
import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.ui.AspectRatios import com.github.damontecres.wholphin.ui.AspectRatios
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
import com.github.damontecres.wholphin.ui.cards.BannerCard import com.github.damontecres.wholphin.ui.cards.BannerCard
import com.github.damontecres.wholphin.ui.cards.PersonRow import com.github.damontecres.wholphin.ui.cards.PersonRow
import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.ErrorMessage
@ -107,6 +106,7 @@ fun SeriesOverviewContent(
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
val scrollConnection = rememberDelayedNestedScroll() val scrollConnection = rememberDelayedNestedScroll()
var requestFocusAfterSeason by remember { mutableStateOf(false) }
Box( Box(
modifier = modifier =
modifier modifier
@ -147,6 +147,7 @@ fun SeriesOverviewContent(
onClick = { onClick = {
selectedTabIndex = it selectedTabIndex = it
onChangeSeason.invoke(it) onChangeSeason.invoke(it)
requestFocusAfterSeason = true
}, },
modifier = modifier =
Modifier Modifier
@ -181,8 +182,14 @@ fun SeriesOverviewContent(
} }
is EpisodeList.Success -> { is EpisodeList.Success -> {
if (requestFocusAfterSeason) {
// Changing seasons, so move focus once the new episodes are loaded
LaunchedEffect(Unit) {
firstItemFocusRequester.tryRequestFocus()
requestFocusAfterSeason = false
}
}
val state = rememberLazyListState(position.episodeRowIndex) val state = rememberLazyListState(position.episodeRowIndex)
RequestOrRestoreFocus(firstItemFocusRequester)
LazyRow( LazyRow(
state = state, state = state,

View file

@ -165,8 +165,9 @@ class SeriesViewModel
} }
} }
} }
val remoteTrailers = trailerService.getRemoteTrailers(item)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
this@SeriesViewModel.trailers.value = remoteTrailers
this@SeriesViewModel.position.update { this@SeriesViewModel.position.update {
it.copy( it.copy(
episodeRowIndex = episodeRowIndex =
@ -179,9 +180,10 @@ class SeriesViewModel
} }
if (seriesPageType == SeriesPageType.DETAILS) { if (seriesPageType == SeriesPageType.DETAILS) {
viewModelScope.launchIO { viewModelScope.launchIO {
val trailers = trailerService.getTrailers(item) trailerService.getLocalTrailers(item).letNotEmpty { localTrailers ->
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
this@SeriesViewModel.trailers.value = trailers this@SeriesViewModel.trailers.value = localTrailers + remoteTrailers
}
} }
} }
viewModelScope.launchIO { viewModelScope.launchIO {
@ -221,9 +223,6 @@ class SeriesViewModel
) { ) {
viewModelScope.launchIO { viewModelScope.launchIO {
themeSongPlayer.playThemeFor(seriesId, playThemeSongs) themeSongPlayer.playThemeFor(seriesId, playThemeSongs)
addCloseable {
themeSongPlayer.stop()
}
} }
} }

View file

@ -234,7 +234,7 @@ fun HomePageContent(
val listState = rememberLazyListState() val listState = rememberLazyListState()
val rowFocusRequesters = remember(homeRows.size) { List(homeRows.size) { FocusRequester() } } val rowFocusRequesters = remember(homeRows.size) { List(homeRows.size) { FocusRequester() } }
var focused by remember { mutableStateOf(false) } var focused by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(homeRows) { LaunchedEffect(homeRows) {
if (!focused) { if (!focused) {
homeRows homeRows
@ -246,6 +246,8 @@ fun HomePageContent(
listState.animateScrollToItem(position.row) listState.animateScrollToItem(position.row)
focused = true focused = true
} }
} else {
rowFocusRequesters.getOrNull(position.row)?.tryRequestFocus()
} }
} }
LaunchedEffect(position) { LaunchedEffect(position) {

View file

@ -157,6 +157,7 @@ class PlaybackViewModel
internal lateinit var item: BaseItem internal lateinit var item: BaseItem
internal var forceTranscoding: Boolean = false internal var forceTranscoding: Boolean = false
private var activityListener: TrackActivityPlaybackListener? = null private var activityListener: TrackActivityPlaybackListener? = null
private val jobs = mutableListOf<Job>()
val nextUp = MutableLiveData<BaseItem?>() val nextUp = MutableLiveData<BaseItem?>()
private var isPlaylist = false private var isPlaylist = false
@ -166,20 +167,22 @@ class PlaybackViewModel
val subtitleSearchLanguage = MutableLiveData<String>(Locale.current.language) val subtitleSearchLanguage = MutableLiveData<String>(Locale.current.language)
init { init {
viewModelScope.launch(ExceptionHandler()) { controllerViewState.observe() }
player.addListener(this)
(player as? ExoPlayer)?.addAnalyticsListener(this)
addCloseable { player.removeListener(this@PlaybackViewModel) }
addCloseable { (player as? ExoPlayer)?.removeAnalyticsListener(this@PlaybackViewModel) }
addCloseable { addCloseable {
player.removeListener(this@PlaybackViewModel)
(player as? ExoPlayer)?.removeAnalyticsListener(this@PlaybackViewModel)
this@PlaybackViewModel.activityListener?.let { this@PlaybackViewModel.activityListener?.let {
it.release() it.release()
player.removeListener(it) player.removeListener(it)
} }
jobs.forEach { it.cancel() }
player.release()
} }
addCloseable { player.release() } viewModelScope.launch(ExceptionHandler()) { controllerViewState.observe() }
subscribe() player.addListener(this)
listenForTranscodeReason() (player as? ExoPlayer)?.addAnalyticsListener(this)
jobs.add(subscribe())
jobs.add(listenForTranscodeReason())
} }
/** /**
@ -874,7 +877,7 @@ class PlaybackViewModel
} }
} }
private fun listenForTranscodeReason() { private fun listenForTranscodeReason(): Job =
viewModelScope.launchIO { viewModelScope.launchIO {
currentPlayback.collectLatest { currentPlayback.collectLatest {
if (it != null) { if (it != null) {
@ -905,7 +908,6 @@ class PlaybackViewModel
} }
} }
} }
}
private var lastInteractionDate: Date = Date() private var lastInteractionDate: Date = Date()
@ -1026,11 +1028,13 @@ class PlaybackViewModel
} }
fun release() { fun release() {
Timber.v("release")
activityListener?.release() activityListener?.release()
player.release() player.release()
activityListener = null
} }
fun subscribe() { fun subscribe(): Job =
api.webSocket api.webSocket
.subscribe<PlaystateMessage>() .subscribe<PlaystateMessage>()
.onEach { message -> .onEach { message ->
@ -1085,7 +1089,6 @@ class PlaybackViewModel
} }
} }
}.launchIn(viewModelScope) }.launchIn(viewModelScope)
}
/** /**
* Atomically update [currentMediaInfo] * Atomically update [currentMediaInfo]

View file

@ -109,6 +109,7 @@ class MpvPlayer(
Timber.v("config-dir=${context.filesDir.path}") Timber.v("config-dir=${context.filesDir.path}")
MPVLib.addLogObserver(mpvLogger) MPVLib.addLogObserver(mpvLogger)
Timber.v("Creating MPVLib")
MPVLib.create(context) MPVLib.create(context)
MPVLib.setOptionString("config", "yes") MPVLib.setOptionString("config", "yes")
MPVLib.setOptionString("config-dir", context.filesDir.path) MPVLib.setOptionString("config-dir", context.filesDir.path)
@ -127,6 +128,7 @@ class MpvPlayer(
MPVLib.setOptionString("demuxer-max-bytes", "${cacheMegs * 1024 * 1024}") MPVLib.setOptionString("demuxer-max-bytes", "${cacheMegs * 1024 * 1024}")
MPVLib.setOptionString("demuxer-max-back-bytes", "${cacheMegs * 1024 * 1024}") MPVLib.setOptionString("demuxer-max-back-bytes", "${cacheMegs * 1024 * 1024}")
Timber.v("Initializing MPVLib")
MPVLib.initialize() MPVLib.initialize()
MPVLib.setOptionString("force-window", "no") MPVLib.setOptionString("force-window", "no")
@ -466,7 +468,6 @@ class MpvPlayer(
override fun clearVideoSurfaceHolder(surfaceHolder: SurfaceHolder?): Unit = throw UnsupportedOperationException() override fun clearVideoSurfaceHolder(surfaceHolder: SurfaceHolder?): Unit = throw UnsupportedOperationException()
override fun setVideoSurfaceView(surfaceView: SurfaceView?) { override fun setVideoSurfaceView(surfaceView: SurfaceView?) {
throwIfReleased()
if (DEBUG) Timber.v("setVideoSurfaceView") if (DEBUG) Timber.v("setVideoSurfaceView")
val surface = surfaceView?.holder?.surface val surface = surfaceView?.holder?.surface
if (surface != null && surface.isValid) { if (surface != null && surface.isValid) {
@ -635,9 +636,9 @@ class MpvPlayer(
val title = it.label ?: "External Subtitles" val title = it.label ?: "External Subtitles"
Timber.v("Adding external subtitle track '$title'") Timber.v("Adding external subtitle track '$title'")
if (it.language.isNotNullOrBlank()) { if (it.language.isNotNullOrBlank()) {
MPVLib.command(arrayOf("sub-add", url, "auto", title, it.language!!)) MPVLib.command(arrayOf("sub-add", url, "select", title, it.language!!))
} else { } else {
MPVLib.command(arrayOf("sub-add", url, "auto", title)) MPVLib.command(arrayOf("sub-add", url, "select", title))
} }
} }
} }
@ -837,7 +838,18 @@ class MpvPlayer(
override fun handleMessage(msg: Message): Boolean { override fun handleMessage(msg: Message): Boolean {
val cmd = MpvCommand.entries[msg.what] val cmd = MpvCommand.entries[msg.what]
Timber.v("handleMessage: cmd=$cmd") Timber.d("handleMessage: cmd=$cmd")
if (isReleased && cmd != MpvCommand.DESTROY) {
Timber.w("Player is released, ignoring command %s", cmd)
return true
}
if (surface == null && !cmd.isLifecycle) {
// If libmpv isn't ready, re-enqueue the messages
// Note: this means nothing will play until it is attached to a surface,
// so MpvPlayer can't be used for background audio/music playback
Timber.v("MPV is not initialized/attached yet, requeue cmd %s", cmd)
internalHandler.sendMessageDelayed(Message.obtain(msg), 50)
}
when (cmd) { when (cmd) {
MpvCommand.PLAY_PAUSE -> { MpvCommand.PLAY_PAUSE -> {
val playWhenReady = msg.obj as Boolean val playWhenReady = msg.obj as Boolean
@ -894,6 +906,7 @@ class MpvPlayer(
MPVLib.detachSurface() MPVLib.detachSurface()
MPVLib.setPropertyString("vo", "null") MPVLib.setPropertyString("vo", "null")
MPVLib.setPropertyString("force-window", "no") MPVLib.setPropertyString("force-window", "no")
Timber.d("Detached surface")
} }
if (surface != null) { if (surface != null) {
MPVLib.attachSurface(surface) MPVLib.attachSurface(surface)
@ -1035,14 +1048,16 @@ private data class MediaAndPosition(
val startPositionMs: Long, val startPositionMs: Long,
) )
enum class MpvCommand { enum class MpvCommand(
PLAY_PAUSE, val isLifecycle: Boolean,
SEEK, ) {
SET_TRACK_SELECTION, PLAY_PAUSE(false),
SET_SPEED, SEEK(false),
SET_SUBTITLE_DELAY, SET_TRACK_SELECTION(false),
LOAD_FILE, SET_SPEED(false),
ATTACH_SURFACE, SET_SUBTITLE_DELAY(false),
INITIALIZE, LOAD_FILE(false),
DESTROY, ATTACH_SURFACE(true),
INITIALIZE(true),
DESTROY(true),
} }

View file

@ -210,7 +210,7 @@ fun createDeviceProfile(
"main", "main",
"baseline", "baseline",
"constrained baseline", "constrained baseline",
if (supportsAVCHigh10) "main 10" else null, if (supportsAVCHigh10) "high 10" else null,
) )
} }
} }

View file

@ -239,7 +239,7 @@
<string name="send_app_logs_summary">Nützlich zum Debuggen</string> <string name="send_app_logs_summary">Nützlich zum Debuggen</string>
<string name="send_crash_reports">Sende Absturzmeldung</string> <string name="send_crash_reports">Sende Absturzmeldung</string>
<string name="skip_back_on_resume_preference">Zurückspringen wenn wiedergabe fortgesetzt wird</string> <string name="skip_back_on_resume_preference">Zurückspringen wenn wiedergabe fortgesetzt wird</string>
<string name="skip_commercials_behavior">Überspringe Werbung verhalten</string> <string name="skip_commercials_behavior">Werbung überspringen</string>
<string name="skip_forward_preference">Vorspringen</string> <string name="skip_forward_preference">Vorspringen</string>
<string name="skip_intro_behavior">Überspringe Intro verhalten</string> <string name="skip_intro_behavior">Überspringe Intro verhalten</string>
<string name="skip_outro_behavior">Überspringe Outro verhalten</string> <string name="skip_outro_behavior">Überspringe Outro verhalten</string>
@ -286,4 +286,39 @@
<string name="remove">Entfernen</string> <string name="remove">Entfernen</string>
<string name="dolby_vision">Dolby Vision</string> <string name="dolby_vision">Dolby Vision</string>
<string name="dolby_atmos">Dolby Atmos</string> <string name="dolby_atmos">Dolby Atmos</string>
<string name="ends_at">Endet um %1$s</string>
<string name="enter_server_address">Server Adresse eingeben</string>
<string name="no_servers_found">Keine Server gefunden</string>
<string name="media_information">Medieninformation</string>
<string name="played">Abgespielt</string>
<string name="mpv_use_gpu_next">MPV: Benutze gpu-next</string>
<string name="mpv_conf">mpv.conf bearbeiten</string>
<string name="discard_change">Änderungen verwerfen?</string>
<string name="subtitle_margin">Abstand</string>
<string name="enter_pin">PIN Eingeben</string>
<string name="sign_in_auto">Automatisch anmelden</string>
<string name="require_pin_code">Profil benötigt PIN</string>
<string name="confirm_pin">PIN bestätigen</string>
<string name="incorrect">Falsch</string>
<string name="pin_too_short">PIN muss 4 Zeichen oder länger sein</string>
<string name="will_remove_pin">PIN wird entfernt</string>
<string name="view_options">Anzeigeoptionen</string>
<string name="columns">Spalten</string>
<string name="spacing">Abstände</string>
<string name="aspect_ratio">Seitenverhältnis</string>
<string name="show_details">Details anzeigen</string>
<string name="image_type">Bild Typ</string>
<string name="automatic">Automatisch</string>
<string name="username_or_password">Verwende Benutzername/Passwort</string>
<string name="login">Anmelden</string>
<string name="title">Titel</string>
<string name="profile">Profil</string>
<string name="resolution">Auflösung</string>
<string name="framerate">Bildrate</string>
<string name="language">Sprache</string>
<string name="channels">Kanäle</string>
<string name="yes">Ja</string>
<string name="no">Nein</string>
<string name="show_titles">Titel anzeigen</string>
<string name="live_tv_repeat">Wiederholen</string>
</resources> </resources>

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="add_favorite">Favorito</string> <string name="add_favorite">Añadir a Favoritos</string>
<string name="add_server">Agregar servidor</string> <string name="add_server">Añadir servidor</string>
<string name="add_user">Agregar usuario</string> <string name="add_user">Añadir usuario</string>
<string name="audio">Audio</string> <string name="audio">Audio</string>
<string name="birthplace">Lugar de nacimiento</string> <string name="birthplace">Lugar de nacimiento</string>
<string name="bitrate">Tasa de bits</string> <string name="bitrate">Tasa de bits</string>
@ -170,8 +170,8 @@
</plurals> </plurals>
<plurals name="theme_songs"> <plurals name="theme_songs">
<item quantity="one">Canciones principales</item> <item quantity="one">Canciones principales</item>
<item quantity="many"></item> <item quantity="many"/>
<item quantity="other"></item> <item quantity="other"/>
</plurals> </plurals>
<plurals name="theme_videos"> <plurals name="theme_videos">
<item quantity="one">Vídeos temáticos</item> <item quantity="one">Vídeos temáticos</item>
@ -303,4 +303,76 @@
<string name="font">Fuente</string> <string name="font">Fuente</string>
<string name="background">Fondo</string> <string name="background">Fondo</string>
<string name="official_rating">Clasificación parental</string> <string name="official_rating">Clasificación parental</string>
<string name="ends_at">Termina en %1$s</string>
<string name="enter_server_address">Introduzca la dirección del servidor</string>
<string name="no_servers_found">No se encontraron servidores</string>
<string name="media_information">Información multimedia</string>
<string name="mpv_use_gpu_next">MPV: Usar gpu-next</string>
<string name="mpv_conf">Editar mpv.conf</string>
<string name="discard_change">¿Descartar los cambios?</string>
<string name="subtitle_margin">Margen</string>
<string name="verbose_logging">Registro detallado</string>
<string name="enter_pin">Introducir PIN</string>
<string name="sign_in_auto">Iniciar sesión automáticamente</string>
<string name="use_server_credentials">Iniciar sesión a través del servidor</string>
<string name="press_enter_to_confirm">Pulsa el centro para confirmar</string>
<string name="require_pin_code">Requerir PIN para el perfil</string>
<string name="confirm_pin">Confirmar PIN</string>
<string name="incorrect">Incorrecto</string>
<string name="pin_too_short">El PIN debe tener 4 teclas o más</string>
<string name="will_remove_pin">Se eliminará el PIN</string>
<string name="image_cache_size">Tamaño de la caché de disco de imágenes (MB)</string>
<string name="view_options">Opciones de visualización</string>
<string name="columns">Columnas</string>
<string name="spacing">Espaciado</string>
<string name="aspect_ratio">Relación de aspecto</string>
<string name="show_details">Mostrar detalles</string>
<string name="image_type">Tipo de imagen</string>
<string name="cast_and_crew"><![CDATA[Reparto y equipo]]></string>
<string name="guest_stars">Estrellas invitadas</string>
<string name="edge_size">Tamaño del borde</string>
<string name="refresh_rate_switching">Cambio de frecuencia de actualización</string>
<string name="automatic">Automático</string>
<string name="username_or_password">Usar nombre de usuario/contraseña</string>
<string name="login">Iniciar sesión</string>
<string name="general">General</string>
<string name="container">Contenedor</string>
<string name="title">Título</string>
<string name="codec">Codec</string>
<string name="profile">Perfil</string>
<string name="level">Nivel</string>
<string name="resolution">Resolución</string>
<string name="anamorphic">Anamórfico</string>
<string name="interlaced">Entrelazado</string>
<string name="framerate">Frecuencia de fotogramas</string>
<string name="bit_depth">Profundidad de bits</string>
<string name="video_range">Rango de vídeo</string>
<string name="video_range_type">Tipo de rango de vídeo</string>
<string name="color_space">Espacio de color</string>
<string name="color_transfer">Transferencia de color</string>
<string name="color_primaries">Primarias de color</string>
<string name="pixel_format">Formato de píxel</string>
<string name="ref_frames">Fotogramas de referencia</string>
<string name="nal">NAL</string>
<string name="language">Idioma</string>
<string name="layout">Distribución</string>
<string name="channels">Canales</string>
<string name="sample_rate">Frecuencia de muestreo</string>
<string name="avc">AVC</string>
<string name="yes"></string>
<string name="no">No</string>
<string name="sdr">SDR</string>
<string name="hdr">HDR</string>
<string name="hdr10">HDR10</string>
<string name="hdr10_plus">HDR10+</string>
<string name="hlg">HLG</string>
<string name="bit_unit">bit</string>
<string name="sample_rate_unit">Hz</string>
<string name="show_titles">Mostrar títulos</string>
<string name="live_tv_repeat">Repetir</string>
<string name="favorite_channels_at_beginning">Mostrar primero los canales favoritos</string>
<string name="sort_channels_recently_watched">Ordenar los canales por vistos recientemente</string>
<string name="color_code_programs">Programas codificados por colores</string>
<string name="subtitle_delay">Retardo de subtítulos</string>
<string name="backdrop_display">Estilo del fondo</string>
</resources> </resources>

View file

@ -358,4 +358,5 @@
<string name="enter_server_address">Sisesta serveri aadress</string> <string name="enter_server_address">Sisesta serveri aadress</string>
<string name="no_servers_found">Ühtegi serverit ei leidu</string> <string name="no_servers_found">Ühtegi serverit ei leidu</string>
<string name="backdrop_display">Tausta dekoratsioon</string> <string name="backdrop_display">Tausta dekoratsioon</string>
<string name="ends_at">Lõppeb kell %1$s</string>
</resources> </resources>

View file

@ -370,4 +370,9 @@
<string name="ref_frames">Images de référence</string> <string name="ref_frames">Images de référence</string>
<string name="sort_channels_recently_watched">Trier les chaînes par date de visionnage</string> <string name="sort_channels_recently_watched">Trier les chaînes par date de visionnage</string>
<string name="color_code_programs">Classer les programmes par code couleur</string> <string name="color_code_programs">Classer les programmes par code couleur</string>
<string name="ends_at">Terminera à %1$s</string>
<string name="enter_server_address">Entrez l\'adresse du serveur</string>
<string name="no_servers_found">Aucun serveur trouvé</string>
<string name="subtitle_delay">Décalage des sous-titres</string>
<string name="backdrop_display">Style d\'arrière-plan</string>
</resources> </resources>

View file

@ -1,36 +1,36 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="about">Névjegy</string> <string name="about">Névjegy</string>
<string name="add_favorite">Kedvenc</string> <string name="add_favorite">Hozzáadás kedvencekhez</string>
<string name="add_server">Szerver hozzáadása</string> <string name="add_server">Szerver hozzáadása</string>
<string name="add_user">Felhasználó hozzáadása</string> <string name="add_user">Felhasználó hozzáadása</string>
<string name="audio">Audio</string> <string name="audio">Hang</string>
<string name="birthplace">Születési hely</string> <string name="birthplace">Születési hely</string>
<string name="bitrate">Bitráta</string> <string name="bitrate">Bitráta</string>
<string name="born">Született</string> <string name="born">Született</string>
<string name="cancel">Mégse</string> <string name="cancel">Mégse</string>
<string name="chapters">Fejezetek</string> <string name="chapters">Fejezetek</string>
<string name="choose_stream">Válassz %1$s</string> <string name="choose_stream">%1$s kiválasztása</string>
<string name="clear_image_cache">Képgyorsítótár törlése</string> <string name="clear_image_cache">Képgyorsítótár törlése</string>
<string name="collection">Gyűjtemény</string> <string name="collection">Gyűjtemény</string>
<string name="collections">Gyűjtemények</string> <string name="collections">Gyűjtemények</string>
<string name="community_rating">Közösségi értékelés</string> <string name="community_rating">Közösségi értékelés</string>
<string name="compose_error_message_title">Hiba történt! Nyomd meg a gombot, hogy elküldd a naplófájlokat a szervernek.</string> <string name="compose_error_message_title">Hiba történt! Nyomja meg a gombot, hogy elküldje a naplófájlokat a szervernek.</string>
<string name="confirm">Megerősít</string> <string name="confirm">Megerősít</string>
<string name="critic_rating">Kritikusi értékelés</string> <string name="critic_rating">Kritikusi értékelés</string>
<string name="decimal_seconds">%.1f másodperc</string> <string name="decimal_seconds">%.1f másodperc</string>
<string name="default_track">Alapértelmezett</string> <string name="default_track">Alapértelmezett</string>
<string name="delete">Törlés</string> <string name="delete">Törlés</string>
<string name="directed_by">Rendezte %1$s</string> <string name="directed_by">Rendezte: %1$s</string>
<string name="director">Rendező</string> <string name="director">Rendező</string>
<string name="disabled">Letiltva</string> <string name="disabled">Letiltva</string>
<string name="discovered_servers">Felfedezett szerverek</string> <string name="discovered_servers">Felfedezett szerverek</string>
<string name="download_and_update"><![CDATA[Letöltés & Frissítés]]></string> <string name="download_and_update"><![CDATA[Letöltés & Frissítés]]></string>
<string name="downloading">Letöltés folyamatban…</string> <string name="downloading">Letöltés folyamatban…</string>
<string name="enabled">Engedélyezve</string> <string name="enabled">Engedélyezve</string>
<string name="enter_server_url">Add meg a szerver IP-t vagy URL-t</string> <string name="enter_server_url">Adja meg a szerver IP-t vagy URL-t</string>
<string name="episodes">Epizódok</string> <string name="episodes">Epizódok</string>
<string name="error_loading_collection">Hiba a gyűjtemény betöltése során: %1$s</string> <string name="error_loading_collection">Hiba a következő gyűjtemény betöltése során: %1$s</string>
<string name="external_track">Külső</string> <string name="external_track">Külső</string>
<string name="favorites">Kedvencek</string> <string name="favorites">Kedvencek</string>
<string name="active_recordings">Aktív felvételek</string> <string name="active_recordings">Aktív felvételek</string>
@ -38,28 +38,28 @@
<string name="cancel_series_recording">Sorozatfelvétel leállítása</string> <string name="cancel_series_recording">Sorozatfelvétel leállítása</string>
<string name="commercial">Reklám</string> <string name="commercial">Reklám</string>
<string name="continue_watching">Megtekintés folytatása</string> <string name="continue_watching">Megtekintés folytatása</string>
<string name="died">Halott</string> <string name="died">Elhunyt</string>
<string name="file_size">Méret</string> <string name="file_size">Méret</string>
<string name="forced_track">Kényszerített</string> <string name="forced_track">Kiegészítő</string>
<string name="genres">Műfajok</string> <string name="genres">Műfajok</string>
<string name="go_to_series">Ugrás a sorozathoz</string> <string name="go_to_series">Ugrás a sorozathoz</string>
<string name="go_to">Ugrás</string> <string name="go_to">Ugrás ide:</string>
<string name="hide_controller_timeout">Lejátszási gombok elrejtése</string> <string name="hide_controller_timeout">Lejátszási gombok elrejtése</string>
<string name="hide_debug_info">Hibajavítási infó elrejtése</string> <string name="hide_debug_info">Hibakeresési infó elrejtése</string>
<string name="hide">Elrejt</string> <string name="hide">Elrejt</string>
<string name="home">Kezdőlap</string> <string name="home">Kezdőlap</string>
<string name="immediate">Azonnal</string> <string name="immediate">Azonnal</string>
<string name="intro">Intro</string> <string name="intro">Intró</string>
<string name="jump_letters">#ABCDEFGHIJKLMNOPQRSTUVWXYZ</string> <string name="jump_letters">#ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
<string name="library">Könyvtár</string> <string name="library">Könyvtár</string>
<string name="license_info">Lincenc információk</string> <string name="license_info">Lincenc információk</string>
<string name="live_tv">Élő TV</string> <string name="live_tv">Élő TV</string>
<string name="loading">Betöltés…</string> <string name="loading">Betöltés…</string>
<string name="mark_entire_series_as_played">Megjelölöd az egész sorozatot megnézettként?</string> <string name="mark_entire_series_as_played">Megjelöli az egész sorozatot megnézettként?</string>
<string name="mark_entire_series_as_unplayed">Megjelölöd az egész sorozatot megnézetlenként?</string> <string name="mark_entire_series_as_unplayed">Megjelöli az egész sorozatot megnézetlenként?</string>
<string name="mark_unwatched">Jelölés megnézetlenként</string> <string name="mark_unwatched">Jelölés megnézetlenként</string>
<string name="mark_watched">Jelölés megnézettként</string> <string name="mark_watched">Jelölés megnézettként</string>
<string name="max_bitrate">Max bitráta</string> <string name="max_bitrate">Maximális bitráta</string>
<string name="more_like_this">Hasonlóak</string> <string name="more_like_this">Hasonlóak</string>
<string name="more">Továbbiak</string> <string name="more">Továbbiak</string>
<string name="movies">Filmek</string> <string name="movies">Filmek</string>
@ -70,15 +70,15 @@
<string name="no_scheduled_recordings">Nincs időzített felvétel</string> <string name="no_scheduled_recordings">Nincs időzített felvétel</string>
<string name="no_update_available">Nincs elérhető frissítés</string> <string name="no_update_available">Nincs elérhető frissítés</string>
<string name="none">Nincs</string> <string name="none">Nincs</string>
<string name="outro">Outro</string> <string name="outro">Stáblista</string>
<string name="path">Elérési út</string> <string name="path">Elérési út</string>
<string name="people">Emberek</string> <string name="people">Stábtagok</string>
<string name="play_count">Lejátszások száma</string> <string name="play_count">Lejátszások száma</string>
<string name="play_from_here">Lejátszás innen</string> <string name="play_from_here">Lejátszás innen</string>
<string name="play">Lejátszás</string> <string name="play">Lejátszás</string>
<string name="playback_debug_info">Lejátszás hibakeresési adatainak mutatása</string> <string name="playback_debug_info">Lejátszás hibakeresési adatainak mutatása</string>
<string name="playback_speed">Lejátszási sebesség</string> <string name="playback_speed">Lejátszási sebesség</string>
<string name="playback">Visszajátszás</string> <string name="playback">Lejátszás</string>
<string name="playlist">Lejátszási lista</string> <string name="playlist">Lejátszási lista</string>
<string name="playlists">Lejátszási listák</string> <string name="playlists">Lejátszási listák</string>
<string name="preview">Előnézet</string> <string name="preview">Előnézet</string>
@ -87,9 +87,9 @@
<string name="recently_added_in">Nemrég hozzáadva: %1$s</string> <string name="recently_added_in">Nemrég hozzáadva: %1$s</string>
<string name="recently_added">Nemrég hozzáadott</string> <string name="recently_added">Nemrég hozzáadott</string>
<string name="recently_recorded">Nemrég rögzített</string> <string name="recently_recorded">Nemrég rögzített</string>
<string name="recently_released">Nemrég kiadott</string> <string name="recently_released">Nemrég megjelent</string>
<string name="recommended">Ajánlott</string> <string name="recommended">Ajánlott</string>
<string name="record_program">Program felvétele</string> <string name="record_program">Műsor felvétele</string>
<string name="record_series">Sorozat felvétele</string> <string name="record_series">Sorozat felvétele</string>
<string name="remove_favorite">Törlés a kedvencekből</string> <string name="remove_favorite">Törlés a kedvencekből</string>
<string name="restart">Újraindítás</string> <string name="restart">Újraindítás</string>
@ -100,117 +100,263 @@
<string name="searching">Keresés…</string> <string name="searching">Keresés…</string>
<string name="select_server">Szerver kiválasztása</string> <string name="select_server">Szerver kiválasztása</string>
<string name="select_user">Felhasználó kiválasztása</string> <string name="select_user">Felhasználó kiválasztása</string>
<string name="show_debug_info">Hibakeresési info megjelenítése</string> <string name="show_debug_info">Hibakeresési infó megjelenítése</string>
<string name="show_next_up_when">Következő mutatása</string> <string name="show_next_up_when">Következő mutatása</string>
<string name="show">Megmutat</string> <string name="show">Megmutat</string>
<string name="shuffle">Véletlenszerű</string> <string name="shuffle">Véletlenszerű</string>
<string name="skip">Átugrás</string> <string name="skip">Átugrás</string>
<string name="sort_by_date_added">Hozzáadva</string> <string name="sort_by_date_added">Hozzáadás dátuma</string>
<string name="sort_by_date_episode_added">Epizód hozzáadásának dátuma</string> <string name="sort_by_date_episode_added">Epizód hozzáadásának dátuma</string>
<string name="sort_by_date_played">Lejátszás dátuma</string> <string name="sort_by_date_played">Lejátszás dátuma</string>
<string name="sort_by_date_released">Megjelenés dátuma</string> <string name="sort_by_date_released">Megjelenés dátuma</string>
<string name="sort_by_name">Név</string> <string name="sort_by_name">Név</string>
<string name="sort_by_random">Random</string> <string name="sort_by_random">Véletlenszerűen</string>
<string name="studios">Stúdiók</string> <string name="studios">Stúdiók</string>
<string name="submit">Beküld</string> <string name="submit">Beküld</string>
<string name="subtitle_download_too_long">A letöltés sok időt vesz igénybe, előfordulhat, hogy újra kell indítanod a lejátszást</string> <string name="subtitle_download_too_long">A letöltés sok időt vesz igénybe, előfordulhat, hogy újra kell indítani a lejátszást</string>
<string name="subtitle">Felirat</string> <string name="subtitle">Felirat</string>
<string name="subtitles">Feliratok</string> <string name="subtitles">Feliratok</string>
<string name="suggestions">Ajánlások</string> <string name="suggestions">Ajánlások</string>
<string name="switch_servers">Váltás szerverek között</string> <string name="switch_servers">Váltás másik szerverre</string>
<string name="switch_user">Váltás</string> <string name="switch_user">Váltás</string>
<string name="top_unwatched">Legjobbra értékelt nem megtekintett</string> <string name="top_unwatched">Legjobbra értékelt, nem megnézett</string>
<string name="trailer">Előzetes</string> <string name="trailer">Előzetes</string>
<plurals name="trailers"> <plurals name="trailers">
<item quantity="one">Előzetesek</item> <item quantity="one">Előzetesek</item>
<item quantity="other"></item> <item quantity="other"/>
</plurals> </plurals>
<string name="tv_dvr_schedule">DVR Időzítés</string> <string name="tv_dvr_schedule">DVR Időzítés</string>
<string name="tv_guide">Műsorújság</string> <string name="tv_guide">Műsorújság</string>
<string name="tv_season">Évad</string> <string name="tv_season">Évad</string>
<string name="tv_seasons">Évadok</string> <string name="tv_seasons">Évadok</string>
<string name="tv_shows">Sorozatok</string> <string name="tv_shows">Sorozatok</string>
<string name="ui_interface">Felület</string> <string name="ui_interface">Kezelőfelület</string>
<string name="unknown">Ismeretlen</string> <string name="unknown">Ismeretlen</string>
<string name="updates">Frissítések</string> <string name="updates">Frissítések</string>
<string name="version">Verzió</string> <string name="version">Verzió</string>
<string name="video_scale">Képarány</string> <string name="video_scale">Kép méretezése</string>
<string name="video">Videó</string> <string name="video">Videó</string>
<string name="videos">Videók</string> <string name="videos">Videók</string>
<string name="watch_live">Nézd élőben</string> <string name="watch_live">Nézze élőben</string>
<string name="years_old">%1$d éves</string> <string name="years_old">%1$d éves</string>
<string name="play_with_transcoding">Lejátszás átalakítással</string> <string name="play_with_transcoding">Lejátszás átalakítással</string>
<string name="show_clock">Aktuális idő mutatása</string> <string name="show_clock">Aktuális idő mutatása</string>
<string name="aired_episode_order">Sorrend epízód vetítése alapján</string> <string name="aired_episode_order">Sorrend epizód vetítése alapján</string>
<string name="create_playlist">Új lejátszási lista létrehozása</string> <string name="create_playlist">Új lejátszási lista létrehozása</string>
<string name="add_to_playlist">Hozzáadás lejátszási listához</string> <string name="add_to_playlist">Hozzáadás lejátszási listához</string>
<string name="one_click_pause">Szüneteltetés egy klikkel</string> <string name="one_click_pause">Szüneteltetés egy klikkel</string>
<string name="one_click_pause_summary_on">Nyomd a D-Pad közepét megállításhoz/lejátszáshoz</string> <string name="one_click_pause_summary_on">Nyomja le a D-Pad közepét megállításhoz/lejátszáshoz</string>
<string name="italic_font">Dőlt betűk</string> <string name="italic_font">Dőlt betűk</string>
<string name="font">Betűtípus</string> <string name="font">Betűk stílusa</string>
<string name="background">Háttérszín</string> <string name="background">Háttér</string>
<string name="success">Sikeres</string> <string name="success">Sikeres</string>
<string name="official_rating">Szülői besorolás</string> <string name="official_rating">Korhatár-besorolás</string>
<string name="runtime_sort">Műsoridő</string> <string name="runtime_sort">Hossz</string>
<string name="extras">Extrák</string> <string name="extras">Extrák</string>
<plurals name="other_extras"> <plurals name="other_extras">
<item quantity="one">Egyéb</item> <item quantity="one">Egyéb</item>
<item quantity="other"></item> <item quantity="other">Egyebek</item>
</plurals> </plurals>
<plurals name="behind_the_scenes"> <plurals name="behind_the_scenes">
<item quantity="one">Színfalak mögött</item> <item quantity="one">Színfalak mögött</item>
<item quantity="other"></item> <item quantity="other"/>
</plurals> </plurals>
<plurals name="theme_songs"> <plurals name="theme_songs">
<item quantity="one">Téma zenék</item> <item quantity="one">Főcímdal</item>
<item quantity="other"></item> <item quantity="other">Főcímdalok</item>
</plurals> </plurals>
<plurals name="theme_videos"> <plurals name="theme_videos">
<item quantity="one">Téma videók</item> <item quantity="one">Téma videó</item>
<item quantity="other"></item> <item quantity="other">Téma videók</item>
</plurals> </plurals>
<plurals name="clips"> <plurals name="clips">
<item quantity="one">Klippek</item> <item quantity="one">Klip</item>
<item quantity="other"></item> <item quantity="other">Klipek</item>
</plurals> </plurals>
<plurals name="deleted_scenes"> <plurals name="deleted_scenes">
<item quantity="one">Törölt jelenetek</item> <item quantity="one">Törölt jelenet</item>
<item quantity="other"></item> <item quantity="other">Törölt jelenetek</item>
</plurals> </plurals>
<plurals name="interviews"> <plurals name="interviews">
<item quantity="one">Interjúk</item> <item quantity="one">Interjú</item>
<item quantity="other"></item> <item quantity="other">Interjúk</item>
</plurals> </plurals>
<plurals name="scenes"> <plurals name="scenes">
<item quantity="one">Jelenetek</item> <item quantity="one">Jelenet</item>
<item quantity="other"></item> <item quantity="other">Jelenetek</item>
</plurals> </plurals>
<plurals name="samples"> <plurals name="samples">
<item quantity="one">Minták</item> <item quantity="one">Minta</item>
<item quantity="other"></item> <item quantity="other">Minták</item>
</plurals> </plurals>
<plurals name="featurettes"> <plurals name="featurettes">
<item quantity="one">Rövidfilmek</item> <item quantity="one">Rövidfilm</item>
<item quantity="other"></item> <item quantity="other">Rövidfilmek</item>
</plurals> </plurals>
<plurals name="shorts"> <plurals name="shorts">
<item quantity="one">Rövidek</item> <item quantity="one">Rövid</item>
<item quantity="other"></item> <item quantity="other">Rövidek</item>
</plurals> </plurals>
<plurals name="downloads"> <plurals name="downloads">
<item quantity="one">%s letöltés</item> <item quantity="one">%s letöltés</item>
<item quantity="other">%s letöltések</item> <item quantity="other">%s letöltés</item>
</plurals> </plurals>
<plurals name="hours"> <plurals name="hours">
<item quantity="one">%d óra</item> <item quantity="one">%d óra</item>
<item quantity="other">%d órák</item> <item quantity="other">%d óra</item>
</plurals> </plurals>
<plurals name="items"> <plurals name="items">
<item quantity="one">%d elem</item> <item quantity="one">%d elem</item>
<item quantity="other">%d elemek</item> <item quantity="other">%d elem</item>
</plurals> </plurals>
<plurals name="seconds"> <plurals name="seconds">
<item quantity="one">%d másodperc</item> <item quantity="one">%d másodperc</item>
<item quantity="other">%d másodpercek</item> <item quantity="other">%d másodperc</item>
</plurals> </plurals>
<string name="ends_at">%1$s-kor ér véget</string>
<string name="no_servers_found">Nem található szerver</string>
<string name="recap">Összefoglaló</string>
<string name="media_information">Médiainformáció</string>
<string name="ac3_supported">A készülék támogatja az AC3/Dolby Digital formátumot</string>
<string name="advanced_settings">Haladó beállítások</string>
<string name="advanced_ui">Haladó kezelőfelület-beállítások</string>
<string name="app_theme">Alkalmazás témája</string>
<string name="auto_check_for_updates">Frissítések automatikus keresése</string>
<string name="auto_play_next_delay">Késleltetés a következő elem lejátszása előtt</string>
<string name="auto_play_next">Következő elem automatikus lejátszása</string>
<string name="check_for_updates">Frissítések keresése</string>
<string name="combine_continue_next_summary">Csak sorozatok esetén</string>
<string name="direct_play_ass">ASS feliratok közvetlen lejátszása</string>
<string name="direct_play_pgs">PGS feliratok közvetlen lejátszása</string>
<string name="downmix_stereo">Mindig konvertálja sztereóvá</string>
<string name="ffmpeg_extension_pref">FFmpeg dekóder modul használata</string>
<string name="global_content_scale">Tartalom alapértelmezett méretezése</string>
<string name="install_update">Frissítés telepítése</string>
<string name="installed_version">Telepített verzió</string>
<string name="max_homepage_items">Maximális elemek soronként a kezdőlapon</string>
<string name="nav_drawer_pins_summary">Válassza ki az alapértelmezett elemeket, amelyek megjelennek, a többi el lesz rejtve</string>
<string name="nav_drawer_pins">Navigációs menü elemeinek testreszabása</string>
<string name="nav_drawer_switch_on_focus_summary_off">Kattintson az oldalak közti váltáshoz</string>
<string name="pass_out_protection">Automatikus leállítás inaktivitás esetén</string>
<string name="play_theme_music">Főcímdal lejátszása</string>
<string name="remember_selected_tab">Kiválasztott lapok megjegyzése</string>
<string name="rewatch_next_up">Újranézés engedélyezése a Következő menüben</string>
<string name="seek_bar_steps">Előre/hátra ugrás intervallum</string>
<string name="send_app_logs_summary">Hibakereséshez hasznos</string>
<string name="send_app_logs">Naplófájlok küldése a jelenlegi szerverre</string>
<string name="send_crash_reports_summary">Megpróbálja elküldeni az legutóbb csatlakozott szerverre</string>
<string name="send_crash_reports">Összeomlási napló küldése</string>
<string name="settings">Beállítások</string>
<string name="skip_back_on_resume_preference">Visszaugrás a lejátszás folytatásakor</string>
<string name="skip_back_preference">Visszaugrás</string>
<string name="skip_commercials_behavior">Reklám-átugrás viselkedés</string>
<string name="skip_forward_preference">Előreugrás</string>
<string name="skip_intro_behavior">Intró-átugrás viselkedés</string>
<string name="skip_outro_behavior">Stáblista-átugrás viselkedés</string>
<string name="skip_previews_behavior">Előzetes-átugrás viselkedés</string>
<string name="skip_recap_behavior">Összefoglaló-átugrás viselkedés</string>
<string name="update_available">Frissítés érhető el</string>
<string name="update_url_summary">Az alkalmazás frissítéseinek ellenőrzésére használt URL</string>
<string name="update_url">Frissítés URL</string>
<string name="font_size">Betűméret</string>
<string name="font_color">Betűszín</string>
<string name="font_opacity">Betűk áttetszősége</string>
<string name="edge_style">Szegély stílusa</string>
<string name="edge_color">Szegély színe</string>
<string name="background_opacity">Háttér áttetszősége</string>
<string name="background_style">Háttér típusa</string>
<string name="subtitle_style">Felirat stílusa</string>
<string name="background_color">Háttér színe</string>
<string name="reset">Visszaállítás</string>
<string name="bold_font">Félkövér betűk</string>
<string name="mpv_hardware_decoding">MPV: hardveres dekódolás használata</string>
<string name="disable_if_crash">Tiltsa le, ha összeomlást tapasztal</string>
<string name="mpv_options">MPV beállítások</string>
<string name="exoplayer_options">ExoPlayer beállítások</string>
<string name="skip_segment_unknown">Szegmens átugrása</string>
<string name="skip_segment_commercial">Reklám átugrása</string>
<string name="skip_segment_preview">Előzetes átugrása</string>
<string name="skip_segment_recap">Összefoglaló átugrása</string>
<string name="skip_segment_outro">Stáblista átugrása</string>
<string name="skip_segment_intro">Intró átugrása</string>
<string name="played">Lejátszott</string>
<string name="filter">Szűrő</string>
<string name="year">Év</string>
<string name="decade">Évtized</string>
<string name="remove">Eltávolítás</string>
<string name="dolby_vision">Dolby Vision</string>
<string name="dolby_atmos">Dolby Atmos</string>
<string name="mpv_use_gpu_next">MPV: gpu-next használata</string>
<string name="mpv_conf">mpv.conf szerkesztése</string>
<string name="discard_change">Elveti a változtatásokat?</string>
<string name="subtitle_margin">Függőleges eltolás</string>
<string name="verbose_logging">Részletes naplózás</string>
<string name="enter_pin">Adja meg a PIN-kódot</string>
<string name="sign_in_auto">Automatikus bejelentkezés</string>
<string name="use_server_credentials">Bejelentkezés szerveren keresztül</string>
<string name="press_enter_to_confirm">Nyomja meg a középső gombot a megerősítéshez</string>
<string name="require_pin_code">Kérjen PIN-kódot a profillal történő bejelentkezéshez</string>
<string name="confirm_pin">PIN-kód megerősítése</string>
<string name="incorrect">Helytelen</string>
<string name="pin_too_short">A PIN-kódnak legalább 4 karakterből kell állnia</string>
<string name="will_remove_pin">PIN-kód eltávolítása</string>
<string name="view_options">Nézetbeállítások</string>
<string name="columns">Oszlopok</string>
<string name="spacing">Helyköz</string>
<string name="aspect_ratio">Képarány</string>
<string name="show_details">Részletek mutatása</string>
<string name="image_type">Kép típusa</string>
<string name="guest_stars">Vendégszereplők</string>
<string name="edge_size">Szegély mérete</string>
<string name="refresh_rate_switching">Tartalomhoz igazodó képfrissítési gyakoriság</string>
<string name="automatic">Automatikus</string>
<string name="username_or_password">Felhasználónév/jelszó használata</string>
<string name="login">Bejelentkezés</string>
<string name="general">Általános</string>
<string name="container">Konténer</string>
<string name="title">Cím</string>
<string name="codec">Kodek</string>
<string name="profile">Profil</string>
<string name="level">Szint</string>
<string name="resolution">Felbontás</string>
<string name="anamorphic">Anamorfikus</string>
<string name="interlaced">Váltottsoros</string>
<string name="framerate">Képfrissítés</string>
<string name="bit_depth">Színmélység</string>
<string name="video_range">Feketeszint tartomány</string>
<string name="video_range_type">Feketeszint tartomány típusa</string>
<string name="color_space">Színtér</string>
<string name="color_transfer">Színátviteli jellemző</string>
<string name="color_primaries">Alapszínek</string>
<string name="pixel_format">Pixelformátum</string>
<string name="ref_frames">Referencia-képkockák</string>
<string name="nal">NAL</string>
<string name="language">Nyelv</string>
<string name="layout">Elrendezés</string>
<string name="channels">Csatornák</string>
<string name="sample_rate">Mintavételezés</string>
<string name="avc">AVC</string>
<string name="yes">Igen</string>
<string name="no">Nem</string>
<string name="sdr">SDR</string>
<string name="hdr">HDR</string>
<string name="hdr10">HDR10</string>
<string name="hdr10_plus">HDR10+</string>
<string name="hlg">HLG</string>
<string name="bit_unit">bit</string>
<string name="sample_rate_unit">Hz</string>
<string name="show_titles">Címek mutatása</string>
<string name="live_tv_repeat">Ismétlés</string>
<string name="favorite_channels_at_beginning">A kedvenc csatornákat mutassa először</string>
<string name="sort_channels_recently_watched">Legutóbb nézett csatornák előre sorolása</string>
<string name="color_code_programs">Műsorok kategória szerinti színezése</string>
<string name="subtitle_delay">Felirat késleltetése</string>
<string name="backdrop_display">Háttér stílusa</string>
<string name="enter_server_address">Adja meg a szerver címét</string>
<string name="combine_continue_next"><![CDATA[Folytatás & Következő menü összevonása]]></string>
<string name="player_backend">Lejátszás backend</string>
<string name="image_cache_size">Kép gyorsítótár méret (MB)</string>
<string name="cast_and_crew"><![CDATA[Stábtagok]]></string>
<string name="nav_drawer_switch_on_focus">Navigációs menü oldalainak váltása fókusznál</string>
<string name="playback_overrides">Lejátszás felülbírálatai</string>
</resources> </resources>

View file

@ -332,4 +332,8 @@
<string name="resolution">Resolusi</string> <string name="resolution">Resolusi</string>
<string name="sample_rate">Sampel rate</string> <string name="sample_rate">Sampel rate</string>
<string name="spacing">Jarak</string> <string name="spacing">Jarak</string>
<string name="ends_at">Berakhir pada %1$s</string>
<string name="enter_server_address">Masukkan alamat server</string>
<string name="no_servers_found">Server tidak ditemukan</string>
<string name="subtitle_delay">Penundaan subtitel</string>
</resources> </resources>

View file

@ -371,4 +371,8 @@
<string name="sort_channels_recently_watched">Ordina canali per ultimo guardato</string> <string name="sort_channels_recently_watched">Ordina canali per ultimo guardato</string>
<string name="color_code_programs">Codifica colori per i programmi</string> <string name="color_code_programs">Codifica colori per i programmi</string>
<string name="subtitle_delay">Ritardo sottotitoli</string> <string name="subtitle_delay">Ritardo sottotitoli</string>
<string name="enter_server_address">Inserisci indirizzo server</string>
<string name="no_servers_found">Nessun server trovato</string>
<string name="backdrop_display">Stile backdrop</string>
<string name="ends_at">Termina alle %1$s</string>
</resources> </resources>

View file

@ -1,3 +1,33 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="about">אודות</string>
<string name="active_recordings">הקלטות פעילות</string>
<string name="add_favorite">הוספה למועדפים</string>
<string name="add_server">הוסף שרת</string>
<string name="add_user">הוסף משתמש</string>
<string name="audio">שמע</string>
<string name="birthplace">מקום לידה</string>
<string name="bitrate">קצב נתונים</string>
<string name="cancel_recording">בטל הקלטה</string>
<string name="cancel_series_recording">בטל הקלטת סדרה</string>
<string name="cancel">בטל</string>
<string name="chapters">פרקים</string>
<string name="clear_image_cache">נקה זיכרון מטמון</string>
<string name="collection">אוסף</string>
<string name="collections">אוספים</string>
<string name="commercial">מסחרי</string>
<string name="community_rating">דירוג קהילתי</string>
<string name="compose_error_message_title">אירעה שגיאה! לחץ על הכפתור על מנת לשלוח יומני אירועים לשרת שלך.</string>
<string name="confirm">לאשר</string>
<string name="continue_watching">המשך צפייה</string>
<string name="critic_rating">דירוג מבקרים</string>
<string name="decimal_seconds">%.1f שניות</string>
<string name="default_track">ברירת מחדל</string>
<string name="delete">מחק</string>
<string name="died">מת</string>
<string name="directed_by">בבימויו של %1$s</string>
<string name="director">במאי</string>
<string name="disabled">מושבת</string>
<string name="born">נולד</string>
<string name="choose_stream">בחר %1$s</string>
</resources> </resources>

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="about">Ɣef</string>
<string name="audio">Imesli</string>
<string name="birthplace">Adig n tlalit</string>
<string name="bitrate">Aktum</string>
<string name="cancel">Semmet</string>
<string name="chapters">Ixfawen</string>
<string name="collection">Tagrumma</string>
<string name="collections">Tigrummiwin</string>
<string name="confirm">Serggeg</string>
<string name="default_track">Amezwer</string>
<string name="delete">Kkes</string>
<string name="died">Yemmut</string>
<string name="hide">Ffer-it</string>
<string name="home">Tazwara</string>
<string name="jump_letters">#ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
<string name="library">Tamkarḍit</string>
<string name="restart">Ales asekker</string>
<string name="resume">Kemmel</string>
<string name="save">Sekles</string>
<string name="search">Nadi</string>
<string name="searching">Yettnadi…</string>
<string name="show">Sken-d</string>
<string name="sort_by_name">Isem</string>
<string name="sort_by_random">Agacuṛan</string>
<string name="version">Lqem</string>
<string name="video">Tavidyut</string>
<string name="videos">Tividyutin</string>
<string name="font">Tasefsit</string>
<string name="background">Agilal</string>
<string name="success">Yedda</string>
<string name="year">Aseggas</string>
<string name="automatic">Awurman</string>
<string name="login">Tuqqna</string>
<string name="general">Amatu</string>
<string name="container">Amagbar</string>
<string name="title">Azwel</string>
<string name="codec">Akudak</string>
<string name="profile">Amaɣnu</string>
<string name="level">Aswir</string>
<string name="hdr10">HDR10</string>
<string name="hdr10_plus">HDR10+</string>
<string name="hlg">HLG</string>
<string name="choose_stream">Fren %1$s</string>
</resources>

View file

@ -3,12 +3,12 @@
<string name="about">Om</string> <string name="about">Om</string>
<string name="active_recordings">Aktive opptak</string> <string name="active_recordings">Aktive opptak</string>
<string name="add_favorite">Favoritt</string> <string name="add_favorite">Favoritt</string>
<string name="add_server">Legg til Server</string> <string name="add_server">Legg til server</string>
<string name="add_user">Legg til Brukar</string> <string name="add_user">Legg til brukar</string>
<string name="audio">Lyd</string> <string name="audio">Lyd</string>
<string name="birthplace">Fødestad</string> <string name="birthplace">Fødestad</string>
<string name="bitrate">Bitrate</string> <string name="bitrate">Bitrate</string>
<string name="born">Født</string> <string name="born">Fødd</string>
<string name="cancel_recording">Avbryt opptak</string> <string name="cancel_recording">Avbryt opptak</string>
<string name="cancel_series_recording">Avbryt opptak av serie</string> <string name="cancel_series_recording">Avbryt opptak av serie</string>
<string name="cancel">Avbryt</string> <string name="cancel">Avbryt</string>
@ -21,4 +21,342 @@
<string name="delete">Slett</string> <string name="delete">Slett</string>
<string name="died">Dødd</string> <string name="died">Dødd</string>
<string name="directed_by">Regissert av %1$s</string> <string name="directed_by">Regissert av %1$s</string>
<string name="ac3_supported">Eininga støttar AC3/Dolby Digital</string>
<string name="clear_image_cache">Tøm biletmellomlager</string>
<string name="commercial">Reklame</string>
<string name="community_rating">Brukarvurdering</string>
<string name="compose_error_message_title">Ein feil har oppstått! Trykk på knappen for å sende loggar til servaren din.</string>
<string name="continue_watching">Hald fram med å sjå</string>
<string name="critic_rating">Meldevurdering</string>
<string name="decimal_seconds">%.1f sekund</string>
<string name="director">Regissør</string>
<string name="disabled">Deaktivert</string>
<string name="discovered_servers">Funne servarar</string>
<string name="download_and_update"><![CDATA[Last ned og oppdater]]></string>
<string name="downloading">Lastar ned…</string>
<string name="enabled">Aktivert</string>
<string name="enter_server_url">Oppgi servar-IP eller URL</string>
<string name="enter_server_address">Oppgi servaradresse</string>
<string name="episodes">Episodar</string>
<string name="error_loading_collection">Feil ved lasting av samling %1$s</string>
<string name="external_track">Ekstern</string>
<string name="favorites">Favorittar</string>
<string name="file_size">Storleik</string>
<string name="forced_track">Tvungen</string>
<string name="genres">Sjangrar</string>
<string name="go_to_series">Gå til serie</string>
<string name="go_to">Gå til</string>
<string name="hide_controller_timeout">Gøym avspelingskontrollar</string>
<string name="hide_debug_info">Gøym feilsøkingsinfo</string>
<string name="hide">Gøym</string>
<string name="home">Heim</string>
<string name="immediate">Omgåande</string>
<string name="intro">Intro</string>
<string name="jump_letters">#ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ</string>
<string name="library">Bibliotek</string>
<string name="license_info">Lisensinformasjon</string>
<string name="live_tv">Direkte-TV</string>
<string name="loading">Lastar…</string>
<string name="mark_entire_series_as_played">Marker heile serien som sett?</string>
<string name="mark_entire_series_as_unplayed">Marker heile serien som usett?</string>
<string name="mark_unwatched">Marker som usett</string>
<string name="mark_watched">Marker som sett</string>
<string name="max_bitrate">Maks bitrate</string>
<string name="more_like_this">Meir som dette</string>
<string name="more">Meir</string>
<string name="movies">Filmar</string>
<string name="name">Namn</string>
<string name="next_up">Neste</string>
<string name="no_data">Ingen data</string>
<string name="no_results">Ingen resultat</string>
<string name="no_servers_found">Ingen servarar funne</string>
<string name="no_scheduled_recordings">Ingen planlagde opptak</string>
<string name="no_update_available">Ingen oppdatering tilgjengeleg</string>
<string name="outro">Outro</string>
<string name="path">Filveg</string>
<string name="people">Folk</string>
<string name="play_count">Antall avspelingar</string>
<string name="play_from_here">Spel av herfrå</string>
<string name="play">Spel av</string>
<string name="playback_debug_info">Vis feilsøkingsinfo for avspeling</string>
<string name="playback_speed">Avspelingsfart</string>
<string name="playback">Avspeling</string>
<string name="playlist">Speleliste</string>
<string name="playlists">Spelelister</string>
<string name="preview">Førehandsvising</string>
<string name="profile_specific_settings">Innstillingar for brukarprofil</string>
<string name="queue"></string>
<string name="recap">Samandrag</string>
<string name="recently_added_in">Sist lagt til i %1$s</string>
<string name="recently_added">Sist lagt til</string>
<string name="recently_recorded">Sist tatt opp</string>
<string name="recently_released">Sist utgitt</string>
<string name="recommended">Tilrådd</string>
<string name="record_program">Ta opp program</string>
<string name="record_series">Ta opp serie</string>
<string name="remove_favorite">Fjern favoritt</string>
<string name="restart">Start på nytt</string>
<string name="resume">Hald fram</string>
<string name="save">Lagre</string>
<string name="search_and_download"><![CDATA[Søk og last ned]]></string>
<string name="search">Søk</string>
<string name="searching">Søker…</string>
<string name="select_server">Vel servar</string>
<string name="select_user">Vel brukar</string>
<string name="show_debug_info">Vis feilsøkingsinfo</string>
<string name="show_next_up_when">Vis \'Neste\'</string>
<string name="show">Serie</string>
<string name="shuffle">Tilfeldig rekkjefølgje</string>
<string name="skip">Hopp over</string>
<string name="sort_by_date_added">Dato lagt til</string>
<string name="sort_by_date_episode_added">Dato episode lagt til</string>
<string name="sort_by_date_played">Dato spela av</string>
<string name="sort_by_date_released">Utginningsdato</string>
<string name="sort_by_name">Namn</string>
<string name="sort_by_random">Tilfeldig</string>
<string name="studios">Studio</string>
<string name="submit">Send inn</string>
<string name="subtitle">Undertekst</string>
<string name="subtitles">Undertekstar</string>
<string name="suggestions">Forslag</string>
<string name="switch_servers">Byt servar</string>
<string name="switch_user">Byt brukar</string>
<string name="top_unwatched">Best vurderte usette</string>
<string name="trailer">Trailer</string>
<plurals name="trailers">
<item quantity="one">Trailerar</item>
<item quantity="other"/>
</plurals>
<string name="tv_dvr_schedule">DVR-plan</string>
<string name="tv_guide">Programoversikt</string>
<string name="tv_season">Sesong</string>
<string name="tv_seasons">Sesongar</string>
<string name="tv_shows">TV-seriar</string>
<string name="ui_interface">Grensesnitt</string>
<string name="unknown">Ukjend</string>
<string name="updates">Oppdateringar</string>
<string name="version">Versjon</string>
<string name="video_scale">Videoskalering</string>
<string name="video">Video</string>
<string name="videos">Videoar</string>
<string name="watch_live">Sjå direkte</string>
<string name="years_old">%1$d år gammal</string>
<string name="play_with_transcoding">Spel av med transkoding</string>
<string name="media_information">Mediainformasjon</string>
<string name="show_clock">Vis klokke</string>
<string name="aired_episode_order">Rekkjefølgje etter sendedato</string>
<string name="create_playlist">Lag ny speleliste</string>
<string name="add_to_playlist">Legg til i speleliste</string>
<string name="one_click_pause">Pause med eitt klikk</string>
<string name="one_click_pause_summary_on">Trykk i midten på styreputa for å pause/spele av</string>
<string name="italic_font">Kursiv skrift</string>
<string name="font">Skrifttype</string>
<string name="background">Bakgrunn</string>
<string name="success">Fullført</string>
<string name="official_rating">Aldersgrense</string>
<string name="runtime_sort">Speltid</string>
<string name="extras">Ekstra</string>
<plurals name="other_extras">
<item quantity="one">Anna</item>
<item quantity="other"/>
</plurals>
<plurals name="behind_the_scenes">
<item quantity="one">Bak kulissane</item>
<item quantity="other"/>
</plurals>
<plurals name="theme_songs">
<item quantity="one">Temasongar</item>
<item quantity="other"/>
</plurals>
<plurals name="theme_videos">
<item quantity="one">Temavideoar</item>
<item quantity="other"/>
</plurals>
<plurals name="clips">
<item quantity="one">Klipp</item>
<item quantity="other"/>
</plurals>
<plurals name="deleted_scenes">
<item quantity="one">Sletta sener</item>
<item quantity="other"/>
</plurals>
<plurals name="interviews">
<item quantity="one">Intervju</item>
<item quantity="other"/>
</plurals>
<plurals name="scenes">
<item quantity="one">Scener</item>
<item quantity="other"/>
</plurals>
<plurals name="samples">
<item quantity="one">Smakebitar</item>
<item quantity="other"/>
</plurals>
<plurals name="featurettes">
<item quantity="one">Bakomfilmar</item>
<item quantity="other"/>
</plurals>
<plurals name="shorts">
<item quantity="one">Kortfilmar</item>
<item quantity="other"/>
</plurals>
<string name="advanced_settings">Avanserte innstillingar</string>
<string name="advanced_ui">Avansert grensesnitt</string>
<string name="app_theme">App-tema</string>
<string name="auto_check_for_updates">Sjekk automatisk etter oppdateringar</string>
<string name="auto_play_next_delay">Forseinking før neste vert spela av</string>
<string name="auto_play_next">Spel neste automatisk</string>
<string name="check_for_updates">Sjekk etter oppdateringar</string>
<string name="combine_continue_next_summary">Gjeld berre TV-seriar</string>
<string name="combine_continue_next">Slå saman \'Hald fram med å sjå\' og \'Neste\'</string>
<string name="direct_play_ass">Direkteavspeling av ASS-undertekstar</string>
<string name="direct_play_pgs">Direkteavspeling av PGS-undertekstar</string>
<string name="downmix_stereo">Alltid nedmiks til stereo</string>
<string name="ffmpeg_extension_pref">Bruk FFmpeg-dekodermodul</string>
<string name="global_content_scale">Standard innhaldsskalering</string>
<string name="install_update">Installer oppdatering</string>
<string name="installed_version">Installert versjon</string>
<string name="max_homepage_items">Maks element på rader på heimskjermen</string>
<string name="nav_drawer_pins">Tilpass element i navigasjonsskuffa</string>
<string name="nav_drawer_switch_on_focus_summary_off">Klikk for å byte side</string>
<string name="nav_drawer_switch_on_focus">Byt side i navigasjonsskuffa ved fokus</string>
<string name="pass_out_protection">Svevn-beskyttelse</string>
<string name="play_theme_music">Spel temamusikk</string>
<string name="playback_overrides">Overstyring av avspeling</string>
<string name="remember_selected_tab">Hugs valde faner</string>
<string name="rewatch_next_up">Tillat gjensjåing i \'Neste\'</string>
<string name="seek_bar_steps">Steglengde for søkelinje</string>
<string name="send_app_logs_summary">Nyttig for feilsøking</string>
<string name="send_app_logs">Send app-loggar til gjeldande servar</string>
<string name="send_crash_reports_summary">Vil prøve å sende til sist tilkopla servar</string>
<string name="send_crash_reports">Send krasjrapportar</string>
<string name="settings">Innstillingar</string>
<string name="skip_back_on_resume_preference">Hopp tilbake ved framhald av avspeling</string>
<string name="skip_back_preference">Hopp tilbake</string>
<string name="skip_commercials_behavior">Handling for overhopping av reklame</string>
<string name="skip_forward_preference">Hopp framover</string>
<string name="skip_intro_behavior">Handling for overhopping av intro</string>
<string name="skip_outro_behavior">Handling for overhopping av outro</string>
<string name="skip_previews_behavior">Handling for overhopping av førehandsvising</string>
<string name="skip_recap_behavior">Handling for overhopping av samandrag</string>
<string name="update_available">Oppdatering tilgjengeleg</string>
<string name="update_url_summary">URL for sjekk av app-oppdateringar</string>
<string name="update_url">URL for oppdatering</string>
<string name="font_size">Skriftstorleik</string>
<string name="font_color">Skriftfarge</string>
<string name="font_opacity">Gjennomsiktigheit for skrift</string>
<string name="edge_style">Kantstil</string>
<string name="edge_color">Kantfarge</string>
<string name="background_opacity">Gjennomsiktigheit for bakgrunn</string>
<string name="background_style">Bakgrunnsstil</string>
<string name="subtitle_style">Stil for undertekst</string>
<string name="background_color">Bakgrunnsfarge</string>
<string name="reset">Nullstill</string>
<string name="bold_font">Feit skrift</string>
<string name="player_backend">Avspelingsmotor</string>
<string name="mpv_hardware_decoding">MPV: Bruk maskinvaredekoding</string>
<string name="disable_if_crash">Deaktiver dersom appen krasjar</string>
<string name="mpv_options">MPV-val</string>
<string name="exoplayer_options">ExoPlayer-val</string>
<string name="skip_segment_unknown">Hopp over segment</string>
<string name="skip_segment_commercial">Hopp over reklame</string>
<string name="skip_segment_preview">Hopp over førehandsvising</string>
<string name="skip_segment_recap">Hopp over samandrag</string>
<string name="skip_segment_outro">Hopp over outro</string>
<string name="skip_segment_intro">Hopp over intro</string>
<string name="played">Sett</string>
<string name="filter">Filter</string>
<string name="year">År</string>
<string name="decade">Tiår</string>
<string name="remove">Fjern</string>
<string name="dolby_vision">Dolby Vision</string>
<string name="dolby_atmos">Dolby Atmos</string>
<string name="mpv_use_gpu_next">MPV: Bruk gpu-next</string>
<string name="mpv_conf">Rediger mpv.conf</string>
<string name="discard_change">Forkast endringar?</string>
<string name="subtitle_margin">Marg</string>
<string name="verbose_logging">Detaljert logging</string>
<string name="enter_pin">Tast inn PIN</string>
<string name="sign_in_auto">Logg inn automatisk</string>
<string name="use_server_credentials">Logg inn via servar</string>
<string name="press_enter_to_confirm">Trykk i midten for å bekrefte</string>
<string name="require_pin_code">Krev PIN for profil</string>
<string name="confirm_pin">Bekreft PIN</string>
<string name="incorrect">Feil</string>
<string name="pin_too_short">PIN må vere 4 tastar eller lenger</string>
<string name="will_remove_pin">Vil fjerne PIN</string>
<string name="image_cache_size">Storleik på biletmellomlager (MB)</string>
<string name="view_options">Visingsval</string>
<string name="columns">Kolonner</string>
<string name="spacing">Mellomrom</string>
<string name="aspect_ratio">Bileteforhold</string>
<string name="show_details">Vis detaljar</string>
<string name="image_type">Bilettype</string>
<string name="cast_and_crew"><![CDATA[Skodespelarar og medarbeidarar]]></string>
<string name="guest_stars">Gjesteroller</string>
<string name="edge_size">Kantstorleik</string>
<string name="refresh_rate_switching">Byting av bildeoppdateringsfrekvens</string>
<string name="automatic">Automatisk</string>
<string name="username_or_password">Bruk brukarnamn/passord</string>
<string name="login">Logg inn</string>
<string name="general">Generelt</string>
<string name="container">Containar</string>
<string name="title">Tittel</string>
<string name="codec">Kodek</string>
<string name="profile">Profil</string>
<string name="level">Nivå</string>
<string name="resolution">Oppløysing</string>
<string name="anamorphic">Anamorfisk</string>
<string name="interlaced">Linjefletta</string>
<string name="framerate">Bildefrekvens</string>
<string name="bit_depth">Bit-dybde</string>
<string name="video_range">Videoområde</string>
<string name="video_range_type">Type videoområde</string>
<string name="color_space">Fargerom</string>
<string name="color_transfer">Fargeoverføring</string>
<string name="color_primaries">Fargeprimærar</string>
<string name="pixel_format">Pikselformat</string>
<string name="ref_frames">Referanserammer</string>
<string name="nal">NAL</string>
<string name="language">Språk</string>
<string name="layout">Layout</string>
<string name="channels">Kanalar</string>
<string name="sample_rate">Samplingsfrekvens</string>
<string name="avc">AVC</string>
<string name="yes">Ja</string>
<string name="no">Nei</string>
<string name="sdr">SDR</string>
<string name="hdr">HDR</string>
<string name="hdr10">HDR10</string>
<string name="hdr10_plus">HDR10+</string>
<string name="hlg">HLG</string>
<string name="bit_unit">bit</string>
<string name="sample_rate_unit">Hz</string>
<string name="show_titles">Vis titlar</string>
<string name="live_tv_repeat">Repeter</string>
<string name="favorite_channels_at_beginning">Vis favorittkanalar først</string>
<string name="sort_channels_recently_watched">Sorter kanalar etter sist sett</string>
<string name="color_code_programs">Fargekod program</string>
<string name="subtitle_delay">Undertekstforseinking</string>
<string name="backdrop_display">Bakgrunnsstil</string>
<string name="none">Ingen</string>
<string name="subtitle_download_too_long">Nedlastinga tek lang tid, du må kanskje starte avspelinga på nytt</string>
<plurals name="downloads">
<item quantity="one">%s nedlasting</item>
<item quantity="other">%s nedlastingar</item>
</plurals>
<plurals name="hours">
<item quantity="one">%d time</item>
<item quantity="other">%d timar</item>
</plurals>
<plurals name="items">
<item quantity="one">%d element</item>
<item quantity="other">%d elementer</item>
</plurals>
<plurals name="seconds">
<item quantity="one">%d sekund</item>
<item quantity="other">%d sekund</item>
</plurals>
<string name="nav_drawer_pins_summary">Vel standardelementa som skal visast, andre vert skjulde</string>
<string name="ends_at">Sluttar %1$s</string>
</resources> </resources>

View file

@ -371,4 +371,8 @@
<string name="sort_channels_recently_watched">Ordenar canais por visualizações recentes</string> <string name="sort_channels_recently_watched">Ordenar canais por visualizações recentes</string>
<string name="color_code_programs">Codificação de cores para os programas</string> <string name="color_code_programs">Codificação de cores para os programas</string>
<string name="subtitle_delay">Atraso da legenda</string> <string name="subtitle_delay">Atraso da legenda</string>
<string name="enter_server_address">Insira o endereço do servidor</string>
<string name="no_servers_found">Nenhum servidor encontrado</string>
<string name="backdrop_display">Estilo de fundo</string>
<string name="ends_at">Termina às %1$s</string>
</resources> </resources>

View file

@ -339,4 +339,8 @@
<string name="sort_channels_recently_watched">按最近观看的频道排序</string> <string name="sort_channels_recently_watched">按最近观看的频道排序</string>
<string name="color_code_programs">节目颜色标记</string> <string name="color_code_programs">节目颜色标记</string>
<string name="subtitle_delay">字幕延迟</string> <string name="subtitle_delay">字幕延迟</string>
<string name="enter_server_address">输入服务器地址</string>
<string name="no_servers_found">未找到服务器</string>
<string name="backdrop_display">背景幕样式</string>
<string name="ends_at">结束于 %1$s</string>
</resources> </resources>

View file

@ -183,8 +183,8 @@
<string name="jump_letters">#ABCDEFGHIJKLMNOPQRSTUVWXYZ</string> <string name="jump_letters">#ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
<string name="license_info">授權資訊</string> <string name="license_info">授權資訊</string>
<string name="live_tv">電視直播</string> <string name="live_tv">電視直播</string>
<string name="mark_entire_series_as_played">標記整部電視劇為已觀看?</string> <string name="mark_entire_series_as_played">標記整部劇為已觀看?</string>
<string name="mark_entire_series_as_unplayed">標記整部電視劇為未觀看?</string> <string name="mark_entire_series_as_unplayed">標記整部劇為未觀看?</string>
<string name="mark_unwatched">標記為未觀看</string> <string name="mark_unwatched">標記為未觀看</string>
<string name="mark_watched">標記為已觀看</string> <string name="mark_watched">標記為已觀看</string>
<string name="max_bitrate">最大位元率</string> <string name="max_bitrate">最大位元率</string>
@ -299,4 +299,48 @@
<string name="username_or_password">使用帳號/密碼</string> <string name="username_or_password">使用帳號/密碼</string>
<string name="login">登入</string> <string name="login">登入</string>
<string name="subtitle_delay">字幕同步</string> <string name="subtitle_delay">字幕同步</string>
<string name="show_titles">顯示標題</string>
<string name="enter_server_address">輸入伺服器位址</string>
<string name="no_servers_found">未找到伺服器</string>
<string name="backdrop_display">背景圖樣式</string>
<string name="color_code_programs">節目顏色標記</string>
<string name="favorite_channels_at_beginning">優先顯示最愛頻道</string>
<string name="sort_channels_recently_watched">按最近觀看的頻道排序</string>
<string name="ends_at">結束於 %1$s</string>
<string name="general">一般</string>
<string name="container">封裝格式</string>
<string name="title">標題</string>
<string name="codec">編碼</string>
<string name="resolution">解析度</string>
<string name="anamorphic">變形寬銀幕</string>
<string name="level">等級</string>
<string name="profile">編碼設定檔</string>
<string name="language">語言</string>
<string name="yes"></string>
<string name="no"></string>
<string name="sdr">SDR</string>
<string name="hdr">HDR</string>
<string name="hdr10">HDR10</string>
<string name="hdr10_plus">HDR10+</string>
<string name="hlg">HLG</string>
<string name="bit_unit">bit</string>
<string name="sample_rate_unit">Hz</string>
<string name="live_tv_repeat">重複</string>
<string name="interlaced">隔行掃描</string>
<string name="ref_frames">參考影格數</string>
<string name="framerate">影格率</string>
<string name="nal">NAL</string>
<string name="bit_depth">位元深度</string>
<string name="avc">AVC</string>
<string name="layout">聲道配置</string>
<string name="channels">聲道數</string>
<string name="video_range">動態範圍</string>
<string name="video_range_type">動態範圍類型</string>
<string name="color_space">色彩空間</string>
<string name="color_transfer">色彩轉換</string>
<string name="color_primaries">色彩原色</string>
<string name="pixel_format">像素格式</string>
<string name="sample_rate">取樣率</string>
<string name="seek_bar_steps">進度條跳轉值</string>
<string name="playback_overrides">播放覆蓋設定</string>
</resources> </resources>

View file

@ -399,6 +399,9 @@
<string name="subtitle_delay">Subtitle delay</string> <string name="subtitle_delay">Subtitle delay</string>
<string name="backdrop_display">Backdrop style</string> <string name="backdrop_display">Backdrop style</string>
<string name="resolution_switching">Resolution switching</string> <string name="resolution_switching">Resolution switching</string>
<string name="local">Local</string>
<string name="play_trailer">Play trailer</string>
<string name="no_trailers">No trailers</string>
<string name="discover">Discover</string> <string name="discover">Discover</string>
<string name="request">Request</string> <string name="request">Request</string>