146 lines
3.3 KiB
Go
146 lines
3.3 KiB
Go
package convert
|
|
|
|
import (
|
|
"compress/gzip"
|
|
"fmt"
|
|
"image/png"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"trbot/plugins/sticker_download/config"
|
|
|
|
"golang.org/x/image/webp"
|
|
)
|
|
|
|
// use golang.org/x/image/webp and image/png
|
|
func WebPToPNG(webpPath, pngPath string) error {
|
|
// 打开 WebP 文件
|
|
webpFile, err := os.Open(webpPath)
|
|
if err != nil {
|
|
return fmt.Errorf("打开 WebP 文件失败: %w", err)
|
|
}
|
|
defer webpFile.Close()
|
|
|
|
// 解码 WebP 图片
|
|
img, err := webp.Decode(webpFile)
|
|
if err != nil {
|
|
return fmt.Errorf("解码 WebP 失败: %w", err)
|
|
}
|
|
|
|
// 创建 PNG 文件
|
|
pngFile, err := os.Create(pngPath)
|
|
if err != nil {
|
|
return fmt.Errorf("创建 PNG 文件失败: %w", err)
|
|
}
|
|
defer pngFile.Close()
|
|
|
|
// 编码 PNG
|
|
err = png.Encode(pngFile, img)
|
|
if err != nil {
|
|
return fmt.Errorf("编码 PNG 失败: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// use ffmpeg
|
|
func WebMToGif(webmPath, gifPath string) error {
|
|
return exec.Command(config.Config.FFmpegPath, "-i", webmPath, gifPath).Run()
|
|
}
|
|
|
|
// use lottie-converter, gifski and compress/gzip
|
|
func TGSToGif(fileID, tgsPath, gifPath string) error {
|
|
var tempDir = filepath.Join(config.TempDir, fileID)
|
|
var fps string = "30"
|
|
if config.Config.TGSConvertFPS != 0 {
|
|
fps = fmt.Sprintf("%d", config.Config.TGSConvertFPS)
|
|
}
|
|
// 创建临时目录
|
|
err := os.MkdirAll(tempDir, 0755)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create temp dir: %w", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
// 若为 .tgs 文件,解压为 .json
|
|
lottiePath := filepath.Join(tempDir, "animation.json")
|
|
inFile, err := os.Open(tgsPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open tgs file: %w", err)
|
|
}
|
|
defer inFile.Close()
|
|
|
|
gzReader, err := gzip.NewReader(inFile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create gzip reader: %w", err)
|
|
}
|
|
defer gzReader.Close()
|
|
|
|
outFile, err := os.Create(lottiePath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create json file: %w", err)
|
|
}
|
|
defer outFile.Close()
|
|
|
|
if _, err := io.Copy(outFile, gzReader); err != nil {
|
|
return fmt.Errorf("failed to decompress tgs: %w", err)
|
|
}
|
|
|
|
// 执行 lottie_to_png
|
|
err = exec.Command(config.Config.LottieToPNGPath,
|
|
"--width", "512",
|
|
"--height", "512",
|
|
"--fps", fps,
|
|
"--threads", "1",
|
|
"--output", tempDir,
|
|
lottiePath,
|
|
).Run()
|
|
if err != nil {
|
|
return fmt.Errorf("lottie_to_png failed: %w", err)
|
|
}
|
|
|
|
// 查找 PNG 文件
|
|
files, err := filepath.Glob(filepath.Join(tempDir, "*.png"))
|
|
if err != nil || len(files) == 0 {
|
|
return fmt.Errorf("no PNG files found: %w", err)
|
|
}
|
|
// 排序文件(按名称)
|
|
// 可以用 sort.Strings(files) 如果你需要强制排序
|
|
|
|
// 调用 gifski 生成 GIF
|
|
args := append([]string{
|
|
"--quiet",
|
|
"-o", gifPath,
|
|
"--fps", fps,
|
|
"--height", "512",
|
|
"--width", "512",
|
|
"--quality", "90",
|
|
}, files...)
|
|
|
|
err = exec.Command(config.Config.GifskiPath, args...).Run()
|
|
if err != nil {
|
|
return fmt.Errorf("gifski failed: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// use ffmpeg
|
|
func MP4ToGif(mp4Path, gifPath string) error {
|
|
return exec.Command(config.Config.FFmpegPath, "-i", mp4Path, gifPath).Run()
|
|
}
|
|
|
|
// func MP4ToWebM(MP4Path, webmPath string) error {
|
|
// return exec.Command(config.Config.FFmpegPath,
|
|
// "-i", MP4Path,
|
|
// "-vf", "scale='if(gt(iw,ih),512,-2)':'if(gt(ih,iw),512,-2)',fps=30",
|
|
// "-c:v", "libvpx-vp9",
|
|
// "-crf", "40",
|
|
// "-b:v", "0",
|
|
// "-an",
|
|
// "-limit_filesize", "262144",
|
|
// webmPath,
|
|
// ).Run()
|
|
// }
|