omakase/gallery/gallery.go
2025-07-03 17:12:05 -03:00

258 lines
6 KiB
Go

package gallery
import (
"crypto/md5"
"encoding/json"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"time"
)
type Gallery struct {
images []string
uuid string
title string
jpTitle string
tags []Tag
source Source
CTime time.Time
artists []string
groups []string
parodies []string
}
func (g *Gallery) Name() string { return g.title }
func (g *Gallery) JpName() string { return g.jpTitle }
func (g *Gallery) Uuid() string { return g.uuid }
func (g *Gallery) Images() []string { return g.images }
func (g *Gallery) Tags() []Tag { return g.tags }
func (g *Gallery) Source() Source { return g.source }
func (g *Gallery) Artists() []string { return g.artists }
func (g *Gallery) Groups() []string { return g.groups }
func (g *Gallery) Parodies() []string { return g.parodies }
func newGallery(
images []string, uuid string, title string, jpTitle string, tags []Tag,
source Source, CTime time.Time, artists []string, groups []string, parodies []string,
) Gallery {
return Gallery{
images: images,
uuid: uuid,
title: title,
jpTitle: jpTitle,
tags: tags,
source: source,
CTime: CTime,
artists: artists,
groups: groups,
parodies: parodies,
}
}
type Source string
const (
Nhentai Source = "nhentai"
Exhentai = "exhentai"
Hitomi = "hitomi"
)
type Gender string
const (
Male Gender = "male"
Female Gender = "female"
Any Gender = "any"
)
type Tag struct {
Name string
Sex Gender
}
func newTag(name string, sex Gender) Tag {
return Tag{
Name: strings.ToLower(name),
Sex: sex,
}
}
func genUuid(title string) string {
return fmt.Sprintf("%x", md5.Sum([]byte(title)))
}
func filterImages(root string, galleryDir []fs.DirEntry) []string {
var paths []string
for _, file := range galleryDir {
if file.IsDir() {
continue
}
name := file.Name()
// fixme
isJpeg := strings.HasSuffix(name, ".jpeg") || strings.HasSuffix(name, ".jpg")
isPng := strings.HasSuffix(name, ".png")
isWebp := strings.HasSuffix(name, ".webp")
isJson := strings.HasSuffix(name, ".json")
if isJson {
continue
}
if isJpeg || isPng || isWebp {
paths = append(paths, filepath.Join(root, name))
}
}
return paths
}
func NewNhentaiGallery(infoBinary []byte, root string, galleryDir []fs.DirEntry) (Gallery, error) {
var data struct {
Title string
Title_en string `json:"title_en"`
Title_ja string
// Gallery_id string `json:"gallery_id"` // troublesome
Media_id int
Date int64
Scanlator string
Artist, Group, Parody, Characters, Tags []string
Type, Lang, Language, Category, Subcategory string
Count int
}
err := json.Unmarshal(infoBinary, &data)
if err != nil {
return Gallery{}, err
}
var tags []Tag
for _, t := range data.Tags {
tags = append(tags, newTag(t, Any))
}
images := filterImages(root, galleryDir)
d, err := os.Stat(root)
if err != nil {
return Gallery{}, err
}
g := newGallery(
images, genUuid(data.Title), data.Title, data.Title_ja, tags,
Nhentai, d.ModTime(), data.Artist, data.Group, data.Parody,
)
return g, nil
}
func NewExhentaiGallery(infoBinary []byte, root string, galleryDir []fs.DirEntry) (Gallery, error) {
var data struct {
Gid int
Token string
Thumb string
Title string
Title_jpn string
Eh_category string
Uploader string
Date string
Parent string
Language string
Filecount string
Favorites string
Rating string
Torrentcount string
Lang string
Category string
Subcategory string
Expunged bool
Filesize int
Tags []string
}
err := json.Unmarshal(infoBinary, &data)
if err != nil {
return Gallery{}, err
}
var (
tags []Tag
artists []string
groups []string
parodies []string
)
for _, t := range data.Tags {
switch {
case strings.HasPrefix(t, "female:"):
tags = append(tags, newTag(t[7:], Female))
case strings.HasPrefix(t, "male:"):
tags = append(tags, newTag(t[5:], Male))
case strings.HasPrefix(t, "artist:"):
artists = append(artists, t[7:])
case strings.HasPrefix(t, "group:"):
groups = append(groups, t[6:])
case strings.HasPrefix(t, "parody:"):
parodies = append(parodies, t[7:])
}
}
d, err := os.Stat(root)
if err != nil {
return Gallery{}, err
}
g := newGallery(
filterImages(root, galleryDir), genUuid(data.Title), data.Title, data.Title_jpn,
tags, Exhentai, d.ModTime(), artists, groups, parodies,
)
return g, nil
}
func NewHitomiGallery(infoBinary []byte, root string, galleryDir []fs.DirEntry) (Gallery, error) {
var data struct {
Gallery_id int
Title string
Title_jpn string
Type string
Language string
Lang string
Date string
Tags []string
Artist []string
Group []string
Parody []string
Characters []string
Count int
Category string
Subcategory string
}
err := json.Unmarshal(infoBinary, &data)
if err != nil {
return Gallery{}, err
}
var tags []Tag
for _, t := range data.Tags {
sex := Any
name := t
if strings.HasSuffix(t, "♀") {
sex = Female
name = t[:len(t)-3]
} else if strings.HasSuffix(t, "♂") {
sex = Male
name = t[:len(t)-3]
}
tags = append(tags, newTag(name, sex))
}
d, err := os.Stat(root)
if err != nil {
return Gallery{}, err
}
g := newGallery(
filterImages(root, galleryDir), genUuid(data.Title), data.Title, data.Title_jpn,
tags, Hitomi, d.ModTime(), data.Artist, data.Group, data.Parody,
)
return g, nil
}
func NewGallery(source Source, infoBinary []byte, root string, galleryDir []fs.DirEntry) (Gallery, error) {
switch source {
case Nhentai:
return NewNhentaiGallery(infoBinary, root, galleryDir)
case Exhentai:
return NewExhentaiGallery(infoBinary, root, galleryDir)
case Hitomi:
return NewHitomiGallery(infoBinary, root, galleryDir)
}
panic(source)
}