refactor ffmpeg, download styling
This commit is contained in:
@@ -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
|
||||
|
36
ffmpeg/ffmpeg.go
Normal file
36
ffmpeg/ffmpeg.go
Normal file
@@ -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
|
||||
}
|
26
ffmpeg/ffprobe.go
Normal file
26
ffmpeg/ffprobe.go
Normal file
@@ -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
|
||||
}
|
12
ffmpeg/init.go
Normal file
12
ffmpeg/init.go
Normal file
@@ -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
|
||||
}
|
28
ffprobe.go
28
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
|
||||
|
11
handlers.go
11
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)
|
||||
|
2
main.go
2
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)
|
||||
|
||||
|
39
static/style/download.css
Normal file
39
static/style/download.css
Normal file
@@ -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;
|
||||
}
|
@@ -5,6 +5,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/static/style/common.css">
|
||||
<link rel="stylesheet" href="/static/style/download.css">
|
||||
{{template "footer-css" .}}
|
||||
<title>Download Video</title>
|
||||
</head>
|
||||
@@ -13,11 +14,12 @@
|
||||
<h1>Download Video</h1>
|
||||
<form method="POST">
|
||||
<input type="url" name="url" placeholder="Video URL" required>
|
||||
<input type="radio" id="audio-video" name="color" value="audio-video" checked>
|
||||
<label for="audio-video">Audio/Video</label>
|
||||
<input type="radio" id="audio-video" name="color" value="audio">
|
||||
<label for="audio">Audio Only</label>
|
||||
|
||||
<div class="radio-group">
|
||||
<input type="radio" id="audio-video" name="color" value="audio-video" checked>
|
||||
<label for="audio-video">Audio/Video</label>
|
||||
<input type="radio" id="audio-video" name="color" value="audio">
|
||||
<label for="audio">Audio Only</label>
|
||||
</div>
|
||||
<button type="submit">Download</button>
|
||||
</form>
|
||||
<a href="/videos">View Downloaded Videos</a>
|
||||
|
40
workers.go
40
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")
|
||||
|
@@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user