feat(latest): extract per-site covers (#58)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package latest
|
||||
|
||||
import (
|
||||
"html"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -146,3 +147,79 @@ func latestChapterFrom(site, seriesURL, body string) (latestChapter, bool) {
|
||||
}
|
||||
return best, found
|
||||
}
|
||||
|
||||
var metaTagRe = regexp.MustCompile(`(?is)<meta\b[^>]*>`)
|
||||
var doubleQuotedMetaAttrRe = regexp.MustCompile(`(?is)([a-z][a-z0-9:_-]*)\s*=\s*"([^"]*)"`)
|
||||
var singleQuotedMetaAttrRe = regexp.MustCompile(`(?is)([a-z][a-z0-9:_-]*)\s*=\s*'([^']*)'`)
|
||||
|
||||
// comix stores its cover in the target series object in the page state rather
|
||||
// than an HTML metadata tag. The medium rendition is deliberately selected
|
||||
// instead of deriving a URL for a thumbnail or retaining the larger rendition.
|
||||
var comixHIDRe = regexp.MustCompile(`"hid"\s*:\s*"[^"]+"`)
|
||||
var comixPosterMediumRe = regexp.MustCompile(`(?s)"poster"\s*:\s*\{.*?"medium"\s*:\s*"([^"]+)"`)
|
||||
|
||||
// kagane's browser-fetched series response carries this published cover URL.
|
||||
// Match the cover field, not merely an image-shaped URL elsewhere in JSON.
|
||||
var kaganeCoverFieldRe = regexp.MustCompile(`"cover"\s*:\s*"([^"]+)"`)
|
||||
|
||||
func comixCoverURL(seriesURL, body string) string {
|
||||
m := comixSlugRe.FindStringSubmatch(seriesURL)
|
||||
if m == nil {
|
||||
return ""
|
||||
}
|
||||
id := m[1]
|
||||
if i := strings.Index(id, "-"); i != -1 {
|
||||
id = id[:i]
|
||||
}
|
||||
hidRe := regexp.MustCompile(`"hid"\s*:\s*"` + regexp.QuoteMeta(id) + `"`)
|
||||
hid := hidRe.FindStringIndex(body)
|
||||
if hid == nil {
|
||||
return ""
|
||||
}
|
||||
end := len(body)
|
||||
if next := comixHIDRe.FindStringIndex(body[hid[1]:]); next != nil {
|
||||
end = hid[1] + next[0]
|
||||
}
|
||||
if m := comixPosterMediumRe.FindStringSubmatch(body[hid[0]:end]); m != nil {
|
||||
return publishedCoverURL(m[1])
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func coverFrom(site, seriesURL, body string) (string, bool) {
|
||||
var cover string
|
||||
switch site {
|
||||
case "asura", "demonic", "lightnovelworld":
|
||||
cover = metaContent(body, "property", "og:image")
|
||||
case "novelfull":
|
||||
cover = metaContent(body, "name", "image")
|
||||
case "comix":
|
||||
cover = comixCoverURL(seriesURL, body)
|
||||
case "kagane":
|
||||
if m := kaganeCoverFieldRe.FindStringSubmatch(body); m != nil {
|
||||
cover = publishedCoverURL(m[1])
|
||||
}
|
||||
}
|
||||
return cover, cover != ""
|
||||
}
|
||||
|
||||
func metaContent(body, attrName, attrValue string) string {
|
||||
for _, tag := range metaTagRe.FindAllString(body, -1) {
|
||||
attrs := make(map[string]string)
|
||||
for _, m := range doubleQuotedMetaAttrRe.FindAllStringSubmatch(tag, -1) {
|
||||
attrs[strings.ToLower(m[1])] = m[2]
|
||||
}
|
||||
for _, m := range singleQuotedMetaAttrRe.FindAllStringSubmatch(tag, -1) {
|
||||
attrs[strings.ToLower(m[1])] = m[2]
|
||||
}
|
||||
if strings.EqualFold(attrs[strings.ToLower(attrName)], attrValue) {
|
||||
return publishedCoverURL(attrs["content"])
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func publishedCoverURL(value string) string {
|
||||
value = strings.TrimSpace(html.UnescapeString(value))
|
||||
return strings.ReplaceAll(value, " ", "%20")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user