mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Better choose audio/subtitles by user preferences
This commit is contained in:
parent
dd08bb3e13
commit
ca7dd3797a
3 changed files with 86 additions and 17 deletions
|
|
@ -12,6 +12,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import org.jellyfin.sdk.Jellyfin
|
import org.jellyfin.sdk.Jellyfin
|
||||||
|
import org.jellyfin.sdk.api.client.util.AuthorizationHeaderBuilder
|
||||||
import org.jellyfin.sdk.api.okhttp.OkHttpFactory
|
import org.jellyfin.sdk.api.okhttp.OkHttpFactory
|
||||||
import org.jellyfin.sdk.createJellyfin
|
import org.jellyfin.sdk.createJellyfin
|
||||||
import org.jellyfin.sdk.model.ClientInfo
|
import org.jellyfin.sdk.model.ClientInfo
|
||||||
|
|
@ -30,6 +31,27 @@ annotation class StandardOkHttpClient
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
object AppModule {
|
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
|
@StandardOkHttpClient
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
|
|
@ -46,6 +68,8 @@ object AppModule {
|
||||||
fun authOkHttpClient(
|
fun authOkHttpClient(
|
||||||
serverRepository: ServerRepository,
|
serverRepository: ServerRepository,
|
||||||
@StandardOkHttpClient okHttpClient: OkHttpClient,
|
@StandardOkHttpClient okHttpClient: OkHttpClient,
|
||||||
|
clientInfo: ClientInfo,
|
||||||
|
deviceInfo: DeviceInfo,
|
||||||
) = okHttpClient
|
) = okHttpClient
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.addInterceptor {
|
.addInterceptor {
|
||||||
|
|
@ -56,7 +80,13 @@ object AppModule {
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.addHeader(
|
.addHeader(
|
||||||
"Authorization",
|
"Authorization",
|
||||||
"MediaBrowser Token=\"$token\"",
|
AuthorizationHeaderBuilder.buildHeader(
|
||||||
|
clientName = clientInfo.name,
|
||||||
|
clientVersion = clientInfo.version,
|
||||||
|
deviceId = deviceInfo.id,
|
||||||
|
deviceName = deviceInfo.name,
|
||||||
|
accessToken = serverRepository.currentUser?.accessToken,
|
||||||
|
),
|
||||||
).build()
|
).build()
|
||||||
}
|
}
|
||||||
it.proceed(newRequest ?: request)
|
it.proceed(newRequest ?: request)
|
||||||
|
|
@ -73,19 +103,13 @@ object AppModule {
|
||||||
fun jellyfin(
|
fun jellyfin(
|
||||||
okHttpFactory: OkHttpFactory,
|
okHttpFactory: OkHttpFactory,
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
|
clientInfo: ClientInfo,
|
||||||
|
deviceInfo: DeviceInfo,
|
||||||
): Jellyfin =
|
): Jellyfin =
|
||||||
createJellyfin {
|
createJellyfin {
|
||||||
this.context = context
|
this.context = context
|
||||||
clientInfo =
|
this.clientInfo = clientInfo
|
||||||
ClientInfo(
|
this.deviceInfo = deviceInfo
|
||||||
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),
|
|
||||||
)
|
|
||||||
apiClientFactory = okHttpFactory
|
apiClientFactory = okHttpFactory
|
||||||
socketConnectionFactory = okHttpFactory
|
socketConnectionFactory = okHttpFactory
|
||||||
minimumServerVersion = Jellyfin.minimumVersion
|
minimumServerVersion = Jellyfin.minimumVersion
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ data class SubtitleStream(
|
||||||
val codec: String?,
|
val codec: String?,
|
||||||
val codecTag: String?,
|
val codecTag: String?,
|
||||||
val external: Boolean,
|
val external: Boolean,
|
||||||
|
val forced: Boolean,
|
||||||
|
val default: Boolean,
|
||||||
) {
|
) {
|
||||||
val displayName: String
|
val displayName: String
|
||||||
get() =
|
get() =
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import org.jellyfin.sdk.model.api.DeviceProfile
|
||||||
import org.jellyfin.sdk.model.api.MediaSourceInfo
|
import org.jellyfin.sdk.model.api.MediaSourceInfo
|
||||||
import org.jellyfin.sdk.model.api.MediaStreamType
|
import org.jellyfin.sdk.model.api.MediaStreamType
|
||||||
import org.jellyfin.sdk.model.api.PlaybackInfoDto
|
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.api.TrickplayInfo
|
||||||
import org.jellyfin.sdk.model.extensions.ticks
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
||||||
|
|
@ -103,7 +104,6 @@ class PlaybackViewModel
|
||||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||||
val base = item?.data ?: api.userLibraryApi.getItem(itemId).content
|
val base = item?.data ?: api.userLibraryApi.getItem(itemId).content
|
||||||
dto = base
|
dto = base
|
||||||
|
|
||||||
val title =
|
val title =
|
||||||
if (base.type == BaseItemKind.EPISODE) {
|
if (base.type == BaseItemKind.EPISODE) {
|
||||||
base.seriesName
|
base.seriesName
|
||||||
|
|
@ -143,6 +143,8 @@ class PlaybackViewModel
|
||||||
it.codec,
|
it.codec,
|
||||||
it.codecTag,
|
it.codecTag,
|
||||||
it.isExternal,
|
it.isExternal,
|
||||||
|
it.isForced,
|
||||||
|
it.isDefault,
|
||||||
)
|
)
|
||||||
}.orEmpty()
|
}.orEmpty()
|
||||||
val audioStreams =
|
val audioStreams =
|
||||||
|
|
@ -160,12 +162,54 @@ class PlaybackViewModel
|
||||||
)
|
)
|
||||||
}?.sortedWith(compareBy<AudioStream> { it.language }.thenByDescending { it.channels })
|
}?.sortedWith(compareBy<AudioStream> { it.language }.thenByDescending { it.channels })
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
// TODO use preferences to select audio/subtitle
|
|
||||||
// TODO audio selection based on channel layout, etc
|
// TODO audio selection based on channel layout, etc
|
||||||
val subtitleIndex = subtitleStreams.firstOrNull { it.language == "eng" }?.index
|
val audioLanguage = preferences.userConfig.audioLanguagePreference
|
||||||
val audioIndex =
|
val audioIndex =
|
||||||
audioStreams.firstOrNull { it.language == "eng" }?.index
|
if (audioLanguage != null) {
|
||||||
?: audioStreams.firstOrNull()?.index
|
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("base.mediaStreams=${base.mediaStreams}")
|
||||||
Timber.v("subtitleTracks=$subtitleStreams")
|
Timber.v("subtitleTracks=$subtitleStreams")
|
||||||
|
|
@ -213,7 +257,6 @@ class PlaybackViewModel
|
||||||
api.mediaInfoApi
|
api.mediaInfoApi
|
||||||
.getPostedPlaybackInfo(
|
.getPostedPlaybackInfo(
|
||||||
itemId,
|
itemId,
|
||||||
// TODO device profile, etc
|
|
||||||
PlaybackInfoDto(
|
PlaybackInfoDto(
|
||||||
startTimeTicks = null,
|
startTimeTicks = null,
|
||||||
deviceProfile = deviceProfile,
|
deviceProfile = deviceProfile,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue