diff --git a/README.md b/README.md
index b27e6d5..27b06ca 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ Build and push this container to ghcr
- [ ] change from Audio -> Video
- [x] Provide a better name for downloaded files
- [x] Environment variable to control whether "Secure" flag set on cookie
-- [ ] Allow custom FPS for video transcode
+- [x] Allow custom FPS for video transcode
- [ ] Provide an about page
- `ffmpeg` version
- `yt-dlp` version
diff --git a/handlers.go b/handlers.go
index 90e7504..1c1fe28 100644
--- a/handlers.go
+++ b/handlers.go
@@ -486,13 +486,14 @@ func addAudioTranscode(mediaId, originalId, bitrate uint, srcKind string) {
db.Create(&t)
}
-func addVideoTranscode(videoId, originalId, targetHeight uint) {
+func addVideoTranscode(videoId, originalId, targetHeight uint, targetFPS float64) {
t := Transcode{
SrcID: videoId,
OriginalID: originalId,
SrcKind: "video",
DstKind: "video",
Height: targetHeight,
+ FPS: targetFPS,
TimeSubmit: time.Now(),
Status: "pending",
}
@@ -532,7 +533,7 @@ func processOriginal(originalID uint) {
// create video transcodes
for _, targetHeight := range []uint{480, 240, 144} {
if targetHeight <= video.Height {
- addVideoTranscode(video.ID, originalID, targetHeight)
+ addVideoTranscode(video.ID, originalID, targetHeight, video.FPS)
break
}
}
@@ -1045,6 +1046,7 @@ func deleteAudioHandler(c echo.Context) error {
func transcodeToVideoHandler(c echo.Context) error {
originalId, _ := strconv.ParseUint(c.FormValue("original_id"), 10, 32)
height, _ := strconv.ParseUint(c.FormValue("height"), 10, 32)
+ fps, _ := strconv.ParseFloat(c.FormValue("fps"), 64)
referrer := c.Request().Referer()
if referrer == "" {
referrer = "/"
@@ -1055,7 +1057,7 @@ func transcodeToVideoHandler(c echo.Context) error {
if err == gorm.ErrRecordNotFound {
log.Errorf("no video record for original %d: %v", originalId, err)
} else {
- addVideoTranscode(video.ID, uint(originalId), uint(height))
+ addVideoTranscode(video.ID, uint(originalId), uint(height), fps)
}
return c.Redirect(http.StatusSeeOther, referrer)
diff --git a/models.go b/models.go
index d021d37..d8a6104 100644
--- a/models.go
+++ b/models.go
@@ -23,9 +23,9 @@ type Transcode struct {
TimeStart time.Time
// video fields
- Height uint // target height
- Width uint // target width
- FPS uint // target FPS
+ Height uint // target height
+ Width uint // target width
+ FPS float64 // target FPS
// audio & video fields
Rate uint
diff --git a/templates/video.html b/templates/video.html
index 6f978d8..8c47914 100644
--- a/templates/video.html
+++ b/templates/video.html
@@ -55,6 +55,7 @@
+
diff --git a/workers.go b/workers.go
index e3f3d83..3c08e61 100644
--- a/workers.go
+++ b/workers.go
@@ -19,7 +19,7 @@ func ensureDirFor(path string) error {
return os.MkdirAll(dir, 0700)
}
-func videoToVideo(transID uint, height uint, srcFilepath string) {
+func videoToVideo(transID uint, height uint, fps float64, srcFilepath string) {
// determine destination path
dstFilename := uuid.Must(uuid.NewV7()).String()
@@ -47,8 +47,14 @@ func videoToVideo(transID uint, height uint, srcFilepath string) {
// start ffmpeg
db.Model(&Transcode{}).Where("id = ?", transID).Update("status", "running")
+ var vf string
+ if fps > 0 {
+ vf = fmt.Sprintf("scale=-2:%d,fps=%f", height, fps)
+ } else {
+ vf = fmt.Sprintf("scale=-2:%d", height)
+ }
stdout, stderr, err := ffmpeg.Ffmpeg("-i", srcFilepath,
- "-vf", fmt.Sprintf("scale=-2:%d", height), "-c:v", "libx264",
+ "-vf", vf, "-c:v", "libx264",
"-crf", "23", "-preset", "veryfast", "-c:a", "aac", "-b:a", fmt.Sprintf("%dk", audioBitrate),
dstFilepath)
if err != nil {
@@ -258,7 +264,7 @@ func transcodePending() {
srcFilepath := filepath.Join(getDataDir(), srcVideo.Filename)
if trans.DstKind == "video" {
- videoToVideo(trans.ID, trans.Height, srcFilepath)
+ videoToVideo(trans.ID, trans.Height, trans.FPS, srcFilepath)
} else if trans.DstKind == "audio" {
videoToAudio(trans.ID, trans.Rate, srcFilepath)
} else {