commit 62ff1d998f2b00da17b2c1fe6038b549a8e3718f
parent fad13ee7a26acb8dad7b892927afe9c33f439d16
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Tue, 14 May 2024 21:28:57 +0200

feat: media upload quality override

Diffstat:
Mcommon/src/main/assets/lang/en_US.json | 22++++++++++++++++++----
Mcommon/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Global.kt | 10+++++++++-
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt | 2+-
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ConfigurationOverride.kt | 2+-
Dcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/global/MediaQualityLevelOverride.kt | 24------------------------
Acore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/global/MediaUploadQualityOverride.kt | 50++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 79 insertions(+), 31 deletions(-)

diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json @@ -686,6 +686,24 @@ "name": "Snapchat Plus", "description": "Enables Snapchat Plus features\nSome Server-sided features may not work" }, + "media_upload_quality": { + "name": "Media Upload Quality", + "description": "Overrides the media upload quality", + "properties": { + "force_video_upload_source_quality": { + "name": "Force Video Upload Source Quality", + "description": "Forces Snapchat to use the source quality when uploading videos\nPlease note that this may not remove metadata from media" + }, + "disable_image_compression": { + "name": "Disable Image Compression", + "description": "Disables image compression when uploading media" + }, + "custom_image_upload_format": { + "name": "Custom Image Upload Format", + "description": "Sets a custom image upload format\nSelect a lossless format (like PNG) for the best quality" + } + } + }, "disable_confirmation_dialogs": { "name": "Disable Confirmation Dialogs", "description": "Automatically confirms selected actions" @@ -738,10 +756,6 @@ "name": "Disable Google Play Services Dialogs", "description": "Prevent Google Play Services availability dialogs from being shown" }, - "force_upload_source_quality": { - "name": "Force Upload Source Quality", - "description": "Forces Snapchat to upload media in the original quality\nPlease note that this may not remove metadata from media" - }, "default_volume_controls": { "name": "Default Volume Controls", "description": "Forces Snapchat to use system volume controls" diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Global.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Global.kt @@ -1,6 +1,7 @@ package me.rhunk.snapenhance.common.config.impl import me.rhunk.snapenhance.common.config.ConfigContainer +import me.rhunk.snapenhance.common.config.ConfigFlag import me.rhunk.snapenhance.common.config.FeatureNotice class Global : ConfigContainer() { @@ -27,8 +28,16 @@ class Global : ConfigContainer() { val spoofBatteryLevel = string("spoof_battery_level") { requireRestart(); inputCheck = { it.isEmpty() || it.toIntOrNull() in 0..100 } } val spoofHeadphones = boolean("spoof_headphones") { requireRestart() } } + + inner class MediaUploadQualityConfig : ConfigContainer() { + val forceVideoUploadSourceQuality = boolean("force_video_upload_source_quality") { requireRestart() } + val disableImageCompression = boolean("disable_image_compression") { requireRestart() } + val customUploadImageFormat = unique("custom_image_upload_format", "jpeg", "png", "webp") { requireRestart(); addFlags(ConfigFlag.NO_TRANSLATE) } + } + val betterLocation = container("better_location", BetterLocation()) val snapchatPlus = boolean("snapchat_plus") { requireRestart() } + val mediaUploadQualityConfig = container("media_upload_quality", MediaUploadQualityConfig()) val disableConfirmationDialogs = multiple("disable_confirmation_dialogs", "erase_message", "remove_friend", "block_friend", "ignore_friend", "hide_friend", "hide_conversation", "clear_conversation") { requireRestart() } val disableMetrics = boolean("disable_metrics") { requireRestart() } val disableStorySections = multiple("disable_story_sections", "friends", "following", "discover") { requireRestart(); requireCleanCache() } @@ -42,7 +51,6 @@ class Global : ConfigContainer() { val defaultVideoPlaybackRate = float("default_video_playback_rate", 1.0F) { requireRestart(); inputCheck = { (it.toFloatOrNull() ?: 1.0F) in 0.1F..4.0F} } val videoPlaybackRateSlider = boolean("video_playback_rate_slider") { requireRestart() } val disableGooglePlayDialogs = boolean("disable_google_play_dialogs") { requireRestart() } - val forceUploadSourceQuality = boolean("force_upload_source_quality") { requireRestart() } val defaultVolumeControls = boolean("default_volume_controls") { requireRestart() } val hideActiveMusic = boolean("hide_active_music") { requireRestart() } val disableSnapSplitting = boolean("disable_snap_splitting") { addNotices(FeatureNotice.INTERNAL_BEHAVIOR) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/FeatureManager.kt @@ -79,7 +79,7 @@ class FeatureManager( SendOverride(), UnlimitedSnapViewTime(), BypassVideoLengthRestriction(), - MediaQualityLevelOverride(), + MediaUploadQualityOverride(), MeoPasscodeBypass(), AppLock(), CameraTweaks(), diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ConfigurationOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/ConfigurationOverride.kt @@ -45,7 +45,7 @@ class ConfigurationOverride : Feature("Configuration Override", loadParams = Fea overrideProperty("STREAK_EXPIRATION_INFO", { context.config.userInterface.streakExpirationInfo.get() }, { true }) - overrideProperty("TRANSCODING_MAX_QUALITY", { context.config.global.forceUploadSourceQuality.get() }, + overrideProperty("TRANSCODING_MAX_QUALITY", { context.config.global.mediaUploadQualityConfig.forceVideoUploadSourceQuality.get() }, { true }, isAppExperiment = true) overrideProperty("CAMERA_ME_ENABLE_HEVC_RECORDING", { context.config.camera.hevcRecording.get() }, diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/global/MediaQualityLevelOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/global/MediaQualityLevelOverride.kt @@ -1,23 +0,0 @@ -package me.rhunk.snapenhance.core.features.impl.global - -import me.rhunk.snapenhance.core.features.Feature -import me.rhunk.snapenhance.core.features.FeatureLoadParams -import me.rhunk.snapenhance.core.util.hook.HookStage -import me.rhunk.snapenhance.core.util.hook.hook -import me.rhunk.snapenhance.mapper.impl.MediaQualityLevelProviderMapper -import java.lang.reflect.Method - -class MediaQualityLevelOverride : Feature("MediaQualityLevelOverride", loadParams = FeatureLoadParams.INIT_SYNC) { - override fun init() { - if (!context.config.global.forceUploadSourceQuality.get()) return - - context.mappings.useMapper(MediaQualityLevelProviderMapper::class) { - mediaQualityLevelProvider.getAsClass()?.hook( - mediaQualityLevelProviderMethod.getAsString()!!, - HookStage.BEFORE - ) { param -> - param.setResult((param.method() as Method).returnType.enumConstants.firstOrNull { it.toString() == "LEVEL_MAX" } ) - } - } - } -}- \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/global/MediaUploadQualityOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/global/MediaUploadQualityOverride.kt @@ -0,0 +1,49 @@ +package me.rhunk.snapenhance.core.features.impl.global + +import android.graphics.Bitmap +import me.rhunk.snapenhance.core.features.Feature +import me.rhunk.snapenhance.core.features.FeatureLoadParams +import me.rhunk.snapenhance.core.util.hook.HookStage +import me.rhunk.snapenhance.core.util.hook.hook +import me.rhunk.snapenhance.mapper.impl.MediaQualityLevelProviderMapper +import java.lang.reflect.Method + +class MediaUploadQualityOverride : Feature("Media Upload Quality Override", loadParams = FeatureLoadParams.INIT_SYNC) { + override fun init() { + if (context.config.global.mediaUploadQualityConfig.forceVideoUploadSourceQuality.get()) { + context.mappings.useMapper(MediaQualityLevelProviderMapper::class) { + mediaQualityLevelProvider.getAsClass()?.hook( + mediaQualityLevelProviderMethod.getAsString()!!, + HookStage.BEFORE + ) { param -> + param.setResult((param.method() as Method).returnType.enumConstants.firstOrNull { it.toString() == "LEVEL_MAX" } ) + } + } + } + + val disableImageCompression by context.config.global.mediaUploadQualityConfig.disableImageCompression + val imageUploadFormat = context.config.global.mediaUploadQualityConfig.customUploadImageFormat.getNullable() + + if (imageUploadFormat != null || disableImageCompression) { + Bitmap::class.java.hook("compress", HookStage.BEFORE) { param -> + if (param.arg<Int>(1) == 0) return@hook + if (param.arg<Any>(0) == Bitmap.CompressFormat.JPEG) { + @Suppress("DEPRECATION") + param.setArg(0, when (imageUploadFormat) { + "png" -> Bitmap.CompressFormat.PNG + "webp" -> Bitmap.CompressFormat.WEBP + "jpeg" -> Bitmap.CompressFormat.JPEG + else -> Bitmap.CompressFormat.JPEG + }) + if (disableImageCompression) { + param.setArg(1, 100) + } + } + } + + findClass("com.snap.camera.jni.SnapImageTranscoder").hook("nativeEncodeBitmapToJpeg", HookStage.BEFORE) { + it.setResult(ByteArray(0)) + } + } + } +}+ \ No newline at end of file