feat: image variants per image
This commit is contained in:
1 parent
f567866161
commit
951fef18ba
92 files changed
+8695
-413
No files matched your search
@@ -99,10 +99,11 @@ func StoredName(imagePath string) string {
|
||||
type Sort string
|
||||
|
||||
const (
|
||||
SortNewest Sort = "newest"
|
||||
SortOldest Sort = "oldest"
|
||||
SortName Sort = "name"
|
||||
SortLargest Sort = "largest"
|
||||
SortNewest Sort = "newest"
|
||||
SortOldest Sort = "oldest"
|
||||
SortName Sort = "name"
|
||||
SortLargest Sort = "largest"
|
||||
SortTotalSize Sort = "total_size"
|
||||
)
|
||||
|
||||
type Filter string
|
||||
@@ -113,13 +114,22 @@ const (
|
||||
FilterDuplicates Filter = "duplicates"
|
||||
)
|
||||
|
||||
type VariantsFilter string
|
||||
|
||||
const (
|
||||
VariantsAny VariantsFilter = ""
|
||||
VariantsWith VariantsFilter = "with"
|
||||
VariantsWithout VariantsFilter = "without"
|
||||
)
|
||||
|
||||
type ListQuery struct {
|
||||
Search string
|
||||
Hash string
|
||||
Filter Filter
|
||||
Sort Sort
|
||||
Limit int
|
||||
Cursor string
|
||||
Search string
|
||||
Hash string
|
||||
Filter Filter
|
||||
Variants VariantsFilter
|
||||
Sort Sort
|
||||
Limit int
|
||||
Cursor string
|
||||
}
|
||||
|
||||
func NewListQuery() ListQuery {
|
||||
@@ -133,7 +143,12 @@ func (q ListQuery) Validate() error {
|
||||
return ErrInvalidQuery
|
||||
}
|
||||
switch q.Sort {
|
||||
case SortNewest, SortOldest, SortName, SortLargest:
|
||||
case SortNewest, SortOldest, SortName, SortLargest, SortTotalSize:
|
||||
default:
|
||||
return ErrInvalidQuery
|
||||
}
|
||||
switch q.Variants {
|
||||
case VariantsAny, VariantsWith, VariantsWithout:
|
||||
default:
|
||||
return ErrInvalidQuery
|
||||
}
|
||||
@@ -175,6 +190,19 @@ func SortImages(images []Image, order Sort) {
|
||||
})
|
||||
}
|
||||
|
||||
func SortImagesByTotalSize(images []Image, totals map[string]VariantFileTotals) {
|
||||
totalOf := func(img Image) int64 {
|
||||
return img.Size + totals[StoredName(img.Path)].Bytes
|
||||
}
|
||||
sort.SliceStable(images, func(i, j int) bool {
|
||||
a, b := images[i], images[j]
|
||||
if totalOf(a) != totalOf(b) {
|
||||
return totalOf(a) > totalOf(b)
|
||||
}
|
||||
return a.ID < b.ID
|
||||
})
|
||||
}
|
||||
|
||||
type Counts struct {
|
||||
All int
|
||||
DuplicateGroups int
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"math"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -17,6 +19,8 @@ var ErrVariantFormatUnavailable = errors.New("variant format cannot be encoded")
|
||||
var ErrUnknownVariant = errors.New("unknown variant")
|
||||
var ErrUnsupportedParameter = errors.New("unsupported parameter")
|
||||
var ErrSourceTooLarge = errors.New("source image is too large to generate this variant")
|
||||
var ErrVariantSourceUnsupported = errors.New("variants are not generated for this format")
|
||||
var ErrVariantSourceUnreadable = errors.New("the source image cannot be read")
|
||||
|
||||
type VariantFit string
|
||||
|
||||
@@ -220,3 +224,62 @@ type VariantFileTotals struct {
|
||||
Files int
|
||||
Bytes int64
|
||||
}
|
||||
|
||||
func OutputSize(sourceWidth int, sourceHeight int, settings VariantSettings) (int, int) {
|
||||
if sourceWidth < 1 || sourceHeight < 1 {
|
||||
return 0, 0
|
||||
}
|
||||
if settings.Width == nil || settings.Height == nil {
|
||||
return proportionalOutput(sourceWidth, sourceHeight, settings)
|
||||
}
|
||||
boxWidth, boxHeight := *settings.Width, *settings.Height
|
||||
switch settings.Fit {
|
||||
case FitContain:
|
||||
return boxWidth, boxHeight
|
||||
case FitInside:
|
||||
return scaledOutput(sourceWidth, sourceHeight, math.Min(1, math.Min(ratio(boxWidth, sourceWidth), ratio(boxHeight, sourceHeight))))
|
||||
default:
|
||||
shrink := math.Min(1, math.Min(ratio(sourceWidth, boxWidth), ratio(sourceHeight, boxHeight)))
|
||||
return scaledOutput(boxWidth, boxHeight, shrink)
|
||||
}
|
||||
}
|
||||
|
||||
func proportionalOutput(sourceWidth int, sourceHeight int, settings VariantSettings) (int, int) {
|
||||
scale := 1.0
|
||||
if settings.Width != nil {
|
||||
scale = math.Min(scale, ratio(*settings.Width, sourceWidth))
|
||||
}
|
||||
if settings.Height != nil {
|
||||
scale = math.Min(scale, ratio(*settings.Height, sourceHeight))
|
||||
}
|
||||
return scaledOutput(sourceWidth, sourceHeight, scale)
|
||||
}
|
||||
|
||||
func scaledOutput(width int, height int, factor float64) (int, int) {
|
||||
return scaledLength(width, factor), scaledLength(height, factor)
|
||||
}
|
||||
|
||||
func scaledLength(length int, factor float64) int {
|
||||
return max(1, int(math.Round(float64(length)*factor)))
|
||||
}
|
||||
|
||||
func ratio(numerator int, denominator int) float64 {
|
||||
return float64(numerator) / float64(denominator)
|
||||
}
|
||||
|
||||
var sourceContentTypeFormats = map[string]VariantFormat{
|
||||
"image/jpeg": FormatJPEG,
|
||||
"image/png": FormatPNG,
|
||||
"image/webp": FormatWebP,
|
||||
"image/gif": FormatPNG,
|
||||
}
|
||||
|
||||
func OutputFormat(format VariantFormat, sourceContentType string, encodable []VariantFormat) VariantFormat {
|
||||
if format == FormatOriginal {
|
||||
format = sourceContentTypeFormats[sourceContentType]
|
||||
}
|
||||
if slices.Contains(encodable, format) {
|
||||
return format
|
||||
}
|
||||
return FormatPNG
|
||||
}
|
||||
@@ -17,6 +17,7 @@ type VariantFileRepository interface {
|
||||
ListBySource(sourceName string) ([]VariantFile, error)
|
||||
Delete(id uint32) error
|
||||
Totals() (map[string]VariantFileTotals, error)
|
||||
TotalsBySource() (map[string]VariantFileTotals, error)
|
||||
}
|
||||
|
||||
type RenderedVariant struct {
|
||||
|
||||
Reference in new issue
Block a user