181 lines
4.4 KiB
Go
181 lines
4.4 KiB
Go
// internal/media/prepare/prepare.go
|
|
package prepare
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"image"
|
|
"image/gif"
|
|
"image/jpeg"
|
|
"image/png"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"golang.org/x/image/webp"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// ImageDimensions represents the dimensions of an image
|
|
type ImageDimensions struct {
|
|
Width int
|
|
Height int
|
|
}
|
|
|
|
// Manager handles media preparation and optimization
|
|
type Manager struct {
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewManager creates a new preparation manager
|
|
func NewManager(logger *zap.Logger) *Manager {
|
|
if logger == nil {
|
|
// Create a default logger if none is provided
|
|
var err error
|
|
logger, err = zap.NewProduction()
|
|
if err != nil {
|
|
// If we can't create a logger, use a no-op logger
|
|
logger = zap.NewNop()
|
|
}
|
|
}
|
|
|
|
return &Manager{
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// GetMediaDimensions gets the dimensions of an image or video file
|
|
func (m *Manager) GetMediaDimensions(filePath string) (*ImageDimensions, error) {
|
|
// Open the file
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open file: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
// Get the file extension
|
|
ext := strings.ToLower(filepath.Ext(filePath))
|
|
|
|
// Decode the image based on the file extension
|
|
var img image.Image
|
|
|
|
switch ext {
|
|
case ".jpg", ".jpeg":
|
|
img, err = jpeg.Decode(file)
|
|
case ".png":
|
|
img, err = png.Decode(file)
|
|
case ".gif":
|
|
img, err = gif.Decode(file)
|
|
case ".webp":
|
|
img, err = webp.Decode(file)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported image format: %s", ext)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to decode image: %w", err)
|
|
}
|
|
|
|
// Get the dimensions
|
|
bounds := img.Bounds()
|
|
return &ImageDimensions{
|
|
Width: bounds.Max.X - bounds.Min.X,
|
|
Height: bounds.Max.Y - bounds.Min.Y,
|
|
}, nil
|
|
}
|
|
|
|
// ResizeImage resizes an image to the specified dimensions
|
|
// Returns the path to the resized image
|
|
func (m *Manager) ResizeImage(filePath string, maxWidth, maxHeight int) (string, error) {
|
|
// For now, just return the original image
|
|
// In a future update, we can add actual resizing functionality
|
|
return filePath, nil
|
|
}
|
|
|
|
// OptimizeImage optimizes an image for web usage
|
|
// Returns the path to the optimized image
|
|
func (m *Manager) OptimizeImage(filePath string) (string, error) {
|
|
// For now, just return the original image
|
|
// In a future update, we can add optimization functionality
|
|
return filePath, nil
|
|
}
|
|
|
|
// GetMediaType gets the media type of a file
|
|
func (m *Manager) GetMediaType(filePath string) (string, error) {
|
|
// Open the file
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to open file: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
// Read the first 512 bytes to detect the content type
|
|
buffer := make([]byte, 512)
|
|
_, err = file.Read(buffer)
|
|
if err != nil && err != io.EOF {
|
|
return "", fmt.Errorf("failed to read file: %w", err)
|
|
}
|
|
|
|
// Detect the content type
|
|
contentType := detectContentType(buffer)
|
|
|
|
return contentType, nil
|
|
}
|
|
|
|
// ExtractMetadata extracts metadata from a media file
|
|
func (m *Manager) ExtractMetadata(filePath string) (map[string]interface{}, error) {
|
|
// Get the media type
|
|
mediaType, err := m.GetMediaType(filePath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get media type: %w", err)
|
|
}
|
|
|
|
// Create the metadata map
|
|
metadata := map[string]interface{}{
|
|
"media_type": mediaType,
|
|
}
|
|
|
|
// If it's an image, get the dimensions
|
|
if strings.HasPrefix(mediaType, "image/") {
|
|
dims, err := m.GetMediaDimensions(filePath)
|
|
if err != nil {
|
|
m.logger.Warn("Failed to get image dimensions",
|
|
zap.String("filePath", filePath),
|
|
zap.Error(err))
|
|
} else {
|
|
metadata["width"] = dims.Width
|
|
metadata["height"] = dims.Height
|
|
metadata["dimensions"] = fmt.Sprintf("%dx%d", dims.Width, dims.Height)
|
|
}
|
|
}
|
|
|
|
// Get the file size
|
|
fileInfo, err := os.Stat(filePath)
|
|
if err != nil {
|
|
m.logger.Warn("Failed to get file size",
|
|
zap.String("filePath", filePath),
|
|
zap.Error(err))
|
|
} else {
|
|
metadata["size"] = fileInfo.Size()
|
|
}
|
|
|
|
return metadata, nil
|
|
}
|
|
|
|
// detectContentType detects the content type of a file
|
|
func detectContentType(buffer []byte) string {
|
|
// Try to detect the content type
|
|
contentType := http.DetectContentType(buffer)
|
|
|
|
// Some additional checks for specific formats
|
|
if contentType == "application/octet-stream" {
|
|
// Check for WebP signature
|
|
if bytes.HasPrefix(buffer, []byte("RIFF")) && bytes.Contains(buffer[8:12], []byte("WEBP")) {
|
|
return "image/webp"
|
|
}
|
|
}
|
|
|
|
return contentType
|
|
} |