diff --git a/app/src/main/java/com/github/damontecres/dolphin/hilt/AppModule.kt b/app/src/main/java/com/github/damontecres/dolphin/hilt/AppModule.kt index 1d4f4706..775422da 100644 --- a/app/src/main/java/com/github/damontecres/dolphin/hilt/AppModule.kt +++ b/app/src/main/java/com/github/damontecres/dolphin/hilt/AppModule.kt @@ -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 diff --git a/app/src/main/java/com/github/damontecres/dolphin/ui/playback/Models.kt b/app/src/main/java/com/github/damontecres/dolphin/ui/playback/Models.kt index d58583b6..cd5d1573 100644 --- a/app/src/main/java/com/github/damontecres/dolphin/ui/playback/Models.kt +++ b/app/src/main/java/com/github/damontecres/dolphin/ui/playback/Models.kt @@ -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() = diff --git a/app/src/main/java/com/github/damontecres/dolphin/ui/playback/PlaybackViewModel.kt b/app/src/main/java/com/github/damontecres/dolphin/ui/playback/PlaybackViewModel.kt index 1ab4658d..a9ea5e3c 100644 --- a/app/src/main/java/com/github/damontecres/dolphin/ui/playback/PlaybackViewModel.kt +++ b/app/src/main/java/com/github/damontecres/dolphin/ui/playback/PlaybackViewModel.kt @@ -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 { 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,