ABS v1 test

This commit is contained in:
mubai
2023-12-24 23:22:20 +08:00
parent b48e53a637
commit 86e0501d2f
59 changed files with 538 additions and 1699 deletions

View File

@@ -110,11 +110,10 @@ const (
//mysql服务配置信息 root:root 设置mysql账户的用户名和密码
MysqlDsn = "root:MuBai0916$@(mysql:3306)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
// Mysql连接信息
MysqlDsn = "root:root@(mysql:3306)/FilmSite?charset=utf8mb4&parseTime=True&loc=Local"
// Redis连接信息
RedisAddr = `redis:6379`
RedisPassword = `root`
RedisPassword = `MuBai0916$`
RedisDBNo = 0
)

View File

@@ -0,0 +1,19 @@
package config
import "time"
/*
对外开放API相关配置
*/
const (
// ResourceExpired API所需要的资源有效期
ResourceExpired = time.Hour * 24 * 90
// OriginalFilmDetailKey 采集时原始数据存储key
OriginalFilmDetailKey = "OriginalResource:FilmDetail:Id%d"
FilmClassKey = "OriginalResource:FilmClass"
PlayForm = "gfm3u8"
PlayFormCloud = "gofilm"
PlayFormAll = "gofilm$$$gfmu38"
RssVersion = "5.1"
)

View File

@@ -18,10 +18,7 @@ const (
func Index(c *gin.Context) {
// 获取首页所需数据
data := logic.IL.IndexPage()
c.JSON(http.StatusOK, gin.H{
"status": StatusOk,
"data": data,
})
system.Success(data, "首页数据获取成功", c)
}
// CategoriesInfo 分类信息获取
@@ -47,54 +44,50 @@ func FilmDetail(c *gin.Context) {
// 获取请求参数
id, err := strconv.Atoi(c.Query("id"))
if err != nil {
c.JSON(http.StatusOK, gin.H{
"status": StatusFailed,
"message": "请求异常,暂无影片信息!!!",
})
system.Failed("请求异常,影片请求参数异常!!!", c)
return
}
// 获取影片详情信息
detail := logic.IL.GetFilmDetail(id)
// 获取相关推荐影片数据
page := system.Page{Current: 0, PageSize: 14}
relateMovie := logic.IL.RelateMovie(detail, &page)
c.JSON(http.StatusOK, gin.H{
"status": StatusOk,
"data": gin.H{
"detail": detail,
"relate": relateMovie,
},
})
relateMovie := logic.IL.RelateMovie(detail.MovieDetail, &page)
system.Success(gin.H{
"detail": detail,
"relate": relateMovie,
}, "影片详情信息获取成功", c)
}
// FilmPlayInfo 影视播放页数据
func FilmPlayInfo(c *gin.Context) {
// 获取请求参数
id, err := strconv.Atoi(c.DefaultQuery("id", "0"))
playFrom, err := strconv.Atoi(c.DefaultQuery("playFrom", "0"))
playFrom := c.DefaultQuery("playFrom", "")
episode, err := strconv.Atoi(c.DefaultQuery("episode", "0"))
if err != nil {
c.JSON(http.StatusOK, gin.H{
"status": StatusFailed,
"message": "请求异常,暂无影片信息!!!",
})
system.Failed("请求异常,暂无影片信息!!!", c)
return
}
// 获取影片详情信息
detail := logic.IL.GetFilmDetail(id)
// 获取当前影片播放信息
var currentPlay system.MovieUrlInfo
for _, v := range detail.List {
if v.Id == playFrom {
currentPlay = v.LinkList[episode]
}
}
// 推荐影片信息
page := system.Page{Current: 0, PageSize: 14}
relateMovie := logic.IL.RelateMovie(detail, &page)
c.JSON(http.StatusOK, gin.H{
"status": StatusOk,
"data": gin.H{
"detail": detail,
"current": detail.PlayList[playFrom][episode],
"currentPlayFrom": playFrom,
"currentEpisode": episode,
"relate": relateMovie,
},
})
relateMovie := logic.IL.RelateMovie(detail.MovieDetail, &page)
system.Success(gin.H{
"detail": detail,
"current": currentPlay,
"currentPlayFrom": playFrom,
"currentEpisode": episode,
"relate": relateMovie,
}, "影片播放信息获取成功", c)
}
// SearchFilm 通过片名模糊匹配库存中的信息
@@ -187,53 +180,3 @@ func FilmClassify(c *gin.Context) {
"page": page,
})
}
// FilmCategory 获取指定分类的影片分页数据,(已弃用)
func FilmCategory(c *gin.Context) {
// 1.1 首先获取Cid 二级分类id是否存在
cidStr := c.DefaultQuery("cid", "")
// 1.2 如果pid也不存在直接返回错误信息
pidStr := c.DefaultQuery("pid", "")
if pidStr == "" {
c.JSON(http.StatusOK, gin.H{
"status": StatusFailed,
"message": "缺少分类信息",
})
return
}
// 1.3 获取pid对应的分类信息
pid, _ := strconv.ParseInt(pidStr, 10, 64)
category := logic.IL.GetPidCategory(pid)
// 2 设置分页信息
currentStr := c.DefaultQuery("current", "1")
current, _ := strconv.Atoi(currentStr)
page := system.Page{PageSize: 49, Current: current}
// 2.1 如果不存在cid则根据Pid进行查询
if cidStr == "" {
// 2.2 如果存在pid则根据pid进行查找
c.JSON(http.StatusOK, gin.H{
"status": StatusOk,
"data": gin.H{
"list": logic.IL.GetFilmCategory(pid, "pid", &page),
"category": category,
"search": logic.IL.SearchTags(pid),
},
"page": page,
})
return
}
// 2.2 如果存在cid 则根据具体的cid去查询数据
cid, _ := strconv.ParseInt(cidStr, 10, 64)
c.JSON(http.StatusOK, gin.H{
"status": StatusOk,
"data": gin.H{
"list": logic.IL.GetFilmCategory(cid, "cid", &page),
"category": category,
"search": logic.IL.SearchTags(pid),
},
"page": page,
})
// 获取请求参数
}

View File

@@ -9,10 +9,9 @@ require (
github.com/redis/go-redis/v9 v9.0.2
github.com/robfig/cron/v3 v3.0.0
gorm.io/driver/mysql v1.4.7
gorm.io/gorm v1.24.6
gorm.io/gorm v1.25.5
)
require (
github.com/PuerkitoBio/goquery v1.5.1 // indirect
github.com/andybalholm/cascadia v1.2.0 // indirect

View File

@@ -207,6 +207,8 @@ gorm.io/driver/mysql v1.4.7/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8o
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@@ -67,15 +67,16 @@ func (i *IndexLogic) IndexPage() map[string]interface{} {
}
// GetFilmDetail 影片详情信息页面处理
func (i *IndexLogic) GetFilmDetail(id int) system.MovieDetail {
func (i *IndexLogic) GetFilmDetail(id int) system.MovieDetailVo {
// 通过Id 获取影片search信息
search := system.SearchInfo{}
db.Mdb.Where("mid", id).First(&search)
// 获取redis中的完整影视信息 MovieDetail:Cid11:Id24676
movieDetail := system.GetDetailByKey(fmt.Sprintf(config.MovieDetailKey, search.Cid, search.Mid))
var res = system.MovieDetailVo{MovieDetail: movieDetail}
//查找其他站点是否存在影片对应的播放源
multipleSource(&movieDetail)
return movieDetail
res.List = multipleSource(&movieDetail)
return res
}
// GetCategoryInfo 分类信息获取, 组装导航栏需要的信息
@@ -183,7 +184,11 @@ func (i *IndexLogic) SearchTags(pid int64) map[string]interface{} {
2. 仅对主站点影片name进行映射关系处理并将结果添加到map中
例如: xxx第一季 xxx
*/
func multipleSource(detail *system.MovieDetail) {
func multipleSource(detail *system.MovieDetail) []system.PlayLinkVo {
// 生成多站点的播放源信息
master := system.GetCollectSourceListByGrade(system.MasterCollect)
var playList = []system.PlayLinkVo{{master[0].Id, master[0].Name, detail.PlayList[0]}}
// 整合多播放源, 初始化存储key map
names := make(map[string]int)
// 1. 判断detail的dbId是否存在, 存在则添加到names中作为匹配条件
@@ -206,18 +211,21 @@ func multipleSource(detail *system.MovieDetail) {
names[system.GenerateHashKey(v)] = 0
}
}
// 遍历站点列表
// 遍历所有附属站点列表
sc := system.GetCollectSourceListByGrade(system.SlaveCollect)
for _, s := range sc {
for k, _ := range names {
pl := system.GetMultiplePlay(s.Name, k)
pl := system.GetMultiplePlay(s.Id, k)
if len(pl) > 0 {
// 如果当前站点已经匹配到数据则直接退出当前循环
detail.PlayList = append(detail.PlayList, pl)
//detail.PlayList = append(detail.PlayList, pl)
playList = append(playList, system.PlayLinkVo{Id: s.Id, Name: s.Name, LinkList: pl})
break
}
}
}
return playList
}
// GetFilmsByTags 通过searchTag 返回满足条件的分页影片信息

View File

@@ -6,7 +6,6 @@ import (
"server/model/system"
"server/plugin/SystemInit"
"server/plugin/db"
"server/plugin/spider"
"server/router"
"time"
)
@@ -31,10 +30,6 @@ func main() {
}
func start() {
// 开启前先判断是否需要执行Spider
//ExecSpider()
// web服务启动后开启定时任务, 用于定期更新资源
//spider.RegularUpdateMovie()
// 启动前先执行数据库内容的初始化工作
DefaultDataInit()
@@ -43,21 +38,14 @@ func start() {
_ = r.Run(fmt.Sprintf(":%s", config.ListenerPort))
}
func ExecSpider() {
// 判断分类信息是否存在
isStart := system.ExistsCategoryTree()
// 如果分类信息不存在则进行一次完整爬取
if !isStart {
DefaultDataInit()
spider.StartSpider()
func DefaultDataInit() {
// 如果系统中不存在用户表则进行初始化
if !system.ExistUserTable() {
// 初始化影视来源列表信息
SystemInit.SpiderInit()
// 初始化数据库相关数据
SystemInit.TableInIt()
// 初始化网站基本配置信息
SystemInit.BasicConfigInit()
}
}
func DefaultDataInit() {
// 初始化影视来源列表信息
SystemInit.SpiderInit()
// 初始化数据库相关数据
SystemInit.TableInIt()
// 初始化网站基本配置信息
SystemInit.BasicConfigInit()
}

View File

@@ -68,6 +68,7 @@ type FilmSource struct {
SyncPictures bool `json:"syncPictures"` // 是否同步图片到服务器
CollectType ResourceType `json:"collectType"` // 采集资源类型
State bool `json:"state"` // 是否启用
Interval int `json:"interval"` // 采集时间间隔 单位/ms
}
// SaveCollectSourceList 保存采集站Api列表

View File

@@ -89,52 +89,59 @@ func GetPicturePage(page *Page) []Picture {
// SaveVirtualPic 保存待同步的图片信息
func SaveVirtualPic(pl []VirtualPicture) error {
// 保存对应的
// 保存对应的待同步图片信息
var zl []redis.Z
for _, p := range pl {
// 首先查询 Gallery 表中是否存在当前ID对应的图片信息, 如果不存在则保存
if !ExistPictureByRid(p.Id) {
m, _ := json.Marshal(p)
zl = append(zl, redis.Z{Score: float64(p.Id), Member: m})
}
//if !ExistPictureByRid(p.Id) {
// m, _ := json.Marshal(p)
// zl = append(zl, redis.Z{Score: float64(p.Id), Member: m})
//}
// 只要开启图片同步则将图片信息存入待同步图片信息集合中, 是否同步图片交由真正同步到本地时进行决断
m, _ := json.Marshal(p)
zl = append(zl, redis.Z{Score: float64(p.Id), Member: m})
}
return db.Rdb.ZAdd(db.Cxt, config.VirtualPictureKey, zl...).Err()
}
// SyncFilmPicture 同步新采集入栈还未同步的图片
func SyncFilmPicture() {
// 获取集合中的元素数量, 如果集合中没有元素则直接返回
count := db.Rdb.ZCard(db.Cxt, config.VirtualPictureKey).Val()
if count <= 0 {
return
}
// 扫描待同步图片的信息, 每次扫描count条
sl, cursor := db.Rdb.ZScan(db.Cxt, config.VirtualPictureKey, 0, "*", config.MaxScanCount).Val()
sl := db.Rdb.ZPopMax(db.Cxt, config.VirtualPictureKey, config.MaxScanCount).Val()
if len(sl) <= 0 {
return
}
// 获取 VirtualPicture
for i, s := range sl {
if i%2 == 0 {
// 获取图片信息
vp := VirtualPicture{}
_ = json.Unmarshal([]byte(s), &vp)
// 删除已经取出的数据
db.Rdb.ZRem(db.Cxt, config.VirtualPictureKey, []byte(s))
// 将图片同步到服务器
fileName, err := util.SaveOnlineFile(vp.Link, config.FilmPictureUploadDir)
if err != nil {
continue
}
// 完成同步后将图片信息保存到 Gallery 中
SaveGallery(Picture{
Link: fmt.Sprint(config.FilmPictureAccess, fileName),
Uid: config.UserIdInitialVal,
RelevanceId: vp.Id,
PicType: 0,
PicUid: regexp.MustCompile(`\.[^.]+$`).ReplaceAllString(fileName, ""),
})
for _, s := range sl {
// 获取图片信息
vp := VirtualPicture{}
_ = json.Unmarshal([]byte(s.Member.(string)), &vp)
// 判断当前影片是否已经同步过图片, 如果已经同步则直接跳过后续逻辑
if ExistPictureByRid(vp.Id) {
continue
}
// 将图片同步到服务器中
fileName, err := util.SaveOnlineFile(vp.Link, config.FilmPictureUploadDir)
if err != nil {
continue
}
// 完成同步后将图片信息保存到 Gallery 中
SaveGallery(Picture{
Link: fmt.Sprint(config.FilmPictureAccess, fileName),
Uid: config.UserIdInitialVal,
RelevanceId: vp.Id,
PicType: 0,
PicUid: regexp.MustCompile(`\.[^.]+$`).ReplaceAllString(fileName, ""),
})
}
// 如果 cursor != 0 则继续递归执行
if cursor > 0 {
SyncFilmPicture()
}
// 递归执行直到图片暂存信息为空
SyncFilmPicture()
}
// ReplaceDetailPic 将影片详情中的图片地址替换为自己的

View File

@@ -160,7 +160,7 @@ func SaveMovieBasicInfo(detail MovieDetail) {
}
// SaveSitePlayList 仅保存播放url列表信息到当前站点
func SaveSitePlayList(siteName string, list []MovieDetail) (err error) {
func SaveSitePlayList(id string, list []MovieDetail) (err error) {
// 如果list 为空则直接返回
if len(list) <= 0 {
return nil
@@ -183,7 +183,7 @@ func SaveSitePlayList(siteName string, list []MovieDetail) (err error) {
// 如果结果不为空,则将数据保存到redis中
if len(res) > 0 {
// 保存形式 key: MultipleSource:siteName Hash[hash(movieName)]list
err = db.Rdb.HMSet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, siteName), res).Err()
err = db.Rdb.HMSet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, id), res).Err()
}
return
}

View File

@@ -1,82 +0,0 @@
package system
/*
量子资源JSON解析
*/
// ClassInfo class 分类数据
type ClassInfo struct {
Id int64 `json:"type_id"` //分类ID
Pid int64 `json:"type_pid"` //上级分类ID
Name string `json:"type_name"` //分类名称
}
// MovieInfo 影片数据
type MovieInfo struct {
Id int64 `json:"vod_id"` // 影片ID
Name string `json:"vod_name"` // 影片名
Cid int64 `json:"type_id"` // 所属分类ID
CName string `json:"type_name"` // 所属分类名称
EnName string `json:"vod_en"` // 英文片名
Time string `json:"vod_time"` // 更新时间
Remarks string `json:"vod_remarks"` // 备注 | 清晰度
PlayFrom string `json:"vod_play_from"` // 播放来源
}
// MovieListInfo 影视列表响应数据
type MovieListInfo struct {
Code int64 `json:"code"`
Msg string `json:"msg"`
Page any `json:"page"`
PageCount int64 `json:"pagecount"`
Limit string `json:"limit"`
Total int64 `json:"total"`
List []MovieInfo `json:"list"`
Class []ClassInfo `json:"class"`
}
// MovieDetailInfo 影片详情数据 (只保留需要的部分)
type MovieDetailInfo struct {
Id int64 `json:"vod_id"` //影片Id
Cid int64 `json:"type_id"` //分类ID
Pid int64 `json:"type_id_1"` //一级分类ID
Name string `json:"vod_name"` //片名
SubTitle string `json:"vod_sub"` //子标题
CName string `json:"type_name"` //分类名称
EnName string `json:"vod_en"` //英文名
Initial string `json:"vod_letter"` //首字母
ClassTag string `json:"vod_class"` //分类标签
Pic string `json:"vod_pic"` //简介图片
Actor string `json:"vod_actor"` //主演
Director string `json:"vod_director"` //导演
Writer string `json:"vod_writer"` //作者
Blurb string `json:"vod_blurb"` //简介, 残缺,不建议使用
Remarks string `json:"vod_remarks"` // 更新情况
PubDate string `json:"vod_pubdate"` //上映时间
Area string `json:"vod_area"` // 地区
Language string `json:"vod_lang"` //语言
Year string `json:"vod_year"` //年份
State string `json:"vod_state"` //影片状态 正片|预告...
UpdateTime string `json:"vod_time"` //更新时间
AddTime int64 `json:"vod_time_add"` //资源添加时间戳
DbId int64 `json:"vod_douban_id"` //豆瓣id
DbScore string `json:"vod_douban_score"` // 豆瓣评分
Hits int64 `json:"vod_hits"` // 总热度
Content string `json:"vod_content"` //内容简介
PlayFrom string `json:"vod_play_from"` // 播放来源
PlaySeparator string `json:"vod_play_note"` // 播放信息分隔符
PlayUrl string `json:"vod_play_url"` //播放地址url
DownFrom string `json:"vod_down_from"` //下载来源 例: http
DownUrl string `json:"vod_down_url"` // 下载url地址
}
// DetailListInfo 影视详情信息
type DetailListInfo struct {
Code int64 `json:"code"`
Msg string `json:"msg"`
Page any `json:"page"`
PageCount int64 `json:"pagecount"`
Limit string `json:"limit"`
Total int64 `json:"total"`
List []MovieDetailInfo `json:"list"`
}

View File

@@ -261,7 +261,6 @@ func BatchSave(list []SearchInfo) {
if err := tx.CreateInBatches(list, len(list)).Error; err != nil {
// 插入失败则回滚事务, 重新进行插入
tx.Rollback()
return
}
// 保存成功后将相应tag数据缓存到redis中
BatchHandleSearchTag(list...)
@@ -357,26 +356,26 @@ func SyncSearchInfo(model int) {
// SearchInfoToMdb 扫描redis中的检索信息, 并批量存入mysql (model 执行模式 0-清空并保存 || 1-更新)
func SearchInfoToMdb(model int) {
// 获取集合中的元素数量, 如果集合中没有元素则直接返回
count := db.Rdb.ZCard(db.Cxt, config.SearchInfoTemp).Val()
if count <= 0 {
return
}
// 1.从redis中批量扫描详情信息
list, cursor := db.Rdb.ZScan(db.Cxt, config.SearchInfoTemp, 0, "*", config.MaxScanCount).Val()
list := db.Rdb.ZPopMax(db.Cxt, config.SearchInfoTemp, config.MaxScanCount).Val()
// 如果扫描到的信息为空则直接退出
if len(list) <= 0 {
return
}
// 2. 处理数据
var sl []SearchInfo
for i, s := range list {
// 3. 判断当前是否是元素
if i%2 == 0 {
info := SearchInfo{}
_ = json.Unmarshal([]byte(s), &info)
info.Model = gorm.Model{}
// 获取完则删除元素, 避免重复保存
db.Rdb.ZRem(db.Cxt, config.SearchInfoTemp, []byte(s))
sl = append(sl, info)
}
for _, s := range list {
// 解析详情数据
info := SearchInfo{}
_ = json.Unmarshal([]byte(s.Member.(string)), &info)
sl = append(sl, info)
}
//
// 通过model执行对应的保存方法
switch model {
case 0:
// 批量添加 SearchInfo
@@ -386,9 +385,7 @@ func SearchInfoToMdb(model int) {
BatchSaveOrUpdate(sl)
}
// 如果 SearchInfoTemp 依然存在数据, 则递归执行
if cursor > 0 {
SearchInfoToMdb(model)
}
SearchInfoToMdb(model)
}
// ================================= API 数据接口信息处理 =================================
@@ -533,8 +530,8 @@ func GetRelateMovieBasicInfo(search SearchInfo, page *Page) []MovieBasicInfo {
}
// GetMultiplePlay 通过影片名hash值匹配播放源
func GetMultiplePlay(siteName, key string) []MovieUrlInfo {
data := db.Rdb.HGet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, siteName), key).Val()
func GetMultiplePlay(siteId, key string) []MovieUrlInfo {
data := db.Rdb.HGet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, siteId), key).Val()
var playList []MovieUrlInfo
_ = json.Unmarshal([]byte(data), &playList)
return playList

View File

@@ -1,5 +1,6 @@
package system
// SearchTagsVO 搜索标签请求参数
type SearchTagsVO struct {
Pid int64 `json:"pid"`
Cid int64 `json:"cid"`
@@ -100,3 +101,15 @@ type UserInfoVo struct {
Avatar string `json:"avatar"` // 头像
Status int `json:"status"` // 状态
}
// PlayLinkVo 多站点播放链接数据列表
type PlayLinkVo struct {
Id string `json:"id"`
Name string `json:"name"`
LinkList []MovieUrlInfo `json:"linkList"`
}
type MovieDetailVo struct {
MovieDetail
List []PlayLinkVo `json:"list"`
}

View File

@@ -21,12 +21,12 @@ func FilmSourceInit() {
return
}
var l []system.FilmSource = []system.FilmSource{
{Id: util.GenerateSalt(), Name: "HD(lzBk)", Uri: `https://cj.lzcaiji.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false,CollectType:system.CollectVideo, State: false},
// {Id: util.GenerateSalt(), Name: "HD(lz)", Uri: `https://cj.lziapi.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: true},
{Id: util.GenerateSalt(), Name: "HD(bf)", Uri: `https://bfzyapi.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: true},
{Id: util.GenerateSalt(), Name: "HD(lzBk)", Uri: `https://cj.lzcaiji.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: true},
{Id: util.GenerateSalt(), Name: "HD(sn)", Uri: `https://suoniapi.com/api.php/provide/vod/from/snm3u8/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: true},
{Id: util.GenerateSalt(), Name: "HD(bf)", Uri: `https://bfzyapi.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: true, Interval: 1600},
{Id: util.GenerateSalt(), Name: "HD(ff)", Uri: `http://cj.ffzyapi.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: true},
{Id: util.GenerateSalt(), Name: "HD(kk)", Uri: `https://kuaikan-api.com/api.php/provide/vod/from/kuaikan/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: true},
{Id: util.GenerateSalt(), Name: "HD(sn)", Uri: `https://suoniapi.com/api.php/provide/vod/from/snm3u8/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: true},
//{Id: util.GenerateSalt(), Name: "HD(lz)", Uri: `https://cj.lziapi.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: true},
//{Id: util.GenerateSalt(), Name: "HD(fs)", Uri: `https://www.feisuzyapi.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: true},
//{Id: util.GenerateSalt(), Name: "HD(bfApp)", Uri: `http://app.bfzyapi.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false, CollectType: system.CollectVideo, State: true},
//Id: util.GenerateSalt(), {Name: "HD(bfBk)", Uri: `http://by.bfzyapi.com/api.php/provide/vod/`, ResultModel: system.JsonResult, Grade: system.SlaveCollect, SyncPictures: false,CollectType:system.CollectVideo, State: false},

View File

@@ -1,38 +0,0 @@
package dp
import (
"server/model/system"
)
// =================Spider数据处理=======================
// CategoryTree 组装树形菜单
func CategoryTree(list []system.ClassInfo) *system.CategoryTree {
// 遍历所有分类进行树形结构组装
tree := &system.CategoryTree{Category: &system.Category{Id: 0, Pid: -1, Name: "分类信息"}}
temp := make(map[int64]*system.CategoryTree)
temp[tree.Id] = tree
for _, c := range list {
// 判断当前节点ID是否存在于 temp中
category, ok := temp[c.Id]
if ok {
// 将当前节点信息保存
category.Category = &system.Category{Id: c.Id, Pid: c.Pid, Name: c.Name}
} else {
// 如果不存在则将当前分类存放到 temp中
category = &system.CategoryTree{Category: &system.Category{Id: c.Id, Pid: c.Pid, Name: c.Name}}
temp[c.Id] = category
}
// 根据 pid获取父节点信息
parent, ok := temp[category.Pid]
if !ok {
// 如果不存在父节点存在, 则将父节点存放到temp中
temp[c.Pid] = parent
}
// 将当前节点存放到父节点的Children中
parent.Children = append(parent.Children, category)
}
return tree
}

View File

@@ -1,155 +0,0 @@
package dp
import (
"server/model/system"
"strings"
)
// ProcessMovieListInfo 处理影片列表中的信息
func ProcessMovieListInfo(list []system.MovieInfo) []system.Movie {
var movies []system.Movie
for _, info := range list {
movies = append(movies, system.Movie{
Id: info.Id,
Name: info.Name,
Cid: info.Cid,
CName: info.CName,
EnName: info.EnName,
Time: info.Time,
Remarks: info.Remarks,
PlayFrom: info.PlayFrom,
})
}
return movies
}
// ProcessMovieDetailList 处理影片详情列表数据
func ProcessMovieDetailList(list []system.MovieDetailInfo) []system.MovieDetail {
var detailList []system.MovieDetail
for _, d := range list {
detailList = append(detailList, ProcessMovieDetail(d))
}
return detailList
}
// ProcessMovieDetail 处理单个影片详情信息
func ProcessMovieDetail(detail system.MovieDetailInfo) system.MovieDetail {
md := system.MovieDetail{
Id: detail.Id,
Cid: detail.Cid,
Pid: detail.Pid,
Name: detail.Name,
Picture: detail.Pic,
DownFrom: detail.DownFrom,
MovieDescriptor: system.MovieDescriptor{
SubTitle: detail.SubTitle,
CName: detail.CName,
EnName: detail.EnName,
Initial: detail.Initial,
ClassTag: detail.ClassTag,
Actor: detail.Actor,
Director: detail.Director,
Writer: detail.Writer,
Blurb: detail.Blurb,
Remarks: detail.Remarks,
ReleaseDate: detail.PubDate,
Area: detail.Area,
Language: detail.Language,
Year: detail.Year,
State: detail.State,
UpdateTime: detail.UpdateTime,
AddTime: detail.AddTime,
DbId: detail.DbId,
DbScore: detail.DbScore,
Hits: detail.Hits,
Content: detail.Content,
},
}
// 通过分割符切分播放源信息 PlaySeparator $$$
md.PlayFrom = strings.Split(detail.PlayFrom, detail.PlaySeparator)
// v2 只保留m3u8播放源
md.PlayList = ProcessPlayInfoV2(detail.PlayUrl, detail.PlaySeparator)
md.DownloadList = ProcessPlayInfoV2(detail.DownUrl, detail.PlaySeparator)
return md
}
// ProcessPlayInfo 处理影片播放数据信息
func ProcessPlayInfo(info, separator string) [][]system.MovieUrlInfo {
var res [][]system.MovieUrlInfo
// 1. 通过分隔符区分多个片源数据
for _, l := range strings.Split(info, separator) {
// 2.对每个片源的集数和播放地址进行分割
var item []system.MovieUrlInfo
for _, p := range strings.Split(l, "#") {
// 3. 处理 Episode$Link 形式的播放信息
if strings.Contains(p, "$") {
item = append(item, system.MovieUrlInfo{
Episode: strings.Split(p, "$")[0],
Link: strings.Split(p, "$")[1],
})
} else {
item = append(item, system.MovieUrlInfo{
Episode: "O(∩_∩)O",
Link: p,
})
}
}
// 3. 将每组播放源对应的播放列表信息存储到列表中
res = append(res, item)
}
return res
}
// ProcessPlayInfoV2 处理影片信息方案二 只保留m3u8播放源
func ProcessPlayInfoV2(info, separator string) [][]system.MovieUrlInfo {
var res [][]system.MovieUrlInfo
if separator != "" {
// 1. 通过分隔符切分播放源地址
for _, l := range strings.Split(info, separator) {
// 只对m3u8播放源 和 .mp4下载地址进行处理
if strings.Contains(l, ".m3u8") || strings.Contains(l, ".mp4") {
// 2.对每个片源的集数和播放地址进行分割
var item []system.MovieUrlInfo
for _, p := range strings.Split(l, "#") {
// 3. 处理 Episode$Link 形式的播放信息
if strings.Contains(p, "$") {
item = append(item, system.MovieUrlInfo{
Episode: strings.Split(p, "$")[0],
Link: strings.Split(p, "$")[1],
})
} else {
item = append(item, system.MovieUrlInfo{
Episode: "O(∩_∩)O",
Link: p,
})
}
}
// 3. 将每组播放源对应的播放列表信息存储到列表中
res = append(res, item)
}
}
} else {
// 只对m3u8播放源 和 .mp4下载地址进行处理
if strings.Contains(info, ".m3u8") || strings.Contains(info, ".mp4") {
// 2.对每个片源的集数和播放地址进行分割
var item []system.MovieUrlInfo
for _, p := range strings.Split(info, "#") {
// 3. 处理 Episode$Link 形式的播放信息
if strings.Contains(p, "$") {
item = append(item, system.MovieUrlInfo{
Episode: strings.Split(p, "$")[0],
Link: strings.Split(p, "$")[1],
})
} else {
item = append(item, system.MovieUrlInfo{
Episode: "O(∩_∩)O",
Link: p,
})
}
}
// 3. 将每组播放源对应的播放列表信息存储到列表中
res = append(res, item)
}
}
return res
}

View File

@@ -25,7 +25,7 @@ func InitMysql() (err error) {
SingularTable: true, //是否使用 结构体名称作为表名 (关闭自动变复数)
//NameReplacer: strings.NewReplacer("spider_", ""), // 替表名和字段中的 Me 为 空
},
Logger: logger.Default.LogMode(logger.Info), //设置日志级别为Info
// Logger: logger.Default.LogMode(logger.Info), //设置日志级别为Info
})
return
}

View File

@@ -1,14 +1,18 @@
package spider
import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"log"
"net/url"
"server/config"
"server/model/collect"
"server/model/system"
"server/plugin/common/conver"
"server/plugin/common/util"
"time"
)
/*
@@ -18,7 +22,7 @@ import (
var spiderCore = &JsonCollect{}
// =========================通用采集方法==============================
// ======================================================= 通用采集方法 =======================================================
// HandleCollect 影视采集 id-采集站ID h-时长/h
func HandleCollect(id string, h int) error {
@@ -64,7 +68,15 @@ func HandleCollect(id string, h int) error {
switch s.CollectType {
case system.CollectVideo:
// 采集视频资源
if pageCount <= config.MAXGoroutine*2 {
// 如果采集源参数中采集间隔参数大于500ms,则使用单线程采集
if s.Interval > 500 {
// 少量数据不开启协程
for i := 1; i <= pageCount; i++ {
collectFilm(s, h, i)
// 执行一次采集后休眠指定时长
time.Sleep(time.Duration(s.Interval) * time.Millisecond)
}
} else if pageCount <= config.MAXGoroutine*2 {
// 少量数据不开启协程
for i := 1; i <= pageCount; i++ {
collectFilm(s, h, i)
@@ -75,8 +87,6 @@ func HandleCollect(id string, h int) error {
}
// 视频数据采集完成后同步相关信息到mysql
if s.Grade == system.MasterCollect {
// 每次成功执行完都清理redis中的相关API接口数据缓存
clearCache()
// 执行影片信息更新操作
if h > 0 {
// 执行数据更新操作
@@ -89,6 +99,8 @@ func HandleCollect(id string, h int) error {
if s.SyncPictures {
system.SyncFilmPicture()
}
// 每次成功执行完都清理redis中的相关API接口数据缓存
clearCache()
}
case system.CollectArticle, system.CollectActor, system.CollectRole, system.CollectWebSite:
@@ -145,7 +157,7 @@ func collectFilm(s *system.FilmSource, h, pg int) {
}
case system.SlaveCollect:
// 附属站点 仅保存影片播放信息到redis
if err = system.SaveSitePlayList(s.Name, list); err != nil {
if err = system.SaveSitePlayList(s.Id, list); err != nil {
log.Println("SaveDetails Error: ", err)
}
}
@@ -217,3 +229,34 @@ func StarZero(h int) {
// 开启自动采集
AutoCollect(h)
}
// ======================================================= 公共方法 =======================================================
// CollectApiTest 测试采集接口是否可用
func CollectApiTest(s system.FilmSource) error {
// 使用当前采集站接口采集一页数据
r := util.RequestInfo{Uri: s.Uri, Params: url.Values{}}
r.Params.Set("ac", s.CollectType.GetActionType())
r.Params.Set("pg", "3")
err := util.ApiTest(&r)
// 首先核对接口返回值类型
if err == nil {
// 如果返回值类型为Json则执行Json序列化
if s.ResultModel == system.JsonResult {
var dp = collect.FilmDetailLPage{}
if err = json.Unmarshal(r.Resp, &dp); err != nil {
return errors.New(fmt.Sprint("测试失败, 返回数据异常, JSON序列化失败: ", err))
}
return nil
} else if s.ResultModel == system.XmlResult {
// 如果返回值类型为XML则执行XML序列化
var rd = collect.RssD{}
if err = xml.Unmarshal(r.Resp, &rd); err != nil {
return errors.New(fmt.Sprint("测试失败, 返回数据异常, XML序列化失败", err))
}
return nil
}
return errors.New("测试失败, 接口返回值类型不符合规范")
}
return errors.New(fmt.Sprint("测试失败, 请求响应异常 : ", err.Error()))
}

View File

@@ -3,7 +3,6 @@ package spider
import (
"encoding/json"
"errors"
"fmt"
"log"
"server/model/collect"
"server/model/system"
@@ -20,8 +19,8 @@ type FilmCollect interface {
GetCategoryTree(r util.RequestInfo) (*system.CategoryTree, error)
// GetPageCount 获取API接口的分页页数
GetPageCount(r util.RequestInfo) (count int, err error)
// GetDetail 获取指定pageNumber的具体数据
GetDetail(pageNumber int, r util.RequestInfo) (list []system.MovieDetail, err error)
// GetFilmDetail 获取影片详情信息,返回影片详情列表
GetFilmDetail(r util.RequestInfo) (list []system.MovieDetail, err error)
}
// ------------------------------------------------- JSON Collect -------------------------------------------------
@@ -55,7 +54,7 @@ func (jc *JsonCollect) GetCategoryTree(r util.RequestInfo) (*system.CategoryTree
return tree, err
}
// GetPageCount 获取总页数
// GetPageCount 获取分页总页数
func (jc *JsonCollect) GetPageCount(r util.RequestInfo) (count int, err error) {
// 发送请求获取pageCount, 默认为获取 ac = detail
if len(r.Params.Get("ac")) <= 0 {
@@ -78,43 +77,6 @@ func (jc *JsonCollect) GetPageCount(r util.RequestInfo) (count int, err error) {
return
}
// GetDetail 处理详情接口请求返回的数据
func (jc *JsonCollect) GetDetail(pageNumber int, r util.RequestInfo) (list []system.MovieDetail, err error) {
// 防止json解析异常引发panic
defer func() {
if e := recover(); e != nil {
log.Println("GetMovieDetail Failed : ", e)
}
}()
// 设置分页请求参数
r.Params.Set(`ac`, `detail`)
r.Params.Set(`pg`, fmt.Sprint(pageNumber))
util.ApiGet(&r)
// 影视详情信息
detailPage := collect.FilmDetailLPage{}
//details := system.DetailListInfo{}
// 如果返回数据为空则直接结束本次循环
if len(r.Resp) <= 0 {
err = errors.New("response is empty")
return
}
// 序列化详情数据
if err = json.Unmarshal(r.Resp, &detailPage); err != nil {
return
}
// 将影视原始详情信息保存到redis中
// 获取主站点uri
mc := system.GetCollectSourceListByGrade(system.MasterCollect)[0]
if mc.Uri == r.Uri {
collect.BatchSaveOriginalDetail(detailPage.List)
}
// 处理details信息
list = conver.ConvertFilmDetails(detailPage.List)
return
}
// GetFilmDetail 通过 RequestInfo 获取并解析出对应的 MovieDetail list
func (jc *JsonCollect) GetFilmDetail(r util.RequestInfo) (list []system.MovieDetail, err error) {
// 防止json解析异常引发panic
@@ -141,10 +103,10 @@ func (jc *JsonCollect) GetFilmDetail(r util.RequestInfo) (list []system.MovieDet
// 将影视原始详情信息保存到redis中
// 获取主站点uri
mc := system.GetCollectSourceListByGrade(system.MasterCollect)[0]
if mc.Uri == r.Uri {
collect.BatchSaveOriginalDetail(detailPage.List)
}
//mc := system.GetCollectSourceListByGrade(system.MasterCollect)[0]
//if mc.Uri == r.Uri {
// collect.BatchSaveOriginalDetail(detailPage.List)
//}
// 处理details信息
list = conver.ConvertFilmDetails(detailPage.List)

View File

@@ -14,42 +14,7 @@ var (
CronCollect *cron.Cron = CreateCron()
)
// RegularUpdateMovie 定时更新, 每半小时获取一次站点的最近x小时数据
func RegularUpdateMovie() {
//创建一个定时任务对象
c := cron.New(cron.WithSeconds())
// 添加定时任务每x 分钟更新一次最近x小时的影片数据
taskId, err := c.AddFunc(config.CornMovieUpdate, func() {
// 执行更新最近x小时影片的Spider
log.Println("执行一次影片更新任务...")
UpdateMovieDetail()
// 执行更新任务后清理redis中的相关API接口数据缓存
clearCache()
})
// 开启定时任务每月最后一天凌晨两点, 执行一次清库重取数据
taskId2, err := c.AddFunc(config.CornUpdateAll, func() {
StartSpiderRe()
})
if err != nil {
log.Println("Corn Start Error: ", err)
}
log.Println(taskId, "------", taskId2)
log.Printf("%v", c.Entries())
//c.Start()
}
// StartCrontab 启动定时任务
func StartCrontab() {
// 从redis中读取待启动的定时任务列表
// 影片更新定时任务列表
CronCollect.Start()
}
// CreateCron 创建定时任务
func CreateCron() *cron.Cron {
return cron.New(cron.WithSeconds())
}

View File

@@ -1,275 +0,0 @@
package spider
import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"log"
"net/url"
"server/config"
"server/model/collect"
"server/model/system"
"server/plugin/common/util"
"time"
)
/*
舍弃第一版的数据处理思路, v2版本
直接分页获取采集站点的影片详情信息
*/
/*
1. 选择一个采集主站点, mysql检索表中只存储主站点检索的信息
2. 采集多个站点数据
2.1 主站点的采集数据完整地保存相关信息, basicInfo movieDetail search 等信息
2.2 其余站点数据只存储 name(影片名称), playUrl(播放url), 存储形式 Key<hash(name)>:value([]MovieUrlInfo)
3. api数据格式不变, 获取影片详情时通过subTitle 去redis匹配其他站点的对应播放源并整合到主站详情信息的playUrl中
4. 影片搜索时不再使用name进行匹配, 改为使用 subTitle 进行匹配
*/
// StartSpider 执行多源spider
func StartSpider() {
// 保存分类树
CategoryList()
log.Println("CategoryList 影片分类信息保存完毕")
// 爬取主站点数据
MainSiteSpider()
log.Println("MainSiteSpider 主站点影片信息保存完毕")
// 查找并创建search数据库, 保存search信息, 添加索引
time.Sleep(time.Second * 10)
system.SyncSearchInfo(0)
system.AddSearchIndex()
log.Println("SearchInfoToMdb 影片检索信息保存完毕")
//获取其他站点数据
scl := system.GetCollectSourceListByGrade(system.SlaveCollect)
go MtSiteSpider(scl...)
log.Println("Spider End , 数据保存执行完成")
time.Sleep(time.Second * 10)
}
// CategoryList 获取分类数据
func CategoryList() {
// 获取主站点uri
mc := system.GetCollectSourceListByGrade(system.MasterCollect)[0]
// 获取分类树形数据
categoryTree, err := spiderCore.GetCategoryTree(util.RequestInfo{Uri: mc.Uri, Params: url.Values{}})
if err != nil {
log.Println("GetCategoryTree Error: ", err)
return
}
// 保存 tree 到redis
err = system.SaveCategoryTree(categoryTree)
if err != nil {
log.Println("SaveCategoryTree Error: ", err)
}
}
// MainSiteSpider 主站点数据处理
func MainSiteSpider() {
// 获取主站点uri
mc := system.GetCollectSourceListByGrade(system.MasterCollect)[0]
// 获取分页页数
pageCount, err := spiderCore.GetPageCount(util.RequestInfo{Uri: mc.Uri, Params: url.Values{}})
// 主站点分页出错直接终止程序
if err != nil {
panic(err)
}
// 开启协程加快分页请求速度
ch := make(chan int, pageCount)
waitCh := make(chan int)
for i := 1; i <= pageCount; i++ {
ch <- i
}
close(ch)
for i := 0; i < config.MAXGoroutine; i++ {
go func() {
defer func() { waitCh <- 0 }()
for {
pg, ok := <-ch
if !ok {
break
}
list, e := spiderCore.GetDetail(pg, util.RequestInfo{Uri: mc.Uri, Params: url.Values{}})
if e != nil {
log.Println("GetMovieDetail Error: ", err)
continue
}
// 保存影片详情信息到redis
if err = system.SaveDetails(list); err != nil {
log.Println("SaveDetails Error: ", err)
}
}
}()
}
for i := 0; i < config.MAXGoroutine; i++ {
<-waitCh
}
}
// MtSiteSpider 附属站点数据源处理
func MtSiteSpider(scl ...system.FilmSource) {
for _, s := range scl {
// 执行每个站点的播放url缓存
PlayDetailSpider(s)
log.Println(s.Name, "playUrl 爬取完毕!!!")
}
}
// PlayDetailSpider SpiderSimpleInfo 获取单个站点的播放源
func PlayDetailSpider(s system.FilmSource) {
// 获取分页页数
pageCount, err := spiderCore.GetPageCount(util.RequestInfo{Uri: s.Uri, Params: url.Values{}})
// 出错直接终止当前站点数据获取
if err != nil {
log.Println(err)
return
}
// 开启协程加快分页请求速度
ch := make(chan int, pageCount)
waitCh := make(chan int)
for i := 1; i <= pageCount; i++ {
ch <- i
}
close(ch)
for i := 0; i < config.MAXGoroutine; i++ {
go func() {
defer func() { waitCh <- 0 }()
for {
pg, ok := <-ch
if !ok {
break
}
list, e := spiderCore.GetDetail(pg, util.RequestInfo{Uri: s.Uri, Params: url.Values{}})
if e != nil || len(list) <= 0 {
log.Println("GetMovieDetail Error: ", err)
continue
}
// 保存影片播放信息到redis
if err = system.SaveSitePlayList(s.Name, list); err != nil {
log.Println("SaveDetails Error: ", err)
}
}
}()
}
for i := 0; i < config.MAXGoroutine; i++ {
<-waitCh
}
}
// UpdateMovieDetail 定时更新主站点和其余播放源信息
func UpdateMovieDetail() {
// 更新主站系列信息
UpdateMainDetail()
// 更新附属播放源数据信息
scl := system.GetCollectSourceListByGrade(system.SlaveCollect)
UpdatePlayDetail(scl...)
}
// UpdateMainDetail 更新主站点的最新影片
func UpdateMainDetail() {
// 获取主站点uri
l := system.GetCollectSourceListByGrade(system.MasterCollect)
mc := system.FilmSource{}
for _, v := range l {
if len(v.Uri) > 0 {
mc = v
break
}
}
// 获取分页页数
r := util.RequestInfo{Uri: mc.Uri, Params: url.Values{}}
r.Params.Set("h", config.UpdateInterval)
pageCount, err := spiderCore.GetPageCount(r)
if err != nil {
log.Printf("Update MianStieDetail failed\n")
}
// 保存本次更新的所有详情信息
var ds []system.MovieDetail
// 获取分页数据
for i := 1; i <= pageCount; i++ {
list, err := spiderCore.GetDetail(i, r)
if err != nil {
continue
}
// 保存更新的影片信息, 同类型直接覆盖
if err = system.SaveDetails(list); err != nil {
log.Println("Update MainSiteDetail failed, SaveDetails Error ")
}
ds = append(ds, list...)
}
// 整合详情信息切片
var sl []system.SearchInfo
for _, d := range ds {
// 通过id 获取对应的详情信息
sl = append(sl, system.ConvertSearchInfo(d))
}
// 调用批量保存或更新方法, 如果对应mid数据存在则更新, 否则执行插入
system.BatchSaveOrUpdate(sl)
}
// UpdatePlayDetail 更新最x小时的影片播放源数据
func UpdatePlayDetail(scl ...system.FilmSource) {
for _, s := range scl {
// 获取单个站点的分页数
r := util.RequestInfo{Uri: s.Uri, Params: url.Values{}}
r.Params.Set("h", config.UpdateInterval)
pageCount, err := spiderCore.GetPageCount(r)
if err != nil {
log.Printf("Update %s playDetail failed\n", s.Name)
}
for i := 1; i <= pageCount; i++ {
// 获取详情信息, 保存到对应hashKey中
list, e := spiderCore.GetDetail(i, r)
if e != nil || len(list) <= 0 {
log.Println("GetMovieDetail Error: ", err)
continue
}
// 保存影片播放信息到redis
if err = system.SaveSitePlayList(s.Name, list); err != nil {
log.Println("SaveDetails Error: ", err)
}
}
}
}
// StartSpiderRe 清空存储数据,从零开始获取
func StartSpiderRe() {
// 删除已有的存储数据, redis 和 mysql中的存储数据全部清空
system.FilmZero()
// 执行完整数据获取
StartSpider()
}
// =========================公共方法==============================
// CollectApiTest 测试采集接口是否可用
func CollectApiTest(s system.FilmSource) error {
// 使用当前采集站接口采集一页数据
r := util.RequestInfo{Uri: s.Uri, Params: url.Values{}}
r.Params.Set("ac", s.CollectType.GetActionType())
r.Params.Set("pg", "3")
err := util.ApiTest(&r)
// 首先核对接口返回值类型
if err == nil {
// 如果返回值类型为Json则执行Json序列化
if s.ResultModel == system.JsonResult {
var dp = collect.FilmDetailLPage{}
if err = json.Unmarshal(r.Resp, &dp); err != nil {
return errors.New(fmt.Sprint("测试失败, 返回数据异常, JSON序列化失败: ", err))
}
return nil
} else if s.ResultModel == system.XmlResult {
// 如果返回值类型为XML则执行XML序列化
var rd = collect.RssD{}
if err = xml.Unmarshal(r.Resp, &rd); err != nil {
return errors.New(fmt.Sprint("测试失败, 返回数据异常, XML序列化失败", err))
}
return nil
}
return errors.New("测试失败, 接口返回值类型不符合规范")
}
return errors.New(fmt.Sprint("测试失败, 请求响应异常 : ", err.Error()))
}

View File

@@ -17,6 +17,7 @@ func SetupRouter() *gin.Engine {
r.Static(config.FilmPictureUrlPath, config.FilmPictureUploadDir)
r.GET(`/index`, controller.Index)
r.GET(`/config/basic`, controller.SiteBasicConfig)
r.GET(`/navCategory`, controller.CategoriesInfo)
r.GET(`/filmDetail`, controller.FilmDetail)
r.GET(`/filmPlayInfo`, controller.FilmPlayInfo)