2021-12-29 15:38:25 +00:00
|
|
|
package media
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"git.netflux.io/rob/clipper/generated/store"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
)
|
|
|
|
|
|
|
|
type VideoThumbnail struct {
|
|
|
|
Data []byte
|
|
|
|
Width, Height int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *MediaSetService) GetVideoThumbnail(ctx context.Context, id uuid.UUID) (VideoThumbnail, error) {
|
|
|
|
mediaSet, err := s.store.GetMediaSet(ctx, id)
|
|
|
|
if err != nil {
|
|
|
|
return VideoThumbnail{}, fmt.Errorf("error getting media set: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if mediaSet.VideoThumbnailS3UploadedAt.Valid {
|
|
|
|
return s.getThumbnailFromFileStore(ctx, mediaSet)
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.getThumbnailFromYoutube(ctx, mediaSet)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *MediaSetService) getThumbnailFromFileStore(ctx context.Context, mediaSet store.MediaSet) (VideoThumbnail, error) {
|
|
|
|
object, err := s.fileStore.GetObject(ctx, mediaSet.VideoThumbnailS3Key.String)
|
|
|
|
if err != nil {
|
|
|
|
return VideoThumbnail{}, fmt.Errorf("error fetching thumbnail from file store: %v", err)
|
|
|
|
}
|
|
|
|
defer object.Close()
|
|
|
|
|
|
|
|
imageData, err := io.ReadAll(object)
|
|
|
|
if err != nil {
|
|
|
|
return VideoThumbnail{}, fmt.Errorf("error reading thumbnail from file store: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return VideoThumbnail{
|
|
|
|
Width: int(mediaSet.VideoThumbnailWidth.Int32),
|
|
|
|
Height: int(mediaSet.VideoThumbnailHeight.Int32),
|
|
|
|
Data: imageData,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *MediaSetService) getThumbnailFromYoutube(ctx context.Context, mediaSet store.MediaSet) (VideoThumbnail, error) {
|
|
|
|
video, err := s.youtube.GetVideoContext(ctx, mediaSet.YoutubeID)
|
|
|
|
if err != nil {
|
|
|
|
return VideoThumbnail{}, fmt.Errorf("error fetching video: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(video.Formats) == 0 {
|
|
|
|
return VideoThumbnail{}, errors.New("no format available")
|
|
|
|
}
|
|
|
|
|
|
|
|
thumbnails := video.Thumbnails
|
|
|
|
SortYoutubeThumbnails(thumbnails)
|
|
|
|
thumbnail := thumbnails[0]
|
|
|
|
|
|
|
|
resp, err := http.Get(thumbnail.URL)
|
|
|
|
if err != nil {
|
|
|
|
return VideoThumbnail{}, fmt.Errorf("error fetching thumbnail: %v", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
imageData, err := io.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return VideoThumbnail{}, fmt.Errorf("error reading thumbnail: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: use mediaSet func to fetch key
|
|
|
|
thumbnailKey := fmt.Sprintf("media_sets/%s/thumbnail.jpg", mediaSet.ID)
|
|
|
|
|
|
|
|
const mimeType = "application/jpeg"
|
2022-01-03 12:32:39 +00:00
|
|
|
_, err = s.fileStore.PutObject(ctx, thumbnailKey, bytes.NewReader(imageData), mimeType)
|
2021-12-29 15:38:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return VideoThumbnail{}, fmt.Errorf("error uploading thumbnail: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
storeParams := store.SetVideoThumbnailUploadedParams{
|
|
|
|
ID: mediaSet.ID,
|
|
|
|
VideoThumbnailMimeType: sqlString(mimeType),
|
|
|
|
VideoThumbnailS3Key: sqlString(thumbnailKey),
|
|
|
|
VideoThumbnailWidth: sqlInt32(int32(thumbnail.Width)),
|
|
|
|
VideoThumbnailHeight: sqlInt32(int32(thumbnail.Height)),
|
|
|
|
}
|
|
|
|
if _, err := s.store.SetVideoThumbnailUploaded(ctx, storeParams); err != nil {
|
|
|
|
return VideoThumbnail{}, fmt.Errorf("error updating media set: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return VideoThumbnail{Width: int(thumbnail.Width), Height: int(thumbnail.Height), Data: imageData}, nil
|
|
|
|
}
|