Files
2026-02-19 05:23:05 -06:00

255 lines
6.5 KiB
Go

package services
import (
"encoding/json"
"fmt"
"image"
_ "image/jpeg"
_ "image/png"
"mime/multipart"
"os"
"path/filepath"
"github.com/disintegration/imaging"
"github.com/google/uuid"
"github.com/rwcarlsen/goexif/exif"
"github.com/wisplite/raster/internal/db"
"github.com/wisplite/raster/internal/models"
"gorm.io/datatypes"
)
func UploadMedia(file *multipart.FileHeader, albumID string, accessToken string) (models.Media, error) {
userID, err := ValidateAccessToken(accessToken)
if err != nil {
return models.Media{}, err
}
accessLevel, err := CheckUserAlbumAccess(userID, albumID)
if err != nil {
return models.Media{}, err
}
if accessLevel < 1 {
return models.Media{}, fmt.Errorf("user does not have permission to upload media to this album")
}
albumPath := albumID
if albumID == "" {
albumPath = "root"
}
// Extract metadata (dimensions and file info)
var meta datatypes.JSON
metadataMap := map[string]interface{}{
"originalFilename": file.Filename,
"fileSize": file.Size,
"contentType": file.Header.Get("Content-Type"),
"exif": map[string]interface{}{},
}
src, err := file.Open()
if err == nil {
defer src.Close()
cfg, format, err := image.DecodeConfig(src)
if err == nil {
metadataMap["width"] = cfg.Width
metadataMap["height"] = cfg.Height
metadataMap["format"] = format
// Check for EXIF orientation
src.Seek(0, 0)
x, err := exif.Decode(src)
if err == nil {
orient, err := x.Get(exif.Orientation)
if err == nil {
val, err := orient.Int(0)
if err == nil {
if val >= 5 && val <= 8 {
metadataMap["width"] = cfg.Height
metadataMap["height"] = cfg.Width
}
}
}
model, err := x.Get(exif.Model)
if err == nil {
val := model.String()
metadataMap["exif"].(map[string]interface{})["model"] = val
}
iso, err := x.Get(exif.ISOSpeedRatings)
if err == nil {
val, err := iso.Int(0)
if err == nil {
metadataMap["exif"].(map[string]interface{})["iso"] = val
}
}
aperture, err := x.Get(exif.FNumber)
if err == nil {
val, err := aperture.Rat(0)
if err == nil {
metadataMap["exif"].(map[string]interface{})["aperture"] = val
}
}
shutterSpeed, err := x.Get(exif.ShutterSpeedValue)
if err == nil {
val, err := shutterSpeed.Rat(0)
if err == nil {
metadataMap["exif"].(map[string]interface{})["shutterSpeed"] = val
}
}
focalLength, err := x.Get(exif.FocalLength)
if err == nil {
val, err := focalLength.Rat(0)
if err == nil {
metadataMap["exif"].(map[string]interface{})["focalLength"] = val
}
}
lensModel, err := x.Get(exif.LensModel)
if err == nil {
val := lensModel.String()
metadataMap["exif"].(map[string]interface{})["lensModel"] = val
}
lensMake, err := x.Get(exif.LensMake)
if err == nil {
val := lensMake.String()
metadataMap["exif"].(map[string]interface{})["lensMake"] = val
}
exposureTime, err := x.Get(exif.ExposureTime)
if err == nil {
val, err := exposureTime.Rat(0)
if err == nil {
metadataMap["exif"].(map[string]interface{})["exposureTime"] = val
}
}
exposureBiasValue, err := x.Get(exif.ExposureBiasValue)
if err == nil {
val, err := exposureBiasValue.Rat(0)
if err == nil {
metadataMap["exif"].(map[string]interface{})["exposureBiasValue"] = val
}
}
}
}
}
b, _ := json.Marshal(metadataMap)
meta = datatypes.JSON(b)
mediaID := uuid.New().String()
mediaPath := fmt.Sprintf("media/%s/%s.%s", albumPath, mediaID, file.Filename)
media := models.Media{
ID: mediaID,
AlbumID: albumID,
Path: mediaPath,
Type: file.Header.Get("Content-Type"),
Title: file.Filename,
Description: "",
Metadata: meta,
}
result := db.GetDB().Create(&media)
if result.Error != nil {
return models.Media{}, result.Error
}
return media, nil
}
func GetAllMediaInAlbum(albumID string, accessToken string) ([]models.Media, error) {
userID, err := ValidateAccessToken(accessToken)
if err != nil {
return []models.Media{}, err
}
accessLevel, err := CheckUserAlbumAccess(userID, albumID)
if err != nil {
return []models.Media{}, err
}
if accessLevel < 0 {
return []models.Media{}, fmt.Errorf("user does not have permission to view media in this album")
}
media := []models.Media{}
result := db.GetDB().Where("album_id = ?", albumID).Find(&media)
if result.Error != nil {
return []models.Media{}, result.Error
}
return media, nil
}
func GetAllMediaInPublicAlbum(albumID string) ([]models.Media, error) {
media := []models.Media{}
result := db.GetDB().Where("album_id = ?", albumID).Find(&media)
if result.Error != nil {
return []models.Media{}, result.Error
}
return media, nil
}
func GetMedia(albumID string, mediaID string) (models.Media, error) {
media := models.Media{}
result := db.GetDB().First(&media, "album_id = ? AND id = ?", albumID, mediaID)
if result.Error != nil {
return models.Media{}, result.Error
}
return media, nil
}
func GetThumbnail(albumID string, mediaID string, width int, height int) (string, error) {
media, err := GetMedia(albumID, mediaID)
if err != nil {
return "", err
}
if width == 0 {
width = 200
}
if height == 0 {
height = 200
}
cacheDir := "cache/thumbnails"
if err := os.MkdirAll(cacheDir, 0755); err != nil {
return "", err
}
ext := filepath.Ext(media.Path)
if ext == "" {
ext = ".jpg"
}
thumbName := fmt.Sprintf("%s_%dx%d%s", mediaID, width, height, ext)
thumbPath := filepath.Join(cacheDir, thumbName)
if _, err := os.Stat(thumbPath); err == nil {
return thumbPath, nil
}
srcImage, err := imaging.Open(media.Path)
if err != nil {
return "", err
}
f, err := os.Open(media.Path)
if err == nil {
x, err := exif.Decode(f)
f.Close()
if err == nil {
orient, err := x.Get(exif.Orientation)
if err == nil {
val, err := orient.Int(0)
if err == nil {
switch val {
case 3:
srcImage = imaging.Rotate180(srcImage)
case 6:
srcImage = imaging.Rotate270(srcImage)
case 8:
srcImage = imaging.Rotate90(srcImage)
}
}
}
}
}
dstImage := imaging.Fit(srcImage, width, height, imaging.Lanczos)
err = imaging.Save(dstImage, thumbPath)
if err != nil {
return "", err
}
return thumbPath, nil
}