This commit is contained in:
truewhile
2026-09-08 23:44:20 +08:00
parent 4239474100
commit fb4e762026
11 changed files with 385 additions and 109 deletions
+40 -26
View File
@@ -7,8 +7,16 @@ import (
"image/jpeg"
_ "image/png" // register PNG decoder
"strings"
"github.com/metatube-community/metatube-sdk-go/detector"
)
const adultPosterRatio = 2.0 / 3.0
var findPrimaryFaceAxisRatio = func(img image.Image, ratio float64, advanced bool) (float64, bool) {
return detector.FindPrimaryFaceAxisRatio(img, ratio, advanced)
}
// IsAdultMediaPathOrMetadata reports whether media is adult based on path, mediaType, nsfw flag, or adult code.
func IsAdultMediaPathOrMetadata(path, mediaType string, nsfw bool) bool {
if nsfw {
@@ -38,7 +46,8 @@ func IsAdultArtworkURL(raw string) bool {
}
// CropAdultCoverPoster checks if the image data is a wide full-jacket DVD cover (width > height * 1.15).
// If so, it crops the right portion (the front cover poster) and returns the encoded JPEG bytes.
// If so, it uses MetaTube's Pigo face detector to position a 2:3 poster crop.
// When no face is detected, it falls back to the conventional right-side front cover.
// If the image is already portrait or cannot be decoded, it safely returns the original data.
func CropAdultCoverPoster(data []byte) ([]byte, string, error) {
if len(data) == 0 {
@@ -59,32 +68,11 @@ func CropAdultCoverPoster(data []byte) ([]byte, string, error) {
return data, format, nil
}
// Front cover width is typically ~0.70-0.72 of the height (or right ~50-52% of total width)
cropWidth := int(float64(height) * 0.71)
maxCropWidth := int(float64(width) * 0.53)
if cropWidth > maxCropWidth {
cropWidth = maxCropWidth
}
if cropWidth <= 0 {
cropWidth = width / 2
}
minX := bounds.Max.X - cropWidth
if minX < bounds.Min.X {
minX = bounds.Min.X
}
cropRect := image.Rect(minX, bounds.Min.Y, bounds.Max.X, bounds.Max.Y)
type subImager interface {
SubImage(r image.Rectangle) image.Image
}
var cropped image.Image
if si, ok := src.(subImager); ok {
cropped = si.SubImage(cropRect)
} else {
dst := image.NewRGBA(image.Rect(0, 0, cropRect.Dx(), cropRect.Dy()))
draw.Draw(dst, dst.Bounds(), src, cropRect.Min, draw.Src)
cropped = dst
position := 1.0
if detected, ok := findPrimaryFaceAxisRatio(src, adultPosterRatio, true); ok {
position = detected
}
cropped := cropImageAtPosition(src, adultPosterRatio, position)
var buf bytes.Buffer
if err := jpeg.Encode(&buf, cropped, &jpeg.Options{Quality: 92}); err != nil {
@@ -92,3 +80,29 @@ func CropAdultCoverPoster(data []byte) ([]byte, string, error) {
}
return buf.Bytes(), "image/jpeg", nil
}
func cropImageAtPosition(src image.Image, ratio, position float64) image.Image {
bounds := src.Bounds()
width := bounds.Dx()
height := bounds.Dy()
cropWidth, cropHeight := width, height
x, y := 0, 0
if candidate := int(float64(height) * ratio); candidate < width {
cropWidth = candidate
x = max(min(int(float64(width)*position)-cropWidth/2, width-cropWidth), 0)
} else if candidate := int(float64(width) / ratio); candidate < height {
cropHeight = candidate
y = max(min(int(float64(height)*position)-cropHeight/2, height-cropHeight), 0)
}
cropRect := image.Rect(0, 0, cropWidth, cropHeight).
Add(image.Pt(x, y)).
Add(bounds.Min)
if sub, ok := src.(interface {
SubImage(image.Rectangle) image.Image
}); ok {
return sub.SubImage(cropRect)
}
dst := image.NewRGBA(image.Rect(0, 0, cropRect.Dx(), cropRect.Dy()))
draw.Draw(dst, dst.Bounds(), src, cropRect.Min, draw.Src)
return dst
}