ca609ccae7
Ports bases from previous session: util (shared helpers), bakkin, fmreader, foolslide, gigaviewer, gmanga, grouple, guya, heancms, hentaihand, kemono, madara, madtheme, mangadventure, mangahub, mangathemesia, mangaworld, mmrcms, senkuro, wpcomics.
123 lines
3.2 KiB
Go
123 lines
3.2 KiB
Go
// Package bakkin implements the Bakkin base.
|
|
// No list/search — all manga from a single JSON URL, client-side title filter.
|
|
package bakkin
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"goyomi/internal/httpclient"
|
|
"goyomi/internal/source"
|
|
"goyomi/sources/base/util"
|
|
)
|
|
|
|
type Config struct {
|
|
Name string
|
|
BaseURL string
|
|
Lang string
|
|
JSONURL string // URL of the source JSON
|
|
}
|
|
|
|
type Source struct {
|
|
cfg Config
|
|
client *httpclient.Client
|
|
id int64
|
|
}
|
|
|
|
func New(cfg Config) *Source {
|
|
if cfg.JSONURL == "" {
|
|
cfg.JSONURL = strings.TrimRight(cfg.BaseURL, "/") + "/manga.json"
|
|
}
|
|
c := httpclient.NewClient(httpclient.WithRateLimit(1, 2))
|
|
return &Source{cfg: cfg, client: c, id: source.GenerateSourceID(cfg.Name, cfg.Lang)}
|
|
}
|
|
|
|
func (s *Source) ID() int64 { return s.id }
|
|
func (s *Source) Name() string { return s.cfg.Name }
|
|
func (s *Source) Lang() string { return s.cfg.Lang }
|
|
func (s *Source) SupportsLatest() bool { return false }
|
|
|
|
func (s *Source) fetchAll(ctx context.Context) ([]source.SManga, error) {
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, s.cfg.JSONURL, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp, err := s.client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("bakkin: HTTP %d", resp.StatusCode)
|
|
}
|
|
body, _ := io.ReadAll(resp.Body)
|
|
var raw map[string]json.RawMessage
|
|
if err := json.Unmarshal(body, &raw); err != nil {
|
|
return nil, err
|
|
}
|
|
var mangas []source.SManga
|
|
for key := range raw {
|
|
m := source.SManga{
|
|
URL: fmt.Sprintf("/%s", key),
|
|
Title: key,
|
|
}
|
|
var details map[string]any
|
|
if err := json.Unmarshal(raw[key], &details); err == nil {
|
|
if title, ok := details["title"].(string); ok {
|
|
m.Title = title
|
|
}
|
|
if thumb, ok := details["thumbnail"].(string); ok {
|
|
m.ThumbnailURL = util.AbsURL(s.cfg.BaseURL, thumb)
|
|
}
|
|
}
|
|
mangas = append(mangas, m)
|
|
}
|
|
return mangas, nil
|
|
}
|
|
|
|
func (s *Source) GetPopularManga(page int) (source.MangasPage, error) {
|
|
if page > 1 {
|
|
return source.MangasPage{}, nil
|
|
}
|
|
mangas, err := s.fetchAll(context.Background())
|
|
return source.MangasPage{Mangas: mangas, HasNextPage: false}, err
|
|
}
|
|
|
|
func (s *Source) GetLatestUpdates(page int) (source.MangasPage, error) {
|
|
return s.GetPopularManga(page)
|
|
}
|
|
|
|
func (s *Source) GetSearchManga(page int, query string, filters []source.Filter) (source.MangasPage, error) {
|
|
mangas, err := s.fetchAll(context.Background())
|
|
if err != nil {
|
|
return source.MangasPage{}, err
|
|
}
|
|
q := strings.ToLower(query)
|
|
var matched []source.SManga
|
|
for _, m := range mangas {
|
|
if strings.Contains(strings.ToLower(m.Title), q) {
|
|
matched = append(matched, m)
|
|
}
|
|
}
|
|
return source.MangasPage{Mangas: matched, HasNextPage: false}, nil
|
|
}
|
|
|
|
func (s *Source) GetMangaDetails(manga source.SManga) (source.SManga, error) {
|
|
return manga, nil
|
|
}
|
|
|
|
func (s *Source) GetChapterList(manga source.SManga) ([]source.SChapter, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (s *Source) GetPageList(chapter source.SChapter) ([]source.Page, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (s *Source) GetImageURL(page source.Page) (string, error) { return page.ImageURL, nil }
|
|
func (s *Source) GetFilterList() []source.Filter { return nil }
|