215 lines
6.0 KiB
Go
215 lines
6.0 KiB
Go
package aventertainments
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/antchfx/htmlquery"
|
|
"github.com/gocolly/colly/v2"
|
|
"golang.org/x/net/html"
|
|
|
|
"github.com/javtube/metatube-sdk-go/common/number"
|
|
"github.com/javtube/metatube-sdk-go/common/parser"
|
|
"github.com/javtube/metatube-sdk-go/model"
|
|
"github.com/javtube/metatube-sdk-go/provider"
|
|
"github.com/javtube/metatube-sdk-go/provider/internal/scraper"
|
|
)
|
|
|
|
var (
|
|
_ provider.MovieProvider = (*AVE)(nil)
|
|
_ provider.MovieSearcher = (*AVE)(nil)
|
|
)
|
|
|
|
const (
|
|
Name = "AVE"
|
|
Priority = 1000 - 2
|
|
)
|
|
|
|
const (
|
|
baseURL = "https://www.aventertainments.com/"
|
|
movieURL = "https://www.aventertainments.com/%s/2/29/product_lists"
|
|
searchURL = "https://www.aventertainments.com/search_Products.aspx?languageID=2&dept_id=29&keyword=%s&searchby=keyword"
|
|
)
|
|
|
|
type AVE struct {
|
|
*scraper.Scraper
|
|
}
|
|
|
|
func New() *AVE {
|
|
return &AVE{scraper.NewDefaultScraper(Name, baseURL, Priority)}
|
|
}
|
|
|
|
func (ave *AVE) GetMovieInfoByID(id string) (info *model.MovieInfo, err error) {
|
|
return ave.GetMovieInfoByURL(fmt.Sprintf(movieURL, url.QueryEscape(id)))
|
|
}
|
|
|
|
func (ave *AVE) ParseIDFromURL(rawURL string) (string, error) {
|
|
homepage, err := url.Parse(rawURL)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
// old url format.
|
|
if productID := homepage.Query().Get("product_id"); productID != "" {
|
|
return productID, nil
|
|
}
|
|
// new url format.
|
|
if ss := regexp.MustCompile(`^/(\d+)/\d+/\d+`).FindStringSubmatch(homepage.Path); len(ss) == 2 {
|
|
return ss[1], nil
|
|
}
|
|
return "", fmt.Errorf("parse id failed: %s", rawURL)
|
|
}
|
|
|
|
func (ave *AVE) GetMovieInfoByURL(rawURL string) (info *model.MovieInfo, err error) {
|
|
id, err := ave.ParseIDFromURL(rawURL)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
info = &model.MovieInfo{
|
|
ID: id,
|
|
Provider: ave.Name(),
|
|
Homepage: rawURL,
|
|
Actors: []string{},
|
|
PreviewImages: []string{},
|
|
Genres: []string{},
|
|
}
|
|
|
|
c := ave.ClonedCollector()
|
|
|
|
// Title
|
|
c.OnXML(`//*[@id="MyBody"]//div[@class="section-title"]/h3`, func(e *colly.XMLElement) {
|
|
info.Title = strings.TrimSpace(e.Text)
|
|
})
|
|
|
|
// Summary (Part 1)
|
|
c.OnXML(`//*[@id="MyBody"]//div[@class="product-description mt-20"]`, func(e *colly.XMLElement) {
|
|
info.Summary += strings.TrimSpace(parseSummary(e))
|
|
})
|
|
|
|
// Summary (Part 2)
|
|
c.OnXML(`//div[@id="category"]`, func(e *colly.XMLElement) {
|
|
info.Summary += strings.TrimSpace(parseSummary(e))
|
|
})
|
|
|
|
// Thumb+Cover
|
|
c.OnXML(`//*[@id="PlayerCover"]/img`, func(e *colly.XMLElement) {
|
|
info.CoverURL = e.Request.AbsoluteURL(e.Attr("src"))
|
|
info.ThumbURL = strings.ReplaceAll(info.CoverURL, "bigcover", "jacket_images")
|
|
})
|
|
|
|
// Thumb (fallback)
|
|
c.OnXML(`//link[@rel="image_src"]`, func(e *colly.XMLElement) {
|
|
if info.ThumbURL != "" {
|
|
return
|
|
}
|
|
info.ThumbURL = e.Request.AbsoluteURL(e.Attr("href"))
|
|
})
|
|
|
|
// Cover (fallback)
|
|
c.OnXML(`//span[@class="grid-gallery"]/a`, func(e *colly.XMLElement) {
|
|
if info.CoverURL != "" {
|
|
return
|
|
}
|
|
info.CoverURL = e.Request.AbsoluteURL(e.Attr("href"))
|
|
})
|
|
|
|
// Screen Shot
|
|
c.OnXML(`//*[@id="sscontainerppv123"]/img`, func(e *colly.XMLElement) {
|
|
if src := e.Attr("src"); src != "" {
|
|
info.PreviewImages = []string{e.Request.AbsoluteURL(src)}
|
|
}
|
|
})
|
|
|
|
// Preview Images
|
|
c.OnXML(`//div[@class="gallery-block grid-gallery"]//a[@class="lightbox"]`, func(e *colly.XMLElement) {
|
|
if href := e.Attr("href"); href != "" {
|
|
info.PreviewImages = append(info.PreviewImages, e.Request.AbsoluteURL(href))
|
|
}
|
|
})
|
|
|
|
// Preview Video
|
|
c.OnXML(`//*[@id="player1"]/source`, func(e *colly.XMLElement) {
|
|
info.PreviewVideoHLSURL = e.Request.AbsoluteURL(e.Attr("src"))
|
|
})
|
|
|
|
// Fields
|
|
c.OnXML(`//*[@id="MyBody"]//div[@class="product-info-block-rev mt-20"]/div[@class="single-info"]`, func(e *colly.XMLElement) {
|
|
switch e.ChildText(`.//span[1]`) {
|
|
case "商品番号":
|
|
info.Number = strings.TrimSpace(e.ChildText(`.//span[2]`))
|
|
case "主演女優":
|
|
parser.ParseTexts(htmlquery.FindOne(e.DOM.(*html.Node), `.//span[2]`),
|
|
(*[]string)(&info.Actors))
|
|
case "スタジオ":
|
|
info.Maker = strings.TrimSpace(e.ChildText(`.//span[2]`))
|
|
case "シリーズ":
|
|
info.Series = strings.TrimSpace(e.ChildText(`.//span[2]`))
|
|
case "カテゴリ":
|
|
parser.ParseTexts(htmlquery.FindOne(e.DOM.(*html.Node), `.//span[2]`),
|
|
(*[]string)(&info.Genres))
|
|
case "発売日":
|
|
info.ReleaseDate = parser.ParseDate(strings.Fields(e.ChildText(`.//span[2]`))[0])
|
|
case "収録時間":
|
|
info.Runtime = parser.ParseRuntime(e.ChildText(`.//span[2]`))
|
|
}
|
|
})
|
|
|
|
err = c.Visit(info.Homepage)
|
|
return
|
|
}
|
|
|
|
func (ave *AVE) NormalizeKeyword(keyword string) string {
|
|
if number.IsSpecial(keyword) {
|
|
return ""
|
|
}
|
|
return strings.ToUpper(keyword)
|
|
}
|
|
|
|
func (ave *AVE) SearchMovie(keyword string) (results []*model.MovieSearchResult, err error) {
|
|
c := ave.ClonedCollector()
|
|
|
|
c.OnXML(`//div[@class="single-slider-product grid-view-product"]`, func(e *colly.XMLElement) {
|
|
href := e.ChildAttr(`.//div[1]/a`, "href")
|
|
thumb := e.ChildAttr(`.//div[1]/a/img`, "src")
|
|
id, _ := ave.ParseIDFromURL(e.Request.AbsoluteURL(href))
|
|
results = append(results, &model.MovieSearchResult{
|
|
ID: id,
|
|
Number: parserNumber(thumb),
|
|
Title: e.ChildText(`.//div[2]/p[@class="product-title"]/a`),
|
|
Provider: ave.Name(),
|
|
Homepage: e.Request.AbsoluteURL(href),
|
|
ThumbURL: e.Request.AbsoluteURL(thumb),
|
|
CoverURL: e.Request.AbsoluteURL(strings.ReplaceAll(thumb, "jacket_images", "bigcover")),
|
|
})
|
|
})
|
|
|
|
err = c.Visit(fmt.Sprintf(searchURL, url.QueryEscape(keyword)))
|
|
return
|
|
}
|
|
|
|
func parserNumber(s string) string {
|
|
if ss := regexp.MustCompile(`(?i)/(?:dvd\d)?([a-z\d-_]+)\.(?:jpe?g|png|gif|webp)`).FindStringSubmatch(s); len(ss) == 2 {
|
|
return strings.ToUpper(ss[1])
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func parseSummary(e *colly.XMLElement) string {
|
|
sb := &strings.Builder{}
|
|
for n := e.DOM.(*html.Node).FirstChild; n != nil; n = n.NextSibling {
|
|
switch {
|
|
case n.Type == html.TextNode:
|
|
sb.WriteString(strings.TrimSpace(n.Data))
|
|
case n.Type == html.ElementNode && n.Data == "br":
|
|
sb.WriteByte('\n')
|
|
}
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
func init() {
|
|
provider.RegisterMovieFactory(Name, New)
|
|
}
|