Skip to content

Commit

Permalink
absolutely no need to panic
Browse files Browse the repository at this point in the history
  • Loading branch information
graynk committed Nov 1, 2021
1 parent 2404da4 commit 02a736b
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 86 deletions.
2 changes: 2 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ loginctl enable-linger $USER
* Distort animated stickers
* Make distortion increase throughout the video
* For stickers create and maintain a separate sticker pack for ease of use
* Don't trust Telegram with reported duration, get my own stats
* Localization, refactoring, tests
69 changes: 40 additions & 29 deletions app/animation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"github.com/pkg/errors"
"log"
"os"
"os/exec"
Expand All @@ -13,37 +14,54 @@ import (

func distortVideo(filename, output string, progressChan chan string) {
progressChan <- "Extracting frames..."
defer close(progressChan)
framesFir := filename + "Frames"
err := os.Mkdir(framesFir, 0755)
if err != nil {
err = errors.WithStack(err)
log.Println(err)
panic(err)
return
}
defer os.RemoveAll(framesFir)
frameRateFraction, totalFrames := getFrameRateFractionAndFrameCount(filename)
frameRateFraction, totalFrames, err := getFrameRateFractionAndFrameCount(filename)
if err != nil {
progressChan <- "Failed"
return
}
numberedFileName := fmt.Sprintf("%s/%s%%04d.png", framesFir, filename)
extractFramesFromVideo(frameRateFraction, filename, numberedFileName)
err = extractFramesFromVideo(frameRateFraction, filename, numberedFileName)
if err != nil {
progressChan <- "Failed"
return
}

distortedFrames := 0
doneChan := make(chan int, 8)
go poolDistortImages(numberedFileName, totalFrames, doneChan)

lastUpdate := time.Now()
for distortedFrames != totalFrames {
distortedFrames += <-doneChan
framesDistorted := <-doneChan
if framesDistorted == -1 {
progressChan <- "Failed"
return
}
distortedFrames += framesDistorted
now := time.Now()
if now.Sub(lastUpdate).Seconds() > 1 {
if now.Sub(lastUpdate).Seconds() > 2 {
lastUpdate = now
progressChan <- generateProgressMessage(distortedFrames, totalFrames)
}
}
progressChan <- "Collecting frames..."
collectFramesToVideo(numberedFileName, frameRateFraction, output)
close(progressChan)
err = collectFramesToVideo(numberedFileName, frameRateFraction, output)
if err != nil {
progressChan <- "Failed"
}
return
}

func getFrameRateFractionAndFrameCount(filename string) (string, int) {
func getFrameRateFractionAndFrameCount(filename string) (string, int, error) {
output, err := exec.Command(
"ffprobe",
"-v", "error",
Expand All @@ -53,43 +71,33 @@ func getFrameRateFractionAndFrameCount(filename string) (string, int) {
"-show_entries", "stream=nb_read_frames,avg_frame_rate",
filename).Output()
if err != nil {
err = errors.WithStack(err)
log.Println(err)
panic(err)
return "", 0, err
}
split := strings.Split(string(output), "\n")
frameCount, err := strconv.Atoi(split[1])
if err != nil {
err = errors.WithStack(err)
log.Println(err)
panic(err)
}
return split[0], frameCount
return split[0], frameCount, err
}

func extractFramesFromVideo(frameRateFraction, filename, numberedFileName string) {
err := exec.Command(
"ffmpeg",
"-i", filename,
func extractFramesFromVideo(frameRateFraction, filename, numberedFileName string) error {
return runFfmpeg("-i", filename,
"-r", frameRateFraction,
numberedFileName).Run()
if err != nil {
log.Println(err)
panic(err)
}
numberedFileName)
}

func collectFramesToVideo(numberedFileName, frameRateFraction, filename string) {
err := exec.Command("ffmpeg",
"-r", frameRateFraction,
func collectFramesToVideo(numberedFileName, frameRateFraction, filename string) error {
return runFfmpeg("-r", frameRateFraction,
"-i", numberedFileName,
"-f", "mp4",
"-c:v", "libx264",
"-an",
"-pix_fmt", "yuv420p",
filename).Run()
if err != nil {
log.Println(err)
panic(err)
}
filename)
}

func poolDistortImages(numberedFileName string, frameCount int, doneChan chan int) {
Expand All @@ -102,7 +110,10 @@ func poolDistortImages(numberedFileName string, frameCount int, doneChan chan in
<-sem
doneChan <- 1
}()
distortImage(fmt.Sprintf(numberedFileName, frame))
err := distortImage(fmt.Sprintf(numberedFileName, frame))
if err != nil {
doneChan <- -1
}
}(frame)
}
}
22 changes: 18 additions & 4 deletions app/distortioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
)

const MaxSizeMb = 20_000_000
const FAILED = "Failed"

func handleAnimationDistortion(b *tb.Bot, m *tb.Message, rl *RateLimiter) {
if m.Animation.Duration > 30 {
Expand All @@ -29,7 +30,11 @@ func handleAnimationDistortion(b *tb.Bot, m *tb.Message, rl *RateLimiter) {
}
defer os.Remove(filename)
defer os.Remove(output)
doneMessageWithRepeater(b, progressMessage)
failed := err != nil
doneMessageWithRepeater(b, progressMessage, failed)
if failed {
return
}

distorted := &tb.Animation{File: tb.FromDisk(output)}
if m.Caption != "" {
Expand All @@ -45,8 +50,12 @@ func handlePhotoDistortion(b *tb.Bot, m *tb.Message) {
return
}
defer os.Remove(filename)
distortImage(filename)
err = distortImage(filename)
// sure would be nice to have generics here
if err != nil {
sendMessageWithRepeater(b, m.Chat, FAILED)
return
}
distorted := &tb.Photo{File: tb.FromDisk(filename)}
if m.Caption != "" {
distorted.Caption = distortText(m.Caption)
Expand All @@ -65,7 +74,11 @@ func handleStickerDistortion(b *tb.Bot, m *tb.Message) {
return
}
defer os.Remove(filename)
distortImage(filename)
err = distortImage(filename)
if err != nil {
sendMessageWithRepeater(b, m.Chat, FAILED)
return
}
distorted := &tb.Sticker{File: tb.FromDisk(filename)}
sendMessageWithRepeater(b, m.Chat, distorted)
}
Expand Down Expand Up @@ -130,7 +143,8 @@ func handleVoiceDistortion(b *tb.Bot, m *tb.Message) {
output := filename + ".ogg"
err = distortSound(filename, output)
if err != nil {
panic(err)
sendMessageWithRepeater(b, m.Chat, FAILED)
return
}
defer os.Remove(output)

Expand Down
25 changes: 25 additions & 0 deletions app/ffmpeg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"bytes"
"github.com/pkg/errors"
"log"
"os/exec"
)

func runFfmpeg(args ...string) error {
var outbuf, errbuf bytes.Buffer
cmd := exec.Command(
"ffmpeg",
args...)
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
err := cmd.Run()
if err != nil {
log.Println(outbuf.String())
log.Println(errbuf.String())
err = errors.WithStack(err)
log.Println(err)
}
return err
}
17 changes: 12 additions & 5 deletions app/handler_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ func handleAnimationCommon(b *tb.Bot, m *tb.Message) (*tb.Message, string, strin
for report := range progressChan {
b.Edit(progressMessage, report, &tb.SendOptions{ParseMode: tb.ModeHTML})
}
return progressMessage, filename, animationOutput, nil
_, err = os.Stat(animationOutput)
return progressMessage, filename, animationOutput, err
}

func handleVideoCommon(b *tb.Bot, m *tb.Message) (string, error) {
progressMessage, filename, animationOutput, err := handleAnimationCommon(b, m)
if err != nil {
if progressMessage != nil {
doneMessageWithRepeater(b, progressMessage, true)
}
return "", err
}
defer os.Remove(filename)
Expand All @@ -42,13 +46,16 @@ func handleVideoCommon(b *tb.Bot, m *tb.Message) (string, error) {
}
output := filename + "Final.mp4"
b.Edit(progressMessage, "Muxing frames with sound back together...")
collectAnimationAndSound(animationOutput, soundOutput, output)
doneMessageWithRepeater(b, progressMessage)
return output, nil
err = collectAnimationAndSound(animationOutput, soundOutput, output)
doneMessageWithRepeater(b, progressMessage, err != nil)
return output, err
}

func doneMessageWithRepeater(b *tb.Bot, m *tb.Message) {
func doneMessageWithRepeater(b *tb.Bot, m *tb.Message, failed bool) {
done := "Done!"
if failed {
done = FAILED
}
_, err := b.Edit(m, done)
for err != nil {
var timeout int
Expand Down
6 changes: 4 additions & 2 deletions app/image.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
package main

import (
"github.com/pkg/errors"
"log"
"os/exec"
)

func distortImage(path string) {
func distortImage(path string) error {
err := exec.Command(
"mogrify",
"-scale", "512x512>", // A reasonable cutoff, I hope
"-liquid-rescale", "50%",
"-scale", "200%",
path).Run()
if err != nil {
err = errors.WithStack(err)
log.Println(err)
panic(err)
}
return err
}
19 changes: 1 addition & 18 deletions app/sound.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,10 @@
package main

import (
"bytes"
"log"
"os/exec"
)

func distortSound(filename, output string) error {
var outbuf, errbuf bytes.Buffer
cmd := exec.Command(
"ffmpeg",
return runFfmpeg(
"-i", filename,
"-vn",
"-c:a", "libopus",
"-af", "vibrato=f=6:d=1",
output)
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
err := cmd.Run()
if err != nil {
log.Println(outbuf.String())
log.Println(errbuf.String())
log.Println(err)
}
return err
}
35 changes: 7 additions & 28 deletions app/video.go
Original file line number Diff line number Diff line change
@@ -1,37 +1,16 @@
package main

import (
"bytes"
"log"
"os/exec"
)

func collectAnimationAndSound(animation, sound, output string) {
var cmd *exec.Cmd
var outbuf, errbuf bytes.Buffer
func collectAnimationAndSound(animation, sound, output string) error {
if sound != "" {
cmd = exec.Command(
"ffmpeg",
"-i", animation,
return runFfmpeg("-i", animation,
"-i", sound,
"-c:v", "copy",
"-c:a", "copy",
output)
} else {
cmd = exec.Command(
"ffmpeg",
"-i", animation,
"-c:v", "copy",
"-an",
output)
}
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
err := cmd.Run()
if err != nil {
log.Println(outbuf.String())
log.Println(errbuf.String())
log.Println(err)
panic(err)
}
return runFfmpeg("ffmpeg",
"-i", animation,
"-c:v", "copy",
"-an",
output)
}

0 comments on commit 02a736b

Please sign in to comment.