Downloader allow duplicate and ffmpeg shortest sync fixes
This commit is contained in:
@@ -283,7 +283,7 @@ class DownloadProcessor (
|
||||
while (true) {
|
||||
val existingFile = outputFileFolder.findFile(finalFileName) ?: break
|
||||
|
||||
if (existingFile.length() == inputFile.length()) {
|
||||
if (existingFile.length() == inputFile.length() && !remoteSideContext.config.root.downloader.allowDuplicate.get()) {
|
||||
val existingInputStream = remoteSideContext.androidContext.contentResolver.openInputStream(existingFile.uri)
|
||||
if (existingInputStream != null && streamsMatch(existingInputStream, inputFile.inputStream())) {
|
||||
return GallerySaveResult(existingFile.uri, alreadyDownloaded = true)
|
||||
@@ -376,7 +376,7 @@ class DownloadProcessor (
|
||||
var destFile = File(destDir, fileName)
|
||||
var suffix = 1
|
||||
while (destFile.exists()) {
|
||||
if (destFile.length() == inputFile.length() && streamsMatch(destFile.inputStream(), inputFile.inputStream())) {
|
||||
if (destFile.length() == inputFile.length() && !remoteSideContext.config.root.downloader.allowDuplicate.get() && streamsMatch(destFile.inputStream(), inputFile.inputStream())) {
|
||||
return GallerySaveResult(Uri.fromFile(destFile), alreadyDownloaded = true)
|
||||
}
|
||||
destFile = File(destDir, appendNameSuffix(fileName, suffix++))
|
||||
|
||||
@@ -168,10 +168,7 @@ class FFMpegProcessor(
|
||||
}
|
||||
Action.MERGE_OVERLAY -> {
|
||||
inputArguments += "-i" to args.overlay!!.absolutePath
|
||||
outputArguments += "-filter_complex" to "\"[1:v][0:v]scale2ref=w=iw:h=ih[ovrl][main];[main][ovrl]overlay=(W-w)/2:(H-h)/2,scale=2*trunc(iw/2):2*trunc(ih/2)[vout]\""
|
||||
outputArguments += "-map" to "\"[vout]\""
|
||||
outputArguments += "-map" to "\"0:a?\""
|
||||
outputArguments += "-shortest" to ""
|
||||
outputArguments += "-filter_complex" to "\"[0]scale2ref[img][vid];[img]setsar=1[img];[vid]nullsink;[img][1]overlay=(W-w)/2:(H-h)/2,scale=2*trunc(iw*sar/2):2*trunc(ih/2)\""
|
||||
}
|
||||
Action.CONVERSION -> {
|
||||
if (ffmpegOptions.customAudioCodec.isEmpty()) {
|
||||
|
||||
@@ -189,19 +189,23 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
|
||||
if (isBatch) {
|
||||
batchSuccessCount.incrementAndGet()
|
||||
if (downloadLogging.contains("success")) {
|
||||
modCtx.inAppOverlay.showStatusToast(
|
||||
icon = Icons.Outlined.DownloadDone,
|
||||
text = translations.format("batch_progress_toast", "current" to (batchSuccessCount.get() + batchFailureCount.get()).toString(), "total" to batchTotalCount.get().toString()),
|
||||
durationMs = 1300
|
||||
)
|
||||
modCtx.runOnUiThread {
|
||||
modCtx.inAppOverlay.showStatusToast(
|
||||
icon = Icons.Outlined.DownloadDone,
|
||||
text = translations.format("batch_progress_toast", "current" to (batchSuccessCount.get() + batchFailureCount.get()).toString(), "total" to batchTotalCount.get().toString()),
|
||||
durationMs = 1300
|
||||
)
|
||||
}
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
if (downloadLogging.contains("success")) {
|
||||
val toastText = translations.format("content_saved_toast", "path" to java.io.File(finalOutputFile).name)
|
||||
if (modCtx.isMainActivityPaused) modCtx.shortToast(toastText)
|
||||
modCtx.inAppOverlay.showStatusToast(Icons.Outlined.DownloadDone, toastText, 1300)
|
||||
modCtx.runOnUiThread {
|
||||
if (modCtx.isMainActivityPaused) modCtx.shortToast(toastText)
|
||||
modCtx.inAppOverlay.showStatusToast(Icons.Outlined.DownloadDone, toastText, 1300)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,16 +213,20 @@ class MediaDownloader : MessagingRuleFeature("MediaDownloader", MessagingRuleTyp
|
||||
override fun onProgress(message: String) {
|
||||
if (isBatch || !downloadLogging.contains("progress")) return
|
||||
val toastText = message.ifBlank { translations["download_started_toast"] ?: "Started" }
|
||||
if (modCtx.isMainActivityPaused) modCtx.shortToast(toastText)
|
||||
modCtx.inAppOverlay.showStatusToast(Icons.Outlined.Info, toastText, 1300)
|
||||
modCtx.runOnUiThread {
|
||||
if (modCtx.isMainActivityPaused) modCtx.shortToast(toastText)
|
||||
modCtx.inAppOverlay.showStatusToast(Icons.Outlined.Info, toastText, 1300)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(message: String, throwable: String?) {
|
||||
if (!downloadLogging.contains("failure")) return
|
||||
val errorText = translations[if (message == "Failed to download") "failed_generic_toast" else message] ?: message
|
||||
if (isBatch) { batchFailureCount.incrementAndGet(); return }
|
||||
if (modCtx.isMainActivityPaused) modCtx.shortToast(errorText)
|
||||
modCtx.inAppOverlay.showStatusToast(Icons.Outlined.ErrorOutline, errorText, 1300)
|
||||
modCtx.runOnUiThread {
|
||||
if (modCtx.isMainActivityPaused) modCtx.shortToast(errorText)
|
||||
modCtx.inAppOverlay.showStatusToast(Icons.Outlined.ErrorOutline, errorText, 1300)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -54,7 +54,6 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A
|
||||
private const val NOTIFICATION_GROUP_KEY = "purrfectsnap.AUTO_OPEN"
|
||||
private const val PREF_TOTAL_OPENED = "auto_open_total_opened"
|
||||
private const val PREF_SESSION_START = "auto_open_session_start"
|
||||
private const val PREF_PROCESSED_IDS = "auto_open_processed_ids"
|
||||
|
||||
private const val LAZY_SAVE_INTERVAL_MS = 600_000L
|
||||
}
|
||||
@@ -79,12 +78,6 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A
|
||||
private val prefs by lazy { this@AutoOpenSnaps.context.androidContext.getSharedPreferences("me.eternal.purrfectsnap_preferences", Context.MODE_PRIVATE) }
|
||||
private val messaging by lazy { this@AutoOpenSnaps.context.feature(Messaging::class) }
|
||||
private var wakeLock: PowerManager.WakeLock? = null
|
||||
private var wakeLockCooldownJob: Job? = null
|
||||
|
||||
// Optimized Metadata Cache: 500 entries limit to prevent OOM crashes
|
||||
private val metadataCache = Collections.synchronizedMap(object : LinkedHashMap<String, String>(100, 0.75f, true) {
|
||||
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<String, String>?): Boolean = size > 500
|
||||
})
|
||||
|
||||
private var currentStatusText = "Monitoring..."
|
||||
private var currentSpeedText = "Full Speed"
|
||||
@@ -117,6 +110,7 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
restorePersistence()
|
||||
createNotificationChannels()
|
||||
|
||||
// NATIVE HOOKS: Ensuring Snapchat never sees the app as "In Background"
|
||||
@@ -127,6 +121,14 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A
|
||||
val state = param.arg<Any>(0).toString()
|
||||
if (state == "INACTIVE" || state == "BACKGROUND") param.setResult(null)
|
||||
}
|
||||
// INDUSTRIAL FIX: Restoring the universal v1.6.8 background wake-up hook
|
||||
hookConstructor(HookStage.AFTER) { param ->
|
||||
methods.firstOrNull { it.name == "appStateChanged" }?.let { method ->
|
||||
val enumClass = method.parameterTypes[0]
|
||||
val activeState = enumClass.enumConstants?.firstOrNull { it.toString() == "ACTIVE" || it.toString() == "FOREGROUND" }
|
||||
if (activeState != null) method.invoke(param.thisObject<Any>(), activeState)
|
||||
}
|
||||
}
|
||||
}
|
||||
findClass("com.snapchat.client.network_manager.NetworkManager\$CppProxy").apply {
|
||||
hook("onAppForegrounded", HookStage.BEFORE) { param -> param.setResult(null) }
|
||||
@@ -201,7 +203,7 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A
|
||||
sessionProcessed.incrementAndGet(); totalProcessed.incrementAndGet(); recordSpeedTimestamp()
|
||||
val duration = System.currentTimeMillis() - startTime
|
||||
averageProcessingTime.set((averageProcessingTime.get() * 0.7 + duration * 0.3).toLong())
|
||||
break
|
||||
triggerLazySave(); break
|
||||
}
|
||||
delay((autoOpenConfig.retryDelay as PropertyValue<Int>).get().toLong())
|
||||
}
|
||||
@@ -265,18 +267,7 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A
|
||||
|
||||
val contentType = message.messageContent?.contentType
|
||||
if (contentType != ContentType.SNAP && contentType != ContentType.EXTERNAL_MEDIA) return@subscribe
|
||||
|
||||
// Prevent processing of snaps already viewed manually by the local user
|
||||
if (message.messageMetadata?.openedBy?.any { it.toString() == this@AutoOpenSnaps.context.database.myUserId } == true) {
|
||||
openedSnapsIds.add(clientMessageId)
|
||||
return@subscribe
|
||||
}
|
||||
|
||||
if (!canUseRule(conversationId)) return@subscribe
|
||||
|
||||
val currentQueueSize = synchronized(queuedSnaps) { queuedSnaps.size }
|
||||
if (currentQueueSize >= (autoOpenConfig.queueSize as PropertyValue<Int>).get()) return@subscribe
|
||||
|
||||
if (openedSnapsIds.contains(clientMessageId)) return@subscribe
|
||||
openedSnapsIds.add(clientMessageId)
|
||||
|
||||
@@ -286,10 +277,32 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A
|
||||
synchronized(queuedSnaps) { queuedSnaps.add(item) }
|
||||
snapChannel.trySend(item)
|
||||
|
||||
acquireWakeLock(); updateStatusNotification()
|
||||
acquireWakeLock(); updateStatusNotification(); triggerLazySave()
|
||||
}
|
||||
}
|
||||
|
||||
private fun triggerLazySave() {
|
||||
needsSaving.set(true)
|
||||
if (isSaving.compareAndSet(false, true)) {
|
||||
this@AutoOpenSnaps.context.coroutineScope.launch(Dispatchers.IO) {
|
||||
while (needsSaving.get() && engineActive.get()) {
|
||||
needsSaving.set(false); saveQueueToDisk(); delay(LAZY_SAVE_INTERVAL_MS)
|
||||
}
|
||||
isSaving.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveQueueToDisk() {
|
||||
prefs.edit { putInt(PREF_TOTAL_OPENED, totalProcessed.get()); putLong(PREF_SESSION_START, sessionStartTime.get()) }
|
||||
}
|
||||
|
||||
private fun restorePersistence() {
|
||||
val savedStartTime = prefs.getLong(PREF_SESSION_START, 0)
|
||||
if (System.currentTimeMillis() - savedStartTime > 21600000) return
|
||||
totalProcessed.set(prefs.getInt(PREF_TOTAL_OPENED, 0)); sessionStartTime.set(savedStartTime)
|
||||
}
|
||||
|
||||
private fun isWifiConnected(): Boolean {
|
||||
val cm = this@AutoOpenSnaps.context.androidContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
return cm.getNetworkCapabilities(cm.activeNetwork)?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true
|
||||
@@ -452,4 +465,4 @@ class AutoOpenSnaps: MessagingRuleFeature("Auto Open Snaps", MessagingRuleType.A
|
||||
private fun getSnapContentType(type: ContentType?): String = when (type) { ContentType.SNAP -> "Photo/Video"; ContentType.EXTERNAL_MEDIA -> "Media"; else -> "Message" }
|
||||
}
|
||||
|
||||
data class SnapQueueItem(val conversationId: String, val messageId: Long, val serverMessageId: Long, val senderId: String, val senderName: String, val conversationType: String, val contentType: String, val timestamp: Long = System.currentTimeMillis())
|
||||
data class SnapQueueItem(val conversationId: String, val messageId: Long, val serverMessageId: Long, val senderId: String, val senderName: String, val conversationType: String, val contentType: String)
|
||||
|
||||
Reference in New Issue
Block a user