From b8cdbb62aa642937a54caa4d710ef1e1e1ce9e82 Mon Sep 17 00:00:00 2001 From: shoce Date: Tue, 5 Nov 2024 02:17:38 +0530 Subject: [PATCH] FfmpegTranscode --- Dockerfile | 27 ++++---- go.mod | 2 +- go.sum | 4 +- tgzebot.go | 196 +++++++++++++++++++++++++---------------------------- 4 files changed, 108 insertions(+), 121 deletions(-) diff --git a/Dockerfile b/Dockerfile index a015524..73d1957 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,18 +2,19 @@ # https://hub.docker.com/_/golang/tags FROM golang:1.23.2 AS build -ARG TARGETARCH -RUN apt update -RUN apt -y -q install xz-utils - -RUN mkdir -p /root/ffmpeg/ -WORKDIR /root/ffmpeg/ -RUN curl -s -S -L -O https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-$TARGETARCH-static.tar.xz -RUN tar -x -J -f ffmpeg-release-$TARGETARCH-static.tar.xz -RUN mv ffmpeg-*-static/ffmpeg ffmpeg -RUN ls -l -a -RUN ./ffmpeg -version +#ARG TARGETARCH +# +#RUN apt update +#RUN apt -y -q install xz-utils +# +#RUN mkdir -p /root/ffmpeg/ +#WORKDIR /root/ffmpeg/ +#RUN curl -s -S -L -O https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-$TARGETARCH-static.tar.xz +#RUN tar -x -J -f ffmpeg-release-$TARGETARCH-static.tar.xz +#RUN mv ffmpeg-*-static/ffmpeg ffmpeg +#RUN ls -l -a +#RUN ./ffmpeg -version RUN mkdir -p /root/tgzebot/ WORKDIR /root/tgzebot/ @@ -30,8 +31,8 @@ FROM alpine:3.20.3 RUN apk add --no-cache tzdata RUN apk add --no-cache gcompat && ln -s -f -v ld-linux-x86-64.so.2 /lib/libresolv.so.2 -COPY --from=build /root/ffmpeg/ffmpeg /root/tgzebot/tgzebot /bin/ -RUN ls -l -a /bin/ffmpeg /bin/tgzebot +#COPY --from=build /root/ffmpeg/ffmpeg /bin/ +COPY --from=build /root/tgzebot/tgzebot /bin/ WORKDIR /root/ ENTRYPOINT ["/bin/tgzebot"] diff --git a/go.mod b/go.mod index e117310..c4a9e8f 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,6 @@ require ( github.com/dlclark/regexp2 v1.11.4 // indirect github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd // indirect github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect - github.com/google/pprof v0.0.0-20241023014458-598669927662 // indirect + github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect golang.org/x/text v0.19.0 // indirect ) diff --git a/go.sum b/go.sum index faab6f2..552f525 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd h1:QMSNEh9uQkDjyPwu/J5 github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/google/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsPdLHF5dNyxq3jOWwu9w9UyH5vA= -github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs= +github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/kkdai/youtube/v2 v2.10.2-0.20240422131028-23aa415a6747 h1:GZ9tzPREMEMR574FluoxNFx9PKaF3KS/vBGzLg4NdIU= github.com/kkdai/youtube/v2 v2.10.2-0.20240422131028-23aa415a6747/go.mod h1:qL8JZv7Q1IoDs4nnaL51o/hmITXEIvyCIXopB0oqgVM= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= diff --git a/tgzebot.go b/tgzebot.go index e2e4e1d..5560fe5 100644 --- a/tgzebot.go +++ b/tgzebot.go @@ -3,11 +3,6 @@ https://pkg.go.dev/github.com/kkdai/youtube/v2/ https://core.telegram.org/bots/api/ -https://johnvansickle.com/ffmpeg/ - -curl -s -S -L https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz | tar -x -J -mv ./ffmpeg-*-amd64-static/ffmpeg ./ffmpeg - go get -a -u -v go get github.com/kkdai/youtube/v2@master go mod tidy @@ -113,8 +108,8 @@ func init() { ytRe = regexp.MustCompile(YtReString) ytlistRe = regexp.MustCompile(YtListReString) - if os.Getenv("YamlConfigPath") != "" { - YamlConfigPath = os.Getenv("YamlConfigPath") + if v := os.Getenv("YamlConfigPath"); v != "" { + YamlConfigPath = v } if YamlConfigPath == "" { log("WARNING YamlConfigPath empty") @@ -161,8 +156,8 @@ func init() { Ctx = context.TODO() YtProxy := http.ProxyFromEnvironment - if GetVar("YtProxyList") != "" { - pp := strings.Split(GetVar("YtProxyList"), " ") + if v := GetVar("YtProxyList"); v != "" { + pp := strings.Split(v, " ") rand.Seed(time.Now().UnixNano()) if err := SetVar("YtProxy", pp[rand.Intn(len(pp))]); err != nil { log("WARNING SetVar YtProxy: %v", err) @@ -213,11 +208,11 @@ func init() { TgUpdateLog = append(TgUpdateLog, i) } - if GetVar("TgZeChatId") == "" { + if v := GetVar("TgZeChatId"); v == "" { log("ERROR TgZeChatId empty") os.Exit(1) } else { - TgZeChatId, err = strconv.ParseInt(GetVar("TgZeChatId"), 10, 0) + TgZeChatId, err = strconv.ParseInt(v, 10, 0) if err != nil { log("ERROR invalid TgZeChatId: %v", err) os.Exit(1) @@ -251,29 +246,29 @@ func init() { os.Exit(1) } - if GetVar("YtMaxResults") != "" { - YtMaxResults, err = strconv.ParseInt(GetVar("YtMaxResults"), 10, 0) + if v := GetVar("YtMaxResults"); v != "" { + YtMaxResults, err = strconv.ParseInt(v, 10, 0) if err != nil { log("ERROR invalid YtMaxResults: %v", err) os.Exit(1) } } - if GetVar("YtHttpClientUserAgent") != "" { - YtHttpClientUserAgent = GetVar("YtHttpClientUserAgent") + if v := GetVar("YtHttpClientUserAgent"); v != "" { + YtHttpClientUserAgent = v } - if GetVar("YtReString") != "" { - YtReString = GetVar("YtReString") + if v := GetVar("YtReString"); v != "" { + YtReString = v } - if GetVar("YtListReString") != "" { - YtListReString = GetVar("YtListReString") + if v := GetVar("YtListReString"); v != "" { + YtListReString = v } - if GetVar("FfmpegPath") != "" { - FfmpegPath = GetVar("FfmpegPath") + if v := GetVar("FfmpegPath"); v != "" { + FfmpegPath = v } - if GetVar("FfmpegGlobalOptions") != "" { - FfmpegGlobalOptions = strings.Split(GetVar("FfmpegGlobalOptions"), " ") + if v := GetVar("FfmpegGlobalOptions"); v != "" { + FfmpegGlobalOptions = strings.Split(v, " ") } for _, s := range strings.Split(GetVar("TgAllChannelsChatIds"), " ") { @@ -1402,52 +1397,17 @@ func postVideo(v YtVideo, vinfo *ytdl.Video, m TgMessage) error { } } - if targetVideoBitrateKbps > 0 { - log("transcoding to audio:%dkbps video:%dkbps", TgAudioBitrateKbps, targetVideoBitrateKbps) - tgvideoTranscodedFilename := fmt.Sprintf("%s.%s.%dk.mp4", ts(), v.Id, targetVideoBitrateKbps) - ffmpegArgs := FfmpegGlobalOptions - ffmpegArgs = append(ffmpegArgs, - "-i", tgvideoFilename, - "-f", "mp4", - "-c:a", "aac", - "-b:a", fmt.Sprintf("%dk", TgAudioBitrateKbps), - "-c:v", "h264", - "-b:v", fmt.Sprintf("%dk", targetVideoBitrateKbps), - tgvideoTranscodedFilename, - ) - ffmpegCmd := exec.Command(FfmpegPath, ffmpegArgs...) - - ffmpegCmdStderrPipe, err := ffmpegCmd.StderrPipe() - if err != nil { - return fmt.Errorf("Ffmpeg StderrPipe: %w", err) - } - - t0 := time.Now() - err = ffmpegCmd.Start() - if err != nil { - return fmt.Errorf("Ffmpeg Start: %w", err) - } - - log("started command `%s`", ffmpegCmd.String()) - - _, err = io.Copy(os.Stderr, ffmpegCmdStderrPipe) - if err != nil { - log("copy from ffmpeg stderr: %w", err) - } - - err = ffmpegCmd.Wait() + if FfmpegPath != "" && targetVideoBitrateKbps > 0 { + filename2 := fmt.Sprintf("%s.%s.v%dk.a%dk.mp4", ts(), v.Id, targetVideoBitrateKbps, TgAudioBitrateKbps) + err := FfmpegTranscode(tgvideoFilename, filename2, targetVideoBitrateKbps, TgAudioBitrateKbps) if err != nil { - return fmt.Errorf("Ffmpeg Wait: %w", err) + return fmt.Errorf("FfmpegTranscode `%s`: %w", tgvideoFilename, err) } - - log("transcoded video in %v", time.Since(t0).Truncate(time.Second)) - + tgvideoCaption += NL + fmt.Sprintf("(transcoded to video:%dkbps audio:%dkbps)", targetVideoBitrateKbps, TgAudioBitrateKbps) if err := os.Remove(tgvideoFilename); err != nil { - log("os.Remove: %v", err) + log("os.Remove `%s`: %v", tgvideoFilename, err) } - - tgvideoCaption += NL + fmt.Sprintf("(transcoded to audio:%dkbps video:%dkbps)", TgAudioBitrateKbps, targetVideoBitrateKbps) - tgvideoFilename = tgvideoTranscodedFilename + tgvideoFilename = filename2 } tgvideoReader, err := os.Open(tgvideoFilename) @@ -1595,50 +1555,17 @@ func postAudio(v YtVideo, vinfo *ytdl.Video, m TgMessage) error { } } - if targetAudioBitrateKbps > 0 { - log("transcoding to audio:%dkbps", targetAudioBitrateKbps) - tgaudioTranscodedFilename := fmt.Sprintf("%s.%s.%dk.m4a", ts(), v.Id, targetAudioBitrateKbps) - ffmpegArgs := FfmpegGlobalOptions - ffmpegArgs = append(ffmpegArgs, - "-i", tgaudioFilename, - "-f", "mp4", - "-c:a", "aac", - "-b:a", fmt.Sprintf("%dk", targetAudioBitrateKbps), - tgaudioTranscodedFilename, - ) - ffmpegCmd := exec.Command(FfmpegPath, ffmpegArgs...) - - ffmpegCmdStderrPipe, err := ffmpegCmd.StderrPipe() - if err != nil { - return fmt.Errorf("Ffmpeg StderrPipe: %w", err) - } - - t0 := time.Now() - err = ffmpegCmd.Start() - if err != nil { - return fmt.Errorf("Ffmpeg Start: %w", err) - } - - log("started command `%s`", ffmpegCmd.String()) - - _, err = io.Copy(os.Stderr, ffmpegCmdStderrPipe) - if err != nil { - log("ERROR copy from ffmpeg stderr: %v", err) - } - - err = ffmpegCmd.Wait() + if FfmpegPath != "" && targetAudioBitrateKbps > 0 { + filename2 := fmt.Sprintf("%s.%s.a%dk.m4a", ts(), v.Id, targetAudioBitrateKbps) + err := FfmpegTranscode(tgaudioFilename, filename2, 0, targetAudioBitrateKbps) if err != nil { - return fmt.Errorf("Ffmpeg Wait: %w", err) + return fmt.Errorf("FfmpegTranscode `%s`: %w", tgaudioFilename, err) } - - log("transcoded audio in %v", time.Since(t0).Truncate(time.Second)) - + tgaudioCaption += NL + fmt.Sprintf("(transcoded to audio:%dkbps)", targetAudioBitrateKbps) if err := os.Remove(tgaudioFilename); err != nil { - log("os.Remove: %v", err) + log("os.Remove `%s`: %v", tgaudioFilename, err) } - - tgaudioCaption += NL + fmt.Sprintf("(transcoded to %dkbps)", targetAudioBitrateKbps) - tgaudioFilename = tgaudioTranscodedFilename + tgaudioFilename = filename2 } tgaudioReader, err := os.Open(tgaudioFilename) @@ -2075,3 +2002,62 @@ func tgdeleteMessage(chatid, messageid int64) error { return nil } + +func FfmpegTranscode(filename, filename2 string, videoBitrateKbps, audioBitrateKbps int64) (err error) { + if videoBitrateKbps > 0 { + log("transcoding to video:%dkbps audio:%dkbps ", videoBitrateKbps, audioBitrateKbps) + } else if audioBitrateKbps > 0 { + log("transcoding to audio:%dkbps", audioBitrateKbps) + } else { + return fmt.Errorf("empty both videoBitrateKbps and audioBitrateKbps") + } + + ffmpegArgs := append(FfmpegGlobalOptions, + "-i", filename, + "-f", "mp4", + ) + if videoBitrateKbps > 0 { + ffmpegArgs = append(ffmpegArgs, + "-c:v", "h264", + "-b:v", fmt.Sprintf("%dk", videoBitrateKbps), + ) + } + if audioBitrateKbps > 0 { + ffmpegArgs = append(ffmpegArgs, + "-c:a", "aac", + "-b:a", fmt.Sprintf("%dk", audioBitrateKbps), + ) + } + ffmpegArgs = append(ffmpegArgs, + filename2, + ) + + ffmpegCmd := exec.Command(FfmpegPath, ffmpegArgs...) + + ffmpegCmdStderrPipe, err := ffmpegCmd.StderrPipe() + if err != nil { + return fmt.Errorf("ffmpeg StderrPipe: %w", err) + } + + t0 := time.Now() + err = ffmpegCmd.Start() + if err != nil { + return fmt.Errorf("ffmpeg Start: %w", err) + } + + log("started command `%s`", ffmpegCmd.String()) + + _, err = io.Copy(os.Stderr, ffmpegCmdStderrPipe) + if err != nil { + log("copy from ffmpeg stderr: %w", err) + } + + err = ffmpegCmd.Wait() + if err != nil { + return fmt.Errorf("ffmpeg Wait: %w", err) + } + + log("transcoded in %v", time.Since(t0).Truncate(time.Second)) + + return nil +}