mirror of
https://github.com/jellyfin/jellyfin-android.git
synced 2026-10-26 16:45:26 +00:00
Propagate max streaming bitrate from web apsettings to native player
This commit is contained in:
1 parent
83dcc6ece4
commit
bc53439b8f
8 files changed
+48
-10
No files matched your search
@@ -1,9 +1,10 @@
|
||||
export class ExoPlayerPlugin {
|
||||
constructor({ events, playbackManager, loading }) {
|
||||
constructor({ events, playbackManager, appSettings, loading }) {
|
||||
window['ExoPlayer'] = this;
|
||||
|
||||
this.events = events;
|
||||
this.playbackManager = playbackManager;
|
||||
this.appSettings = appSettings;
|
||||
this.loading = loading;
|
||||
|
||||
this.name = 'ExoPlayer';
|
||||
@@ -26,8 +27,13 @@ export class ExoPlayerPlugin {
|
||||
options.ids = options.items.map(item => item.Id);
|
||||
delete options.items;
|
||||
|
||||
const preferences = {
|
||||
maxStreamingBitrateLocal: this.appSettings.maxStreamingBitrate(true, 'Video'),
|
||||
maxStreamingBitrateRemote: this.appSettings.maxStreamingBitrate(false, 'Video'),
|
||||
};
|
||||
|
||||
this._paused = false;
|
||||
this._nativePlayer.loadPlayer(JSON.stringify(options));
|
||||
this._nativePlayer.loadPlayer(JSON.stringify(options), JSON.stringify(preferences));
|
||||
this.loading.hide();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ package org.jellyfin.mobile.bridge
|
||||
|
||||
import android.webkit.JavascriptInterface
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.jellyfin.mobile.app.AppPreferences
|
||||
import org.jellyfin.mobile.events.ActivityEvent
|
||||
import org.jellyfin.mobile.events.ActivityEventHandler
|
||||
import org.jellyfin.mobile.player.interaction.PlayOptions
|
||||
import org.jellyfin.mobile.player.interaction.PlayerEvent
|
||||
import org.jellyfin.mobile.player.interaction.PlayerWebPreferences
|
||||
import org.jellyfin.mobile.settings.VideoPlayerType
|
||||
import org.jellyfin.sdk.model.extensions.ticks
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
@@ -22,10 +24,11 @@ class NativePlayer(
|
||||
fun isEnabled() = appPreferences.videoPlayerType == VideoPlayerType.EXO_PLAYER
|
||||
|
||||
@JavascriptInterface
|
||||
fun loadPlayer(args: String) {
|
||||
PlayOptions.fromJson(args)?.let { options ->
|
||||
activityEventHandler.emit(ActivityEvent.LaunchNativePlayer(options))
|
||||
}
|
||||
fun loadPlayer(playOptionsRaw: String, preferencesRaw: String) {
|
||||
val playOptions = PlayOptions.fromJson(playOptionsRaw) ?: return
|
||||
val preferences = Json.decodeFromString<PlayerWebPreferences>(preferencesRaw)
|
||||
|
||||
activityEventHandler.emit(ActivityEvent.LaunchNativePlayer(playOptions, preferences))
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package org.jellyfin.mobile.events
|
||||
|
||||
import org.jellyfin.mobile.player.interaction.PlayOptions
|
||||
import org.jellyfin.mobile.player.interaction.PlayerWebPreferences
|
||||
import org.json.JSONArray
|
||||
import java.util.UUID
|
||||
|
||||
sealed class ActivityEvent {
|
||||
class ChangeFullscreen(val isFullscreen: Boolean) : ActivityEvent()
|
||||
class LaunchNativePlayer(val playOptions: PlayOptions) : ActivityEvent()
|
||||
class LaunchNativePlayer(val playOptions: PlayOptions, val preferences: PlayerWebPreferences? = null) : ActivityEvent()
|
||||
class OpenUrl(val uri: String, val grantReadPermission: Boolean = false) : ActivityEvent()
|
||||
class DownloadItems(val itemIds: Collection<UUID>) : ActivityEvent()
|
||||
class CastMessage(val action: String, val args: JSONArray) : ActivityEvent()
|
||||
|
||||
@@ -62,6 +62,7 @@ class ActivityEventHandler(
|
||||
is ActivityEvent.LaunchNativePlayer -> {
|
||||
val args = Bundle().apply {
|
||||
putParcelable(Constants.EXTRA_MEDIA_PLAY_OPTIONS, event.playOptions)
|
||||
putParcelable(Constants.EXTRA_WEB_PREFERENCES, event.preferences)
|
||||
}
|
||||
supportFragmentManager.addFragment<PlayerFragment>(args)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.jellyfin.mobile.player.interaction
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Parcelize
|
||||
data class PlayerWebPreferences(
|
||||
val maxStreamingBitrateLocal: Int,
|
||||
val maxStreamingBitrateRemote: Int,
|
||||
) : Parcelable
|
||||
@@ -17,6 +17,7 @@ import org.jellyfin.mobile.player.PlayerException
|
||||
import org.jellyfin.mobile.player.PlayerViewModel
|
||||
import org.jellyfin.mobile.player.deviceprofile.DeviceProfileBuilder
|
||||
import org.jellyfin.mobile.player.interaction.PlayOptions
|
||||
import org.jellyfin.mobile.player.interaction.PlayerWebPreferences
|
||||
import org.jellyfin.mobile.player.source.ExternalSubtitleStream
|
||||
import org.jellyfin.mobile.player.source.JellyfinMediaSource
|
||||
import org.jellyfin.mobile.player.source.LocalJellyfinMediaSource
|
||||
@@ -24,6 +25,7 @@ import org.jellyfin.mobile.player.source.MediaSourceResolver
|
||||
import org.jellyfin.mobile.player.source.PlaybackDetails
|
||||
import org.jellyfin.mobile.player.source.RemoteJellyfinMediaSource
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.systemApi
|
||||
import org.jellyfin.sdk.api.client.extensions.videosApi
|
||||
import org.jellyfin.sdk.api.operations.VideosApi
|
||||
import org.jellyfin.sdk.model.api.MediaProtocol
|
||||
@@ -67,7 +69,7 @@ class QueueManager(
|
||||
*
|
||||
* @return an error of type [PlayerException] or null on success.
|
||||
*/
|
||||
suspend fun initializePlaybackQueue(playOptions: PlayOptions): PlayerException? {
|
||||
suspend fun initializePlaybackQueue(playOptions: PlayOptions, preferences: PlayerWebPreferences? = null): PlayerException? {
|
||||
currentQueue = playOptions.ids
|
||||
currentQueueIndex = playOptions.startIndex
|
||||
resetPlaybackFallback()
|
||||
@@ -77,6 +79,16 @@ class QueueManager(
|
||||
else -> playOptions.mediaSourceId?.toUUIDOrNull()
|
||||
} ?: return PlayerException.InvalidPlayOptions()
|
||||
|
||||
val maxStreamingBitrate = preferences?.let {
|
||||
withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
val endpoint by apiClient.systemApi.getEndpointInfo()
|
||||
if (endpoint.isInNetwork) it.maxStreamingBitrateLocal
|
||||
else it.maxStreamingBitrateRemote
|
||||
}
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
when (playOptions.playFromDownloads) {
|
||||
true -> playOptions.mediaSourceId?.let {
|
||||
startDownloadPlayback(
|
||||
@@ -87,7 +99,7 @@ class QueueManager(
|
||||
else -> startRemotePlayback(
|
||||
itemId = itemId,
|
||||
mediaSourceId = playOptions.mediaSourceId,
|
||||
maxStreamingBitrate = null,
|
||||
maxStreamingBitrate = maxStreamingBitrate,
|
||||
startTime = playOptions.startPosition,
|
||||
audioStreamIndex = playOptions.audioStreamIndex,
|
||||
subtitleStreamIndex = playOptions.subtitleStreamIndex,
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.jellyfin.mobile.databinding.FragmentPlayerBinding
|
||||
import org.jellyfin.mobile.player.PlayerException
|
||||
import org.jellyfin.mobile.player.PlayerViewModel
|
||||
import org.jellyfin.mobile.player.interaction.PlayOptions
|
||||
import org.jellyfin.mobile.player.interaction.PlayerWebPreferences
|
||||
import org.jellyfin.mobile.player.ui.playermenuhelper.PlayerMenuHelper
|
||||
import org.jellyfin.mobile.utils.AndroidVersion
|
||||
import org.jellyfin.mobile.utils.BackPressInterceptor
|
||||
@@ -136,11 +137,12 @@ class PlayerFragment : Fragment(), BackPressInterceptor {
|
||||
lifecycleScope.launch {
|
||||
val context = requireContext()
|
||||
val playOptions = requireArguments().getParcelableCompat<PlayOptions>(Constants.EXTRA_MEDIA_PLAY_OPTIONS)
|
||||
val preferences = requireArguments().getParcelableCompat<PlayerWebPreferences>(Constants.EXTRA_WEB_PREFERENCES)
|
||||
if (playOptions == null) {
|
||||
context.toast(R.string.player_error_invalid_play_options)
|
||||
return@launch
|
||||
}
|
||||
when (viewModel.queueManager.initializePlaybackQueue(playOptions)) {
|
||||
when (viewModel.queueManager.initializePlaybackQueue(playOptions, preferences)) {
|
||||
is PlayerException.InvalidPlayOptions -> context.toast(R.string.player_error_invalid_play_options)
|
||||
is PlayerException.NetworkFailure -> context.toast(R.string.player_error_network_failure)
|
||||
is PlayerException.UnsupportedContent -> context.toast(R.string.player_error_unsupported_content)
|
||||
|
||||
@@ -143,6 +143,7 @@ object Constants {
|
||||
|
||||
// Video player intent extras
|
||||
const val EXTRA_MEDIA_PLAY_OPTIONS = "org.jellyfin.mobile.MEDIA_PLAY_OPTIONS"
|
||||
const val EXTRA_WEB_PREFERENCES = "org.jellyfin.mobile.WEB_PREFERENCES"
|
||||
|
||||
// External player result actions
|
||||
const val MPV_PLAYER_RESULT_ACTION = "is.xyz.mpv.MPVActivity.result"
|
||||
|
||||
Reference in new issue
Block a user