bugfixes: deviceId / code: New Enum VideoQuality

This commit is contained in:
nomadics9 2024-07-20 08:36:23 +03:00
parent ba580f8769
commit 6dded2e726
11 changed files with 121 additions and 109 deletions

View file

@ -45,6 +45,7 @@ import dev.jdtech.jellyfin.viewmodels.PlayerEvents
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import dev.jdtech.jellyfin.core.R as CoreR
var isControlsLocked: Boolean = false
@ -348,20 +349,44 @@ class PlayerActivity : BasePlayerActivity() {
}
private fun showQualitySelectionDialog() {
val height = viewModel.getOriginalHeight() // TODO: rewrite getting height stuff I don't like that its only update after changing quality
val qualities = when (height) {
0 -> arrayOf("Auto", "Original - Max", "720p - 2Mbps", "480p - 1Mbps", "360p - 800kbps")
in 1001..1999 -> arrayOf("Auto", "Original (1080p) - Max", "720p - 2Mbps", "480p - 1Mbps", "360p - 800kbps")
in 2000..3000 -> arrayOf("Auto", "Original (4K) - Max", "720p - 2Mbps", "480p - 1Mbps", "360p - 800kbps")
else -> arrayOf("Auto", "Original - Max", "720p - 2Mbps", "480p - 1Mbps", "360p - 800kbps")
val height = viewModel.getOriginalHeight()
val qualityEntries = resources.getStringArray(CoreR.array.quality_entries).toList()
val qualityValues = resources.getStringArray(CoreR.array.quality_values).toList()
// Map entries to values
val qualityMap = qualityEntries.zip(qualityValues).toMap()
val qualities: List<String> =
when (height) {
0 -> qualityEntries
in 1001..1999 ->
listOf(
qualityEntries[0],
"${qualityEntries[1]} (1080p)",
qualityEntries[2],
qualityEntries[3],
qualityEntries[4],
qualityEntries[5],
)
in 2000..3000 ->
listOf(
qualityEntries[0],
"${qualityEntries[1]} (4K)",
qualityEntries[2],
qualityEntries[3],
qualityEntries[4],
qualityEntries[5],
)
else -> qualityEntries
}
MaterialAlertDialogBuilder(this)
.setTitle("Select Video Quality")
.setItems(qualities) { _, which ->
val selectedQuality = qualities[which]
viewModel.changeVideoQuality(selectedQuality)
}
.show()
.setItems(qualities.toTypedArray()) { _, which ->
val selectedQualityEntry = qualities[which]
val selectedQualityValue =
qualityMap.entries.find { it.key.contains(selectedQualityEntry.split(" ")[0]) }?.value ?: selectedQualityEntry
viewModel.changeVideoQuality(selectedQualityValue)
}.show()
}
override fun onPictureInPictureModeChanged(

View file

@ -413,8 +413,8 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
}
private fun createPickQualityDialog() {
val qualityEntries = resources.getStringArray(CoreR.array.quality_entries)
val qualityValues = resources.getStringArray(CoreR.array.quality_values)
val qualityEntries = resources.getStringArray(CoreR.array.download_quality_entries)
val qualityValues = resources.getStringArray(CoreR.array.download_quality_values)
val quality = appPreferences.downloadQuality
val currentQualityIndex = qualityValues.indexOf(quality)
var selectedQuality = quality

View file

@ -506,8 +506,8 @@ class MovieFragment : Fragment() {
}
private fun createPickQualityDialog() {
val qualityEntries = resources.getStringArray(CoreR.array.quality_entries)
val qualityValues = resources.getStringArray(CoreR.array.quality_values)
val qualityEntries = resources.getStringArray(CoreR.array.download_quality_entries)
val qualityValues = resources.getStringArray(CoreR.array.download_quality_values)
val quality = appPreferences.downloadQuality
val currentQualityIndex = qualityValues.indexOf(quality)
var selectedQuality = quality

View file

@ -16,6 +16,7 @@ import dev.jdtech.jellyfin.models.FindroidSource
import dev.jdtech.jellyfin.models.FindroidSources
import dev.jdtech.jellyfin.models.FindroidTrickplayInfo
import dev.jdtech.jellyfin.models.UiText
import dev.jdtech.jellyfin.models.VideoQuality
import dev.jdtech.jellyfin.models.toFindroidEpisodeDto
import dev.jdtech.jellyfin.models.toFindroidMediaStreamDto
import dev.jdtech.jellyfin.models.toFindroidMovieDto
@ -395,19 +396,12 @@ class DownloaderImpl(
itemId: UUID,
quality: String,
): Uri? {
val maxBitrate =
when (quality) {
"720p" -> 2000000 // 2 Mbps
"480p" -> 1000000 // 1 Mbps
"360p" -> 800000 // 800Kbps
else -> 2000000
}
val videoQuality = VideoQuality.fromString(quality)!!
return try {
val deviceProfile =
jellyfinRepository.buildDeviceProfile(maxBitrate, "mkv", EncodingContext.STATIC)
jellyfinRepository.buildDeviceProfile(VideoQuality.getBitrate(videoQuality), "mkv", EncodingContext.STATIC)
val playbackInfo =
jellyfinRepository.getPostedPlaybackInfo(itemId, false, deviceProfile, maxBitrate)
jellyfinRepository.getPostedPlaybackInfo(itemId, false, deviceProfile, VideoQuality.getBitrate(videoQuality))
val mediaSourceId =
playbackInfo.content.mediaSources
.firstOrNull()
@ -420,36 +414,14 @@ class DownloaderImpl(
deviceId,
mediaSourceId,
playSessionId,
maxBitrate,
VideoQuality.getBitrate(videoQuality),
"ts",
VideoQuality.getQualityInt(videoQuality)
)
val transcodeUri = buildTranscodeUri(downloadUrl, maxBitrate, quality)
transcodeUri
downloadUrl.toUri()
} catch (e: Exception) {
null
}
}
// TODO: I believe building upon the uri is not necessary anymore all is handled in the sdk api
private fun buildTranscodeUri(
transcodingUrl: String,
maxBitrate: Int,
quality: String,
): Uri {
val resolution =
when (quality) {
"720p" -> "720"
"480p" -> "480"
"360p" -> "360"
else -> "720"
}
return Uri
.parse(transcodingUrl)
.buildUpon()
.appendQueryParameter("MaxVideoHeight", resolution)
.appendQueryParameter("MaxVideoBitRate", maxBitrate.toString())
.appendQueryParameter("subtitleMethod", "External")
.build()
}
}

View file

@ -26,13 +26,31 @@
<item>opensles</item>
</string-array>
<string-array name="quality_entries">
<item>Auto</item>
<item>Original</item>
<item>720p - 2Mbps</item>
<item>480p - 1Mbps</item>
<item>1080p - 8Mbps</item>
<item>720p - 3Mbps</item>
<item>480p - 1.5Mbps</item>
<item>360p - 800Kbps</item>
</string-array>
<string-array name="quality_values">
<item>Auto</item>
<item>Original</item>
<item>1080p</item>
<item>720p</item>
<item>480p</item>
<item>360p</item>
</string-array>
<string-array name="download_quality_entries">
<item>Original</item>
<item>1080p - 8Mbps</item>
<item>720p - 3Mbps</item>
<item>480p - 1.5Mbps</item>
<item>360p - 800Kbps</item>
</string-array>
<string-array name="download_quality_values">
<item>Original</item>
<item>1080p</item>
<item>720p</item>
<item>480p</item>
<item>360p</item>

View file

@ -14,8 +14,8 @@
android:key="pref_downloads_quality"
android:title="Download Quality"
android:defaultValue="Original"
android:entries="@array/quality_entries"
android:entryValues="@array/quality_values"
android:entries="@array/download_quality_entries"
android:entryValues="@array/download_quality_values"
android:summary="%s" />
<SwitchPreferenceCompat
android:defaultValue="false"

View file

@ -0,0 +1,25 @@
package dev.jdtech.jellyfin.models
enum class VideoQuality(
val bitrate: Int,
val qualityString: String,
val qualityInt: Int,
) {
PAuto(10000000, "Auto", 1080),
POriginal(1000000000, "Original", 1080),
P1080(8000000, "1080p", 1080),
P720(3000000, "720p", 720),
P480(1500000, "480p", 480),
P360(800000, "360p", 360),
;
companion object {
fun fromString(quality: String): VideoQuality? = entries.find { it.qualityString == quality }
fun getBitrate(quality: VideoQuality): Int = quality.bitrate
fun getQualityString(quality: VideoQuality): String = quality.qualityString
fun getQualityInt(quality: VideoQuality): Int = quality.qualityInt
}
}

View file

@ -143,8 +143,6 @@ interface JellyfinRepository {
suspend fun getDeviceId(): String
suspend fun getVideoTranscodeBitRate(transcodeResolution: Int): Pair<Int, Int>
suspend fun buildDeviceProfile(
maxBitrate: Int,
container: String,
@ -156,10 +154,9 @@ interface JellyfinRepository {
deviceId: String,
mediaSourceId: String,
playSessionId: String,
videoBitrate:
@Suppress("ktlint:standard:max-line-length")
Int,
videoBitrate: Int,
container: String,
maxHeight: Int,
): String
suspend fun getTranscodedVideoStream(

View file

@ -16,6 +16,7 @@ import dev.jdtech.jellyfin.models.FindroidShow
import dev.jdtech.jellyfin.models.FindroidSource
import dev.jdtech.jellyfin.models.Intro
import dev.jdtech.jellyfin.models.SortBy
import dev.jdtech.jellyfin.models.VideoQuality
import dev.jdtech.jellyfin.models.toFindroidCollection
import dev.jdtech.jellyfin.models.toFindroidEpisode
import dev.jdtech.jellyfin.models.toFindroidItem
@ -609,15 +610,6 @@ class JellyfinRepositoryImpl(
override fun getUserId(): UUID = jellyfinApi.userId!!
override suspend fun getVideoTranscodeBitRate(transcodeResolution: Int): Pair<Int, Int> =
when (transcodeResolution) {
1080 -> 8000000 to 384000 // Adjusted for personal can be other values
720 -> 2000000 to 384000 // 720p
480 -> 1000000 to 384000 // 480p
360 -> 800000 to 128000 // 360p
else -> 12000000 to 384000 // its adaptive but setting max here
}
override suspend fun buildDeviceProfile(
maxBitrate: Int,
container: String,
@ -631,7 +623,7 @@ class JellyfinRepositoryImpl(
supportsPersistentIdentifier = true,
deviceProfile =
DeviceProfile(
name = "AnanasUser",
name = "FindroidUser",
id = getUserId().toString(),
maxStaticBitrate = maxBitrate,
maxStreamingBitrate = maxBitrate,
@ -648,8 +640,8 @@ class JellyfinRepositoryImpl(
container = container,
context = context,
protocol = MediaStreamProtocol.HLS,
audioCodec = "aac,ac3,eac3",
videoCodec = "hevc,h264",
audioCodec = "aac",
videoCodec = "h264",
type = DlnaProfileType.VIDEO,
conditions =
listOf(
@ -712,6 +704,7 @@ class JellyfinRepositoryImpl(
playSessionId: String,
videoBitrate: Int,
container: String,
maxHeight: Int,
): String {
val url =
jellyfinApi.videosApi.getVideoStreamByContainerUrl(
@ -721,10 +714,11 @@ class JellyfinRepositoryImpl(
mediaSourceId = mediaSourceId,
playSessionId = playSessionId,
videoBitRate = videoBitrate,
audioBitRate = 384000,
videoCodec = "hevc",
audioCodec = "aac,ac3,eac3",
audioBitRate = 128000,
videoCodec = "h264",
audioCodec = "aac",
container = container,
maxHeight = maxHeight,
startTimeTicks = 0,
copyTimestamps = true,
subtitleMethod = SubtitleDeliveryMethod.EXTERNAL,
@ -739,7 +733,7 @@ class JellyfinRepositoryImpl(
playSessionId: String,
videoBitrate: Int,
): String {
val isAuto = videoBitrate == 12000000
val isAuto = videoBitrate == VideoQuality.getBitrate(VideoQuality.PAuto)
val url =
if (!isAuto) {
jellyfinApi.api.dynamicHlsApi.getMasterHlsVideoPlaylistUrl(
@ -750,9 +744,9 @@ class JellyfinRepositoryImpl(
playSessionId = playSessionId,
videoBitRate = videoBitrate,
enableAdaptiveBitrateStreaming = false,
audioBitRate = 384000, // could also be passed with audioBitrate but i preferred not as its not much data anyways
videoCodec = "hevc,h264",
audioCodec = "aac,ac3,eac3",
audioBitRate = 128000,
videoCodec = "h264",
audioCodec = "aac",
startTimeTicks = 0,
copyTimestamps = true,
subtitleMethod = SubtitleDeliveryMethod.EXTERNAL,
@ -768,8 +762,8 @@ class JellyfinRepositoryImpl(
mediaSourceId = mediaSourceId,
playSessionId = playSessionId,
enableAdaptiveBitrateStreaming = true,
videoCodec = "hevc",
audioCodec = "aac,ac3,eac3",
videoCodec = "h264",
audioCodec = "aac",
startTimeTicks = 0,
copyTimestamps = true,
subtitleMethod = SubtitleDeliveryMethod.EXTERNAL,
@ -782,10 +776,7 @@ class JellyfinRepositoryImpl(
}
override suspend fun getDeviceId(): String {
val devices = jellyfinApi.devicesApi.getDevices(getUserId())
return devices.content.items
?.firstOrNull()
?.id!!
return jellyfinApi.api.deviceInfo.id
}
override suspend fun stopEncodingProcess(playSessionId: String) {

View file

@ -330,10 +330,6 @@ class JellyfinRepositoryOfflineImpl(
TODO("Not yet implemented")
}
override suspend fun getVideoTranscodeBitRate(transcodeResolution: Int): Pair<Int, Int> {
TODO("Not yet implemented")
}
override suspend fun buildDeviceProfile(
maxBitrate: Int,
container: String,
@ -349,6 +345,7 @@ class JellyfinRepositoryOfflineImpl(
playSessionId: String,
videoBitrate: Int,
container: String,
maxHeight: Int,
): String {
TODO("Not yet implemented")
}

View file

@ -28,6 +28,7 @@ import dev.jdtech.jellyfin.models.Intro
import dev.jdtech.jellyfin.models.PlayerChapter
import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.models.Trickplay
import dev.jdtech.jellyfin.models.VideoQuality
import dev.jdtech.jellyfin.mpv.MPVPlayer
import dev.jdtech.jellyfin.player.video.R
import dev.jdtech.jellyfin.repository.JellyfinRepository
@ -464,17 +465,6 @@ constructor(
eventsChannel.trySend(PlayerEvents.IsPlayingChanged(isPlaying))
}
private fun getTranscodeResolutions(preferredQuality: String): Int {
return when (preferredQuality) {
"1080p" -> 1080 // TODO: 1080p this logic is based on 1080p being original
"720p - 2Mbps" -> 720
"480p - 1Mbps" -> 480
"360p - 800kbps" -> 360
"Auto" -> 1
else -> 1080 //default to Original
}
}
fun changeVideoQuality(quality: String) {
val mediaId = player.currentMediaItem?.mediaId ?: return
val currentItem = items.firstOrNull { it.itemId.toString() == mediaId } ?: return
@ -482,12 +472,9 @@ constructor(
viewModelScope.launch {
try {
val transcodingResolution = getTranscodeResolutions(quality)
val (videoBitRate, audioBitRate) = jellyfinRepository.getVideoTranscodeBitRate(
transcodingResolution
)
val deviceProfile = jellyfinRepository.buildDeviceProfile(videoBitRate, "mkv", EncodingContext.STREAMING)
val playbackInfo = jellyfinRepository.getPostedPlaybackInfo(currentItem.itemId,true,deviceProfile,videoBitRate)
val videoQuality = VideoQuality.fromString(quality)!!
val deviceProfile = jellyfinRepository.buildDeviceProfile(VideoQuality.getBitrate(videoQuality), "mkv", EncodingContext.STREAMING)
val playbackInfo = jellyfinRepository.getPostedPlaybackInfo(currentItem.itemId,true,deviceProfile,VideoQuality.getBitrate(videoQuality))
val playSessionId = playbackInfo.content.playSessionId
if (playSessionId != null) {
jellyfinRepository.stopEncodingProcess(playSessionId)
@ -537,18 +524,18 @@ constructor(
val allSubtitles =
if (transcodingResolution == 1080) {
if (VideoQuality.getQualityString(videoQuality) == "Original") {
externalSubtitles
}else {
embeddedSubtitles.apply { addAll(externalSubtitles) }
}
val url = if (transcodingResolution == 1080){
val url = if (VideoQuality.getQualityString(videoQuality) == "Original"){
jellyfinRepository.getStreamUrl(currentItem.itemId, currentItem.mediaSourceId, playSessionId)
} else {
val mediaSourceId = mediaSources[currentMediaItemIndex].id
val deviceId = jellyfinRepository.getDeviceId()
val url = jellyfinRepository.getTranscodedVideoStream(currentItem.itemId, deviceId ,mediaSourceId, playSessionId!!, videoBitRate)
val url = jellyfinRepository.getTranscodedVideoStream(currentItem.itemId, deviceId ,mediaSourceId, playSessionId!!, VideoQuality.getBitrate(videoQuality))
val uriBuilder = url.toUri().buildUpon()
val apiKey = jellyfinApi.api.accessToken // TODO: add in repo
uriBuilder.appendQueryParameter("api_key",apiKey )