feat: override video playback rate

This commit is contained in:
rhunk
2024-02-14 17:40:55 +01:00
parent 60ee3680a4
commit 93e9a67daf
5 changed files with 117 additions and 23 deletions

View File

@ -7,6 +7,8 @@ import me.rhunk.snapenhance.core.util.hook.hook
import me.rhunk.snapenhance.mapper.impl.OperaViewerParamsMapper
class OperaViewerParamsOverride : Feature("OperaViewerParamsOverride", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
var currentPlaybackRate = 1.0F
data class OverrideKey(
val name: String,
val defaultValue: Any?
@ -24,6 +26,12 @@ class OperaViewerParamsOverride : Feature("OperaViewerParamsOverride", loadParam
overrideMap[key] = Override(filter, value)
}
currentPlaybackRate = context.config.global.defaultVideoPlaybackRate.getNullable()?.takeIf { it > 0 } ?: 1.0F
if (context.config.global.videoPlaybackRateSlider.get() || currentPlaybackRate != 1.0F) {
overrideParam("video_playback_rate", { currentPlaybackRate != 1.0F }, { _, _ -> currentPlaybackRate.toDouble() })
}
if (context.config.messaging.loopMediaPlayback.get()) {
//https://github.com/rodit/SnapMod/blob/master/app/src/main/java/xyz/rodit/snapmod/features/opera/SnapDurationModifier.kt
overrideParam("auto_advance_mode", { true }, { key, _ -> key.defaultValue })
@ -37,29 +45,36 @@ class OperaViewerParamsOverride : Feature("OperaViewerParamsOverride", loadParam
}
context.mappings.useMapper(OperaViewerParamsMapper::class) {
classReference.get()?.hook(putMethod.get()!!, HookStage.BEFORE) { param ->
val key = param.argNullable<Any>(0)?.let { key ->
val fields = key::class.java.fields
OverrideKey(
name = fields.firstOrNull {
it.type == String::class.java
}?.get(key)?.toString() ?: return@hook,
defaultValue = fields.firstOrNull {
it.type == Object::class.java
}?.get(key)
)
} ?: return@hook
val value = param.argNullable<Any>(1) ?: return@hook
fun overrideParamResult(paramKey: Any, value: Any?): Any? {
val fields = paramKey::class.java.fields
val key = OverrideKey(
name = fields.firstOrNull {
it.type == String::class.java
}?.get(paramKey)?.toString() ?: return value,
defaultValue = fields.firstOrNull {
it.type == Object::class.java
}?.get(paramKey)
)
overrideMap[key.name]?.let { override ->
if (override.filter(value)) {
runCatching {
param.setArg(1, override.value(key, value))
return override.value(key, value)
}.onFailure {
context.log.error("Failed to override param $key", it)
}
}
}
return value
}
classReference.get()?.hook(getMethod.get()!!, HookStage.AFTER) { param ->
param.setResult(overrideParamResult(param.arg(0), param.getResult()))
}
classReference.get()?.hook(getOrDefaultMethod.get()!!, HookStage.AFTER) { param ->
param.setResult(overrideParamResult(param.arg(0), param.getResult()))
}
}
}

View File

@ -8,11 +8,27 @@ import android.widget.Button
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import me.rhunk.snapenhance.common.ui.createComposeView
import me.rhunk.snapenhance.core.features.impl.OperaViewerParamsOverride
import me.rhunk.snapenhance.core.features.impl.downloader.MediaDownloader
import me.rhunk.snapenhance.core.ui.applyTheme
import me.rhunk.snapenhance.core.ui.menu.AbstractMenu
import me.rhunk.snapenhance.core.ui.triggerCloseTouchEvent
import me.rhunk.snapenhance.core.util.ktx.getId
import me.rhunk.snapenhance.core.util.ktx.getIdentifier
import me.rhunk.snapenhance.core.util.ktx.vibrateLongPress
import me.rhunk.snapenhance.core.wrapper.impl.ScSize
import java.text.DateFormat
@ -129,6 +145,45 @@ class OperaContextActionMenu : AbstractMenu() {
}
}
if (context.config.global.videoPlaybackRateSlider.get()) {
val operaViewerParamsOverride = context.feature(OperaViewerParamsOverride::class)
linearLayout.addView(createComposeView(view.context) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
var value by remember { mutableFloatStateOf(operaViewerParamsOverride.currentPlaybackRate) }
Slider(
value = value,
onValueChange = {
value = it
operaViewerParamsOverride.currentPlaybackRate = it
},
valueRange = 0.1F..4.0F,
steps = 0,
modifier = Modifier.fillMaxWidth()
)
Text(
text = "x" + value.toString().take(4),
color = remember {
view.context.theme.obtainStyledAttributes(
intArrayOf(view.context.resources.getIdentifier("sigColorTextPrimary", "attr"))
).getColor(0, 0).let { Color(it) }
},
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
}.apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
})
}
if (context.config.downloader.downloadContextMenu.get()) {
linearLayout.addView(Button(view.context).apply {
text = translation["download"]