mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-30 21:40:17 +02:00
fix: unique hash (#81)
* fix: media hash reference * fix: download manager receiver longToast -> shortToast * fix: media downloader sanitize file name fix playlistUrl bug fix dash download duration --------- Co-authored-by: rhunk <101876869+rhunk@users.noreply.github.com>
This commit is contained in:
parent
cc59f9d060
commit
c501682a2f
@ -87,6 +87,7 @@
|
|||||||
"conversation_info": "\uD83D\uDC64 Conversation Info"
|
"conversation_info": "\uD83D\uDC64 Conversation Info"
|
||||||
},
|
},
|
||||||
"download_options": {
|
"download_options": {
|
||||||
|
"allow_duplicate": "Allow duplicate downloads",
|
||||||
"format_user_folder": "Create folder for each user",
|
"format_user_folder": "Create folder for each user",
|
||||||
"format_hash": "Add a unique hash to the file path",
|
"format_hash": "Add a unique hash to the file path",
|
||||||
"format_username": "Add the username to the file path",
|
"format_username": "Add the username to the file path",
|
||||||
@ -232,6 +233,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"download_manager_receiver": {
|
"download_manager_receiver": {
|
||||||
|
"already_queued_toast": "Media already in queue!",
|
||||||
|
"already_downloaded_toast": "Media already downloaded!",
|
||||||
"saved_toast": "Saved to {path}",
|
"saved_toast": "Saved to {path}",
|
||||||
"download_toast": "Downloading {path}...",
|
"download_toast": "Downloading {path}...",
|
||||||
"processing_toast": "Processing {path}...",
|
"processing_toast": "Processing {path}...",
|
||||||
|
@ -16,7 +16,7 @@ object Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun error(throwable: Throwable) {
|
fun error(throwable: Throwable) {
|
||||||
Log.e(TAG, "",throwable)
|
Log.e(TAG, "", throwable)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun error(message: Any?) {
|
fun error(message: Any?) {
|
||||||
|
@ -115,8 +115,16 @@ enum class ConfigProperty(
|
|||||||
"download_options",
|
"download_options",
|
||||||
ConfigCategory.MEDIA_MANAGEMENT,
|
ConfigCategory.MEDIA_MANAGEMENT,
|
||||||
ConfigStateListValue(
|
ConfigStateListValue(
|
||||||
listOf("format_user_folder", "format_hash", "format_date_time", "format_username", "merge_overlay"),
|
listOf(
|
||||||
|
"allow_duplicate",
|
||||||
|
"format_user_folder",
|
||||||
|
"format_hash",
|
||||||
|
"format_date_time",
|
||||||
|
"format_username",
|
||||||
|
"merge_overlay"
|
||||||
|
),
|
||||||
mutableMapOf(
|
mutableMapOf(
|
||||||
|
"allow_duplicate" to false,
|
||||||
"format_user_folder" to true,
|
"format_user_folder" to true,
|
||||||
"format_hash" to true,
|
"format_hash" to true,
|
||||||
"format_date_time" to true,
|
"format_date_time" to true,
|
||||||
|
@ -13,7 +13,8 @@ class DownloadManagerClient (
|
|||||||
private val outputPath: String,
|
private val outputPath: String,
|
||||||
private val mediaDisplaySource: String?,
|
private val mediaDisplaySource: String?,
|
||||||
private val mediaDisplayType: String?,
|
private val mediaDisplayType: String?,
|
||||||
private val iconUrl: String?
|
private val iconUrl: String?,
|
||||||
|
private val uniqueHash: String?
|
||||||
) {
|
) {
|
||||||
private fun sendToBroadcastReceiver(bundle: Bundle) {
|
private fun sendToBroadcastReceiver(bundle: Bundle) {
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
@ -32,10 +33,11 @@ class DownloadManagerClient (
|
|||||||
putString("mediaDisplaySource", mediaDisplaySource)
|
putString("mediaDisplaySource", mediaDisplaySource)
|
||||||
putString("mediaDisplayType", mediaDisplayType)
|
putString("mediaDisplayType", mediaDisplayType)
|
||||||
putString("iconUrl", iconUrl)
|
putString("iconUrl", iconUrl)
|
||||||
|
putString("uniqueHash", uniqueHash)
|
||||||
}.apply(extras))
|
}.apply(extras))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun downloadDashMedia(playlistUrl: String, offsetTime: Long, duration: Long) {
|
fun downloadDashMedia(playlistUrl: String, offsetTime: Long, duration: Long?) {
|
||||||
sendToBroadcastReceiver(
|
sendToBroadcastReceiver(
|
||||||
DownloadRequest(
|
DownloadRequest(
|
||||||
inputMedias = arrayOf(playlistUrl),
|
inputMedias = arrayOf(playlistUrl),
|
||||||
@ -44,8 +46,8 @@ class DownloadManagerClient (
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
putBundle("dashOptions", Bundle().apply {
|
putBundle("dashOptions", Bundle().apply {
|
||||||
putLong("offsetTime", offsetTime)
|
putString("offsetTime", offsetTime.toString())
|
||||||
putLong("duration", duration)
|
duration?.let { putString("duration", it.toString()) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ class DownloadManagerReceiver : BroadcastReceiver() {
|
|||||||
if (!it.endsWith("/")) "$it/" else it
|
if (!it.endsWith("/")) "$it/" else it
|
||||||
}
|
}
|
||||||
|
|
||||||
longToast(
|
shortToast(
|
||||||
translation.format("saved_toast", "path" to outputFile.absolutePath.replace(parentName ?: "", ""))
|
translation.format("saved_toast", "path" to outputFile.absolutePath.replace(parentName ?: "", ""))
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -255,9 +255,19 @@ class DownloadManagerReceiver : BroadcastReceiver() {
|
|||||||
this.context = context
|
this.context = context
|
||||||
SharedContext.ensureInitialized(context)
|
SharedContext.ensureInitialized(context)
|
||||||
|
|
||||||
|
|
||||||
val downloadRequest = DownloadRequest.fromBundle(intent.extras!!)
|
val downloadRequest = DownloadRequest.fromBundle(intent.extras!!)
|
||||||
|
|
||||||
|
SharedContext.downloadTaskManager.canDownloadMedia(downloadRequest.getUniqueHash())?.let { downloadStage ->
|
||||||
|
shortToast(
|
||||||
|
translation[if (downloadStage.isFinalStage) {
|
||||||
|
"already_downloaded_toast"
|
||||||
|
} else {
|
||||||
|
"already_queued_toast"
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
val pendingDownloadObject = PendingDownload.fromBundle(intent.extras!!)
|
val pendingDownloadObject = PendingDownload.fromBundle(intent.extras!!)
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ class DownloadTaskManager {
|
|||||||
SQLiteDatabaseHelper.createTablesFromSchema(this, mapOf(
|
SQLiteDatabaseHelper.createTablesFromSchema(this, mapOf(
|
||||||
"tasks" to listOf(
|
"tasks" to listOf(
|
||||||
"id INTEGER PRIMARY KEY AUTOINCREMENT",
|
"id INTEGER PRIMARY KEY AUTOINCREMENT",
|
||||||
|
"hash VARCHAR UNIQUE",
|
||||||
"outputPath TEXT",
|
"outputPath TEXT",
|
||||||
"outputFile TEXT",
|
"outputFile TEXT",
|
||||||
"mediaDisplayType TEXT",
|
"mediaDisplayType TEXT",
|
||||||
@ -32,8 +33,9 @@ class DownloadTaskManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun addTask(task: PendingDownload): Int {
|
fun addTask(task: PendingDownload): Int {
|
||||||
taskDatabase.execSQL("INSERT INTO tasks (outputPath, outputFile, mediaDisplayType, mediaDisplaySource, iconUrl, downloadStage) VALUES (?, ?, ?, ?, ?, ?)",
|
taskDatabase.execSQL("INSERT INTO tasks (hash, outputPath, outputFile, mediaDisplayType, mediaDisplaySource, iconUrl, downloadStage) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||||
arrayOf(
|
arrayOf(
|
||||||
|
task.uniqueHash,
|
||||||
task.outputPath,
|
task.outputPath,
|
||||||
task.outputFile,
|
task.outputFile,
|
||||||
task.mediaDisplayType,
|
task.mediaDisplayType,
|
||||||
@ -51,8 +53,9 @@ class DownloadTaskManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateTask(task: PendingDownload) {
|
fun updateTask(task: PendingDownload) {
|
||||||
taskDatabase.execSQL("UPDATE tasks SET outputPath = ?, outputFile = ?, mediaDisplayType = ?, mediaDisplaySource = ?, iconUrl = ?, downloadStage = ? WHERE id = ?",
|
taskDatabase.execSQL("UPDATE tasks SET hash = ?, outputPath = ?, outputFile = ?, mediaDisplayType = ?, mediaDisplaySource = ?, iconUrl = ?, downloadStage = ? WHERE id = ?",
|
||||||
arrayOf(
|
arrayOf(
|
||||||
|
task.uniqueHash,
|
||||||
task.outputPath,
|
task.outputPath,
|
||||||
task.outputFile,
|
task.outputFile,
|
||||||
task.mediaDisplayType,
|
task.mediaDisplayType,
|
||||||
@ -71,6 +74,28 @@ class DownloadTaskManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("Range")
|
||||||
|
fun canDownloadMedia(mediaIdentifier: String?): DownloadStage? {
|
||||||
|
if (mediaIdentifier == null) return null
|
||||||
|
|
||||||
|
val cursor = taskDatabase.rawQuery("SELECT * FROM tasks WHERE hash = ?", arrayOf(mediaIdentifier))
|
||||||
|
if (cursor.count > 0) {
|
||||||
|
cursor.moveToFirst()
|
||||||
|
val downloadStage = DownloadStage.valueOf(cursor.getString(cursor.getColumnIndex("downloadStage")))
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
//if the stage has reached a final stage and is not in a saved state, remove the task
|
||||||
|
if (downloadStage.isFinalStage && downloadStage != DownloadStage.SAVED) {
|
||||||
|
taskDatabase.execSQL("DELETE FROM tasks WHERE hash = ?", arrayOf(mediaIdentifier))
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return downloadStage
|
||||||
|
}
|
||||||
|
cursor.close()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
fun isEmpty(): Boolean {
|
fun isEmpty(): Boolean {
|
||||||
return cachedTasks.isEmpty() && pendingTasks.isEmpty()
|
return cachedTasks.isEmpty() && pendingTasks.isEmpty()
|
||||||
}
|
}
|
||||||
@ -127,6 +152,7 @@ class DownloadTaskManager {
|
|||||||
outputPath = cursor.getString(cursor.getColumnIndex("outputPath")),
|
outputPath = cursor.getString(cursor.getColumnIndex("outputPath")),
|
||||||
mediaDisplayType = cursor.getString(cursor.getColumnIndex("mediaDisplayType")),
|
mediaDisplayType = cursor.getString(cursor.getColumnIndex("mediaDisplayType")),
|
||||||
mediaDisplaySource = cursor.getString(cursor.getColumnIndex("mediaDisplaySource")),
|
mediaDisplaySource = cursor.getString(cursor.getColumnIndex("mediaDisplaySource")),
|
||||||
|
uniqueHash = cursor.getString(cursor.getColumnIndex("hash")),
|
||||||
iconUrl = cursor.getString(cursor.getColumnIndex("iconUrl"))
|
iconUrl = cursor.getString(cursor.getColumnIndex("iconUrl"))
|
||||||
).apply {
|
).apply {
|
||||||
downloadStage = DownloadStage.valueOf(cursor.getString(cursor.getColumnIndex("downloadStage")))
|
downloadStage = DownloadStage.valueOf(cursor.getString(cursor.getColumnIndex("downloadStage")))
|
||||||
|
@ -19,7 +19,8 @@ class DownloadRequest(
|
|||||||
private val flags: Int = 0,
|
private val flags: Int = 0,
|
||||||
private val dashOptions: Map<String, String?>? = null,
|
private val dashOptions: Map<String, String?>? = null,
|
||||||
private val mediaDisplaySource: String? = null,
|
private val mediaDisplaySource: String? = null,
|
||||||
private val mediaDisplayType: String? = null
|
private val mediaDisplayType: String? = null,
|
||||||
|
private val uniqueHash: String? = null
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun fromBundle(bundle: Bundle): DownloadRequest {
|
fun fromBundle(bundle: Bundle): DownloadRequest {
|
||||||
@ -39,7 +40,8 @@ class DownloadRequest(
|
|||||||
options.getString(key)
|
options.getString(key)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
flags = bundle.getInt("flags", 0)
|
flags = bundle.getInt("flags", 0),
|
||||||
|
uniqueHash = bundle.getString("uniqueHash")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,6 +64,7 @@ class DownloadRequest(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
putInt("flags", flags)
|
putInt("flags", flags)
|
||||||
|
putString("uniqueHash", uniqueHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,10 +88,6 @@ class DownloadRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getInputMedia(index: Int): String? {
|
|
||||||
return inputMedias.getOrNull(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getInputMedias(): List<InputMedia> {
|
fun getInputMedias(): List<InputMedia> {
|
||||||
return inputMedias.mapIndexed { index, uri ->
|
return inputMedias.mapIndexed { index, uri ->
|
||||||
InputMedia(
|
InputMedia(
|
||||||
@ -102,4 +101,6 @@ class DownloadRequest(
|
|||||||
fun getInputType(index: Int): DownloadMediaType? {
|
fun getInputType(index: Int): DownloadMediaType? {
|
||||||
return inputTypes.getOrNull(index)?.let { DownloadMediaType.valueOf(it) }
|
return inputTypes.getOrNull(index)?.let { DownloadMediaType.valueOf(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getUniqueHash() = uniqueHash
|
||||||
}
|
}
|
@ -13,7 +13,8 @@ data class PendingDownload(
|
|||||||
val outputPath: String,
|
val outputPath: String,
|
||||||
val mediaDisplayType: String?,
|
val mediaDisplayType: String?,
|
||||||
val mediaDisplaySource: String?,
|
val mediaDisplaySource: String?,
|
||||||
val iconUrl: String?
|
val iconUrl: String?,
|
||||||
|
val uniqueHash: String?
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun fromBundle(bundle: Bundle): PendingDownload {
|
fun fromBundle(bundle: Bundle): PendingDownload {
|
||||||
@ -21,7 +22,8 @@ data class PendingDownload(
|
|||||||
outputPath = bundle.getString("outputPath")!!,
|
outputPath = bundle.getString("outputPath")!!,
|
||||||
mediaDisplayType = bundle.getString("mediaDisplayType"),
|
mediaDisplayType = bundle.getString("mediaDisplayType"),
|
||||||
mediaDisplaySource = bundle.getString("mediaDisplaySource"),
|
mediaDisplaySource = bundle.getString("mediaDisplaySource"),
|
||||||
iconUrl = bundle.getString("iconUrl")
|
iconUrl = bundle.getString("iconUrl"),
|
||||||
|
uniqueHash = bundle.getString("uniqueHash")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,13 +30,14 @@ import me.rhunk.snapenhance.hook.HookAdapter
|
|||||||
import me.rhunk.snapenhance.hook.HookStage
|
import me.rhunk.snapenhance.hook.HookStage
|
||||||
import me.rhunk.snapenhance.hook.Hooker
|
import me.rhunk.snapenhance.hook.Hooker
|
||||||
import me.rhunk.snapenhance.ui.download.MediaFilter
|
import me.rhunk.snapenhance.ui.download.MediaFilter
|
||||||
|
import me.rhunk.snapenhance.util.download.RemoteMediaResolver
|
||||||
|
import me.rhunk.snapenhance.util.getObjectField
|
||||||
|
import me.rhunk.snapenhance.util.protobuf.ProtoReader
|
||||||
|
import me.rhunk.snapenhance.util.snap.BitmojiSelfie
|
||||||
import me.rhunk.snapenhance.util.snap.EncryptionHelper
|
import me.rhunk.snapenhance.util.snap.EncryptionHelper
|
||||||
import me.rhunk.snapenhance.util.snap.MediaDownloaderHelper
|
import me.rhunk.snapenhance.util.snap.MediaDownloaderHelper
|
||||||
import me.rhunk.snapenhance.util.snap.MediaType
|
import me.rhunk.snapenhance.util.snap.MediaType
|
||||||
import me.rhunk.snapenhance.util.snap.PreviewUtils
|
import me.rhunk.snapenhance.util.snap.PreviewUtils
|
||||||
import me.rhunk.snapenhance.util.getObjectField
|
|
||||||
import me.rhunk.snapenhance.util.protobuf.ProtoReader
|
|
||||||
import me.rhunk.snapenhance.util.snap.BitmojiSelfie
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@ -56,10 +57,13 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
|||||||
|
|
||||||
private fun provideClientDownloadManager(
|
private fun provideClientDownloadManager(
|
||||||
pathSuffix: String,
|
pathSuffix: String,
|
||||||
|
mediaIdentifier: String,
|
||||||
mediaDisplaySource: String? = null,
|
mediaDisplaySource: String? = null,
|
||||||
mediaDisplayType: String? = null,
|
mediaDisplayType: String? = null,
|
||||||
friendInfo: FriendInfo? = null
|
friendInfo: FriendInfo? = null
|
||||||
): DownloadManagerClient {
|
): DownloadManagerClient {
|
||||||
|
val generatedHash = mediaIdentifier.hashCode().toString(16)
|
||||||
|
|
||||||
val iconUrl = friendInfo?.takeIf {
|
val iconUrl = friendInfo?.takeIf {
|
||||||
it.bitmojiAvatarId != null && it.bitmojiSelfieId != null
|
it.bitmojiAvatarId != null && it.bitmojiSelfieId != null
|
||||||
}?.let {
|
}?.let {
|
||||||
@ -68,7 +72,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
|||||||
|
|
||||||
val outputPath = File(
|
val outputPath = File(
|
||||||
context.config.string(ConfigProperty.SAVE_FOLDER),
|
context.config.string(ConfigProperty.SAVE_FOLDER),
|
||||||
createNewFilePath(pathSuffix.hashCode(), pathSuffix)
|
createNewFilePath(generatedHash, pathSuffix)
|
||||||
).absolutePath
|
).absolutePath
|
||||||
|
|
||||||
return DownloadManagerClient(
|
return DownloadManagerClient(
|
||||||
@ -76,6 +80,11 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
|||||||
mediaDisplaySource = mediaDisplaySource,
|
mediaDisplaySource = mediaDisplaySource,
|
||||||
mediaDisplayType = mediaDisplayType,
|
mediaDisplayType = mediaDisplayType,
|
||||||
iconUrl = iconUrl,
|
iconUrl = iconUrl,
|
||||||
|
uniqueHash =
|
||||||
|
// If duplicate is allowed, we don't need to pass the hash
|
||||||
|
if (context.config.options(ConfigProperty.DOWNLOAD_OPTIONS)["allow_duplicate"] == false) {
|
||||||
|
generatedHash
|
||||||
|
} else null,
|
||||||
outputPath = outputPath
|
outputPath = outputPath
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -85,9 +94,12 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
|||||||
return isFFmpegPresent
|
return isFFmpegPresent
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createNewFilePath(hash: Int, pathPrefix: String): String {
|
private fun createNewFilePath(hexHash: String, pathPrefix: String): String {
|
||||||
val hexHash = Integer.toHexString(hash)
|
|
||||||
val downloadOptions = context.config.options(ConfigProperty.DOWNLOAD_OPTIONS)
|
val downloadOptions = context.config.options(ConfigProperty.DOWNLOAD_OPTIONS)
|
||||||
|
val sanitizedPathPrefix = pathPrefix
|
||||||
|
.replace(" ", "_")
|
||||||
|
.replace(Regex("[\\\\/:*?\"<>|]"), "")
|
||||||
|
.ifEmpty { hexHash }
|
||||||
|
|
||||||
val currentDateTime = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.ENGLISH).format(System.currentTimeMillis())
|
val currentDateTime = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.ENGLISH).format(System.currentTimeMillis())
|
||||||
|
|
||||||
@ -102,13 +114,13 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (downloadOptions["format_user_folder"] == true) {
|
if (downloadOptions["format_user_folder"] == true) {
|
||||||
finalPath.append(pathPrefix).append("/")
|
finalPath.append(sanitizedPathPrefix).append("/")
|
||||||
}
|
}
|
||||||
if (downloadOptions["format_hash"] == true) {
|
if (downloadOptions["format_hash"] == true) {
|
||||||
appendFileName(hexHash)
|
appendFileName(hexHash)
|
||||||
}
|
}
|
||||||
if (downloadOptions["format_username"] == true) {
|
if (downloadOptions["format_username"] == true) {
|
||||||
appendFileName(pathPrefix)
|
appendFileName(sanitizedPathPrefix)
|
||||||
}
|
}
|
||||||
if (downloadOptions["format_date_time"] == true) {
|
if (downloadOptions["format_date_time"] == true) {
|
||||||
appendFileName(currentDateTime)
|
appendFileName(currentDateTime)
|
||||||
@ -186,7 +198,10 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
|||||||
//messages
|
//messages
|
||||||
paramMap["MESSAGE_ID"]?.toString()?.takeIf { forceDownload || canAutoDownload("friend_snaps") }?.let { id ->
|
paramMap["MESSAGE_ID"]?.toString()?.takeIf { forceDownload || canAutoDownload("friend_snaps") }?.let { id ->
|
||||||
val messageId = id.substring(id.lastIndexOf(":") + 1).toLong()
|
val messageId = id.substring(id.lastIndexOf(":") + 1).toLong()
|
||||||
val senderId: String = context.database.getConversationMessageFromId(messageId)!!.sender_id!!
|
val conversationMessage = context.database.getConversationMessageFromId(messageId)!!
|
||||||
|
|
||||||
|
val senderId = conversationMessage.sender_id!!
|
||||||
|
val conversationId = conversationMessage.client_conversation_id!!
|
||||||
|
|
||||||
if (!forceDownload && context.feature(AntiAutoDownload::class).isUserIgnored(senderId)) {
|
if (!forceDownload && context.feature(AntiAutoDownload::class).isUserIgnored(senderId)) {
|
||||||
return
|
return
|
||||||
@ -194,7 +209,15 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
|||||||
|
|
||||||
val author = context.database.getFriendInfo(senderId) ?: return
|
val author = context.database.getFriendInfo(senderId) ?: return
|
||||||
val authorUsername = author.usernameForSorting!!
|
val authorUsername = author.usernameForSorting!!
|
||||||
downloadOperaMedia(provideClientDownloadManager(authorUsername, authorUsername, MediaFilter.CHAT_MEDIA.mediaDisplayType, friendInfo = author), mediaInfoMap)
|
|
||||||
|
downloadOperaMedia(provideClientDownloadManager(
|
||||||
|
pathSuffix = authorUsername,
|
||||||
|
mediaIdentifier = "$conversationId$senderId$messageId",
|
||||||
|
mediaDisplaySource = authorUsername,
|
||||||
|
mediaDisplayType = MediaFilter.CHAT_MEDIA.mediaDisplayType,
|
||||||
|
friendInfo = author
|
||||||
|
), mediaInfoMap)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,15 +225,20 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
|||||||
paramMap["PLAYLIST_V2_GROUP"]?.toString()?.takeIf {
|
paramMap["PLAYLIST_V2_GROUP"]?.toString()?.takeIf {
|
||||||
it.contains("storyUserId=") && (forceDownload || canAutoDownload("friend_stories"))
|
it.contains("storyUserId=") && (forceDownload || canAutoDownload("friend_stories"))
|
||||||
}?.let { playlistGroup ->
|
}?.let { playlistGroup ->
|
||||||
val storyIdStartIndex = playlistGroup.indexOf("storyUserId=") + 12
|
val storyUserId = (playlistGroup.indexOf("storyUserId=") + 12).let {
|
||||||
val storyUserId = playlistGroup.substring(
|
playlistGroup.substring(it, playlistGroup.indexOf(",", it))
|
||||||
storyIdStartIndex,
|
}
|
||||||
playlistGroup.indexOf(",", storyIdStartIndex)
|
|
||||||
)
|
|
||||||
val author = context.database.getFriendInfo(if (storyUserId == "null") context.database.getMyUserId()!! else storyUserId) ?: return
|
val author = context.database.getFriendInfo(if (storyUserId == "null") context.database.getMyUserId()!! else storyUserId) ?: return
|
||||||
val authorName = author.usernameForSorting!!
|
val authorName = author.usernameForSorting!!
|
||||||
|
|
||||||
downloadOperaMedia(provideClientDownloadManager(authorName, authorName, MediaFilter.STORY.mediaDisplayType, friendInfo = author), mediaInfoMap, )
|
downloadOperaMedia(provideClientDownloadManager(
|
||||||
|
pathSuffix = authorName,
|
||||||
|
mediaIdentifier = paramMap["MEDIA_ID"].toString(),
|
||||||
|
mediaDisplaySource = authorName,
|
||||||
|
mediaDisplayType = MediaFilter.STORY.mediaDisplayType,
|
||||||
|
friendInfo = author
|
||||||
|
), mediaInfoMap)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,13 +250,24 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
|||||||
val userDisplayName = (if (paramMap.containsKey("USER_DISPLAY_NAME")) paramMap["USER_DISPLAY_NAME"].toString() else "").replace(
|
val userDisplayName = (if (paramMap.containsKey("USER_DISPLAY_NAME")) paramMap["USER_DISPLAY_NAME"].toString() else "").replace(
|
||||||
"[^\\x00-\\x7F]".toRegex(),
|
"[^\\x00-\\x7F]".toRegex(),
|
||||||
"")
|
"")
|
||||||
downloadOperaMedia(provideClientDownloadManager("Public-Stories/$userDisplayName", userDisplayName, "Public Story"), mediaInfoMap)
|
|
||||||
|
downloadOperaMedia(provideClientDownloadManager(
|
||||||
|
pathSuffix = "Public-Stories/$userDisplayName",
|
||||||
|
mediaIdentifier = paramMap["SNAP_ID"].toString(),
|
||||||
|
mediaDisplayType = userDisplayName,
|
||||||
|
mediaDisplaySource = "Public Story"
|
||||||
|
), mediaInfoMap)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//spotlight
|
//spotlight
|
||||||
if (snapSource == "SINGLE_SNAP_STORY" && (forceDownload || canAutoDownload("spotlight"))) {
|
if (snapSource == "SINGLE_SNAP_STORY" && (forceDownload || canAutoDownload("spotlight"))) {
|
||||||
downloadOperaMedia(provideClientDownloadManager("Spotlight", mediaDisplayType = MediaFilter.SPOTLIGHT.mediaDisplayType, mediaDisplaySource = paramMap["TIME_STAMP"].toString()), mediaInfoMap)
|
downloadOperaMedia(provideClientDownloadManager(
|
||||||
|
pathSuffix = "Spotlight",
|
||||||
|
mediaIdentifier = paramMap["SNAP_ID"].toString(),
|
||||||
|
mediaDisplayType = MediaFilter.SPOTLIGHT.mediaDisplayType,
|
||||||
|
mediaDisplaySource = paramMap["TIME_STAMP"].toString()
|
||||||
|
), mediaInfoMap)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,12 +295,24 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
|||||||
|
|
||||||
//add 100ms to the start time to prevent the video from starting too early
|
//add 100ms to the start time to prevent the video from starting too early
|
||||||
val snapChapterTimestamp = snapChapter.startTimeMs.plus(100)
|
val snapChapterTimestamp = snapChapter.startTimeMs.plus(100)
|
||||||
val duration = nextChapter?.startTimeMs?.minus(snapChapterTimestamp) ?: 0
|
val duration: Long? = nextChapter?.startTimeMs?.minus(snapChapterTimestamp)
|
||||||
|
|
||||||
//get the mpd playlist and append the cdn url to baseurl nodes
|
//get the mpd playlist and append the cdn url to baseurl nodes
|
||||||
val playlistUrl = paramMap["MEDIA_ID"].toString().let { it.substring(it.indexOf("https://cf-st.sc-cdn.net")) }
|
val playlistUrl = paramMap["MEDIA_ID"].toString().let {
|
||||||
val clientDownloadManager = provideClientDownloadManager("Pro-Stories/${storyName}", storyName, "Pro Story")
|
val urlIndex = it.indexOf("https://cf-st.sc-cdn.net")
|
||||||
clientDownloadManager.downloadDashMedia(
|
if (urlIndex == -1) {
|
||||||
|
"${RemoteMediaResolver.CF_ST_CDN_D}$it"
|
||||||
|
} else {
|
||||||
|
it.substring(urlIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provideClientDownloadManager(
|
||||||
|
pathSuffix = "Pro-Stories/${storyName}",
|
||||||
|
mediaIdentifier = "${paramMap["STORY_ID"]}-${snapItem.snapId}",
|
||||||
|
mediaDisplaySource = storyName,
|
||||||
|
mediaDisplayType = "Pro Story"
|
||||||
|
).downloadDashMedia(
|
||||||
playlistUrl,
|
playlistUrl,
|
||||||
snapChapterTimestamp,
|
snapChapterTimestamp,
|
||||||
duration
|
duration
|
||||||
@ -384,7 +435,13 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
|||||||
runCatching {
|
runCatching {
|
||||||
if (!isPreviewMode) {
|
if (!isPreviewMode) {
|
||||||
val encryptionKeys = EncryptionHelper.getEncryptionKeys(contentType, messageReader, isArroyo = isArroyoMessage)
|
val encryptionKeys = EncryptionHelper.getEncryptionKeys(contentType, messageReader, isArroyo = isArroyoMessage)
|
||||||
provideClientDownloadManager(authorName, authorName, MediaFilter.CHAT_MEDIA.mediaDisplayType, friendInfo = friendInfo).downloadMedia(
|
provideClientDownloadManager(
|
||||||
|
pathSuffix = authorName,
|
||||||
|
mediaIdentifier = "${message.client_conversation_id}${message.sender_id}${message.client_message_id}",
|
||||||
|
mediaDisplaySource = authorName,
|
||||||
|
mediaDisplayType = MediaFilter.CHAT_MEDIA.mediaDisplayType,
|
||||||
|
friendInfo = friendInfo
|
||||||
|
).downloadMedia(
|
||||||
Base64.UrlSafe.encode(urlProto),
|
Base64.UrlSafe.encode(urlProto),
|
||||||
DownloadMediaType.PROTO_MEDIA,
|
DownloadMediaType.PROTO_MEDIA,
|
||||||
encryption = encryptionKeys?.toKeyPair()
|
encryption = encryptionKeys?.toKeyPair()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user