feat: initial Phase 1 implementation — core framework + Docker
- Data types (SManga, SChapter, Page, MangasPage, all Filter variants) - Source interfaces (Source, CatalogueSource) with MD5-based ID generation matching Tachiyomi/Suwayomi - HTTP client with per-host rate limiting, cookie jar, and 429 retry - FlareSolverr v1 client (FLARESOLVERR_URL env) - Generic GraphQL POST helper - goquery HTML parser wrappers - Source registry (panics on duplicate ID) - Multi-stage Dockerfile (golang:1.26-alpine + distroless) and compose.yml (postgres, flaresolverr, app)
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
func Parse(html string) (*goquery.Document, error) {
|
||||
return goquery.NewDocumentFromReader(strings.NewReader(html))
|
||||
}
|
||||
|
||||
func ParseResponse(resp *http.Response) (*goquery.Document, error) {
|
||||
defer resp.Body.Close()
|
||||
return goquery.NewDocumentFromReader(resp.Body)
|
||||
}
|
||||
|
||||
func Select(doc *goquery.Document, css string) *goquery.Selection {
|
||||
return doc.Find(css)
|
||||
}
|
||||
|
||||
func SelectFrom(sel *goquery.Selection, css string) *goquery.Selection {
|
||||
return sel.Find(css)
|
||||
}
|
||||
|
||||
func Attr(sel *goquery.Selection, name string) string {
|
||||
val, _ := sel.Attr(name)
|
||||
return val
|
||||
}
|
||||
|
||||
// AbsURL resolves a relative URL attribute against baseURL.
|
||||
func AbsURL(sel *goquery.Selection, attr string, baseURL string) string {
|
||||
val := Attr(sel, attr)
|
||||
if val == "" {
|
||||
return ""
|
||||
}
|
||||
base, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return val
|
||||
}
|
||||
ref, err := url.Parse(val)
|
||||
if err != nil {
|
||||
return val
|
||||
}
|
||||
return base.ResolveReference(ref).String()
|
||||
}
|
||||
|
||||
// OwnText returns the text content of the element excluding child elements.
|
||||
func OwnText(sel *goquery.Selection) string {
|
||||
clone := sel.Clone()
|
||||
clone.Children().Remove()
|
||||
return strings.TrimSpace(clone.Text())
|
||||
}
|
||||
|
||||
func TextTrim(sel *goquery.Selection) string {
|
||||
return strings.TrimSpace(sel.Text())
|
||||
}
|
||||
|
||||
func First(sel *goquery.Selection) *goquery.Selection {
|
||||
return sel.First()
|
||||
}
|
||||
|
||||
func Each(sel *goquery.Selection, fn func(i int, s *goquery.Selection)) {
|
||||
sel.Each(fn)
|
||||
}
|
||||
Reference in New Issue
Block a user