mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-28 12:30:12 +02:00
feat: bypass video length restriction
- single and split mode
This commit is contained in:
parent
e4443279d6
commit
0ba1eb4a8b
@ -362,9 +362,9 @@
|
||||
"name": "Block Ads",
|
||||
"description": "Prevents Advertisements from being displayed"
|
||||
},
|
||||
"disable_video_length_restrictions": {
|
||||
"name": "Disable Video Length Restrictions",
|
||||
"description": "Disables Snapchat's maximum video length restriction"
|
||||
"bypass_video_length_restriction": {
|
||||
"name": "Bypass Video Length Restrictions",
|
||||
"description": "Single: sends a single video\nSplit: split videos after editing"
|
||||
},
|
||||
"disable_google_play_dialogs": {
|
||||
"name": "Disable Google Play Services Dialogs",
|
||||
@ -664,6 +664,10 @@
|
||||
"added_by_group_chat": "By Group Chat",
|
||||
"added_by_qr_code": "By QR Code",
|
||||
"added_by_community": "By Community"
|
||||
},
|
||||
"bypass_video_length_restriction": {
|
||||
"single": "Single media",
|
||||
"split": "Split media"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -7,7 +7,7 @@ class Global : ConfigContainer() {
|
||||
val snapchatPlus = boolean("snapchat_plus") { addNotices(FeatureNotice.BAN_RISK); requireRestart() }
|
||||
val disableMetrics = boolean("disable_metrics")
|
||||
val blockAds = boolean("block_ads")
|
||||
val disableVideoLengthRestrictions = boolean("disable_video_length_restrictions") { addNotices(FeatureNotice.BAN_RISK) }
|
||||
val bypassVideoLengthRestriction = unique("bypass_video_length_restriction", "split", "single") { addNotices(FeatureNotice.BAN_RISK); requireRestart() }
|
||||
val disableGooglePlayDialogs = boolean("disable_google_play_dialogs") { requireRestart() }
|
||||
val forceMediaSourceQuality = boolean("force_media_source_quality")
|
||||
val disableSnapSplitting = boolean("disable_snap_splitting") { addNotices(FeatureNotice.INTERNAL_BEHAVIOR) }
|
||||
|
@ -0,0 +1,72 @@
|
||||
package me.rhunk.snapenhance.features.impl.tweaks
|
||||
|
||||
import android.os.Build
|
||||
import android.os.FileObserver
|
||||
import com.google.gson.JsonParser
|
||||
import me.rhunk.snapenhance.core.event.events.impl.SendMessageWithContentEvent
|
||||
import me.rhunk.snapenhance.core.util.ktx.setObjectField
|
||||
import me.rhunk.snapenhance.features.Feature
|
||||
import me.rhunk.snapenhance.features.FeatureLoadParams
|
||||
import me.rhunk.snapenhance.hook.HookStage
|
||||
import me.rhunk.snapenhance.hook.hookConstructor
|
||||
import java.io.File
|
||||
|
||||
class BypassVideoLengthRestriction :
|
||||
Feature("BypassVideoLengthRestriction", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) {
|
||||
private lateinit var fileObserver: FileObserver
|
||||
|
||||
override fun asyncOnActivityCreate() {
|
||||
val mode = context.config.global.bypassVideoLengthRestriction.getNullable()
|
||||
|
||||
if (mode == "single") {
|
||||
//fix black videos when story is posted
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val postedStorySnapFolder =
|
||||
File(context.androidContext.filesDir, "file_manager/posted_story_snap")
|
||||
|
||||
fileObserver = (object : FileObserver(postedStorySnapFolder, MOVED_TO) {
|
||||
override fun onEvent(event: Int, path: String?) {
|
||||
if (event != MOVED_TO || path?.endsWith("posted_story_snap.2") != true) return
|
||||
fileObserver.stopWatching()
|
||||
|
||||
val file = File(postedStorySnapFolder, path)
|
||||
runCatching {
|
||||
val fileContent = JsonParser.parseReader(file.reader()).asJsonObject
|
||||
if (fileContent["timerOrDuration"].asLong < 0) file.delete()
|
||||
}.onFailure {
|
||||
context.log.error("Failed to read story metadata file", it)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
context.event.subscribe(SendMessageWithContentEvent::class) { event ->
|
||||
if (event.destinations.stories.isEmpty()) return@subscribe
|
||||
fileObserver.startWatching()
|
||||
}
|
||||
}
|
||||
|
||||
context.mappings.getMappedClass("DefaultMediaItem")
|
||||
.hookConstructor(HookStage.BEFORE) { param ->
|
||||
//set the video length argument
|
||||
param.setArg(5, -1L)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: allow split from any source
|
||||
if (mode == "split") {
|
||||
val cameraRollId = context.mappings.getMappedMap("CameraRollMediaId")
|
||||
// memories grid
|
||||
findClass(cameraRollId["class"].toString()).hookConstructor(HookStage.AFTER) { param ->
|
||||
//set the durationMs field
|
||||
param.thisObject<Any>()
|
||||
.setObjectField(cameraRollId["durationMsField"].toString(), -1L)
|
||||
}
|
||||
|
||||
// chat camera roll grid
|
||||
findClass("com.snap.impala.common.media.MediaLibraryItem").hookConstructor(HookStage.BEFORE) { param ->
|
||||
//set the video length argument
|
||||
param.setArg(3, -1L)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package me.rhunk.snapenhance.features.impl.tweaks
|
||||
|
||||
import android.os.Build
|
||||
import android.os.FileObserver
|
||||
import com.google.gson.JsonParser
|
||||
import me.rhunk.snapenhance.core.event.events.impl.SendMessageWithContentEvent
|
||||
import me.rhunk.snapenhance.features.Feature
|
||||
import me.rhunk.snapenhance.features.FeatureLoadParams
|
||||
import me.rhunk.snapenhance.hook.HookStage
|
||||
import me.rhunk.snapenhance.hook.Hooker
|
||||
import java.io.File
|
||||
|
||||
class DisableVideoLengthRestriction : Feature("DisableVideoLengthRestriction", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) {
|
||||
private lateinit var fileObserver: FileObserver
|
||||
|
||||
override fun asyncOnActivityCreate() {
|
||||
val defaultMediaItem = context.mappings.getMappedClass("DefaultMediaItem")
|
||||
val isState by context.config.global.disableVideoLengthRestrictions
|
||||
|
||||
//fix black videos when story is posted
|
||||
if (isState && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val postedStorySnapFolder = File(context.androidContext.filesDir, "file_manager/posted_story_snap")
|
||||
|
||||
fileObserver = (object : FileObserver(postedStorySnapFolder, MOVED_TO) {
|
||||
override fun onEvent(event: Int, path: String?) {
|
||||
if (event != MOVED_TO || path?.endsWith("posted_story_snap.2") != true) return
|
||||
fileObserver.stopWatching()
|
||||
|
||||
val file = File(postedStorySnapFolder, path)
|
||||
runCatching {
|
||||
val fileContent = JsonParser.parseReader(file.reader()).asJsonObject
|
||||
if (fileContent["timerOrDuration"].asLong < 0) file.delete()
|
||||
}.onFailure {
|
||||
context.log.error("Failed to read story metadata file", it)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
context.event.subscribe(SendMessageWithContentEvent::class) { event ->
|
||||
if (event.destinations.stories.isEmpty()) return@subscribe
|
||||
fileObserver.startWatching()
|
||||
}
|
||||
}
|
||||
|
||||
Hooker.hookConstructor(defaultMediaItem, HookStage.BEFORE, { isState }) { param ->
|
||||
//set the video length argument
|
||||
param.setArg(5, -1L)
|
||||
}
|
||||
}
|
||||
}
|
@ -84,7 +84,7 @@ class FeatureManager(
|
||||
ConfigurationOverride::class,
|
||||
SendOverride::class,
|
||||
UnlimitedSnapViewTime::class,
|
||||
DisableVideoLengthRestriction::class,
|
||||
BypassVideoLengthRestriction::class,
|
||||
MediaQualityLevelOverride::class,
|
||||
MeoPasscodeBypass::class,
|
||||
AppPasscode::class,
|
||||
|
@ -1,11 +1,24 @@
|
||||
package me.rhunk.snapenhance.mapper.impl
|
||||
|
||||
import me.rhunk.snapenhance.mapper.AbstractClassMapper
|
||||
import me.rhunk.snapenhance.mapper.ext.findConstString
|
||||
import me.rhunk.snapenhance.mapper.ext.getClassName
|
||||
import me.rhunk.snapenhance.mapper.ext.isAbstract
|
||||
|
||||
class DefaultMediaItemMapper : AbstractClassMapper() {
|
||||
init {
|
||||
mapper {
|
||||
for (clazz in classes) {
|
||||
if (clazz.methods.find { it.name == "toString" }?.implementation?.findConstString("CameraRollMediaId", contains = true) != true) {
|
||||
continue
|
||||
}
|
||||
val durationMsField = clazz.fields.firstOrNull { it.type == "J" } ?: continue
|
||||
|
||||
addMapping("CameraRollMediaId", "class" to clazz.getClassName(), "durationMsField" to durationMsField.name)
|
||||
return@mapper
|
||||
}
|
||||
}
|
||||
|
||||
mapper {
|
||||
for (clazz in classes) {
|
||||
val superClass = getClass(clazz.superclass) ?: continue
|
||||
|
Loading…
x
Reference in New Issue
Block a user