repo_description: An organizer for your porn, written in Go.  Documentation:  https://docs.stashapp.cc
repo_topics: go golang graphql hacktoberfest json media organize organizer porn video
code: package api
  
  import (
  	"errors"
  	"fmt"
  	"io"
  	"io/fs"
  	"os"
  	"strings"
  
  	"github.com/stashapp/stash/internal/static"
  	"github.com/stashapp/stash/pkg/hash"
  	"github.com/stashapp/stash/pkg/logger"
  	"github.com/stashapp/stash/pkg/models"
  )
  
  type imageBox struct {
  	box   fs.FS
  	files []string
  }
  
  var imageBoxExts = []string{
  	".jpg",
  	".jpeg",
  	".png",
  	".gif",
  	".svg",
  	".webp",
  	".avif",
  }
  
  func newImageBox(box fs.FS) (*imageBox, error) {
  	ret := &imageBox{
  		box: box,
  	}
  
  	err := fs.WalkDir(box, ".", func(path string, d fs.DirEntry, err error) error {
  		if err != nil {
  			return err
  		}
  
  		if d.IsDir() {
  			return nil
  		}
  
  		baseName := strings.ToLower(d.Name())
  		for _, ext := range imageBoxExts {
  			if strings.HasSuffix(baseName, ext) {
  				ret.files = append(ret.files, path)
  				break
  			}
  		}
  
  		return nil
  	})
  
  	return ret, err
  }
  
  func (box *imageBox) GetRandomImageByName(name string) ([]byte, error) {
  	files := box.files
  	if len(files) == 0 {
  		return nil, errors.New("box is empty")
  	}
  
  	index := hash.IntFromString(name) % uint64(len(files))
  	img, err := box.box.Open(files[index])
  	if err != nil {
  		return nil, err
  	}
  	defer img.Close()
  
  	return io.ReadAll(img)
  }
  
  var performerBox *imageBox
  var performerBoxMale *imageBox
  var performerBoxCustom *imageBox
  
  func init() {
  	var err error
  	performerBox, err = newImageBox(static.Sub(static.Performer))
  	if err != nil {
  		panic(fmt.Sprintf("loading performer images: %v", err))
  	}
  	performerBoxMale, err = newImageBox(static.Sub(static.PerformerMale))
  	if err != nil {
  		panic(fmt.Sprintf("loading male performer images: %v", err))
  	}
  }
  
  func initCustomPerformerImages(customPath string) {
  	if customPath != "" {
  		logger.Debugf("Loading custom performer images from %s", customPath)
  		var err error
  		performerBoxCustom, err = newImageBox(os.DirFS(customPath))
  		if err != nil {
  			logger.Warnf("error loading custom performer images from %s: %v", customPath, err)
  		}
  	} else {
  		performerBoxCustom = nil
  	}
  }
  
  func getDefaultPerformerImage(name string, gender *models.GenderEnum, sfwMode bool) []byte {
  	// try the custom box first if we have one
  	if performerBoxCustom != nil {
  		ret, err := performerBoxCustom.GetRandomImageByName(name)
  		if err == nil {
  			return ret
  		}
  		logger.Warnf("error loading custom default performer image: %v", err)
  	}
  
  	if sfwMode {
  		return static.ReadAll(static.DefaultSFWPerformerImage)
  	}
  
  	var g models.GenderEnum
  	if gender != nil {
  		g = *gender
  	}
  
  	var box *imageBox
  	switch g {
  	case models.GenderEnumFemale, models.GenderEnumTransgenderFemale:
  		box = performerBox
  	case models.GenderEnumMale, models.GenderEnumTransgenderMale:
  		box = performerBoxMale
  	default:
  		box = performerBox
  	}
  
  	ret, err := box.GetRandomImageByName(name)
  	if err != nil {
  		logger.Warnf("error loading default performer image: %v", err)
  	}
  	return ret
  }
  