413 lines
15 KiB
Go
Executable File
413 lines
15 KiB
Go
Executable File
package mediacontentser
|
|
|
|
import (
|
|
"91porn-server/app/appg"
|
|
"91porn-server/common"
|
|
"91porn-server/common/log"
|
|
topser "91porn-server/common/top"
|
|
topasist "91porn-server/common/top/asistant"
|
|
"91porn-server/models/v/media_buy_record_mod"
|
|
"91porn-server/models/v/mediamod"
|
|
"91porn-server/models/v/usermod"
|
|
"91porn-server/models/v/walletmod"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"91porn-server/models/cache/mediacontentdata"
|
|
"91porn-server/models/v/mediacontentmod"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
)
|
|
|
|
// MediaContentInfo 移动端返回内容
|
|
type MediaContentInfo struct {
|
|
ID primitive.ObjectID `json:"id"` // 文档id
|
|
MediaID primitive.ObjectID `json:"mediaId"` // 媒体资源ID
|
|
MediaType string `json:"mediaType"` // 媒体类型
|
|
EpisodeNumber int `json:"episodeNumber"` // 第几集
|
|
ListenPermission int `json:"listenPermission"` // 收听权限 0:会员 1:金币购买 2:免费
|
|
Price int64 `json:"price"` // 购买价格
|
|
Name string `json:"name"` // 章节名
|
|
Cover string `json:"cover"` // 封面
|
|
Text string `json:"text"` // 内容(小说专用)
|
|
Md5 string `json:"md5"` // 媒体md5
|
|
VideoUrl string `json:"videoUrl"` // 视频地址
|
|
H265Url string `json:"h265Url"` // H.265 视频地址
|
|
AudioUrl string `json:"audioUrl"` // 音频地址
|
|
UrlSet []string `json:"urlSet"` // 地址集(多个资源地址用 例如漫画)
|
|
Height int `json:"height"` // 高
|
|
Weight int `json:"weight"` // 宽
|
|
MediaSize int64 `json:"mediaSize"` // 资源大小
|
|
PlayTime uint `json:"playTime"` // 影片长度
|
|
IsActive bool `json:"isActive"` // 是否激活
|
|
Ratio float64 `json:"ratio"` // 宽高比
|
|
CreatedAt time.Time `json:"createdAt"` // 文档创建时间
|
|
UpdateTime time.Time `json:"updateTime"` // 文档更新时间
|
|
HasBuy bool `json:"hasBuy"` // 是否已购买
|
|
IsFree bool `json:"isFree"`
|
|
HasDramaCard bool `json:"hasDramaCard"`
|
|
CanPlay bool `json:"canPlay"`
|
|
AccessType string `json:"accessType"`
|
|
PreviewEnabled bool `json:"previewEnabled"`
|
|
PreviewStart int `json:"previewStart"`
|
|
PreviewSeconds int `json:"previewSeconds"`
|
|
PreviewVideoUrl string `json:"previewVideoUrl"`
|
|
PreviewH265Url string `json:"previewH265Url"`
|
|
Prev *OtherContent `json:"prev"` // 上一集
|
|
Next *OtherContent `json:"next"` // 下一集
|
|
Paywall *PaywallInfo `json:"paywall"`
|
|
}
|
|
|
|
type OtherContent struct {
|
|
Id primitive.ObjectID `json:"id"`
|
|
EpisodeNumber int `json:"episodeNumber"`
|
|
Name string `json:"name"`
|
|
Cover string `json:"cover"`
|
|
}
|
|
|
|
type PaywallInfo struct {
|
|
CheckoutContextID string `json:"checkoutContextId"`
|
|
Title string `json:"title"`
|
|
CoinBalance int64 `json:"coinBalance"`
|
|
UnlockCoin int64 `json:"unlockCoin"`
|
|
CoinButtonText string `json:"coinButtonText"`
|
|
CardButtonText string `json:"cardButtonText"`
|
|
}
|
|
|
|
type AppQueryListReq struct {
|
|
PageNumber uint64 `form:"pageNumber" json:"pageNumber" binding:"required,min=1"`
|
|
PageSize uint64 `form:"pageSize" json:"pageSize" binding:"required,min=1,max=200"`
|
|
MediaId string `json:"mediaId" form:"mediaId" binding:"required"`
|
|
SortType int `json:"sortType" form:"sortType" binding:"omitempty,oneof=0 1"` // 排序类型 0-正序 1-倒序
|
|
}
|
|
|
|
func (p *AppQueryListReq) Skip() uint64 { return (p.PageNumber - 1) * p.PageSize }
|
|
func (p *AppQueryListReq) Limit() uint64 { return p.PageSize }
|
|
|
|
type AppListRes struct {
|
|
Total int64 `json:"total"`
|
|
HasNext bool `json:"hasNext"`
|
|
List []AppMediaContent `json:"list"`
|
|
}
|
|
|
|
type AppMediaContent struct {
|
|
ID primitive.ObjectID `json:"id"` // 文档id
|
|
MediaID primitive.ObjectID `json:"mediaId"` // 媒体资源ID
|
|
MediaType string `json:"mediaType"` // 媒体类型
|
|
EpisodeNumber int `json:"episodeNumber"` // 第几集
|
|
ListenPermission int `json:"listenPermission"` // 收听权限 0:会员 1:金币购买 2:免费
|
|
VideoUrl string `json:"videoUrl"` // 视频地址
|
|
H265Url string `json:"h265Url"` // H.265 视频地址
|
|
AudioUrl string `json:"audioUrl"` // 音频地址
|
|
PlayTime uint `json:"playTime"` // 播放时长
|
|
Price int64 `json:"price"` // 购买价格
|
|
Name string `json:"name"` // 章节名
|
|
Cover string `json:"cover"` // 封面
|
|
HasBuy bool `json:"hasBuy"` // 是否已购买
|
|
IsFree bool `json:"isFree"`
|
|
HasDramaCard bool `json:"hasDramaCard"`
|
|
CanPlay bool `json:"canPlay"`
|
|
AccessType string `json:"accessType"`
|
|
PreviewEnabled bool `json:"previewEnabled"`
|
|
PreviewStart int `json:"previewStart"`
|
|
PreviewSeconds int `json:"previewSeconds"`
|
|
PreviewVideoUrl string `json:"previewVideoUrl"`
|
|
PreviewH265Url string `json:"previewH265Url"`
|
|
Ratio float64 `json:"ratio"`
|
|
CreatedAt time.Time `json:"createdAt"` // 创建时间
|
|
UpdateTime time.Time `json:"updateTime"`
|
|
}
|
|
|
|
const (
|
|
dramaAccessFree = "free"
|
|
dramaAccessBought = "bought"
|
|
dramaAccessCard = "card"
|
|
dramaAccessCoin = "coin"
|
|
)
|
|
|
|
func ActiveDramaCard(uid uint64) bool {
|
|
ok, err := ActiveDramaCardStatus(uid)
|
|
return err == nil && ok
|
|
}
|
|
|
|
func ActiveDramaCardStatus(uid uint64) (bool, error) {
|
|
user, err := usermod.FindUserByUID(uid)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return user.DramaExpire.After(time.Now()), nil
|
|
}
|
|
|
|
func ResolveDramaAccess(
|
|
item mediacontentmod.MediaContent,
|
|
media mediamod.Media,
|
|
hasBuy, hasCard bool,
|
|
) (isFree, canPlay bool, accessType string) {
|
|
isFree = item.ListenPermission == 2 || item.Price <= 0 ||
|
|
(media.FreeEpisode > 0 && item.EpisodeNumber <= media.FreeEpisode)
|
|
switch {
|
|
case isFree:
|
|
return true, true, dramaAccessFree
|
|
case hasBuy:
|
|
return false, true, dramaAccessBought
|
|
case hasCard:
|
|
return false, true, dramaAccessCard
|
|
default:
|
|
return false, false, dramaAccessCoin
|
|
}
|
|
}
|
|
|
|
func newPaywall(uid uint64, price int64) *PaywallInfo {
|
|
balance := int64(0)
|
|
if wallet, err := walletmod.GetWallet(uid); err == nil && wallet != nil {
|
|
balance = wallet.Amount + wallet.Income
|
|
}
|
|
return &PaywallInfo{
|
|
CheckoutContextID: "drama-checkout-" + primitive.NewObjectID().Hex(),
|
|
Title: "更多精彩解锁即享",
|
|
CoinBalance: balance,
|
|
UnlockCoin: price,
|
|
CoinButtonText: fmt.Sprintf("%d金币解锁", price),
|
|
CardButtonText: "开通短剧卡免费看",
|
|
}
|
|
}
|
|
|
|
// GetList 获取列表
|
|
func (p *AppQueryListReq) GetList(uid uint64) (res AppListRes, err error) {
|
|
mediaId, err := primitive.ObjectIDFromHex(p.MediaId)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
media, err := mediamod.GetInfo(mediaId)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
filter := bson.M{"mediaId": mediaId, "isActive": true, "isDelete": false}
|
|
if media.MediaType == mediamod.MediaTypeDrama {
|
|
filter["mediaType"] = mediamod.MediaTypeDrama
|
|
if media.Status != 1 || media.IsDelete {
|
|
return res, fmt.Errorf("drama is inactive")
|
|
}
|
|
}
|
|
|
|
sort := bson.D{{Key: "episodeNumber", Value: 1}}
|
|
if p.SortType == 1 {
|
|
sort = bson.D{{Key: "episodeNumber", Value: -1}}
|
|
}
|
|
// 获取列表
|
|
var data []mediacontentmod.MediaContent
|
|
data, res.Total, res.HasNext, err = mediacontentdata.GetListFromCache(filter, int64(p.Skip()), int64(p.Limit()), sort)
|
|
if err != nil {
|
|
log.Error("AppQueryListReq.GetList mediacontentdata.GetListFromCache fail", log.Any("req", p), log.E(err))
|
|
return res, err
|
|
}
|
|
contentIds := []primitive.ObjectID{}
|
|
// 获取是否已经购买
|
|
for _, item := range data {
|
|
contentIds = append(contentIds, item.ID)
|
|
}
|
|
buyMap, isWholeBuy, err := media_buy_record_mod.IsBuyBatch(uid, mediaId, contentIds)
|
|
if err != nil {
|
|
log.Error("AppQueryListReq.GetList media_buy_record_mod.IsBuyBatch fail", log.Any("req", p), log.E(err))
|
|
return res, err
|
|
}
|
|
hasCard := false
|
|
if media.MediaType == mediamod.MediaTypeDrama {
|
|
hasCard, err = ActiveDramaCardStatus(uid)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
}
|
|
// 组装返回数据
|
|
for _, item := range data {
|
|
tmp := AppMediaContent{
|
|
ID: item.ID,
|
|
MediaID: item.MediaID,
|
|
MediaType: item.MediaType,
|
|
EpisodeNumber: item.EpisodeNumber,
|
|
ListenPermission: item.ListenPermission,
|
|
Price: item.Price,
|
|
Name: item.Name,
|
|
PlayTime: item.PlayTime,
|
|
Cover: item.Cover,
|
|
AudioUrl: item.AudioUrl,
|
|
VideoUrl: item.VideoUrl,
|
|
H265Url: appg.H265URLForApp(item.H265Url),
|
|
HasBuy: buyMap[item.ID],
|
|
Ratio: item.Ratio,
|
|
CreatedAt: item.CreatedAt,
|
|
UpdateTime: item.UpdateTime,
|
|
}
|
|
if strings.TrimSpace(tmp.Name) == "" {
|
|
tmp.Name = fmt.Sprintf("第%v集", item.EpisodeNumber)
|
|
}
|
|
// 是否整本购买
|
|
if isWholeBuy {
|
|
tmp.HasBuy = true
|
|
}
|
|
if item.MediaType == mediamod.MediaTypeDrama {
|
|
tmp.HasDramaCard = hasCard
|
|
tmp.IsFree, tmp.CanPlay, tmp.AccessType = ResolveDramaAccess(item, media, tmp.HasBuy, hasCard)
|
|
tmp.PreviewEnabled = item.PreviewEnabled
|
|
tmp.PreviewStart = item.PreviewStart
|
|
tmp.PreviewSeconds = item.PreviewSeconds
|
|
if !tmp.CanPlay {
|
|
if tmp.PreviewEnabled {
|
|
tmp.PreviewVideoUrl = tmp.VideoUrl
|
|
tmp.PreviewH265Url = tmp.H265Url
|
|
}
|
|
tmp.AudioUrl = ""
|
|
tmp.VideoUrl = ""
|
|
tmp.H265Url = ""
|
|
}
|
|
}
|
|
if tmp.Cover == "" && len(item.UrlSet) > 0 {
|
|
tmp.Cover = item.UrlSet[0]
|
|
}
|
|
res.List = append(res.List, tmp)
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
type AppQueryInfoReq struct {
|
|
ID string `json:"id" form:"id"` // id
|
|
}
|
|
|
|
// GetInfo 获取详情
|
|
func (p *AppQueryInfoReq) GetInfo(uid uint64) (res MediaContentInfo, err error) {
|
|
oid, err := primitive.ObjectIDFromHex(p.ID)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
|
|
var item mediacontentmod.MediaContent
|
|
item, err = mediacontentdata.GetInfoFromCache(oid)
|
|
if err != nil {
|
|
log.Error("AppQueryInfoReq.GetInfo mediacontentdata.GetInfoFromCache fail", log.Any("mediaContentId", p.ID), log.E(err))
|
|
return
|
|
}
|
|
var media mediamod.Media
|
|
if item.MediaType == mediamod.MediaTypeDrama {
|
|
media, err = mediamod.GetInfo(item.MediaID)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
if media.MediaType != mediamod.MediaTypeDrama || media.Status != 1 || media.IsDelete || !item.IsActive || item.IsDelete {
|
|
return res, fmt.Errorf("drama episode is inactive")
|
|
}
|
|
}
|
|
|
|
// 判断是否已经购买
|
|
hasBuy, err := media_buy_record_mod.IsBuy(uid, item.MediaID, item.ID)
|
|
if err != nil {
|
|
log.Error("AppQueryInfoReq.GetInfo media_buy_record_mod.IsBuy fail", log.Any("mediaContentId", p.ID), log.E(err))
|
|
return
|
|
}
|
|
res = MediaContentInfo{
|
|
ID: item.ID,
|
|
MediaID: item.MediaID,
|
|
MediaType: item.MediaType,
|
|
EpisodeNumber: item.EpisodeNumber,
|
|
ListenPermission: item.ListenPermission,
|
|
Price: item.Price,
|
|
Name: item.Name,
|
|
Cover: item.Cover,
|
|
Text: item.Text,
|
|
Md5: item.Md5,
|
|
VideoUrl: item.VideoUrl,
|
|
H265Url: appg.H265URLForApp(item.H265Url),
|
|
AudioUrl: item.AudioUrl,
|
|
UrlSet: item.UrlSet,
|
|
Height: item.Height,
|
|
Weight: item.Weight,
|
|
MediaSize: item.MediaSize,
|
|
PlayTime: item.PlayTime,
|
|
IsActive: item.IsActive,
|
|
//Ratio: item.Ratio,
|
|
Ratio: 1,
|
|
CreatedAt: item.CreatedAt,
|
|
UpdateTime: item.UpdateTime,
|
|
HasBuy: hasBuy,
|
|
PreviewEnabled: item.PreviewEnabled,
|
|
PreviewStart: item.PreviewStart,
|
|
PreviewSeconds: item.PreviewSeconds,
|
|
}
|
|
if item.MediaType == mediamod.MediaTypeDrama {
|
|
res.HasDramaCard, err = ActiveDramaCardStatus(uid)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
res.IsFree, res.CanPlay, res.AccessType = ResolveDramaAccess(item, media, hasBuy, res.HasDramaCard)
|
|
res.Ratio = item.Ratio
|
|
if !res.CanPlay {
|
|
if res.PreviewEnabled {
|
|
res.PreviewVideoUrl = res.VideoUrl
|
|
res.PreviewH265Url = res.H265Url
|
|
}
|
|
res.VideoUrl = ""
|
|
res.H265Url = ""
|
|
res.AudioUrl = ""
|
|
res.UrlSet = []string{}
|
|
res.Text = ""
|
|
res.Md5 = ""
|
|
res.Paywall = newPaywall(uid, item.Price)
|
|
}
|
|
}
|
|
if strings.TrimSpace(res.Name) == "" {
|
|
res.Name = fmt.Sprintf("第%v集", item.EpisodeNumber)
|
|
}
|
|
if res.Cover == "" && len(item.UrlSet) > 0 {
|
|
res.Cover = item.UrlSet[0]
|
|
}
|
|
EpisodeNumbers := []int{}
|
|
if item.EpisodeNumber-1 > 0 {
|
|
EpisodeNumbers = append(EpisodeNumbers, item.EpisodeNumber-1)
|
|
}
|
|
|
|
EpisodeNumbers = append(EpisodeNumbers, item.EpisodeNumber+1)
|
|
|
|
// 获取上一集下一集
|
|
otherFilter := bson.M{"mediaId": item.MediaID, "episodeNumber": bson.M{"$in": EpisodeNumbers}, "isActive": true, "isDelete": false}
|
|
if item.MediaType == mediamod.MediaTypeDrama {
|
|
otherFilter["mediaType"] = mediamod.MediaTypeDrama
|
|
}
|
|
otherContents, _, _, err := mediacontentdata.GetListFromCache(otherFilter, 0, 2, bson.D{})
|
|
if err != nil {
|
|
log.Error("AppQueryInfoReq.GetInfo mediacontentdata.GetListData fail", log.Any("mediaContentId", p.ID), log.E(err))
|
|
return
|
|
}
|
|
for _, v := range otherContents {
|
|
if v.Cover == "" && len(v.UrlSet) > 0 {
|
|
v.Cover = v.UrlSet[0]
|
|
}
|
|
if v.EpisodeNumber == item.EpisodeNumber-1 {
|
|
// 上一集
|
|
res.Prev = &OtherContent{
|
|
Id: v.ID,
|
|
EpisodeNumber: v.EpisodeNumber,
|
|
Name: v.Name,
|
|
Cover: v.Cover,
|
|
}
|
|
}
|
|
if v.EpisodeNumber == item.EpisodeNumber+1 {
|
|
// 下一集
|
|
res.Next = &OtherContent{
|
|
Id: v.ID,
|
|
EpisodeNumber: v.EpisodeNumber,
|
|
Name: v.Name,
|
|
Cover: v.Cover,
|
|
}
|
|
}
|
|
}
|
|
// 累加浏览数
|
|
common.Go(func() {
|
|
// 增加父级的浏览数量
|
|
mediamod.IncBrowseCount(item.MediaID, 1)
|
|
topasist.Incr(topser.TypeMedia(item.MediaType), item.ID.Hex(), 1)
|
|
})
|
|
return
|
|
}
|