Better choose audio/subtitles by user preferences

This commit is contained in:
Damontecres 2025-10-05 17:14:24 -04:00
parent dd08bb3e13
commit ca7dd3797a
No known key found for this signature in database
3 changed files with 86 additions and 17 deletions

View file

@ -12,6 +12,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import org.jellyfin.sdk.Jellyfin
import org.jellyfin.sdk.api.client.util.AuthorizationHeaderBuilder
import org.jellyfin.sdk.api.okhttp.OkHttpFactory
import org.jellyfin.sdk.createJellyfin
import org.jellyfin.sdk.model.ClientInfo
@ -30,6 +31,27 @@ annotation class StandardOkHttpClient
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun clientInfo(): ClientInfo =
ClientInfo(
name = "Dolphin",
version = BuildConfig.VERSION_NAME,
)
@Provides
@Singleton
fun deviceInfo(
@ApplicationContext context: Context,
): DeviceInfo =
DeviceInfo(
id = @SuppressLint("HardwareIds") Settings.Secure.getString(
context.contentResolver,
Settings.Secure.ANDROID_ID,
),
name = Settings.Global.getString(context.contentResolver, Settings.Global.DEVICE_NAME),
)
@StandardOkHttpClient
@Provides
@Singleton
@ -46,6 +68,8 @@ object AppModule {
fun authOkHttpClient(
serverRepository: ServerRepository,
@StandardOkHttpClient okHttpClient: OkHttpClient,
clientInfo: ClientInfo,
deviceInfo: DeviceInfo,
) = okHttpClient
.newBuilder()
.addInterceptor {
@ -56,7 +80,13 @@ object AppModule {
.newBuilder()
.addHeader(
"Authorization",
"MediaBrowser Token=\"$token\"",
AuthorizationHeaderBuilder.buildHeader(
clientName = clientInfo.name,
clientVersion = clientInfo.version,
deviceId = deviceInfo.id,
deviceName = deviceInfo.name,
accessToken = serverRepository.currentUser?.accessToken,
),
).build()
}
it.proceed(newRequest ?: request)
@ -73,19 +103,13 @@ object AppModule {
fun jellyfin(
okHttpFactory: OkHttpFactory,
@ApplicationContext context: Context,
clientInfo: ClientInfo,
deviceInfo: DeviceInfo,
): Jellyfin =
createJellyfin {
this.context = context
clientInfo =
ClientInfo(
name = "Dolphin",
version = BuildConfig.VERSION_NAME,
)
deviceInfo =
DeviceInfo(
id = @SuppressLint("HardwareIds") Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID),
name = Settings.Global.getString(context.contentResolver, Settings.Global.DEVICE_NAME),
)
this.clientInfo = clientInfo
this.deviceInfo = deviceInfo
apiClientFactory = okHttpFactory
socketConnectionFactory = okHttpFactory
minimumServerVersion = Jellyfin.minimumVersion

View file

@ -7,6 +7,8 @@ data class SubtitleStream(
val codec: String?,
val codecTag: String?,
val external: Boolean,
val forced: Boolean,
val default: Boolean,
) {
val displayName: String
get() =

View file

@ -39,6 +39,7 @@ import org.jellyfin.sdk.model.api.DeviceProfile
import org.jellyfin.sdk.model.api.MediaSourceInfo
import org.jellyfin.sdk.model.api.MediaStreamType
import org.jellyfin.sdk.model.api.PlaybackInfoDto
import org.jellyfin.sdk.model.api.SubtitlePlaybackMode
import org.jellyfin.sdk.model.api.TrickplayInfo
import org.jellyfin.sdk.model.extensions.ticks
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
@ -103,7 +104,6 @@ class PlaybackViewModel
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
val base = item?.data ?: api.userLibraryApi.getItem(itemId).content
dto = base
val title =
if (base.type == BaseItemKind.EPISODE) {
base.seriesName
@ -143,6 +143,8 @@ class PlaybackViewModel
it.codec,
it.codecTag,
it.isExternal,
it.isForced,
it.isDefault,
)
}.orEmpty()
val audioStreams =
@ -160,12 +162,54 @@ class PlaybackViewModel
)
}?.sortedWith(compareBy<AudioStream> { it.language }.thenByDescending { it.channels })
.orEmpty()
// TODO use preferences to select audio/subtitle
// TODO audio selection based on channel layout, etc
val subtitleIndex = subtitleStreams.firstOrNull { it.language == "eng" }?.index
val audioLanguage = preferences.userConfig.audioLanguagePreference
val audioIndex =
audioStreams.firstOrNull { it.language == "eng" }?.index
?: audioStreams.firstOrNull()?.index
if (audioLanguage != null) {
audioStreams.firstOrNull { it.language == audioLanguage }?.index
?: audioStreams.firstOrNull()?.index
} else {
audioStreams.firstOrNull()?.index
}
val subtitleMode = preferences.userConfig.subtitleMode
val subtitleLanguage = preferences.userConfig.subtitleLanguagePreference
val subtitleIndex =
when (subtitleMode) {
SubtitlePlaybackMode.ALWAYS -> {
if (subtitleLanguage != null) {
subtitleStreams.firstOrNull { it.language == subtitleLanguage }?.index
} else {
subtitleStreams.firstOrNull()?.index
}
}
SubtitlePlaybackMode.ONLY_FORCED ->
if (subtitleLanguage != null) {
subtitleStreams.firstOrNull { it.language == subtitleLanguage && it.forced }?.index
} else {
subtitleStreams.firstOrNull { it.forced }?.index
}
SubtitlePlaybackMode.SMART -> {
if (audioLanguage != null && subtitleLanguage != null && audioLanguage != subtitleLanguage) {
subtitleStreams.firstOrNull { it.language == subtitleLanguage }?.index
} else {
null
}
}
SubtitlePlaybackMode.DEFAULT -> {
// TODO check for language?
(
subtitleStreams.firstOrNull { it.default && it.forced }
?: subtitleStreams.firstOrNull { it.default }
?: subtitleStreams.firstOrNull { it.forced }
)?.index
}
SubtitlePlaybackMode.NONE -> null
}
Timber.v("base.mediaStreams=${base.mediaStreams}")
Timber.v("subtitleTracks=$subtitleStreams")
@ -213,7 +257,6 @@ class PlaybackViewModel
api.mediaInfoApi
.getPostedPlaybackInfo(
itemId,
// TODO device profile, etc
PlaybackInfoDto(
startTimeTicks = null,
deviceProfile = deviceProfile,