From 30a2407e462a9362ad88ea21302b036185694c43 Mon Sep 17 00:00:00 2001 From: Carl Pearson Date: Mon, 14 Oct 2024 05:36:34 -0600 Subject: [PATCH] refactor ffmpeg, download styling --- Dockerfile | 1 + ffmpeg/ffmpeg.go | 36 +++++++++++++++++++++++++++++++++++ ffmpeg/ffprobe.go | 26 +++++++++++++++++++++++++ ffmpeg/init.go | 12 ++++++++++++ ffprobe.go | 28 ++++----------------------- handlers.go | 11 ++++++----- main.go | 2 ++ static/style/download.css | 39 ++++++++++++++++++++++++++++++++++++++ templates/download.html | 12 +++++++----- workers.go | 40 ++++++++++++--------------------------- ytdlp/ytdlp.go | 9 --------- 11 files changed, 145 insertions(+), 71 deletions(-) create mode 100644 ffmpeg/ffmpeg.go create mode 100644 ffmpeg/ffprobe.go create mode 100644 ffmpeg/init.go create mode 100644 static/style/download.css diff --git a/Dockerfile b/Dockerfile index e731559..7c79684 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,7 @@ RUN wget -q -d https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_ ADD *.go /src/. ADD database /src/database +Add ffmpeg /src/ffmpeg ADD handlers /src/handlers ADD media /src/media ADD originals /src/originals diff --git a/ffmpeg/ffmpeg.go b/ffmpeg/ffmpeg.go new file mode 100644 index 0000000..5f58a9d --- /dev/null +++ b/ffmpeg/ffmpeg.go @@ -0,0 +1,36 @@ +package ffmpeg + +import ( + "bytes" + "fmt" + "os/exec" + "strings" +) + +func Clip(src, dst string, from, to float64) error { + _, _, err := Ffmpeg("-i", src, + "-ss", fmt.Sprintf("%f", from), + "-to", fmt.Sprintf("%f", to), + "-c", "copy", + dst) + return err +} + +// runs ffprobe with the provided args and returns (stdout, stderr, error) +func Ffmpeg(args ...string) ([]byte, []byte, error) { + ffmpeg := "ffmpeg" + log.Infoln(ffmpeg, strings.Join(args, " ")) + cmd := exec.Command(ffmpeg, args...) + var stdout bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + + if err != nil { + log.Errorf("ffmpeg error: %v", err) + } + log.Infoln("stdout:", stdout.String()) + log.Infoln("stderr:", stderr.String()) + return stdout.Bytes(), stderr.Bytes(), err +} diff --git a/ffmpeg/ffprobe.go b/ffmpeg/ffprobe.go new file mode 100644 index 0000000..5c9e93f --- /dev/null +++ b/ffmpeg/ffprobe.go @@ -0,0 +1,26 @@ +package ffmpeg + +import ( + "bytes" + "os/exec" + "strings" +) + +// runs ffprobe with the provided args and returns (stdout, stderr, error) +func Ffprobe(args ...string) ([]byte, []byte, error) { + ffprobe := "ffprobe" + log.Infoln(ffprobe, strings.Join(args, " ")) + cmd := exec.Command(ffprobe, args...) + var stdout bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + + if err != nil { + log.Errorf("ffprobe error: %v", err) + } + log.Infoln("stdout:", stdout.String()) + log.Infoln("stderr:", stderr.String()) + return stdout.Bytes(), stderr.Bytes(), err +} diff --git a/ffmpeg/init.go b/ffmpeg/init.go new file mode 100644 index 0000000..1455194 --- /dev/null +++ b/ffmpeg/init.go @@ -0,0 +1,12 @@ +package ffmpeg + +import "github.com/sirupsen/logrus" + +var log *logrus.Logger + +func Init(logger *logrus.Logger) error { + log = logger.WithFields(logrus.Fields{ + "component": "ffmpeg", + }).Logger + return nil +} diff --git a/ffprobe.go b/ffprobe.go index d4e4321..bf8d07e 100644 --- a/ffprobe.go +++ b/ffprobe.go @@ -1,12 +1,11 @@ package main import ( - "bytes" "encoding/json" "fmt" - "os/exec" "strconv" "strings" + "ytdlp-site/ffmpeg" ) type FFProbeOutput struct { @@ -15,27 +14,8 @@ type FFProbeOutput struct { } `json:"streams"` } -// runs ffprobe with the provided args and returns (stdout, stderr, error) -func runFfprobe(args ...string) ([]byte, []byte, error) { - ffprobe := "ffprobe" - log.Infoln(ffprobe, strings.Join(args, " ")) - cmd := exec.Command(ffprobe, args...) - var stdout bytes.Buffer - var stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() - - if err != nil { - log.Errorf("ffprobe error: %v", err) - } - log.Infoln("stdout:", stdout.String()) - log.Infoln("stderr:", stderr.String()) - return stdout.Bytes(), stderr.Bytes(), err -} - func getAudioFormat(filename string) (string, error) { - output, _, err := runFfprobe("-v", "quiet", "-print_format", "json", "-show_streams", filename) + output, _, err := ffmpeg.Ffprobe("-v", "quiet", "-print_format", "json", "-show_streams", filename) if err != nil { log.Errorln("ffprobe error:", err) return "", err @@ -65,7 +45,7 @@ func getStreamBitrate(path string, stream int) (uint, error) { "-of", "default=noprint_wrappers=1:nokey=1", path} - stdout, _, err := runFfprobe(ffprobeArgs...) + stdout, _, err := ffmpeg.Ffprobe(ffprobeArgs...) if err != nil { fmt.Println("ffprobe error:", err, string(stdout)) return 0, err @@ -87,7 +67,7 @@ func getFormatBitrate(path string) (uint, error) { "-of", "default=noprint_wrappers=1:nokey=1", path} - stdout, _, err := runFfprobe(ffprobeArgs...) + stdout, _, err := ffmpeg.Ffprobe(ffprobeArgs...) if err != nil { fmt.Println("ffprobe error:", err, string(stdout)) return 0, err diff --git a/handlers.go b/handlers.go index d2bd520..5f8f08b 100644 --- a/handlers.go +++ b/handlers.go @@ -16,6 +16,7 @@ import ( "golang.org/x/crypto/bcrypt" "gorm.io/gorm" + "ytdlp-site/ffmpeg" "ytdlp-site/media" "ytdlp-site/originals" "ytdlp-site/playlists" @@ -258,7 +259,7 @@ func getYtdlpVideoMeta(url string) (Meta, error) { // return the length in seconds of a video file at `path` func getLength(path string) (float64, error) { - stdout, _, err := runFfprobe("-v", "error", "-show_entries", "format=duration", + stdout, _, err := ffmpeg.Ffprobe("-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", path) if err != nil { log.Errorln("ffprobe error:", err) @@ -273,7 +274,7 @@ func getLength(path string) (float64, error) { } func getVideoWidth(path string) (uint, error) { - stdout, _, err := runFfprobe("-v", "error", "-select_streams", + stdout, _, err := ffmpeg.Ffprobe("-v", "error", "-select_streams", "v:0", "-count_packets", "-show_entries", "stream=width", "-of", "csv=p=0", path) @@ -290,7 +291,7 @@ func getVideoWidth(path string) (uint, error) { } func getVideoHeight(path string) (uint, error) { - stdout, _, err := runFfprobe("-v", "error", "-select_streams", + stdout, _, err := ffmpeg.Ffprobe("-v", "error", "-select_streams", "v:0", "-count_packets", "-show_entries", "stream=height", "-of", "csv=p=0", path) @@ -308,7 +309,7 @@ func getVideoHeight(path string) (uint, error) { func getVideoFPS(path string) (float64, error) { - stdout, _, err := runFfprobe("-v", "error", "-select_streams", + stdout, _, err := ffmpeg.Ffprobe("-v", "error", "-select_streams", "v:0", "-count_packets", "-show_entries", "stream=r_frame_rate", "-of", "csv=p=0", path) if err != nil { @@ -421,7 +422,7 @@ func getVideoMeta(path string) (VideoMeta, error) { func getAudioDuration(path string) (float64, error) { - stdout, _, err := runFfprobe("-v", "error", + stdout, _, err := ffmpeg.Ffprobe("-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", path) diff --git a/main.go b/main.go index cd5c88a..a60b7ac 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ import ( "gorm.io/gorm/logger" "ytdlp-site/database" + "ytdlp-site/ffmpeg" "ytdlp-site/handlers" "ytdlp-site/media" "ytdlp-site/originals" @@ -52,6 +53,7 @@ func main() { log.Infof("GitSHA: %s", getGitSHA()) log.Infof("BuildDate: %s", getBuildDate()) + ffmpeg.Init(log) handlers.Init(log) ytdlp.Init(log) diff --git a/static/style/download.css b/static/style/download.css new file mode 100644 index 0000000..4dfa7c0 --- /dev/null +++ b/static/style/download.css @@ -0,0 +1,39 @@ +h1 { + font-size: 24px; + margin-bottom: 20px; +} + +form { + display: flex; + flex-direction: column; + gap: 15px; +} + +input[type="url"] { + width: 100%; + padding: 1rem; + font-size: 1.25rem; +} + +.radio-group { + display: flex; + gap: 10px; + /* align-items: center; */ + justify-content: center; +} + +.radio-group label { + display: flex; + align-items: center; + gap: 5px; + cursor: pointer; +} + +button { + background-color: #007bff; + color: white; + border: none; + padding: 12px; + font-size: 16px; + cursor: pointer; +} \ No newline at end of file diff --git a/templates/download.html b/templates/download.html index 185ea03..5254337 100644 --- a/templates/download.html +++ b/templates/download.html @@ -5,6 +5,7 @@ + {{template "footer-css" .}} Download Video @@ -13,11 +14,12 @@

Download Video

- - - - - +
+ + + + +
View Downloaded Videos diff --git a/workers.go b/workers.go index c02fa8e..ecd9c65 100644 --- a/workers.go +++ b/workers.go @@ -1,13 +1,11 @@ package main import ( - "bytes" "fmt" "os" - "os/exec" "path/filepath" - "strings" "time" + "ytdlp-site/ffmpeg" "ytdlp-site/media" "ytdlp-site/originals" @@ -48,20 +46,14 @@ func videoToVideo(transID uint, height uint, srcFilepath string) { } // start ffmpeg - ffmpeg := "ffmpeg" - ffmpegArgs := []string{"-i", srcFilepath, + db.Model(&Transcode{}).Where("id = ?", transID).Update("status", "running") + stdout, stderr, err := ffmpeg.Ffmpeg("-i", srcFilepath, "-vf", fmt.Sprintf("scale=-2:%d", height), "-c:v", "libx264", "-crf", "23", "-preset", "veryfast", "-c:a", "aac", "-b:a", fmt.Sprintf("%dk", audioBitrate), - dstFilepath} - fmt.Println(ffmpeg, strings.Join(ffmpegArgs, " ")) - cmd := exec.Command(ffmpeg, ffmpegArgs...) - var stdout bytes.Buffer - var stderr bytes.Buffer - cmd.Stdout, cmd.Stderr = &stdout, &stderr - db.Model(&Transcode{}).Where("id = ?", transID).Update("status", "running") - err = cmd.Run() + dstFilepath) if err != nil { - fmt.Println("Error: convert to video file", srcFilepath, "->", dstFilepath, stdout.String(), stderr.String()) + fmt.Println("Error: convert to video file", srcFilepath, "->", dstFilepath, string(stdout), string(stderr)) + db.Model(&Transcode{}).Where("id = ?", transID).Update("status", "failed") return } @@ -112,15 +104,11 @@ func videoToAudio(transID uint, kbps uint, videoFilepath string) { return } - ffmpeg := "ffmpeg" - ffmpegArgs := []string{"-i", videoFilepath, "-vn", "-acodec", + db.Model(&Transcode{}).Where("id = ?", transID).Update("status", "running") + _, _, err = ffmpeg.Ffmpeg("-i", videoFilepath, "-vn", "-acodec", "mp3", "-b:a", fmt.Sprintf("%dk", kbps), - audioFilepath} - fmt.Println(ffmpeg, strings.Join(ffmpegArgs, " ")) - cmd := exec.Command(ffmpeg, ffmpegArgs...) - db.Model(&Transcode{}).Where("id = ?", transID).Update("status", "running") - err = cmd.Run() + audioFilepath) if err != nil { fmt.Println("Error: convert to audio file", videoFilepath, "->", audioFilepath) db.Model(&Transcode{}).Where("id = ?", transID).Update("status", "failed") @@ -170,15 +158,11 @@ func audioToAudio(transID uint, kbps uint, srcFilepath string) { return } - ffmpeg := "ffmpeg" - ffmpegArgs := []string{"-i", srcFilepath, "-vn", "-acodec", + db.Model(&Transcode{}).Where("id = ?", transID).Update("status", "running") + _, _, err = ffmpeg.Ffmpeg("-i", srcFilepath, "-vn", "-acodec", "mp3", "-b:a", fmt.Sprintf("%dk", kbps), - dstFilepath} - fmt.Println(ffmpeg, strings.Join(ffmpegArgs, " ")) - cmd := exec.Command(ffmpeg, ffmpegArgs...) - db.Model(&Transcode{}).Where("id = ?", transID).Update("status", "running") - err = cmd.Run() + dstFilepath) if err != nil { fmt.Println("Error: convert to audio file", srcFilepath, "->", dstFilepath) db.Model(&Transcode{}).Where("id = ?", transID).Update("status", "failed") diff --git a/ytdlp/ytdlp.go b/ytdlp/ytdlp.go index 7caf08b..486b953 100644 --- a/ytdlp/ytdlp.go +++ b/ytdlp/ytdlp.go @@ -2,7 +2,6 @@ package ytdlp import ( "bytes" - "fmt" "os/exec" "strings" ) @@ -25,11 +24,3 @@ func Run(args ...string) ([]byte, []byte, error) { log.Infoln("stderr:", stderr.String()) return stdout.Bytes(), stderr.Bytes(), err } - -func Clip(src, dst string, from, to float64) error { - _, _, err := Run("-i", src, - "-ss", fmt.Sprintf("%f", from), - "-to", fmt.Sprintf("%f", to), - dst) - return err -}