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.
169 lines
5.3 KiB
Go
169 lines
5.3 KiB
Go
// Package gigaviewer implements the GigaViewer (Hatena) base.
|
|
// GET {base}/series returns all manga; no pagination.
|
|
package gigaviewer
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/PuerkitoBio/goquery"
|
|
|
|
"goyomi/internal/httpclient"
|
|
"goyomi/internal/source"
|
|
"goyomi/sources/base/util"
|
|
)
|
|
|
|
type Config struct {
|
|
Name string
|
|
BaseURL string
|
|
Lang string
|
|
}
|
|
|
|
type Source struct {
|
|
cfg Config
|
|
client *httpclient.Client
|
|
id int64
|
|
}
|
|
|
|
func New(cfg Config) *Source {
|
|
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 true }
|
|
|
|
func (s *Source) get(ctx context.Context, rawURL string) (*goquery.Document, error) {
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Origin", s.cfg.BaseURL)
|
|
req.Header.Set("Referer", s.cfg.BaseURL+"/")
|
|
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("gigaviewer: HTTP %d", resp.StatusCode)
|
|
}
|
|
return goquery.NewDocumentFromReader(resp.Body)
|
|
}
|
|
|
|
func (s *Source) fetchAllManga() ([]source.SManga, error) {
|
|
doc, err := s.get(context.Background(), strings.TrimRight(s.cfg.BaseURL, "/")+"/series")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var mangas []source.SManga
|
|
doc.Find("ul.series-list li a").Each(func(_ int, el *goquery.Selection) {
|
|
m := source.SManga{}
|
|
if href, ok := el.Attr("href"); ok {
|
|
m.URL = href
|
|
}
|
|
el.Find("h2.series-list-title").First().Each(func(_ int, e *goquery.Selection) {
|
|
m.Title = strings.TrimSpace(e.Text())
|
|
})
|
|
el.Find("div.series-list-thumb img").First().Each(func(_ int, img *goquery.Selection) {
|
|
if v, ok := img.Attr("data-src"); ok && v != "" {
|
|
m.ThumbnailURL = util.AbsURL(s.cfg.BaseURL, v)
|
|
} else if v, ok := img.Attr("src"); ok && v != "" {
|
|
m.ThumbnailURL = util.AbsURL(s.cfg.BaseURL, v)
|
|
}
|
|
})
|
|
if m.URL != "" {
|
|
mangas = append(mangas, m)
|
|
}
|
|
})
|
|
return mangas, nil
|
|
}
|
|
|
|
func (s *Source) GetPopularManga(page int) (source.MangasPage, error) {
|
|
if page > 1 {
|
|
return source.MangasPage{HasNextPage: false}, nil
|
|
}
|
|
mangas, err := s.fetchAllManga()
|
|
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.fetchAllManga()
|
|
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) {
|
|
doc, err := s.get(context.Background(), util.AbsURL(s.cfg.BaseURL, manga.URL))
|
|
if err != nil {
|
|
return manga, err
|
|
}
|
|
result := source.SManga{URL: manga.URL}
|
|
doc.Find("section.series-information div.series-header").First().Each(func(_ int, el *goquery.Selection) {
|
|
el.Find("h1.series-header-title").First().Each(func(_ int, e *goquery.Selection) { result.Title = strings.TrimSpace(e.Text()) })
|
|
el.Find("h2.series-header-author").First().Each(func(_ int, e *goquery.Selection) { result.Author = strings.TrimSpace(e.Text()) })
|
|
el.Find("p.series-header-description").First().Each(func(_ int, e *goquery.Selection) { result.Description = strings.TrimSpace(e.Text()) })
|
|
el.Find("div.series-header-image img").First().Each(func(_ int, img *goquery.Selection) {
|
|
result.ThumbnailURL = util.AbsURL(s.cfg.BaseURL, img.AttrOr("src", ""))
|
|
})
|
|
})
|
|
return result, nil
|
|
}
|
|
|
|
func (s *Source) GetChapterList(manga source.SManga) ([]source.SChapter, error) {
|
|
doc, err := s.get(context.Background(), util.AbsURL(s.cfg.BaseURL, manga.URL))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var chapters []source.SChapter
|
|
doc.Find("ul.series-episode-list li a, li.episode a").Each(func(_ int, a *goquery.Selection) {
|
|
ch := source.SChapter{}
|
|
if href, ok := a.Attr("href"); ok {
|
|
ch.URL = href
|
|
}
|
|
a.Find(".series-episode-list-title, .episode-title").First().Each(func(_ int, e *goquery.Selection) {
|
|
ch.Name = strings.TrimSpace(e.Text())
|
|
})
|
|
if ch.URL != "" {
|
|
chapters = append(chapters, ch)
|
|
}
|
|
})
|
|
return chapters, nil
|
|
}
|
|
|
|
func (s *Source) GetPageList(chapter source.SChapter) ([]source.Page, error) {
|
|
doc, err := s.get(context.Background(), util.AbsURL(s.cfg.BaseURL, chapter.URL))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rawURL := util.AbsURL(s.cfg.BaseURL, chapter.URL)
|
|
var pages []source.Page
|
|
doc.Find("div.js-page-viewer img, .page-image img").Each(func(i int, img *goquery.Selection) {
|
|
imgURL := util.AbsURL(s.cfg.BaseURL, img.AttrOr("src", img.AttrOr("data-src", "")))
|
|
if imgURL != "" {
|
|
pages = append(pages, source.Page{Index: i, URL: rawURL, ImageURL: imgURL})
|
|
}
|
|
})
|
|
return pages, nil
|
|
}
|
|
|
|
func (s *Source) GetImageURL(page source.Page) (string, error) { return page.ImageURL, nil }
|
|
func (s *Source) GetFilterList() []source.Filter { return nil }
|