1
Fork 0
photoview/api/scanner/process_video.go

162 lines
4.6 KiB
Go
Raw Normal View History

2020-07-10 12:58:11 +02:00
package scanner
import (
2020-07-11 13:13:31 +02:00
"context"
"fmt"
2020-07-10 12:58:11 +02:00
"log"
"os"
2020-07-11 13:13:31 +02:00
"path"
"strings"
"time"
2020-07-10 12:58:11 +02:00
"github.com/pkg/errors"
"github.com/viktorstrate/photoview/api/graphql/models"
2020-07-11 13:13:31 +02:00
"github.com/viktorstrate/photoview/api/utils"
"gopkg.in/vansante/go-ffprobe.v2"
2020-11-24 11:46:49 +01:00
"gorm.io/gorm"
2020-07-10 12:58:11 +02:00
)
2020-11-24 11:46:49 +01:00
func processVideo(tx *gorm.DB, mediaData *EncodeMediaData, videoCachePath *string) (bool, error) {
2020-07-11 13:13:31 +02:00
video := mediaData.media
2020-07-10 12:58:11 +02:00
didProcess := false
log.Printf("Processing video: %s", video.Path)
2020-11-30 21:29:49 +01:00
mediaURLFromDB := makePhotoURLChecker(tx, video.ID)
2020-07-10 12:58:11 +02:00
2020-11-30 21:29:49 +01:00
videoWebURL, err := mediaURLFromDB(models.VideoWeb)
2020-07-10 12:58:11 +02:00
if err != nil {
return false, errors.Wrap(err, "error processing video web-format")
}
2020-11-30 21:29:49 +01:00
videoThumbnailURL, err := mediaURLFromDB(models.VideoThumbnail)
2020-07-11 13:39:11 +02:00
if err != nil {
return false, errors.Wrap(err, "error processing video thumbnail")
}
2020-07-10 12:58:11 +02:00
if videoWebURL == nil {
2020-07-12 14:31:50 +02:00
didProcess = true
2020-07-11 13:13:31 +02:00
web_video_name := fmt.Sprintf("web_video_%s_%s", path.Base(video.Path), utils.GenerateToken())
web_video_name = strings.ReplaceAll(web_video_name, ".", "_")
web_video_name = strings.ReplaceAll(web_video_name, " ", "_")
web_video_name = web_video_name + ".mp4"
webVideoPath := path.Join(*videoCachePath, web_video_name)
err = FfmpegCli.EncodeMp4(video.Path, webVideoPath)
if err != nil {
return false, errors.Wrapf(err, "could not encode mp4 video (%s)", video.Path)
}
2020-07-11 13:39:11 +02:00
webMetadata, err := readVideoStreamMetadata(webVideoPath)
2020-07-11 13:13:31 +02:00
if err != nil {
return false, errors.Wrapf(err, "failed to read metadata for encoded web-video (%s)", video.Title)
}
fileStats, err := os.Stat(webVideoPath)
if err != nil {
return false, errors.Wrap(err, "reading file stats of web-optimized video")
}
2020-11-25 23:06:47 +01:00
mediaURL := models.MediaURL{
MediaID: video.ID,
MediaName: web_video_name,
Width: webMetadata.Width,
Height: webMetadata.Height,
Purpose: models.VideoWeb,
ContentType: "video/mp4",
FileSize: fileStats.Size(),
}
if err := tx.Create(&mediaURL).Error; err != nil {
2020-07-11 13:13:31 +02:00
return false, errors.Wrapf(err, "failed to insert encoded web-video into database (%s)", video.Title)
}
2020-07-10 12:58:11 +02:00
}
2020-07-11 13:39:11 +02:00
if videoThumbnailURL == nil {
2020-07-12 14:31:50 +02:00
didProcess = true
2020-07-11 13:39:11 +02:00
video_thumb_name := fmt.Sprintf("video_thumb_%s_%s", path.Base(video.Path), utils.GenerateToken())
video_thumb_name = strings.ReplaceAll(video_thumb_name, ".", "_")
video_thumb_name = strings.ReplaceAll(video_thumb_name, " ", "_")
video_thumb_name = video_thumb_name + ".jpg"
thumbImagePath := path.Join(*videoCachePath, video_thumb_name)
err = FfmpegCli.EncodeVideoThumbnail(video.Path, thumbImagePath, mediaData)
if err != nil {
return false, errors.Wrapf(err, "failed to generate thumbnail for video (%s)", video.Title)
}
thumbDimensions, err := GetPhotoDimensions(thumbImagePath)
if err != nil {
return false, errors.Wrap(err, "get dimensions of video thumbnail image")
}
fileStats, err := os.Stat(thumbImagePath)
if err != nil {
return false, errors.Wrap(err, "reading file stats of video thumbnail")
}
2020-11-25 23:06:47 +01:00
thumbMediaURL := models.MediaURL{
MediaID: video.ID,
MediaName: video_thumb_name,
Width: thumbDimensions.Width,
Height: thumbDimensions.Height,
Purpose: models.VideoThumbnail,
ContentType: "image/jpeg",
FileSize: fileStats.Size(),
}
if err := tx.Create(&thumbMediaURL).Error; err != nil {
2020-07-11 13:39:11 +02:00
return false, errors.Wrapf(err, "failed to insert video thumbnail image into database (%s)", video.Title)
}
}
2020-07-10 12:58:11 +02:00
return didProcess, nil
}
2020-07-11 13:13:31 +02:00
2020-07-11 13:39:11 +02:00
func (enc *EncodeMediaData) VideoMetadata() (*ffprobe.ProbeData, error) {
2020-07-11 13:13:31 +02:00
if enc._videoMetadata != nil {
return enc._videoMetadata, nil
}
2020-07-11 13:39:11 +02:00
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFn()
data, err := ffprobe.ProbeURL(ctx, enc.media.Path)
2020-07-11 13:13:31 +02:00
if err != nil {
2020-07-11 13:39:11 +02:00
return nil, errors.Wrapf(err, "could not read video metadata (%s)", enc.media.Title)
2020-07-11 13:13:31 +02:00
}
2020-07-11 13:39:11 +02:00
enc._videoMetadata = data
return enc._videoMetadata, nil
2020-07-11 13:13:31 +02:00
}
2020-07-12 14:17:49 +02:00
func readVideoMetadata(videoPath string) (*ffprobe.ProbeData, error) {
2020-07-11 13:13:31 +02:00
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFn()
data, err := ffprobe.ProbeURL(ctx, videoPath)
if err != nil {
return nil, errors.Wrapf(err, "could not read video metadata (%s)", path.Base(videoPath))
}
2020-07-12 14:17:49 +02:00
return data, nil
}
func readVideoStreamMetadata(videoPath string) (*ffprobe.Stream, error) {
data, err := readVideoMetadata(videoPath)
if err != nil {
return nil, errors.Wrap(err, "read video stream metadata")
}
2020-07-11 13:13:31 +02:00
stream := data.FirstVideoStream()
if stream == nil {
return nil, errors.Wrapf(err, "could not get stream from file metadata (%s)", path.Base(videoPath))
}
return stream, nil
}