Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ open class FullScreenPlayer : AbstractPlayerFragment<FragmentPlayerBinding>(

protected var androidTVInterfaceOffSeekTime = 10000L
protected var androidTVInterfaceOnSeekTime = 30000L
protected var playBackSpeedEnabled = false
protected var playBackSpeedEnabled = true
protected var playerResizeEnabled = false
protected var playerRotateEnabled = false
protected var rotatedManually = false
Expand Down Expand Up @@ -639,7 +639,7 @@ open class FullScreenPlayer : AbstractPlayerFragment<FragmentPlayerBinding>(
binding.speedText.text = "%.2fx".format(speed).replace(".0x", "x")
// Android crashes if you don't round to an exact step size
binding.speedBar.value =
(speed.coerceIn(0.1f, 2.0f) / binding.speedBar.stepSize).roundToInt()
(speed.coerceIn(PlaybackSpeeds.MIN_SPEED, PlaybackSpeeds.MAX_DIALOG_SPEED) / binding.speedBar.stepSize).roundToInt()
.toFloat() * binding.speedBar.stepSize
}

Expand All @@ -655,10 +655,15 @@ open class FullScreenPlayer : AbstractPlayerFragment<FragmentPlayerBinding>(
updateSpeedDialogBinding(binding)
for ((view, speed) in arrayOf(
binding.speed25 to 0.25f,
binding.speed50 to 0.5f,
binding.speed75 to 0.75f,
binding.speed100 to 1.0f,
binding.speed125 to 1.25f,
binding.speed150 to 1.5f,
binding.speed175 to 1.75f,
binding.speed200 to 2.0f,
binding.speed300 to 3.0f,
binding.speed400 to 4.0f,
)) {
view.setOnClickListener {
setPlayBackSpeed(speed)
Expand All @@ -667,12 +672,12 @@ open class FullScreenPlayer : AbstractPlayerFragment<FragmentPlayerBinding>(
}

binding.speedMinus.setOnClickListener {
setPlayBackSpeed(maxOf((player.getPlaybackSpeed() - 0.1f), 0.1f))
setPlayBackSpeed(maxOf(player.getPlaybackSpeed() - 0.1f, PlaybackSpeeds.MIN_SPEED))
updateSpeedDialogBinding(binding)
}

binding.speedPlus.setOnClickListener {
setPlayBackSpeed(minOf((player.getPlaybackSpeed() + 0.1f), 2.0f))
setPlayBackSpeed(minOf(player.getPlaybackSpeed() + 0.1f, PlaybackSpeeds.MAX_DIALOG_SPEED))
updateSpeedDialogBinding(binding)
}

Expand Down Expand Up @@ -1410,7 +1415,7 @@ open class FullScreenPlayer : AbstractPlayerFragment<FragmentPlayerBinding>(

playBackSpeedEnabled = settingsManager.getBoolean(
ctx.getString(R.string.playback_speed_enabled_key),
false
true
)
playBackCompressorEnabled = settingsManager.getBoolean(
ctx.getString(R.string.compressor_enabled_key),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.lagradost.cloudstream3.ui.player

import java.text.DecimalFormat

/**
* Shared constants and helpers for playback speed, used by the speed dialog
* ([FullScreenPlayer]) and the hold-to-speed-up gesture ([PlayerGestureHelper]).
*/
object PlaybackSpeeds {
const val MIN_SPEED = 0.1f

/** Upper bound of the speed dialog (slider, +/- buttons and presets). */
const val MAX_DIALOG_SPEED = 4.0f

/** Upper bound for the long-press speed, including the automatic escalation. */
const val MAX_HOLD_SPEED = 8.0f

/** Default long-press speed, stored in percent in the settings (200 = 2x). */
const val DEFAULT_HOLD_SPEED_PERCENT = 200

private const val EPSILON = 0.001f

/** Converts the stored percent setting (200 = 2x) to a speed multiplier. */
fun holdSpeedFromPercent(percent: Int): Float =
(percent / 100f).coerceIn(MIN_SPEED, MAX_HOLD_SPEED)

/**
* Speed to use while the screen is held.
*
* Starts at the [configured] long-press speed, but if playback is already running at that
* speed (or faster) the speed is doubled until it is actually faster than [current],
* so with a 2x setting: 1x -> 2x, 2x -> 4x, 4x -> 8x. Never exceeds [MAX_HOLD_SPEED].
*/
fun getHoldSpeed(current: Float, configured: Float): Float {
var target = configured.coerceIn(MIN_SPEED, MAX_HOLD_SPEED)
while (target <= current + EPSILON && target < MAX_HOLD_SPEED) {
target *= 2f
}
return target.coerceAtMost(MAX_HOLD_SPEED)
}

/** Formats a speed for display without trailing zeroes, e.g. 2.0 -> "2x", 1.25 -> "1.25x". */
fun format(speed: Float): String = DecimalFormat("0.##").format(speed.toDouble()) + "x"
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import android.view.WindowInsets
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.TextView
import androidx.annotation.OptIn
import androidx.core.content.ContextCompat
import androidx.core.view.isGone
Expand All @@ -40,7 +41,6 @@ import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.UIHelper.getStatusBarHeight
import com.lagradost.cloudstream3.utils.Vector2
import kotlin.math.abs
Expand Down Expand Up @@ -128,12 +128,20 @@ class PlayerGestureHelper(private val playerView: PlayerView) {
var swipeHorizontalEnabled: Boolean = false
var extraBrightnessEnabled: Boolean = false
var speedupEnabled: Boolean = false
/** Configured long-press speed (see [PlaybackSpeeds.getHoldSpeed] for the escalation). */
var speedupSpeed: Float = PlaybackSpeeds.holdSpeedFromPercent(PlaybackSpeeds.DEFAULT_HOLD_SPEED_PERCENT)

/** Hold / speed-up */
val holdHandler = Handler(Looper.getMainLooper())
var hasTriggeredSpeedUp = false
/** Speed that was active before the hold started, restored on release. */
private var speedBeforeHold = 1.0f
val holdRunnable = Runnable {
playerView.player.setPlaybackSpeed(2.0f)
// If we are already at (or above) the long-press speed, go faster: 2x -> 4x -> 8x...
speedBeforeHold = playerView.player.getPlaybackSpeed()
val holdSpeed = PlaybackSpeeds.getHoldSpeed(speedBeforeHold, speedupSpeed)
playerView.player.setPlaybackSpeed(holdSpeed)
updateSpeedUpText(holdSpeed)
showOrHideSpeedUp(true)
playerView.callbacks?.onHoldSpeedUp(true)
hasTriggeredSpeedUp = true
Expand Down Expand Up @@ -219,7 +227,13 @@ class PlayerGestureHelper(private val playerView: PlayerView) {
swipeVerticalEnabled = sm.getBoolean(context.getString(R.string.swipe_vertical_enabled_key), true)
swipeHorizontalEnabled = sm.getBoolean(context.getString(R.string.swipe_enabled_key), true)
extraBrightnessEnabled = sm.getBoolean(context.getString(R.string.extra_brightness_key), false)
speedupEnabled = sm.getBoolean(context.getString(R.string.speedup_key), false)
speedupEnabled = sm.getBoolean(context.getString(R.string.speedup_key), true)
speedupSpeed = PlaybackSpeeds.holdSpeedFromPercent(
sm.getInt(
context.getString(R.string.speedup_speed_key),
PlaybackSpeeds.DEFAULT_HOLD_SPEED_PERCENT
)
)
doubleTapEnabled = sm.getBoolean(context.getString(R.string.double_tap_enabled_key), false)
doubleTapPauseEnabled = sm.getBoolean(context.getString(R.string.double_tap_pause_enabled_key), false)
fastForwardTime = sm.getInt(context.getString(R.string.double_tap_seek_time_key), 10).toLong() * 1000L
Expand Down Expand Up @@ -282,6 +296,11 @@ class PlayerGestureHelper(private val playerView: PlayerView) {

/** Speed-up */

/** Shows the current hold speed (e.g. "4x") next to the icon on the speed-up indicator. */
private fun updateSpeedUpText(speed: Float) {
(playerView.playerSpeedupButton as? TextView)?.text = PlaybackSpeeds.format(speed)
}

fun showOrHideSpeedUp(show: Boolean) {
playerView.playerSpeedupButton?.let { btn ->
btn.clearAnimation()
Expand Down Expand Up @@ -1164,7 +1183,7 @@ class PlayerGestureHelper(private val playerView: PlayerView) {
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
holdHandler.removeCallbacks(holdRunnable)
if (hasTriggeredSpeedUp) {
playerView.player.setPlaybackSpeed(DataStoreHelper.playBackSpeed)
playerView.player.setPlaybackSpeed(speedBeforeHold)
showOrHideSpeedUp(false)
playerView.callbacks?.onHoldSpeedUp(false)
hasTriggeredSpeedUp = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.BasePreferenceFragmentCompat
import com.lagradost.cloudstream3.ui.player.PlaybackSpeeds
import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
Expand Down Expand Up @@ -48,7 +49,8 @@ class SettingsPlayer : BasePreferenceFragmentCompat() {
R.string.pref_category_gestures_key,
R.string.rotate_video_key,
R.string.auto_rotate_video_key,
R.string.speedup_key
R.string.speedup_key,
R.string.speedup_speed_key
),
TV or EMULATOR
)
Expand Down Expand Up @@ -96,6 +98,28 @@ class SettingsPlayer : BasePreferenceFragmentCompat() {
return@setOnPreferenceClickListener true
}

getPref(R.string.speedup_speed_key)?.setOnPreferenceClickListener {
val prefNames = resources.getStringArray(R.array.speedup_speed_names)
val prefValues = resources.getIntArray(R.array.speedup_speed_values)
val current = settingsManager.getInt(
getString(R.string.speedup_speed_key),
PlaybackSpeeds.DEFAULT_HOLD_SPEED_PERCENT
)

activity?.showBottomDialog(
prefNames.toList(),
prefValues.indexOf(current),
getString(R.string.speedup_speed_title),
true,
{}
) {
settingsManager.edit {
putInt(getString(R.string.speedup_speed_key), prefValues[it])
}
}
return@setOnPreferenceClickListener true
}

getPref(R.string.software_decoding_key)?.setOnPreferenceClickListener {
val prefNames = resources.getStringArray(R.array.software_decoding_switch)
val prefValues = resources.getIntArray(R.array.software_decoding_switch_values)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ object SettingsPlayerScreen : SearchableSettings {
subtitle = stringResource(R.string.speedup_summary),
icon = painterResource(R.drawable.speedup),
),
Preference.PreferenceItem.ListPreference(
preference = settings.player.tiktokSpeed,
title = stringResource(R.string.speedup_speed_title),
subtitle = stringResource(R.string.speedup_speed_summary) + "\n%s",
icon = painterResource(R.drawable.speedup),
entries = integerArrayResource(R.array.speedup_speed_values).zip(
stringArrayResource(R.array.speedup_speed_names)
).toMap()
),
Preference.PreferenceItem.SwitchPreference(
preference = settings.player.autoPlayEnabled,
title = stringResource(R.string.autoplay_next_settings),
Expand Down
10 changes: 8 additions & 2 deletions app/src/main/res/layout/player_custom_layout.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1103,15 +1103,21 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/player_speedup_button"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="45dp"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:minWidth="45dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="30dp"
app:iconPadding="0dp"
android:paddingStart="12dp"
android:paddingEnd="14dp"
app:iconPadding="4dp"
app:iconGravity="textStart"
android:clickable="false"
android:focusable="false"
android:textAllCaps="false"
android:textColor="@color/textColor"
android:textSize="14sp"
tools:text="4x"
android:visibility="gone"
app:icon="@drawable/speedup"
app:iconTint="@color/textColor"
Expand Down
11 changes: 9 additions & 2 deletions app/src/main/res/layout/player_custom_layout_tv.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1153,15 +1153,22 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/player_speedup_button"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="45dp"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:minWidth="45dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="70dp"
android:paddingStart="12dp"
android:paddingEnd="14dp"
android:clickable="false"
android:textAllCaps="false"
android:textColor="@color/textColor"
android:textSize="14sp"
android:visibility="gone"
app:icon="@drawable/speedup"
app:iconGravity="top"
app:iconGravity="textStart"
app:iconPadding="4dp"
tools:text="4x"
app:iconTint="@color/textColor"
app:rippleColor="?attr/colorPrimary"
tools:visibility="visible" />
Expand Down
Loading
Loading