1
Fork 0

Start on video processing

This commit is contained in:
viktorstrate 2020-07-10 12:58:11 +02:00
parent 7caad82059
commit 3564866f41
7 changed files with 102 additions and 67 deletions

View File

@ -21,12 +21,13 @@ func (p *Photo) ID() int {
return p.PhotoID
}
type PhotoPurpose string
type MediaPurpose string
const (
PhotoThumbnail PhotoPurpose = "thumbnail"
PhotoHighRes PhotoPurpose = "high-res"
PhotoOriginal PhotoPurpose = "original"
PhotoThumbnail MediaPurpose = "thumbnail"
PhotoHighRes MediaPurpose = "high-res"
MediaOriginal MediaPurpose = "original"
VideoWeb MediaPurpose = "video-web"
)
type PhotoURL struct {
@ -35,7 +36,7 @@ type PhotoURL struct {
PhotoName string
Width int
Height int
Purpose PhotoPurpose
Purpose MediaPurpose
ContentType string
}

View File

@ -96,7 +96,7 @@ func (r *photoResolver) Downloads(ctx context.Context, obj *models.Photo) ([]*mo
var title string
switch {
case url.Purpose == models.PhotoOriginal:
case url.Purpose == models.MediaOriginal:
title = "Original"
case url.Purpose == models.PhotoThumbnail:
title = "Small"
@ -119,7 +119,7 @@ func (r *photoResolver) HighRes(ctx context.Context, obj *models.Photo) (*models
// Try high res first, then
web_types_questions := strings.Repeat("?,", len(scanner.WebMimetypes))[:len(scanner.WebMimetypes)*2-1]
args := make([]interface{}, 0)
args = append(args, obj.PhotoID, models.PhotoHighRes, models.PhotoOriginal)
args = append(args, obj.PhotoID, models.PhotoHighRes, models.MediaOriginal)
for _, webtype := range scanner.WebMimetypes {
args = append(args, webtype)
}

View File

@ -25,7 +25,7 @@ func RegisterPhotoRoutes(db *sql.DB, router *mux.Router) {
row := db.QueryRow("SELECT photo_url.purpose, photo_url.content_type, photo_url.photo_id FROM photo_url, photo WHERE photo_url.photo_name = ? AND photo_url.photo_id = photo.photo_id", image_name)
var purpose models.PhotoPurpose
var purpose models.MediaPurpose
var content_type string
var photo_id int
@ -136,7 +136,7 @@ func RegisterPhotoRoutes(db *sql.DB, router *mux.Router) {
cachedPath = path.Join(scanner.PhotoCache(), strconv.Itoa(photo.AlbumId), strconv.Itoa(photo_id), image_name)
}
if purpose == models.PhotoOriginal {
if purpose == models.MediaOriginal {
cachedPath = photo.Path
}
@ -151,7 +151,7 @@ func RegisterPhotoRoutes(db *sql.DB, router *mux.Router) {
return
}
_, err = scanner.ProcessPhoto(tx, photo)
_, err = scanner.ProcessMedia(tx, photo)
if err != nil {
log.Printf("ERROR: processing image not found in cache: %s\n", err)
w.WriteHeader(http.StatusInternalServerError)

View File

@ -71,37 +71,6 @@ var SupportedMimetypes = [...]MediaType{
TypeTiff,
TypeWebp,
TypeBmp,
TypeDNG,
TypeARW,
TypeSR2,
TypeSRF,
TypeCR2,
TypeCRW,
TypeERF,
TypeDCS,
TypeDRF,
TypeDCR,
TypeK25,
TypeKDC,
TypeMRW,
TypeMDC,
TypeNEF,
TypeNRW,
TypeORF,
TypePEF,
TypeRAF,
TypeRAW,
TypeRW2,
TypeGPR,
Type3FR,
TypeFFF,
TypeMEF,
TypeCap,
TypeIIQ,
TypeMOS,
TypeRWL,
TypeSRW,
}
var WebMimetypes = [...]MediaType{
@ -224,6 +193,17 @@ func (imgType *MediaType) isWebCompatible() bool {
return false
}
func (imgType *MediaType) isVideo() bool {
for _, video_mime := range VideoMimetypes {
if video_mime == *imgType {
return true
}
}
return false
}
// isSupported determines if the given type can be processed
func (imgType *MediaType) isSupported() bool {
for _, supported_mime := range SupportedMimetypes {
if supported_mime == *imgType {
@ -231,6 +211,14 @@ func (imgType *MediaType) isSupported() bool {
}
}
if DarktableCli.IsInstalled() && imgType.isRaw() {
return true
}
if FfmpegCli.IsInstalled() && imgType.isVideo() {
return true
}
return false
}

View File

@ -23,13 +23,13 @@ import (
)
// Higher order function used to check if PhotoURL for a given PhotoPurpose exists
func makePhotoURLChecker(tx *sql.Tx, photoID int) (func(purpose models.PhotoPurpose) (*models.PhotoURL, error), error) {
func makePhotoURLChecker(tx *sql.Tx, photoID int) (func(purpose models.MediaPurpose) (*models.PhotoURL, error), error) {
photoURLExistsStmt, err := tx.Prepare("SELECT * FROM photo_url WHERE photo_id = ? AND purpose = ?")
if err != nil {
return nil, err
}
return func(purpose models.PhotoPurpose) (*models.PhotoURL, error) {
return func(purpose models.MediaPurpose) (*models.PhotoURL, error) {
row := photoURLExistsStmt.QueryRow(photoID, purpose)
photoURL, err := models.NewPhotoURLFromRow(row)
if err != nil {
@ -43,23 +43,44 @@ func makePhotoURLChecker(tx *sql.Tx, photoID int) (func(purpose models.PhotoPurp
}, nil
}
func ProcessPhoto(tx *sql.Tx, photo *models.Photo) (bool, error) {
func ProcessMedia(tx *sql.Tx, photo *models.Photo) (bool, error) {
imageData := EncodeImageData{
photo: photo,
}
contentType, err := imageData.ContentType()
if err != nil {
return false, errors.Wrapf(err, "get content-type of media (%s)", photo.Path)
}
// Make sure photo cache directory exists
mediaCachePath, err := makeMediaCacheDir(photo)
if err != nil {
return false, errors.Wrap(err, "cache directory error")
}
if contentType.isVideo() {
return processVideo(tx, &imageData, mediaCachePath)
} else {
return processPhoto(tx, &imageData, mediaCachePath)
}
}
func processPhoto(tx *sql.Tx, imageData *EncodeImageData, photoCachePath *string) (bool, error) {
photo := imageData.photo
log.Printf("Processing photo: %s\n", photo.Path)
didProcess := false
imageData := EncodeImageData{
photo: photo,
}
photoUrlFromDB, err := makePhotoURLChecker(tx, photo.PhotoID)
if err != nil {
return false, err
}
// original photo url
origURL, err := photoUrlFromDB(models.PhotoOriginal)
origURL, err := photoUrlFromDB(models.MediaOriginal)
if err != nil {
return false, err
}
@ -67,19 +88,13 @@ func ProcessPhoto(tx *sql.Tx, photo *models.Photo) (bool, error) {
// Thumbnail
thumbURL, err := photoUrlFromDB(models.PhotoThumbnail)
if err != nil {
return false, errors.Wrap(err, "error processing thumbnail")
return false, errors.Wrap(err, "error processing photo thumbnail")
}
// Highres
highResURL, err := photoUrlFromDB(models.PhotoHighRes)
if err != nil {
return false, errors.Wrap(err, "error processing highres")
}
// Make sure photo cache directory exists
photoCachePath, err := makePhotoCacheDir(photo)
if err != nil {
return false, errors.Wrap(err, "cache directory error")
return false, errors.Wrap(err, "error processing photo highres")
}
// Generate high res jpeg
@ -195,13 +210,12 @@ func ProcessPhoto(tx *sql.Tx, photo *models.Photo) (bool, error) {
return didProcess, nil
}
func makePhotoCacheDir(photo *models.Photo) (*string, error) {
func makeMediaCacheDir(photo *models.Photo) (*string, error) {
// Make root cache dir if not exists
if _, err := os.Stat(PhotoCache()); os.IsNotExist(err) {
if err := os.Mkdir(PhotoCache(), os.ModePerm); err != nil {
log.Println("ERROR: Could not make root image cache directory")
return nil, err
return nil, errors.Wrap(err, "could not make root image cache directory")
}
}
@ -209,8 +223,7 @@ func makePhotoCacheDir(photo *models.Photo) (*string, error) {
albumCachePath := path.Join(PhotoCache(), strconv.Itoa(photo.AlbumId))
if _, err := os.Stat(albumCachePath); os.IsNotExist(err) {
if err := os.Mkdir(albumCachePath, os.ModePerm); err != nil {
log.Println("ERROR: Could not make album image cache directory")
return nil, err
return nil, errors.Wrap(err, "could not make album image cache directory")
}
}
@ -218,15 +231,14 @@ func makePhotoCacheDir(photo *models.Photo) (*string, error) {
photoCachePath := path.Join(albumCachePath, strconv.Itoa(photo.PhotoID))
if _, err := os.Stat(photoCachePath); os.IsNotExist(err) {
if err := os.Mkdir(photoCachePath, os.ModePerm); err != nil {
log.Println("ERROR: Could not make photo image cache directory")
return nil, err
return nil, errors.Wrap(err, "could not make photo image cache directory")
}
}
return &photoCachePath, nil
}
func saveOriginalPhotoToDB(tx *sql.Tx, photo *models.Photo, imageData EncodeImageData, photoDimensions *PhotoDimensions) error {
func saveOriginalPhotoToDB(tx *sql.Tx, photo *models.Photo, imageData *EncodeImageData, photoDimensions *PhotoDimensions) error {
photoName := path.Base(photo.Path)
photoBaseName := photoName[0 : len(photoName)-len(path.Ext(photoName))]
photoBaseExt := path.Ext(photoName)
@ -239,7 +251,7 @@ func saveOriginalPhotoToDB(tx *sql.Tx, photo *models.Photo, imageData EncodeImag
return err
}
_, err = tx.Exec("INSERT INTO photo_url (photo_id, photo_name, width, height, purpose, content_type) VALUES (?, ?, ?, ?, ?, ?)", photo.PhotoID, original_image_name, photoDimensions.Width, photoDimensions.Height, models.PhotoOriginal, contentType)
_, err = tx.Exec("INSERT INTO photo_url (photo_id, photo_name, width, height, purpose, content_type) VALUES (?, ?, ?, ?, ?, ?)", photo.PhotoID, original_image_name, photoDimensions.Width, photoDimensions.Height, models.MediaOriginal, contentType)
if err != nil {
log.Printf("Could not insert original photo url: %d, %s\n", photo.PhotoID, photoName)
return err

View File

@ -0,0 +1,34 @@
package scanner
import (
"database/sql"
"log"
"github.com/pkg/errors"
"github.com/viktorstrate/photoview/api/graphql/models"
)
func processVideo(tx *sql.Tx, imageData *EncodeImageData, videoCachePath *string) (bool, error) {
video := imageData.photo
didProcess := false
log.Printf("Processing video: %s", video.Path)
mediaUrlFromDB, err := makePhotoURLChecker(tx, video.PhotoID)
if err != nil {
return false, err
}
videoWebURL, err := mediaUrlFromDB(models.VideoWeb)
if err != nil {
return false, errors.Wrap(err, "error processing video web-format")
}
if videoWebURL == nil {
// TODO: Process web video
}
// TODO: Process video thumbnail
return didProcess, nil
}

View File

@ -42,7 +42,7 @@ func scanAlbum(album *models.Album, cache *AlbumScannerCache, db *sql.DB) {
ScannerError("Failed to begin database transaction: %s", err)
}
processing_was_needed, err := ProcessPhoto(tx, photo)
processing_was_needed, err := ProcessMedia(tx, photo)
if err != nil {
tx.Rollback()
ScannerError("Failed to process photo (%s): %s", photo.Path, err)