Files
GoFilm/server/model/system/Movies.go
2023-12-24 23:22:20 +08:00

321 lines
11 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package system
import (
"encoding/json"
"fmt"
"github.com/redis/go-redis/v9"
"hash/fnv"
"regexp"
"server/config"
"server/plugin/db"
"strconv"
"strings"
"time"
)
// Movie 影片基本信息
type Movie struct {
Id int64 `json:"id"` // 影片ID
Name string `json:"name"` // 影片名
Cid int64 `json:"cid"` // 所属分类ID
CName string `json:"CName"` // 所属分类名称
EnName string `json:"enName"` // 英文片名
Time string `json:"time"` // 更新时间
Remarks string `json:"remarks"` // 备注 | 清晰度
PlayFrom string `json:"playFrom"` // 播放来源
}
// MovieDescriptor 影片详情介绍信息
type MovieDescriptor struct {
SubTitle string `json:"subTitle"` //子标题
CName string `json:"cName"` //分类名称
EnName string `json:"enName"` //英文名
Initial string `json:"initial"` //首字母
ClassTag string `json:"classTag"` //分类标签
Actor string `json:"actor"` //主演
Director string `json:"director"` //导演
Writer string `json:"writer"` //作者
Blurb string `json:"blurb"` //简介, 残缺,不建议使用
Remarks string `json:"remarks"` // 更新情况
ReleaseDate string `json:"releaseDate"` //上映时间
Area string `json:"area"` // 地区
Language string `json:"language"` //语言
Year string `json:"year"` //年份
State string `json:"state"` //影片状态 正片|预告...
UpdateTime string `json:"updateTime"` //更新时间
AddTime int64 `json:"addTime"` //资源添加时间戳
DbId int64 `json:"dbId"` //豆瓣id
DbScore string `json:"dbScore"` // 豆瓣评分
Hits int64 `json:"hits"` //影片热度
Content string `json:"content"` //内容简介
}
// MovieBasicInfo 影片基本信息
type MovieBasicInfo struct {
Id int64 `json:"id"` //影片Id
Cid int64 `json:"cid"` //分类ID
Pid int64 `json:"pid"` //一级分类ID
Name string `json:"name"` //片名
SubTitle string `json:"subTitle"` //子标题
CName string `json:"cName"` //分类名称
State string `json:"state"` //影片状态 正片|预告...
Picture string `json:"picture"` //简介图片
Actor string `json:"actor"` //主演
Director string `json:"director"` //导演
Blurb string `json:"blurb"` //简介, 不完整
Remarks string `json:"remarks"` // 更新情况
Area string `json:"area"` // 地区
Year string `json:"year"` //年份
}
// MovieUrlInfo 影视资源url信息
type MovieUrlInfo struct {
Episode string `json:"episode"` // 集数
Link string `json:"link"` // 播放地址
}
// MovieDetail 影片详情信息
type MovieDetail struct {
Id int64 `json:"id"` //影片Id
Cid int64 `json:"cid"` //分类ID
Pid int64 `json:"pid"` //一级分类ID
Name string `json:"name"` //片名
Picture string `json:"picture"` //简介图片
PlayFrom []string `json:"playFrom"` // 播放来源
DownFrom string `json:"DownFrom"` //下载来源 例: http
//PlaySeparator string `json:"playSeparator"` // 播放信息分隔符
PlayList [][]MovieUrlInfo `json:"playList"` //播放地址url
DownloadList [][]MovieUrlInfo `json:"downloadList"` // 下载url地址
MovieDescriptor `json:"descriptor"` //影片描述信息
}
// ===================================Redis数据交互========================================================
// SaveDetails 保存影片详情信息到redis中 格式: MovieDetail:Cid?:Id?
func SaveDetails(list []MovieDetail) (err error) {
// 遍历list中的信息
for _, detail := range list {
// 序列化影片详情信息
data, _ := json.Marshal(detail)
// 1. 原使用Zset存储, 但是不便于单个检索 db.Rdb.ZAdd(db.Cxt, fmt.Sprintf("%s:Cid%d", config.MovieDetailKey, detail.Cid), redis.Z{Score: float64(detail.Id), Member: member}).Err()
// 改为普通 k v 存储, k-> id关键字, v json序列化的结果
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.CategoryTreeExpired).Err()
// 2. 同步保存简略信息到redis中
SaveMovieBasicInfo(detail)
// 3. 保存 Search tag redis中
if err == nil {
// 转换 detail信息
searchInfo := ConvertSearchInfo(detail)
// 只存储用于检索对应影片的关键字信息
SaveSearchTag(searchInfo)
}
}
// 保存一份search信息到mysql, 批量存储
BatchSaveSearchInfo(list)
return err
}
// SaveDetail 保存单部影片信息
func SaveDetail(detail MovieDetail) (err error) {
// 序列化影片详情信息
data, _ := json.Marshal(detail)
// 保存影片信息到Redis
err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.CategoryTreeExpired).Err()
if err != nil {
return err
}
// 2. 同步保存简略信息到redis中
SaveMovieBasicInfo(detail)
// 转换 detail信息
searchInfo := ConvertSearchInfo(detail)
// 3. 保存 Search tag redis中
// 只存储用于检索对应影片的关键字信息
SaveSearchTag(searchInfo)
// 保存影片检索信息到searchTable
err = SaveSearchInfo(searchInfo)
return err
}
// SaveMovieBasicInfo 摘取影片的详情部分信息转存为影视基本信息
func SaveMovieBasicInfo(detail MovieDetail) {
basicInfo := MovieBasicInfo{
Id: detail.Id,
Cid: detail.Cid,
Pid: detail.Pid,
Name: detail.Name,
SubTitle: detail.SubTitle,
CName: detail.CName,
State: detail.State,
Picture: detail.Picture,
Actor: detail.Actor,
Director: detail.Director,
Blurb: detail.Blurb,
Remarks: detail.Remarks,
Area: detail.Area,
Year: detail.Year,
}
data, _ := json.Marshal(basicInfo)
_ = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, detail.Cid, detail.Id), data, config.CategoryTreeExpired).Err()
}
// SaveSitePlayList 仅保存播放url列表信息到当前站点
func SaveSitePlayList(id string, list []MovieDetail) (err error) {
// 如果list 为空则直接返回
if len(list) <= 0 {
return nil
}
res := make(map[string]string)
for _, d := range list {
if len(d.PlayList) > 0 {
data, _ := json.Marshal(d.PlayList[0])
// 不保存电影解说类
if strings.Contains(d.CName, "解说") {
continue
}
// 如果DbId不为0, 则以dbID作为key进行hash额外存储一次
if d.DbId != 0 {
res[GenerateHashKey(d.DbId)] = string(data)
}
res[GenerateHashKey(d.Name)] = string(data)
}
}
// 如果结果不为空,则将数据保存到redis中
if len(res) > 0 {
// 保存形式 key: MultipleSource:siteName Hash[hash(movieName)]list
err = db.Rdb.HMSet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, id), res).Err()
}
return
}
// BatchSaveSearchInfo 批量保存Search信息
func BatchSaveSearchInfo(list []MovieDetail) {
var infoList []SearchInfo
for _, v := range list {
infoList = append(infoList, ConvertSearchInfo(v))
}
// 将检索信息存入redis中做一次转存
RdbSaveSearchInfo(infoList)
}
// ConvertSearchInfo 将detail信息处理成 searchInfo
func ConvertSearchInfo(detail MovieDetail) SearchInfo {
score, _ := strconv.ParseFloat(detail.DbScore, 64)
stamp, _ := time.ParseInLocation(time.DateTime, detail.UpdateTime, time.Local)
// detail中的年份信息并不准确, 因此采用 ReleaseDate中的年份
year, err := strconv.ParseInt(regexp.MustCompile(`[1-9][0-9]{3}`).FindString(detail.ReleaseDate), 10, 64)
if err != nil {
year = 0
}
return SearchInfo{
Mid: detail.Id,
Cid: detail.Cid,
Pid: detail.Pid,
Name: detail.Name,
SubTitle: detail.SubTitle,
CName: detail.CName,
ClassTag: detail.ClassTag,
Area: detail.Area,
Language: detail.Language,
Year: year,
Initial: detail.Initial,
Score: score,
Hits: detail.Hits,
UpdateStamp: stamp.Unix(),
State: detail.State,
Remarks: detail.Remarks,
// ReleaseDate 部分影片缺失该参数, 所以使用添加时间作为上映时间排序
ReleaseStamp: detail.AddTime,
}
}
// GetBasicInfoByKey 获取Id对应的影片基本信息
func GetBasicInfoByKey(key string) MovieBasicInfo {
// 反序列化得到的结果
data := []byte(db.Rdb.Get(db.Cxt, key).Val())
basic := MovieBasicInfo{}
_ = json.Unmarshal(data, &basic)
// 执行本地图片匹配
ReplaceBasicDetailPic(&basic)
return basic
}
// GetDetailByKey 获取影片对应的详情信息
func GetDetailByKey(key string) MovieDetail {
// 反序列化得到的结果
data := []byte(db.Rdb.Get(db.Cxt, key).Val())
detail := MovieDetail{}
_ = json.Unmarshal(data, &detail)
// 执行本地图片匹配
ReplaceDetailPic(&detail)
return detail
}
// GetBasicInfoBySearchInfos 通过searchInfo 获取影片的基本信息
func GetBasicInfoBySearchInfos(infos ...SearchInfo) []MovieBasicInfo {
var list []MovieBasicInfo
for _, s := range infos {
data := []byte(db.Rdb.Get(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, s.Cid, s.Mid)).Val())
basic := MovieBasicInfo{}
_ = json.Unmarshal(data, &basic)
// 执行本地图片匹配
ReplaceBasicDetailPic(&basic)
list = append(list, basic)
}
return list
}
/*
对附属播放源入库时的name|dbID进行处理,保证唯一性
1. 去除name中的所有空格
2. 去除name中含有的别名.*
3. 去除name首尾的标点符号
4. 将处理完成后的name转化为hash值作为存储时的key
*/
// GenerateHashKey 存储播放源信息时对影片名称进行处理, 提高各站点间同一影片的匹配度
func GenerateHashKey[K string | ~int | int64](key K) string {
mName := fmt.Sprint(key)
//1. 去除name中的所有空格
mName = regexp.MustCompile(`\s`).ReplaceAllString(mName, "")
//2. 去除name中含有的别名.*
mName = regexp.MustCompile(`.*$`).ReplaceAllString(mName, "")
//3. 去除name首尾的标点符号
mName = regexp.MustCompile(`^[[:punct:]]+|[[:punct:]]+$`).ReplaceAllString(mName, "")
// 部分站点包含 动画版, 特殊别名 等字符, 需进行删除
//mName = regexp.MustCompile(`动画版`).ReplaceAllString(mName, "")
mName = regexp.MustCompile(`季.*`).ReplaceAllString(mName, "季")
//4. 将处理完成后的name转化为hash值作为存储时的key
h := fnv.New32a()
_, err := h.Write([]byte(mName))
if err != nil {
return ""
}
return fmt.Sprint(h.Sum32())
}
// ============================采集方案.v1 遗留==================================================
// SaveMoves 保存影片分页请求list
func SaveMoves(list []Movie) (err error) {
// 整合数据
for _, m := range list {
//score, _ := time.ParseInLocation(time.DateTime, m.Time, time.Local)
movie, _ := json.Marshal(m)
// 以Cid为目录为集合进行存储, 便于后续搜索, 以影片id为分值进行存储 例 MovieList:Cid%d
err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf(config.MovieListInfoKey, m.Cid), redis.Z{Score: float64(m.Id), Member: movie}).Err()
}
return err
}
// AllMovieInfoKey 获取redis中所有的影视列表信息key MovieList:Cid
func AllMovieInfoKey() []string {
return db.Rdb.Keys(db.Cxt, fmt.Sprint("MovieList:Cid*")).Val()
}
// GetMovieListByKey 获取指定分类的影片列表数据
func GetMovieListByKey(key string) []string {
return db.Rdb.ZRange(db.Cxt, key, 0, -1).Val()
}