157 lines
4.3 KiB
Go
157 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"image"
|
|
"image/png"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gen2brain/avif"
|
|
"golang.org/x/image/draw"
|
|
"golang.org/x/sync/errgroup"
|
|
"zombiezen.com/go/sqlite"
|
|
"zombiezen.com/go/sqlite/sqlitex"
|
|
)
|
|
|
|
func main() {
|
|
var (
|
|
mdb string
|
|
out string
|
|
)
|
|
flag.StringVar(&mdb, "mdb", "", "`path` to master.mdb")
|
|
flag.StringVar(&out, "o", "", "output `dir`ectory")
|
|
flag.Parse()
|
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
|
go func() {
|
|
<-ctx.Done()
|
|
stop()
|
|
}()
|
|
|
|
db, err := sqlitex.NewPool(mdb, sqlitex.PoolOptions{Flags: sqlite.OpenReadOnly})
|
|
if err != nil {
|
|
slog.Error("mdb", slog.Any("err", err))
|
|
os.Exit(1)
|
|
}
|
|
if err := os.MkdirAll(out, 0777); err != nil {
|
|
slog.Error("output", slog.Any("err", err))
|
|
os.Exit(1)
|
|
}
|
|
|
|
skill, err := skillIconIDs(ctx, db)
|
|
if err != nil {
|
|
slog.Error("skills", slog.Any("err", err))
|
|
}
|
|
chara, err := charaIDs(ctx, db)
|
|
if err != nil {
|
|
slog.Error("characters", slog.Any("err", err))
|
|
}
|
|
rank, err := rankIDs(ctx, db)
|
|
if err != nil {
|
|
slog.Error("ranks", slog.Any("err", err))
|
|
}
|
|
|
|
g, ctx := errgroup.WithContext(ctx)
|
|
tick := time.NewTicker(250 * time.Millisecond)
|
|
idouts(ctx, g, tick, skill, filepath.Join(out, "skill"), "https://media.gametora.com/umamusume/skills/icon/%d.png", image.Pt(64, 64), image.Pt(32, 32), image.Pt(16, 16))
|
|
idouts(ctx, g, tick, chara, filepath.Join(out, "chara"), "https://gametora.com/images/umamusume/characters/icons/chr_icon_%d.png", image.Pt(256, 256), image.Pt(64, 64), image.Pt(32, 32), image.Pt(16, 16))
|
|
idouts(ctx, g, tick, rank, filepath.Join(out, "rank"), "https://media.gametora.com/umamusume/ui/rank/%02d.png", image.Pt(128, 128), image.Pt(64, 64), image.Pt(32, 32), image.Pt(16, 16))
|
|
idouts(ctx, g, tick, rank, filepath.Join(out, "rank", "simple"), "https://media.gametora.com/umamusume/ui/rank/simple/%02d.png", image.Pt(50, 50), image.Pt(32, 32), image.Pt(16, 16))
|
|
if err := g.Wait(); err != nil {
|
|
slog.Error("output", slog.Any("err", err))
|
|
}
|
|
}
|
|
|
|
var pngenc = &png.Encoder{CompressionLevel: png.BestCompression}
|
|
|
|
func idouts(ctx context.Context, g *errgroup.Group, tick *time.Ticker, ids []int32, p, urlf string, sizes ...image.Point) {
|
|
for _, id := range ids {
|
|
g.Go(func() error {
|
|
n := strconv.Itoa(int(id))
|
|
p := filepath.Join(p, n)
|
|
_, err := os.Stat(p)
|
|
if !errors.Is(err, os.ErrNotExist) {
|
|
slog.DebugContext(ctx, "exists", slog.String("path", p))
|
|
return nil
|
|
}
|
|
if err := os.MkdirAll(p, 0777); err != nil {
|
|
return err
|
|
}
|
|
url := fmt.Sprintf(urlf, id)
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-tick.C: // continue on
|
|
}
|
|
img, err := dlimg(ctx, url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
slog.InfoContext(ctx, "resize", slog.String("url", url))
|
|
rs := resize(img, sizes...)
|
|
for _, r := range rs {
|
|
g.Go(func() error {
|
|
m := r.Bounds().Max
|
|
o := filepath.Join(p, fmt.Sprintf("%dx%d.png", m.X, m.Y))
|
|
slog.InfoContext(ctx, "write", slog.String("path", o))
|
|
f, err := os.Create(o)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
return pngenc.Encode(f, r)
|
|
})
|
|
g.Go(func() error {
|
|
m := r.Bounds().Max
|
|
o := filepath.Join(p, fmt.Sprintf("%dx%d.avif", m.X, m.Y))
|
|
slog.InfoContext(ctx, "write", slog.String("path", o))
|
|
f, err := os.Create(o)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
return avif.Encode(f, r)
|
|
})
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
}
|
|
|
|
func dlimg(ctx context.Context, url string) (image.Image, error) {
|
|
slog.InfoContext(ctx, "download", slog.String("url", url))
|
|
ctx, stop := context.WithTimeout(ctx, 5*time.Second)
|
|
defer stop()
|
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
slog.InfoContext(ctx, "decode", slog.String("url", url))
|
|
img, _, err := image.Decode(resp.Body)
|
|
return img, err
|
|
}
|
|
|
|
func resize(img image.Image, sizes ...image.Point) []image.Image {
|
|
rs := make([]image.Image, 0, len(sizes))
|
|
for _, sz := range sizes {
|
|
d := image.NewNRGBA(image.Rectangle{Min: image.Point{}, Max: sz})
|
|
draw.CatmullRom.Scale(d, d.Bounds(), img, img.Bounds(), draw.Src, nil)
|
|
rs = append(rs, d)
|
|
}
|
|
return rs
|
|
}
|