mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-29 04:50:15 +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"
|
||||
},
|
||||
"download_options": {
|
||||
"allow_duplicate": "Allow duplicate downloads",
|
||||
"format_user_folder": "Create folder for each user",
|
||||
"format_hash": "Add a unique hash to the file path",
|
||||
"format_username": "Add the username to the file path",
|
||||
@ -232,6 +233,8 @@
|
||||
}
|
||||
},
|
||||
"download_manager_receiver": {
|
||||
"already_queued_toast": "Media already in queue!",
|
||||
"already_downloaded_toast": "Media already downloaded!",
|
||||
"saved_toast": "Saved to {path}",
|
||||
"download_toast": "Downloading {path}...",
|
||||
"processing_toast": "Processing {path}...",
|
||||
|
@ -16,7 +16,7 @@ object Logger {
|
||||
}
|
||||
|
||||
fun error(throwable: Throwable) {
|
||||
Log.e(TAG, "",throwable)
|
||||
Log.e(TAG, "", throwable)
|
||||
}
|
||||
|
||||
fun error(message: Any?) {
|
||||
|
@ -115,8 +115,16 @@ enum class ConfigProperty(
|
||||
"download_options",
|
||||
ConfigCategory.MEDIA_MANAGEMENT,
|
||||
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(
|
||||
"allow_duplicate" to false,
|
||||
"format_user_folder" to true,
|
||||
"format_hash" to true,
|
||||
"format_date_time" to true,
|
||||
|
@ -13,7 +13,8 @@ class DownloadManagerClient (
|
||||
private val outputPath: String,
|
||||
private val mediaDisplaySource: String?,
|
||||
private val mediaDisplayType: String?,
|
||||
private val iconUrl: String?
|
||||
private val iconUrl: String?,
|
||||
private val uniqueHash: String?
|
||||
) {
|
||||
private fun sendToBroadcastReceiver(bundle: Bundle) {
|
||||
val intent = Intent()
|
||||
@ -32,10 +33,11 @@ class DownloadManagerClient (
|
||||
putString("mediaDisplaySource", mediaDisplaySource)
|
||||
putString("mediaDisplayType", mediaDisplayType)
|
||||
putString("iconUrl", iconUrl)
|
||||
putString("uniqueHash", uniqueHash)
|
||||
}.apply(extras))
|
||||
}
|
||||
|
||||
fun downloadDashMedia(playlistUrl: String, offsetTime: Long, duration: Long) {
|
||||
fun downloadDashMedia(playlistUrl: String, offsetTime: Long, duration: Long?) {
|
||||
sendToBroadcastReceiver(
|
||||
DownloadRequest(
|
||||
inputMedias = arrayOf(playlistUrl),
|
||||
@ -44,8 +46,8 @@ class DownloadManagerClient (
|
||||
)
|
||||
) {
|
||||
putBundle("dashOptions", Bundle().apply {
|
||||
putLong("offsetTime", offsetTime)
|
||||
putLong("duration", duration)
|
||||
putString("offsetTime", offsetTime.toString())
|
||||
duration?.let { putString("duration", it.toString()) }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ class DownloadManagerReceiver : BroadcastReceiver() {
|
||||
if (!it.endsWith("/")) "$it/" else it
|
||||
}
|
||||
|
||||
longToast(
|
||||
shortToast(
|
||||
translation.format("saved_toast", "path" to outputFile.absolutePath.replace(parentName ?: "", ""))
|
||||
)
|
||||
|
||||
@ -255,9 +255,19 @@ class DownloadManagerReceiver : BroadcastReceiver() {
|
||||
this.context = context
|
||||
SharedContext.ensureInitialized(context)
|
||||
|
||||
|
||||
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) {
|
||||
val pendingDownloadObject = PendingDownload.fromBundle(intent.extras!!)
|
||||
|
||||
|
@ -20,6 +20,7 @@ class DownloadTaskManager {
|
||||
SQLiteDatabaseHelper.createTablesFromSchema(this, mapOf(
|
||||
"tasks" to listOf(
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT",
|
||||
"hash VARCHAR UNIQUE",
|
||||
"outputPath TEXT",
|
||||
"outputFile TEXT",
|
||||
"mediaDisplayType TEXT",
|
||||
@ -32,8 +33,9 @@ class DownloadTaskManager {
|
||||
}
|
||||
|
||||
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(
|
||||
task.uniqueHash,
|
||||
task.outputPath,
|
||||
task.outputFile,
|
||||
task.mediaDisplayType,
|
||||
@ -51,8 +53,9 @@ class DownloadTaskManager {
|
||||
}
|
||||
|
||||
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(
|
||||
task.uniqueHash,
|
||||
task.outputPath,
|
||||
task.outputFile,
|
||||
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 {
|
||||
return cachedTasks.isEmpty() && pendingTasks.isEmpty()
|
||||
}
|
||||
@ -127,6 +152,7 @@ class DownloadTaskManager {
|
||||
outputPath = cursor.getString(cursor.getColumnIndex("outputPath")),
|
||||
mediaDisplayType = cursor.getString(cursor.getColumnIndex("mediaDisplayType")),
|
||||
mediaDisplaySource = cursor.getString(cursor.getColumnIndex("mediaDisplaySource")),
|
||||
uniqueHash = cursor.getString(cursor.getColumnIndex("hash")),
|
||||
iconUrl = cursor.getString(cursor.getColumnIndex("iconUrl"))
|
||||
).apply {
|
||||
downloadStage = DownloadStage.valueOf(cursor.getString(cursor.getColumnIndex("downloadStage")))
|
||||
|
@ -19,7 +19,8 @@ class DownloadRequest(
|
||||
private val flags: Int = 0,
|
||||
private val dashOptions: Map<String, String?>? = null,
|
||||
private val mediaDisplaySource: String? = null,
|
||||
private val mediaDisplayType: String? = null
|
||||
private val mediaDisplayType: String? = null,
|
||||
private val uniqueHash: String? = null
|
||||
) {
|
||||
companion object {
|
||||
fun fromBundle(bundle: Bundle): DownloadRequest {
|
||||
@ -39,7 +40,8 @@ class DownloadRequest(
|
||||
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)
|
||||
putString("uniqueHash", uniqueHash)
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,10 +88,6 @@ class DownloadRequest(
|
||||
}
|
||||
}
|
||||
|
||||
fun getInputMedia(index: Int): String? {
|
||||
return inputMedias.getOrNull(index)
|
||||
}
|
||||
|
||||
fun getInputMedias(): List<InputMedia> {
|
||||
return inputMedias.mapIndexed { index, uri ->
|
||||
InputMedia(
|
||||
@ -102,4 +101,6 @@ class DownloadRequest(
|
||||
fun getInputType(index: Int): DownloadMediaType? {
|
||||
return inputTypes.getOrNull(index)?.let { DownloadMediaType.valueOf(it) }
|
||||
}
|
||||
|
||||
fun getUniqueHash() = uniqueHash
|
||||
}
|
@ -13,7 +13,8 @@ data class PendingDownload(
|
||||
val outputPath: String,
|
||||
val mediaDisplayType: String?,
|
||||
val mediaDisplaySource: String?,
|
||||
val iconUrl: String?
|
||||
val iconUrl: String?,
|
||||
val uniqueHash: String?
|
||||
) {
|
||||
companion object {
|
||||
fun fromBundle(bundle: Bundle): PendingDownload {
|
||||
@ -21,7 +22,8 @@ data class PendingDownload(
|
||||
outputPath = bundle.getString("outputPath")!!,
|
||||
mediaDisplayType = bundle.getString("mediaDisplayType"),
|
||||
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.Hooker
|
||||
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.MediaDownloaderHelper
|
||||
import me.rhunk.snapenhance.util.snap.MediaType
|
||||
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.nio.file.Paths
|
||||
import java.text.SimpleDateFormat
|
||||
@ -56,10 +57,13 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
||||
|
||||
private fun provideClientDownloadManager(
|
||||
pathSuffix: String,
|
||||
mediaIdentifier: String,
|
||||
mediaDisplaySource: String? = null,
|
||||
mediaDisplayType: String? = null,
|
||||
friendInfo: FriendInfo? = null
|
||||
): DownloadManagerClient {
|
||||
val generatedHash = mediaIdentifier.hashCode().toString(16)
|
||||
|
||||
val iconUrl = friendInfo?.takeIf {
|
||||
it.bitmojiAvatarId != null && it.bitmojiSelfieId != null
|
||||
}?.let {
|
||||
@ -68,7 +72,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
||||
|
||||
val outputPath = File(
|
||||
context.config.string(ConfigProperty.SAVE_FOLDER),
|
||||
createNewFilePath(pathSuffix.hashCode(), pathSuffix)
|
||||
createNewFilePath(generatedHash, pathSuffix)
|
||||
).absolutePath
|
||||
|
||||
return DownloadManagerClient(
|
||||
@ -76,6 +80,11 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
||||
mediaDisplaySource = mediaDisplaySource,
|
||||
mediaDisplayType = mediaDisplayType,
|
||||
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
|
||||
)
|
||||
}
|
||||
@ -85,9 +94,12 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
||||
return isFFmpegPresent
|
||||
}
|
||||
|
||||
private fun createNewFilePath(hash: Int, pathPrefix: String): String {
|
||||
val hexHash = Integer.toHexString(hash)
|
||||
private fun createNewFilePath(hexHash: String, pathPrefix: String): String {
|
||||
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())
|
||||
|
||||
@ -102,13 +114,13 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
||||
}
|
||||
|
||||
if (downloadOptions["format_user_folder"] == true) {
|
||||
finalPath.append(pathPrefix).append("/")
|
||||
finalPath.append(sanitizedPathPrefix).append("/")
|
||||
}
|
||||
if (downloadOptions["format_hash"] == true) {
|
||||
appendFileName(hexHash)
|
||||
}
|
||||
if (downloadOptions["format_username"] == true) {
|
||||
appendFileName(pathPrefix)
|
||||
appendFileName(sanitizedPathPrefix)
|
||||
}
|
||||
if (downloadOptions["format_date_time"] == true) {
|
||||
appendFileName(currentDateTime)
|
||||
@ -186,7 +198,10 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
||||
//messages
|
||||
paramMap["MESSAGE_ID"]?.toString()?.takeIf { forceDownload || canAutoDownload("friend_snaps") }?.let { id ->
|
||||
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)) {
|
||||
return
|
||||
@ -194,7 +209,15 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
||||
|
||||
val author = context.database.getFriendInfo(senderId) ?: return
|
||||
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
|
||||
}
|
||||
|
||||
@ -202,15 +225,20 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
||||
paramMap["PLAYLIST_V2_GROUP"]?.toString()?.takeIf {
|
||||
it.contains("storyUserId=") && (forceDownload || canAutoDownload("friend_stories"))
|
||||
}?.let { playlistGroup ->
|
||||
val storyIdStartIndex = playlistGroup.indexOf("storyUserId=") + 12
|
||||
val storyUserId = playlistGroup.substring(
|
||||
storyIdStartIndex,
|
||||
playlistGroup.indexOf(",", storyIdStartIndex)
|
||||
)
|
||||
val storyUserId = (playlistGroup.indexOf("storyUserId=") + 12).let {
|
||||
playlistGroup.substring(it, playlistGroup.indexOf(",", it))
|
||||
}
|
||||
|
||||
val author = context.database.getFriendInfo(if (storyUserId == "null") context.database.getMyUserId()!! else storyUserId) ?: return
|
||||
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
|
||||
}
|
||||
|
||||
@ -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(
|
||||
"[^\\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
|
||||
}
|
||||
|
||||
//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
|
||||
}
|
||||
|
||||
@ -256,12 +295,24 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
||||
|
||||
//add 100ms to the start time to prevent the video from starting too early
|
||||
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
|
||||
val playlistUrl = paramMap["MEDIA_ID"].toString().let { it.substring(it.indexOf("https://cf-st.sc-cdn.net")) }
|
||||
val clientDownloadManager = provideClientDownloadManager("Pro-Stories/${storyName}", storyName, "Pro Story")
|
||||
clientDownloadManager.downloadDashMedia(
|
||||
val playlistUrl = paramMap["MEDIA_ID"].toString().let {
|
||||
val urlIndex = it.indexOf("https://cf-st.sc-cdn.net")
|
||||
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,
|
||||
snapChapterTimestamp,
|
||||
duration
|
||||
@ -384,7 +435,13 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam
|
||||
runCatching {
|
||||
if (!isPreviewMode) {
|
||||
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),
|
||||
DownloadMediaType.PROTO_MEDIA,
|
||||
encryption = encryptionKeys?.toKeyPair()
|
||||
|
Loading…
x
Reference in New Issue
Block a user