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.
156 lines
4.9 KiB
Go
156 lines
4.9 KiB
Go
// Package grouple implements the Grouple reader base.
|
|
// GET {base}/list?sortType=rate&offset={50*(n-1)}
|
|
package grouple
|
|
|
|
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
|
|
}
|
|
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("grouple: HTTP %d", resp.StatusCode)
|
|
}
|
|
return goquery.NewDocumentFromReader(resp.Body)
|
|
}
|
|
|
|
func (s *Source) base() string { return strings.TrimRight(s.cfg.BaseURL, "/") }
|
|
|
|
func (s *Source) parseMangaList(doc *goquery.Document) source.MangasPage {
|
|
var mangas []source.SManga
|
|
doc.Find("div.tile.hovertr").Each(func(_ int, el *goquery.Selection) {
|
|
m := source.SManga{}
|
|
el.Find("a.non-existent, h3 a").First().Each(func(_ int, a *goquery.Selection) {
|
|
if href, ok := a.Attr("href"); ok {
|
|
m.URL = href
|
|
}
|
|
m.Title = strings.TrimSpace(a.Text())
|
|
})
|
|
el.Find("img.lazy").First().Each(func(_ int, img *goquery.Selection) {
|
|
m.ThumbnailURL = util.AbsURL(s.cfg.BaseURL, img.AttrOr("data-original", img.AttrOr("src", "")))
|
|
})
|
|
if m.URL != "" {
|
|
mangas = append(mangas, m)
|
|
}
|
|
})
|
|
hasNext := doc.Find(".pagination .next, li.next a").Length() > 0
|
|
return source.MangasPage{Mangas: mangas, HasNextPage: hasNext}
|
|
}
|
|
|
|
func (s *Source) GetPopularManga(page int) (source.MangasPage, error) {
|
|
offset := 50 * (page - 1)
|
|
doc, err := s.get(context.Background(), fmt.Sprintf("%s/list?sortType=rate&offset=%d", s.base(), offset))
|
|
if err != nil {
|
|
return source.MangasPage{}, err
|
|
}
|
|
return s.parseMangaList(doc), nil
|
|
}
|
|
|
|
func (s *Source) GetLatestUpdates(page int) (source.MangasPage, error) {
|
|
offset := 50 * (page - 1)
|
|
doc, err := s.get(context.Background(), fmt.Sprintf("%s/list?sortType=update&offset=%d", s.base(), offset))
|
|
if err != nil {
|
|
return source.MangasPage{}, err
|
|
}
|
|
return s.parseMangaList(doc), nil
|
|
}
|
|
|
|
func (s *Source) GetSearchManga(page int, query string, filters []source.Filter) (source.MangasPage, error) {
|
|
doc, err := s.get(context.Background(), fmt.Sprintf("%s/search?q=%s&page=%d", s.base(), query, page))
|
|
if err != nil {
|
|
return source.MangasPage{}, err
|
|
}
|
|
return s.parseMangaList(doc), 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("h1.names span.name").First().Each(func(_ int, el *goquery.Selection) { result.Title = strings.TrimSpace(el.Text()) })
|
|
doc.Find("div.elementList div.subject-meta").First().Each(func(_ int, el *goquery.Selection) { result.Description = strings.TrimSpace(el.Text()) })
|
|
doc.Find("img.hidden-xs").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("div.chapters-link a, ul.chapters li a").Each(func(_ int, a *goquery.Selection) {
|
|
ch := source.SChapter{}
|
|
if href, ok := a.Attr("href"); ok {
|
|
ch.URL = href
|
|
}
|
|
ch.Name = strings.TrimSpace(a.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("img.reader-images, div#sps img").Each(func(i int, img *goquery.Selection) {
|
|
imgURL := util.AbsURL(s.cfg.BaseURL, img.AttrOr("data-src", img.AttrOr("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 }
|