mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2025-12-18 20:50:12 +08:00
436 lines
11 KiB
Go
436 lines
11 KiB
Go
// Package movies 电影查询
|
|
package movies
|
|
|
|
import (
|
|
"encoding/json"
|
|
"image"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/FloatTech/floatbox/file"
|
|
"github.com/FloatTech/floatbox/web"
|
|
"github.com/FloatTech/gg"
|
|
"github.com/FloatTech/imgfactory"
|
|
"github.com/FloatTech/rendercard"
|
|
ctrl "github.com/FloatTech/zbpctrl"
|
|
"github.com/FloatTech/zbputils/control"
|
|
"github.com/FloatTech/zbputils/img/text"
|
|
zero "github.com/wdvxdr1123/ZeroBot"
|
|
"github.com/wdvxdr1123/ZeroBot/message"
|
|
)
|
|
|
|
const (
|
|
apiURL = "https://m.maoyan.com/ajax/"
|
|
ua = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36"
|
|
)
|
|
|
|
var (
|
|
mu sync.RWMutex
|
|
todayPic = make([][]byte, 2)
|
|
lasttime time.Time
|
|
en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
|
DisableOnDefault: false,
|
|
Brief: "电影查询",
|
|
Help: "- 今日电影\n" +
|
|
"- 预售电影",
|
|
PrivateDataFolder: "movies",
|
|
})
|
|
)
|
|
|
|
func init() {
|
|
en.OnFullMatch("今日电影").SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
|
if todayPic != nil && time.Since(lasttime) < 12*time.Hour {
|
|
ctx.SendChain(message.ImageBytes(todayPic[0]))
|
|
return
|
|
}
|
|
lasttime = time.Now()
|
|
movieComingList, err := getMovieList("今日电影")
|
|
if err != nil {
|
|
ctx.SendChain(message.Text("[ERROR]:", err))
|
|
return
|
|
}
|
|
if len(movieComingList) == 0 {
|
|
ctx.SendChain(message.Text("没有今日电影"))
|
|
return
|
|
}
|
|
pic, err := drawOnListPic(movieComingList)
|
|
if err != nil {
|
|
ctx.SendChain(message.Text("[ERROR]:", err))
|
|
return
|
|
}
|
|
todayPic[0] = pic
|
|
ctx.SendChain(message.ImageBytes(pic))
|
|
})
|
|
en.OnFullMatch("预售电影").SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
|
if todayPic[1] != nil && time.Since(lasttime) < 12*time.Hour {
|
|
ctx.SendChain(message.ImageBytes(todayPic[1]))
|
|
return
|
|
}
|
|
lasttime = time.Now()
|
|
movieComingList, err := getMovieList("预售电影")
|
|
if err != nil {
|
|
ctx.SendChain(message.Text("[ERROR]:", err))
|
|
return
|
|
}
|
|
if len(movieComingList) == 0 {
|
|
ctx.SendChain(message.Text("没有预售电影"))
|
|
return
|
|
}
|
|
pic, err := drawComListPic(movieComingList)
|
|
if err != nil {
|
|
ctx.SendChain(message.Text("[ERROR]:", err))
|
|
return
|
|
}
|
|
todayPic[1] = pic
|
|
ctx.SendChain(message.ImageBytes(pic))
|
|
})
|
|
}
|
|
|
|
type movieInfo struct {
|
|
ID int64 `json:"id"` // 电影ID
|
|
Img string `json:"img"` // 海报
|
|
|
|
Nm string `json:"nm"` // 名称
|
|
|
|
Dir string `json:"dir"` // 导演
|
|
Star string `json:"star"` // 演员
|
|
|
|
OriLang string `json:"oriLang"` // 原语言
|
|
Cat string `json:"cat"` // 类型
|
|
|
|
Version string `json:"version"` // 电影格式
|
|
Rt string `json:"rt"` // 上映时间
|
|
|
|
ShowInfo string `json:"showInfo"` // 今日上映信息
|
|
ComingTitle string `json:"comingTitle"` // 预售信息
|
|
|
|
Sc float64 `json:"sc"` // 评分
|
|
Wish int64 `json:"wish"` // 观看人数
|
|
Watched int64 `json:"watched"` // 观看数
|
|
}
|
|
type movieOnList struct {
|
|
MovieList []movieInfo `json:"movieList"`
|
|
}
|
|
type comingList struct {
|
|
MovieList []movieInfo `json:"coming"`
|
|
}
|
|
type movieShow struct {
|
|
MovieInfo movieInfo `json:"detailMovie"`
|
|
}
|
|
|
|
type cardInfo struct {
|
|
Avatar image.Image
|
|
TopLeftText string
|
|
BottomLeftText []string
|
|
RightText string
|
|
Rank string
|
|
}
|
|
|
|
func getMovieList(mode string) (movieList []movieInfo, err error) {
|
|
var data []byte
|
|
if mode == "今日电影" {
|
|
data, err = web.RequestDataWith(web.NewDefaultClient(), apiURL+"movieOnInfoList", "", "GET", ua, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
var parsed movieOnList
|
|
err = json.Unmarshal(data, &parsed)
|
|
if err != nil {
|
|
return
|
|
}
|
|
movieList = parsed.MovieList
|
|
} else {
|
|
data, err = web.RequestDataWith(web.NewDefaultClient(), apiURL+"comingList?token=", "", "GET", ua, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
var parsed comingList
|
|
err = json.Unmarshal(data, &parsed)
|
|
if err != nil {
|
|
return
|
|
}
|
|
movieList = parsed.MovieList
|
|
}
|
|
if len(movieList) == 0 {
|
|
return
|
|
}
|
|
for i, info := range movieList {
|
|
movieID := strconv.FormatInt(info.ID, 10)
|
|
data, err = web.RequestDataWith(web.NewDefaultClient(), apiURL+"detailmovie?movieId="+movieID, "", "GET", ua, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
var movieInfo movieShow
|
|
err = json.Unmarshal(data, &movieInfo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if mode != "今日电影" {
|
|
movieInfo.MovieInfo.ComingTitle = movieList[i].ComingTitle
|
|
}
|
|
movieList[i] = movieInfo.MovieInfo
|
|
}
|
|
// 整理数据,进行排序
|
|
sort.Slice(movieList, func(i, j int) bool {
|
|
if movieList[i].Sc != movieList[j].Sc {
|
|
return movieList[i].Sc > movieList[j].Sc
|
|
}
|
|
if mode == "今日电影" {
|
|
return movieList[i].Watched > movieList[j].Watched
|
|
}
|
|
return movieList[i].Wish > movieList[j].Wish
|
|
})
|
|
return movieList, nil
|
|
}
|
|
func drawOnListPic(lits []movieInfo) (data []byte, err error) {
|
|
rankinfo := make([]*cardInfo, len(lits))
|
|
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(len(lits))
|
|
for i := 0; i < len(lits); i++ {
|
|
go func(i int) {
|
|
info := lits[i]
|
|
defer wg.Done()
|
|
img, err := avatar(&info)
|
|
if err != nil {
|
|
return
|
|
}
|
|
movieType := "2D"
|
|
if info.Version != "" {
|
|
movieType = info.Version
|
|
}
|
|
watched := ""
|
|
switch {
|
|
case info.Watched > 100000000:
|
|
watched = strconv.FormatFloat(float64(info.Watched)/100000000, 'f', 2, 64) + "亿"
|
|
case info.Watched > 10000:
|
|
watched = strconv.FormatFloat(float64(info.Watched)/10000, 'f', 2, 64) + "万"
|
|
default:
|
|
watched = strconv.FormatInt(info.Watched, 10)
|
|
}
|
|
rankinfo[i] = &cardInfo{
|
|
TopLeftText: info.Nm + " (" + strconv.FormatInt(info.ID, 10) + ")",
|
|
BottomLeftText: []string{
|
|
"导演:" + info.Dir,
|
|
"演员:" + info.Star,
|
|
"标签:" + info.Cat,
|
|
"语言: " + info.OriLang + " 类型: " + movieType,
|
|
"上映时间: " + info.Rt,
|
|
},
|
|
RightText: watched + "人已看",
|
|
Avatar: img,
|
|
Rank: strconv.FormatFloat(info.Sc, 'f', 1, 64),
|
|
}
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
fontbyte, err := file.GetLazyData(text.GlowSansFontFile, control.Md5File, true)
|
|
if err != nil {
|
|
return
|
|
}
|
|
img, err := drawRankingCard(fontbyte, "今日电影", rankinfo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
data, err = imgfactory.ToBytes(img)
|
|
return
|
|
}
|
|
|
|
func drawComListPic(lits []movieInfo) (data []byte, err error) {
|
|
rankinfo := make([]*cardInfo, len(lits))
|
|
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(len(lits))
|
|
for i := 0; i < len(lits); i++ {
|
|
go func(i int) {
|
|
info := lits[i]
|
|
defer wg.Done()
|
|
img, err := avatar(&info)
|
|
if err != nil {
|
|
return
|
|
}
|
|
movieType := "2D"
|
|
if info.Version != "" {
|
|
movieType = info.Version
|
|
}
|
|
wish := ""
|
|
switch {
|
|
case info.Wish > 100000000:
|
|
wish = strconv.FormatFloat(float64(info.Wish)/100000000, 'f', 2, 64) + "亿"
|
|
case info.Wish > 10000:
|
|
wish = strconv.FormatFloat(float64(info.Wish)/10000, 'f', 2, 64) + "万"
|
|
default:
|
|
wish = strconv.FormatInt(info.Wish, 10)
|
|
}
|
|
rankinfo[i] = &cardInfo{
|
|
TopLeftText: info.Nm + " (" + strconv.FormatInt(info.ID, 10) + ")",
|
|
BottomLeftText: []string{
|
|
"导演:" + info.Dir,
|
|
"演员:" + info.Star,
|
|
"标签:" + info.Cat,
|
|
"语言: " + info.OriLang + " 类型: " + movieType,
|
|
"上映时间: " + info.Rt + " 播放时间: " + info.ComingTitle,
|
|
},
|
|
RightText: wish + "人期待",
|
|
Avatar: img,
|
|
Rank: strconv.Itoa(i + 1),
|
|
}
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
fontbyte, err := file.GetLazyData(text.GlowSansFontFile, control.Md5File, true)
|
|
if err != nil {
|
|
return
|
|
}
|
|
img, err := drawRankingCard(fontbyte, "预售电影", rankinfo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
data, err = imgfactory.ToBytes(img)
|
|
return
|
|
}
|
|
|
|
func drawRankingCard(fontdata []byte, title string, rankinfo []*cardInfo) (img image.Image, err error) {
|
|
line := len(rankinfo)
|
|
const lineh = 130
|
|
const w = 800
|
|
h := 64 + (lineh+14)*line + 20 - 14
|
|
canvas := gg.NewContext(w, h)
|
|
canvas.SetRGBA255(255, 255, 255, 255)
|
|
canvas.Clear()
|
|
|
|
cardh, cardw := lineh, 770
|
|
cardspac := 14
|
|
hspac, wspac := 64.0, 16.0
|
|
r := 16.0
|
|
|
|
wg := &sync.WaitGroup{}
|
|
wg.Add(line)
|
|
cardimgs := make([]image.Image, line)
|
|
for i := 0; i < line; i++ {
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
card := gg.NewContext(w, cardh)
|
|
|
|
card.NewSubPath()
|
|
|
|
card.MoveTo(wspac+float64(cardh)/2, 0)
|
|
|
|
card.LineTo(wspac+float64(cardw)-r, 0)
|
|
card.DrawArc(wspac+float64(cardw)-r, r, r, gg.Radians(-90), gg.Radians(0))
|
|
card.LineTo(wspac+float64(cardw), float64(cardh)-r)
|
|
card.DrawArc(wspac+float64(cardw)-r, float64(cardh)-r, r, gg.Radians(0), gg.Radians(90))
|
|
card.LineTo(wspac+float64(cardh)/2, float64(cardh))
|
|
card.DrawArc(wspac+r, float64(cardh)-r, r, gg.Radians(90), gg.Radians(180))
|
|
card.LineTo(wspac, r)
|
|
card.DrawArc(wspac+r, r, r, gg.Radians(180), gg.Radians(270))
|
|
|
|
card.ClosePath()
|
|
|
|
card.ClipPreserve()
|
|
|
|
avatar := rankinfo[i].Avatar
|
|
|
|
PicH := cardh - 20
|
|
picW := int(float64(avatar.Bounds().Dx()) * float64(PicH) / float64(avatar.Bounds().Dy()))
|
|
card.DrawImageAnchored(imgfactory.Size(avatar, picW, PicH).Image(), int(wspac)+10+picW/2, cardh/2, 0.5, 0.5)
|
|
|
|
card.ResetClip()
|
|
card.SetRGBA255(0, 0, 0, 127)
|
|
card.Stroke()
|
|
|
|
card.SetRGBA255(240, 210, 140, 200)
|
|
card.DrawRoundedRectangle(wspac+float64(cardw-8-250), (float64(cardh)-50)/2, 250, 50, 25)
|
|
card.Fill()
|
|
card.SetRGB255(rendercard.RandJPColor())
|
|
card.DrawRoundedRectangle(wspac+float64(cardw-8-60), (float64(cardh)-50)/2, 60, 50, 25)
|
|
card.Fill()
|
|
cardimgs[i] = card.Image()
|
|
}(i)
|
|
}
|
|
|
|
canvas.SetRGBA255(0, 0, 0, 255)
|
|
err = canvas.ParseFontFace(fontdata, 32)
|
|
if err != nil {
|
|
return
|
|
}
|
|
canvas.DrawStringAnchored(title, w/2, 64/2, 0.5, 0.5)
|
|
|
|
err = canvas.ParseFontFace(fontdata, 22)
|
|
if err != nil {
|
|
return
|
|
}
|
|
wg.Wait()
|
|
for i := 0; i < line; i++ {
|
|
canvas.DrawImageAnchored(cardimgs[i], w/2, int(hspac)+((cardh+cardspac)*i), 0.5, 0)
|
|
canvas.DrawStringAnchored(rankinfo[i].TopLeftText, wspac+10+80+10, hspac+float64((cardspac+cardh)*i+cardh*3/16), 0, 0.5)
|
|
}
|
|
|
|
// canvas.SetRGBA255(63, 63, 63, 255)
|
|
err = canvas.ParseFontFace(fontdata, 14)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for i := 0; i < line; i++ {
|
|
for j, text := range rankinfo[i].BottomLeftText {
|
|
canvas.DrawStringAnchored(text, wspac+10+80+10, hspac+float64((cardspac+cardh)*i+cardh*6/16)+float64(j*16), 0, 0.5)
|
|
}
|
|
}
|
|
canvas.SetRGBA255(0, 0, 0, 255)
|
|
err = canvas.ParseFontFace(fontdata, 20)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for i := 0; i < line; i++ {
|
|
canvas.DrawStringAnchored(rankinfo[i].RightText, w-wspac-8-60-8, hspac+float64((cardspac+cardh)*i+cardh/2), 1, 0.5)
|
|
}
|
|
|
|
canvas.SetRGBA255(255, 255, 255, 255)
|
|
err = canvas.ParseFontFace(fontdata, 28)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for i := 0; i < line; i++ {
|
|
canvas.DrawStringAnchored(rankinfo[i].Rank, w-wspac-8-30, hspac+float64((cardspac+cardh)*i+cardh/2), 0.5, 0.5)
|
|
}
|
|
|
|
img = canvas.Image()
|
|
return
|
|
}
|
|
|
|
// avatar 获取电影海报,图片大且多,存本地增加响应速度
|
|
func avatar(movieInfo *movieInfo) (pic image.Image, err error) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
|
|
aimgfile := filepath.Join(en.DataFolder(), movieInfo.Nm+"("+strconv.FormatInt(movieInfo.ID, 10)+").jpg")
|
|
if file.IsNotExist(aimgfile) {
|
|
err = file.DownloadTo(movieInfo.Img, aimgfile)
|
|
if err != nil {
|
|
return urlToImg(movieInfo.Img)
|
|
}
|
|
}
|
|
f, err := os.Open(filepath.Join(file.BOTPATH, aimgfile))
|
|
if err != nil {
|
|
return urlToImg(movieInfo.Img)
|
|
}
|
|
defer f.Close()
|
|
pic, _, err = image.Decode(f)
|
|
return
|
|
}
|
|
|
|
func urlToImg(url string) (img image.Image, err error) {
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
img, _, err = image.Decode(resp.Body)
|
|
return
|
|
}
|