Files
metatube-server/provider/kin8tengoku/kin8tengoku.go
T
2022-12-15 23:13:04 +08:00

225 lines
6.5 KiB
Go

package kin8tengoku
import (
"encoding/json"
"fmt"
"net/url"
"path"
"regexp"
"strconv"
"strings"
"time"
"github.com/antchfx/htmlquery"
"github.com/gocolly/colly/v2"
"golang.org/x/net/html"
"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 = (*KIN8)(nil)
const (
Name = "KIN8"
Priority = 1000
)
const (
baseURL = "https://www.kin8tengoku.com/"
movieURL = "https://www.kin8tengoku.com/moviepages/%04s/index.html"
reviewURL = "https://m-template.heyzo.com/snstb/api/review?%s"
)
type KIN8 struct {
*scraper.Scraper
}
func New() *KIN8 {
return &KIN8{scraper.NewDefaultScraper(Name, baseURL, Priority)}
}
func (k8 *KIN8) NormalizeID(id string) string {
if ss := regexp.MustCompile(`^(?i)(?:kin8[-_])?(\d+)$`).FindStringSubmatch(id); len(ss) == 2 {
return ss[1]
}
return ""
}
func (k8 *KIN8) GetMovieInfoByID(id string) (info *model.MovieInfo, err error) {
return k8.GetMovieInfoByURL(fmt.Sprintf(movieURL, id))
}
func (k8 *KIN8) ParseIDFromURL(rawURL string) (string, error) {
homepage, err := url.Parse(rawURL)
if err != nil {
return "", err
}
return path.Base(path.Dir(homepage.Path)), nil
}
func (k8 *KIN8) GetMovieInfoByURL(rawURL string) (info *model.MovieInfo, err error) {
id, err := k8.ParseIDFromURL(rawURL)
if err != nil {
return
}
info = &model.MovieInfo{
ID: id,
Number: fmt.Sprintf("KIN8-%s", id),
Provider: k8.Name(),
Homepage: rawURL,
Maker: "金髪天國",
Actors: []string{},
PreviewImages: []string{},
Genres: []string{},
}
c := k8.ClonedCollector()
// Title
c.OnXML(`//*[@id="sub_main"]/p[@class="sub_title" or @class="sub_title_vip"]`, func(e *colly.XMLElement) {
info.Title = strings.TrimSpace(e.Text)
})
// Title (fallback)
c.OnXML(`//meta[@name="keywords"]`, func(e *colly.XMLElement) {
if info.Title != "" {
return
}
info.Title = strings.TrimSpace(e.Attr("content"))
})
// Summary
c.OnXML(`//*[@id="comment"]`, func(e *colly.XMLElement) {
info.Summary = strings.TrimSpace(e.Text)
})
// Fields
c.OnXML(`//*[@id="detail_box" or @id="detail_box_vip"]//tr`, func(e *colly.XMLElement) {
switch e.ChildText(`//td[@class="movie_table_td"]`) {
case "モデル":
parser.ParseTexts(htmlquery.FindOne(e.DOM.(*html.Node), `//td[@class="movie_table_td2"]`),
(*[]string)(&info.Actors))
case "カテゴリー":
parser.ParseTexts(htmlquery.FindOne(e.DOM.(*html.Node), `//td[@class="movie_table_td2"]`),
(*[]string)(&info.Genres))
case "再生時間":
info.Runtime = parser.ParseRuntime(e.ChildText(`//td[@class="movie_table_td2"]`))
case "更新日":
info.ReleaseDate = parser.ParseDate(e.ChildText(`//td[@class="movie_table_td2"]`))
}
})
// Thumb+Cover+Preview Video
c.OnXML(`//*[@id="movie"]/script`, func(e *colly.XMLElement) {
if info.CoverURL != "" {
return
}
if re := regexp.MustCompile(`imgurl\s*=\s*'(.+?)';`); re.MatchString(e.Text) {
if ss := re.FindStringSubmatch(e.Text); len(ss) == 2 {
info.CoverURL = e.Request.AbsoluteURL(ss[1])
info.ThumbURL = info.CoverURL /* use cover as thumb */
}
}
if re := regexp.MustCompile(`samplelimit\s*=\s*(\d+);`); re.MatchString(e.Text) {
if ss := re.FindStringSubmatch(e.Text); len(ss) == 2 {
sampleLimit, _ := strconv.Atoi(ss[1])
videoID, _ := strconv.Atoi(info.ID)
if sampleLimit > 0 && videoID > 0 {
// get video urls.
if re := regexp.MustCompile(`videourl\s*=\s*'(.+?)'`); re.MatchString(e.Text) {
if ss := re.FindAllStringSubmatch(e.Text, -1); len(ss) == 1 {
info.PreviewVideoURL = e.Request.AbsoluteURL(ss[0][1])
} else if len(ss) == 2 {
if videoID >= sampleLimit {
info.PreviewVideoURL = e.Request.AbsoluteURL(ss[0][1])
} else {
info.PreviewVideoURL = e.Request.AbsoluteURL(ss[1][1])
}
}
}
}
}
}
})
// Preview Images
c.OnXML(`//*[@id="gallery" or @id="gallery_vip"]/div/a`, func(e *colly.XMLElement) {
if href := e.Attr("href"); href != "" {
info.PreviewImages = append(info.PreviewImages, e.Request.AbsoluteURL(href))
}
})
// Score
//$(document).ready(function() {
// var d2p_site_id = $('#review_list').data('d2p_site_id');
// var movie_seq = $('#review_list').data('movie_seq');
//
// var domain_app_server_public = 'm-template.heyzo.com';
//
// $.getJSON('//' + domain_app_server_public + '/snstb/api/review?callback=?', {
// site_id: d2p_site_id,
// movie_seq: movie_seq,
// json: 1
// }, function(json) {
// //debugger; //check what you get in json object when you use debugger.
// var html = "";
// var product_id = "";
// $.each(json, function(i, review) {
// // index, each data
// if (product_id == '') {
// product_id = review.product_id;
// }
// html += "<div class='rev-box guest'>";
// html += "<div class='usr-rev-area'>";
// html += "<h5><span class='rev_star" + review.user_rating + "'></span></h5>";
// html += "<p>" + review.user_comment + "</p>";
// html += "</div><!-- .usr-rev-area -->";
// html += "<div class='usr-foot-area'><p class='px11'>by <span>" + review.profile_name + "</span>投稿日 : " + review.created_jp + "</p></div>";
// html += "</div><!-- .rev-box -->";
// });
// if (product_id != '') {
// html += "<div class='usr-button-see-more seeall'><a target='_blank' href='https://sns.d2pass.com/product/movies/" + product_id + "'>もっと見る</a></div>";
// }
// $("#review_overflow").html(html);
// });
//})
c.OnXML(`//*[@id="review_list"]`, func(e *colly.XMLElement) {
siteId := e.Attr("data-d2p_site_id")
movieSeq := e.Attr("data-movie_seq")
jqTimestamp := strconv.Itoa(int(time.Now().UnixMilli()))
d := c.Clone()
d.OnResponse(func(r *colly.Response) {
if ss := regexp.MustCompile(`(?s)\w+\((.+?)\);`).FindSubmatch(r.Body); len(ss) == 2 {
var data []struct {
UserRating float64 `json:"user_rating"`
}
if json.Unmarshal(ss[1], &data) == nil {
total := 0.0
for _, i := range data {
total += i.UserRating
}
info.Score = total / float64(len(data))
}
}
})
q := &url.Values{}
q.Add("callback", fmt.Sprintf("jQuery_%s", jqTimestamp))
q.Add("site_id", siteId)
q.Add("movie_seq", movieSeq)
q.Add("json", "1")
q.Add("_", jqTimestamp)
d.Visit(fmt.Sprintf(reviewURL, q.Encode()))
})
err = c.Visit(info.Homepage)
return
}
func init() {
provider.RegisterMovieFactory(Name, New)
}