@@ -0,0 +1,52 @@
|
||||
package mediacontentser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"91porn-server/models/v/mediacontentmod"
|
||||
"91porn-server/models/v/mediamod"
|
||||
)
|
||||
|
||||
func TestDramaResponsesDoNotExposeServerProgress(t *testing.T) {
|
||||
tests := map[string]any{
|
||||
"media": mediamod.AppMediaBase{},
|
||||
"episode list": AppMediaContent{},
|
||||
"episode info": MediaContentInfo{},
|
||||
}
|
||||
for name, response := range tests {
|
||||
data, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal %s response: %v", name, err)
|
||||
}
|
||||
if bytes.Contains(data, []byte(`"resume"`)) || bytes.Contains(data, []byte(`"progressSeconds"`)) {
|
||||
t.Fatalf("%s response still exposes server progress: %s", name, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveDramaAccess(t *testing.T) {
|
||||
media := mediamod.Media{FreeEpisode: 1}
|
||||
tests := []struct {
|
||||
name string
|
||||
content mediacontentmod.MediaContent
|
||||
hasBuy bool
|
||||
hasCard bool
|
||||
wantCan bool
|
||||
wantAccess string
|
||||
}{
|
||||
{name: "free episode", content: mediacontentmod.MediaContent{EpisodeNumber: 1, Price: 30, ListenPermission: 1}, wantCan: true, wantAccess: dramaAccessFree},
|
||||
{name: "bought episode", content: mediacontentmod.MediaContent{EpisodeNumber: 2, Price: 30, ListenPermission: 1}, hasBuy: true, wantCan: true, wantAccess: dramaAccessBought},
|
||||
{name: "card episode", content: mediacontentmod.MediaContent{EpisodeNumber: 2, Price: 30, ListenPermission: 1}, hasCard: true, wantCan: true, wantAccess: dramaAccessCard},
|
||||
{name: "locked episode", content: mediacontentmod.MediaContent{EpisodeNumber: 2, Price: 30, ListenPermission: 1}, wantCan: false, wantAccess: dramaAccessCoin},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, canPlay, accessType := ResolveDramaAccess(tt.content, media, tt.hasBuy, tt.hasCard)
|
||||
if canPlay != tt.wantCan || accessType != tt.wantAccess {
|
||||
t.Fatalf("ResolveDramaAccess() = (%v, %q), want (%v, %q)", canPlay, accessType, tt.wantCan, tt.wantAccess)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Executable
+412
@@ -0,0 +1,412 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package mediacontentser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMediaContentInfoMarshalH265URL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
h265URL string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "resource exists",
|
||||
h265URL: "https://cdn.example.com/h265/index.m3u8",
|
||||
want: "https://cdn.example.com/h265/index.m3u8",
|
||||
},
|
||||
{
|
||||
name: "resource missing",
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
data, err := json.Marshal(MediaContentInfo{
|
||||
VideoUrl: "https://cdn.example.com/h264/index.m3u8",
|
||||
H265Url: tt.h265URL,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("marshal media content info: %v", err)
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
t.Fatalf("unmarshal response: %v", err)
|
||||
}
|
||||
if response["videoUrl"] != "https://cdn.example.com/h264/index.m3u8" {
|
||||
t.Fatalf("unexpected H.264 URL: %v", response["videoUrl"])
|
||||
}
|
||||
if response["h265Url"] != tt.want {
|
||||
t.Fatalf("unexpected H.265 URL: %v", response["h265Url"])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppMediaContentMarshalH265URL(t *testing.T) {
|
||||
data, err := json.Marshal(AppMediaContent{
|
||||
VideoUrl: "https://cdn.example.com/h264/index.m3u8",
|
||||
H265Url: "https://cdn.example.com/h265/index.m3u8",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("marshal app media content: %v", err)
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
t.Fatalf("unmarshal app media content: %v", err)
|
||||
}
|
||||
if response["videoUrl"] != "https://cdn.example.com/h264/index.m3u8" {
|
||||
t.Fatalf("unexpected H.264 URL: %v", response["videoUrl"])
|
||||
}
|
||||
if response["h265Url"] != "https://cdn.example.com/h265/index.m3u8" {
|
||||
t.Fatalf("unexpected H.265 URL: %v", response["h265Url"])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user