Merge pull request #4849 from Bnyro/master

feat: option to set a different default resolution when not in fullscreen
This commit is contained in:
Bnyro 2023-09-25 10:43:56 +02:00 committed by GitHub
commit 38549dbdb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 31 deletions

View File

@ -36,10 +36,10 @@ import com.github.libretube.enums.SbSkipOptions
import com.github.libretube.extensions.updateParameters import com.github.libretube.extensions.updateParameters
import com.github.libretube.obj.VideoStats import com.github.libretube.obj.VideoStats
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils
import kotlinx.coroutines.runBlocking
import java.util.Locale import java.util.Locale
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlinx.coroutines.runBlocking
object PlayerHelper { object PlayerHelper {
private const val ACTION_MEDIA_CONTROL = "media_control" private const val ACTION_MEDIA_CONTROL = "media_control"
@ -324,13 +324,17 @@ object PlayerHelper {
true true
) )
fun getDefaultResolution(context: Context): String { fun getDefaultResolution(context: Context, isFullscreen: Boolean): Int? {
val prefKey = if (NetworkHelper.isNetworkMetered(context)) { var prefKey = if (NetworkHelper.isNetworkMetered(context)) {
PreferenceKeys.DEFAULT_RESOLUTION_MOBILE PreferenceKeys.DEFAULT_RESOLUTION_MOBILE
} else { } else {
PreferenceKeys.DEFAULT_RESOLUTION PreferenceKeys.DEFAULT_RESOLUTION
} }
if (!isFullscreen) prefKey += "_no_fullscreen"
return PreferenceHelper.getString(prefKey, "") return PreferenceHelper.getString(prefKey, "")
.replace("p", "")
.toIntOrNull()
} }
/** /**
@ -502,9 +506,9 @@ object PlayerHelper {
if (currentPosition in segmentStart until segmentEnd) { if (currentPosition in segmentStart until segmentEnd) {
if (sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC || if (sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC ||
( (
sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE && sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE &&
!segment.skipped !segment.skipped
) )
) { ) {
if (sponsorBlockNotifications) { if (sponsorBlockNotifications) {
runCatching { runCatching {
@ -516,9 +520,9 @@ object PlayerHelper {
segment.skipped = true segment.skipped = true
} else if (sponsorBlockConfig[segment.category] == SbSkipOptions.MANUAL || } else if (sponsorBlockConfig[segment.category] == SbSkipOptions.MANUAL ||
( (
sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE && sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE &&
segment.skipped segment.skipped
) )
) { ) {
return segment return segment
} }
@ -543,9 +547,9 @@ object PlayerHelper {
return chapters return chapters
.filter { .filter {
it.highlightDrawable == null || it.highlightDrawable == null ||
// remove the video highlight if it's already longer ago than [ChapterSegment.HIGHLIGHT_LENGTH], // remove the video highlight if it's already longer ago than [ChapterSegment.HIGHLIGHT_LENGTH],
// otherwise the SponsorBlock highlight would be shown from its starting point to the end // otherwise the SponsorBlock highlight would be shown from its starting point to the end
(currentPositionSeconds - it.start) < ChapterSegment.HIGHLIGHT_LENGTH (currentPositionSeconds - it.start) < ChapterSegment.HIGHLIGHT_LENGTH
} }
.sortedBy { it.start } .sortedBy { it.start }
.indexOfLast { currentPositionSeconds >= it.start } .indexOfLast { currentPositionSeconds >= it.start }
@ -701,9 +705,9 @@ object PlayerHelper {
*/ */
fun haveAudioTrackRoleFlagSet(@C.RoleFlags roleFlags: Int): Boolean { fun haveAudioTrackRoleFlagSet(@C.RoleFlags roleFlags: Int): Boolean {
return isFlagSet(roleFlags, C.ROLE_FLAG_DESCRIBES_VIDEO) || return isFlagSet(roleFlags, C.ROLE_FLAG_DESCRIBES_VIDEO) ||
isFlagSet(roleFlags, C.ROLE_FLAG_DUB) || isFlagSet(roleFlags, C.ROLE_FLAG_DUB) ||
isFlagSet(roleFlags, C.ROLE_FLAG_MAIN) || isFlagSet(roleFlags, C.ROLE_FLAG_MAIN) ||
isFlagSet(roleFlags, C.ROLE_FLAG_ALTERNATE) isFlagSet(roleFlags, C.ROLE_FLAG_ALTERNATE)
} }
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class) @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
@ -716,7 +720,8 @@ object PlayerHelper {
val audioInfo = "${player.audioFormat?.codecs.orEmpty()} ${ val audioInfo = "${player.audioFormat?.codecs.orEmpty()} ${
TextUtils.formatBitrate(player.audioFormat?.bitrate) TextUtils.formatBitrate(player.audioFormat?.bitrate)
}" }"
val videoQuality = "${player.videoFormat?.width}x${player.videoFormat?.height} ${player.videoFormat?.frameRate?.toInt()}fps" val videoQuality =
"${player.videoFormat?.width}x${player.videoFormat?.height} ${player.videoFormat?.frameRate?.toInt()}fps"
return VideoStats(videoId, videoInfo, videoQuality, audioInfo) return VideoStats(videoId, videoInfo, videoQuality, audioInfo)
} }
} }

View File

@ -159,33 +159,32 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
*/ */
private lateinit var streams: Streams private lateinit var streams: Streams
/** // progress state of the motion layout transition
* for the transition
*/
private var transitionStartId = 0 private var transitionStartId = 0
private var transitionEndId = 0 private var transitionEndId = 0
private var isTransitioning = true private var isTransitioning = true
/** // data and objects stored for the player
* for the player
*/
private lateinit var exoPlayer: ExoPlayer private lateinit var exoPlayer: ExoPlayer
private lateinit var trackSelector: DefaultTrackSelector private lateinit var trackSelector: DefaultTrackSelector
private var currentSubtitle = Subtitle(code = PlayerHelper.defaultSubtitleCode) private var currentSubtitle = Subtitle(code = PlayerHelper.defaultSubtitleCode)
// if null, it's been set to automatic
private var fullscreenResolution: Int? = null
// the resolution to use when the video is not played in fullscreen
// if null, use same quality as fullscreen
private var noFullscreenResolution: Int? = null
private val cronetDataSourceFactory = CronetDataSource.Factory( private val cronetDataSourceFactory = CronetDataSource.Factory(
CronetHelper.cronetEngine, CronetHelper.cronetEngine,
Executors.newCachedThreadPool() Executors.newCachedThreadPool()
) )
/** // for the player notification
* for the player notification
*/
private lateinit var nowPlayingNotification: NowPlayingNotification private lateinit var nowPlayingNotification: NowPlayingNotification
/** // SponsorBlock
* SponsorBlock
*/
private var segments = listOf<Segment>() private var segments = listOf<Segment>()
private var sponsorBlockEnabled = PlayerHelper.sponsorBlockEnabled private var sponsorBlockEnabled = PlayerHelper.sponsorBlockEnabled
private var sponsorBlockConfig = PlayerHelper.getSponsorBlockCategories() private var sponsorBlockConfig = PlayerHelper.getSponsorBlockCategories()
@ -263,6 +262,9 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
1000, 1000,
1000 1000
) )
fullscreenResolution = PlayerHelper.getDefaultResolution(requireContext(), true)
noFullscreenResolution = PlayerHelper.getDefaultResolution(requireContext(), false)
} }
override fun onCreateView( override fun onCreateView(
@ -541,6 +543,8 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
updateFullscreenOrientation() updateFullscreenOrientation()
viewModel.isFullscreen.value = true viewModel.isFullscreen.value = true
updateResolutionOnFullscreenChange(true)
} }
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
@ -570,6 +574,8 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
} }
viewModel.isFullscreen.value = false viewModel.isFullscreen.value = false
updateResolutionOnFullscreenChange(false)
} }
private fun toggleDescription() { private fun toggleDescription() {
@ -1324,9 +1330,16 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
} }
} }
private fun updateResolutionOnFullscreenChange(isFullscreen: Boolean) {
if (!isFullscreen && noFullscreenResolution != null) {
setPlayerResolution(noFullscreenResolution!!)
} else {
setPlayerResolution(fullscreenResolution ?: Int.MAX_VALUE)
}
}
private suspend fun setStreamSource() { private suspend fun setStreamSource() {
val defaultResolution = PlayerHelper.getDefaultResolution(requireContext()).replace("p", "") updateResolutionOnFullscreenChange(viewModel.isFullscreen.value == true)
if (defaultResolution.isNotEmpty()) setPlayerResolution(defaultResolution.toInt())
val (uri, mimeType) = when { val (uri, mimeType) = when {
// LBRY HLS // LBRY HLS
@ -1503,7 +1516,15 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
if (currentQuality == it.resolution) "${it.name}" else it.name if (currentQuality == it.resolution) "${it.name}" else it.name
} }
) { which -> ) { which ->
setPlayerResolution(resolutions[which].resolution) val newResolution = resolutions[which].resolution
setPlayerResolution(newResolution)
// save the selected resolution to update on fullscreen change
if (noFullscreenResolution != null && viewModel.isFullscreen.value != true) {
noFullscreenResolution = newResolution
} else {
fullscreenResolution = newResolution
}
} }
.show(childFragmentManager) .show(childFragmentManager)
} }

View File

@ -177,6 +177,17 @@
<item>240p</item> <item>240p</item>
<item>144p</item> <item>144p</item>
</string-array> </string-array>
<string-array name="defresNoPortrait">
<item>@string/same_as_fullscreen</item>
<item>2160p</item>
<item>1440p</item>
<item>1080p</item>
<item>720p</item>
<item>480p</item>
<item>360p</item>
<item>240p</item>
<item>144p</item>
</string-array>
<string-array name="defresValue"> <string-array name="defresValue">
<item /> <item />
<item>2160p</item> <item>2160p</item>

View File

@ -445,6 +445,9 @@
<string name="uploader_name">Uploader name</string> <string name="uploader_name">Uploader name</string>
<string name="duration_span">Duration: %1$s</string> <string name="duration_span">Duration: %1$s</string>
<string name="remove_watched_videos">Remove watched videos</string> <string name="remove_watched_videos">Remove watched videos</string>
<string name="no_fullscreen_resolution">No-Fullscreen resolution</string>
<string name="same_as_fullscreen">Same as fullscreen</string>
<!-- Backup & Restore Settings --> <!-- Backup & Restore Settings -->
<string name="import_subscriptions_from">Import subscriptions from</string> <string name="import_subscriptions_from">Import subscriptions from</string>
<string name="export_subscriptions_to">Export subscriptions to</string> <string name="export_subscriptions_to">Export subscriptions to</string>

View File

@ -13,6 +13,15 @@
app:title="@string/defres" app:title="@string/defres"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<ListPreference
android:icon="@drawable/ic_fullscreen"
app:defaultValue=""
app:entries="@array/defresNoPortrait"
app:entryValues="@array/defresValue"
app:key="default_res_no_fullscreen"
app:title="@string/no_fullscreen_resolution"
app:useSimpleSummaryProvider="true" />
<ListPreference <ListPreference
android:icon="@drawable/ic_headphones" android:icon="@drawable/ic_headphones"
app:defaultValue="auto" app:defaultValue="auto"
@ -35,6 +44,15 @@
app:title="@string/defres" app:title="@string/defres"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<ListPreference
android:icon="@drawable/ic_fullscreen"
app:defaultValue=""
app:entries="@array/defresNoPortrait"
app:entryValues="@array/defresValue"
app:key="default_res_mobile_no_fullscreen"
app:title="@string/no_fullscreen_resolution"
app:useSimpleSummaryProvider="true" />
<ListPreference <ListPreference
android:icon="@drawable/ic_headphones" android:icon="@drawable/ic_headphones"
app:defaultValue="auto" app:defaultValue="auto"