Skip to content

Commit

Permalink
feat: Re-added save webm and tgs to gif
Browse files Browse the repository at this point in the history
* Now use FFmpegKit AAR
  • Loading branch information
YuKongA committed Jan 16, 2025
1 parent 1d136e5 commit dbfb992
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 8 deletions.
3 changes: 3 additions & 0 deletions TMessagesProj/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ dependencies {
implementation(libs.ktor.client.contentNegotiation)
implementation(libs.ktor.serialization.json)

implementation(files("libs/ffmpeg-kit-video-4.4.LTS.aar"))
implementation(libs.lottie)

implementation(project(":libs:tcp2ws"))
implementation(project(":libs:pangu"))
ksp(project(":libs:ksp"))
Expand Down
Binary file added TMessagesProj/libs/ffmpeg-kit-video-4.4.LTS.aar
Binary file not shown.
12 changes: 8 additions & 4 deletions TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -29812,6 +29812,11 @@ public void setAutoDeleteHistory(int time, int action) {
options.add(OPTION_ADD_TO_STICKERS_OR_MASKS);
icons.add(R.drawable.msg_sticker);
} else {
// if (!selectedObject.isAnimatedSticker()) {
items.add(LocaleController.getString(R.string.SaveToGallery));
options.add(OPTION_SAVE_STICKER_TO_GALLERY);
icons.add(R.drawable.msg_gallery);
// }
items.add(LocaleController.getString(R.string.AddToStickers));
options.add(OPTION_ADD_TO_STICKERS_OR_MASKS);
icons.add(R.drawable.msg_sticker);
Expand Down Expand Up @@ -29848,12 +29853,11 @@ public void setAutoDeleteHistory(int time, int action) {
icons.add(R.drawable.msg_callback);
}
} else if (type == 9) {
if (!selectedObject.isAnimatedSticker()) {
items.add(LocaleController.getString("SaveToGallery",
R.string.SaveToGallery));
// if (!selectedObject.isAnimatedSticker()) {
items.add(LocaleController.getString(R.string.SaveToGallery));
options.add(OPTION_SAVE_STICKER_TO_GALLERY);
icons.add(R.drawable.msg_gallery);
}
// }
TLRPC.Document document = selectedObject.getDocument();
if (!getMediaDataController().isStickerInFavorites(document)) {
if (getMediaDataController().canAddStickerToFavorites()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import android.content.Context
import android.content.DialogInterface
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.net.Uri
import android.text.TextUtils
import android.util.Base64
Expand All @@ -41,7 +42,14 @@ import android.view.inputmethod.EditorInfo
import android.widget.FrameLayout
import android.widget.TextView
import android.widget.TimePicker
import android.widget.Toast
import androidx.core.content.FileProvider
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieDrawable
import com.airbnb.lottie.LottieResult
import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.ReturnCode
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
Expand Down Expand Up @@ -90,10 +98,12 @@ import org.telegram.ui.Components.EditTextBoldCursor
import org.telegram.ui.Components.Forum.ForumUtilities
import org.telegram.ui.Components.LayoutHelper
import org.telegram.ui.Components.TranscribeButton
import xyz.nextalone.gen.Config
import xyz.nextalone.nnngram.helpers.QrHelper
import xyz.nextalone.nnngram.helpers.QrHelper.readQr
import xyz.nextalone.nnngram.tryOrLog
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
Expand Down Expand Up @@ -417,7 +427,7 @@ class MessageUtils(num: Int) : BaseController(num) {
}

fun saveStickerToGallery(activity: Activity, messageObject: MessageObject, callback: Utilities.Callback<Uri>) {
saveStickerToGallery(activity, getPathToMessage(messageObject), messageObject.isVideoSticker, callback)
saveStickerToGallery(activity, getPathToMessage(messageObject), messageObject.isVideoSticker, messageObject.isAnimatedSticker, callback)
}

fun addMessageToClipboard(selectedObject: MessageObject, callback: Runnable) {
Expand Down Expand Up @@ -867,14 +877,84 @@ class MessageUtils(num: Int) : BaseController(num) {
if (!temp.exists()) {
return
}
saveStickerToGallery(activity, path, MessageObject.isVideoSticker(document), callback)
saveStickerToGallery(activity, path, MessageObject.isVideoSticker(document), MessageObject.isAnimatedStickerDocument(document), callback)
}

private fun saveStickerToGallery(activity: Activity, path: String?, video: Boolean, callback: Utilities.Callback<Uri>) {
private fun saveStickerToGallery(activity: Activity, path: String?, video: Boolean, animated: Boolean, callback: Utilities.Callback<Uri>) {
Utilities.globalQueue.postRunnable {
tryOrLog {
if (video) {
MediaController.saveFile(path, activity, 1, null, null, callback)
val outputPath =
path!!.replace(".webm", ".gif")
if (File(outputPath).exists()) {
File(outputPath).delete()
}
val cmd = "-y -vcodec libvpx-vp9 -i '$path' -lavfi split[v],palettegen,[v]paletteuse '$outputPath'"
FFmpegKit.executeAsync(cmd) { session ->
val returnCode = session.returnCode
if (ReturnCode.isSuccess(returnCode)) {
MediaController.saveFile(outputPath, activity, 0, null, null, callback)
} else {
Log.e("FFmpegKit", "Failed to convert to GIF: $returnCode, file: $path")
Toast.makeText(activity, "Failed to convert to GIF, Use Mp4", Toast.LENGTH_SHORT).show()
MediaController.saveFile(path, activity, 1, null, null, callback)
}
}
} else if (animated) {
CoroutineScope(Dispatchers.IO).launch {
val outputPath = path!!.replace(".tgs", ".gif")
if (File(outputPath).exists()) {
File(outputPath).delete()
}

val result: LottieResult<LottieComposition> = LottieCompositionFactory.fromJsonInputStreamSync(
FileInputStream(File(path)), path)
val composition: LottieComposition? = result.value

composition?.let { comp ->
val lottieDrawable = LottieDrawable().apply { this.composition = comp }

lottieDrawable.setBounds(0, 0, comp.bounds.width(), comp.bounds.height())

val tempDir = File(activity.cacheDir, "temp_${System.currentTimeMillis()}")
if (!tempDir.exists()) {
tempDir.mkdirs()
}

for (i in comp.startFrame.toInt() until comp.endFrame.toInt()) {
lottieDrawable.frame = i

val bitmap = Bitmap.createBitmap(comp.bounds.width(), comp.bounds.height(), Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
lottieDrawable.draw(canvas)

val file = File(tempDir, "$i.png")
FileOutputStream(file).use { fos ->
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
}
}
val generatePaletteCommand = "-i '${tempDir.absolutePath}/%d.png' -vf palettegen=stats_mode=diff -y '${tempDir.absolutePath}/palette.png'"
val createGifCommand = "-framerate 60 -i '${tempDir.absolutePath}/%d.png' -i '${tempDir.absolutePath}/palette.png' -filter_complex [0:v]scale=320:-1:flags=lanczos[v];[v][1:v]paletteuse=dither=none:diff_mode=rectangle -y '$outputPath'"
FFmpegKit.executeAsync(generatePaletteCommand) { session ->
var returnCode = session.returnCode
if (ReturnCode.isSuccess(returnCode)) {
FFmpegKit.executeAsync(createGifCommand) { session1 ->
returnCode = session1.returnCode
if (ReturnCode.isSuccess(returnCode)) {
MediaController.saveFile(outputPath, activity, 0, null, null, callback)
} else {
Log.e("FFmpegKit", "Failed to convert to GIF: $returnCode, file: $path")
Toast.makeText(activity, "Failed to convert to GIF, Use tgs", Toast.LENGTH_SHORT).show()
}
tempDir.deleteRecursively()
}
} else {
Log.e("FFmpegKit", "Failed to convert to GIF: $returnCode, file: $path")
Toast.makeText(activity, "Failed to convert to GIF, Use tgs", Toast.LENGTH_SHORT).show()
}
}
}
}
} else {
val image = BitmapFactory.decodeFile(path)
if (image != null) {
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ kotlinxCoroutinesAndroid = "1.9.0"
kotlinxSerializationJson = "1.8.0"
ktor = "3.0.3"
languageId = "17.0.6"
lottie = "6.4.1"
osmdroidAndroid = "6.1.20"
playServicesLocation = "21.3.0"
playServicesVision = "20.1.3"
Expand Down Expand Up @@ -53,6 +54,7 @@ ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref =
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
language-id = { module = "com.google.mlkit:language-id", version.ref = "languageId" }
lottie = { module = "com.airbnb.android:lottie", version.ref = "lottie" }
osmdroid-android = { module = "org.osmdroid:osmdroid-android", version.ref = "osmdroidAndroid" }
play-services-location = { module = "com.google.android.gms:play-services-location", version.ref = "playServicesLocation" }
play-services-vision = { module = "com.google.android.gms:play-services-vision", version.ref = "playServicesVision" }
Expand Down

0 comments on commit dbfb992

Please sign in to comment.