package feed import ( "encoding/json" "strconv" "strings" cdm "go-common/app/interface/main/app-card/model" "go-common/app/interface/main/app-card/model/card/ai" "go-common/app/interface/main/app-card/model/card/audio" "go-common/app/interface/main/app-card/model/card/bangumi" "go-common/app/interface/main/app-card/model/card/banner" "go-common/app/interface/main/app-card/model/card/cm" "go-common/app/interface/main/app-card/model/card/live" "go-common/app/interface/main/app-card/model/card/operate" "go-common/app/interface/main/app-card/model/card/rank" "go-common/app/interface/main/app-card/model/card/show" "go-common/app/interface/main/app-feed/model" "go-common/app/interface/main/app-feed/model/dislike" livemdl "go-common/app/interface/main/app-feed/model/live" bustag "go-common/app/interface/main/tag/model" tag "go-common/app/interface/main/tag/model" article "go-common/app/interface/openplatform/article/model" account "go-common/app/service/main/account/model" "go-common/app/service/main/archive/model/archive" feed "go-common/app/service/main/feed/model" relation "go-common/app/service/main/relation/model" episodegrpc "go-common/app/service/openplatform/pgc-season/api/grpc/episode/v1" xtime "go-common/library/time" ) const ( _rankTitle = "全站排行榜" _rankURI = "http://www.bilibili.com/index/rank/all-03.json" _rankCount = 3 _convergeMinCount = 2 _bangumiRcmdUpdates = 99 ) // Item is feed item, contains av, bangumi, live, banner, feed... type Item struct { Title string `json:"title,omitempty"` Subtitle string `json:"subtitle,omitempty"` Cover string `json:"cover,omitempty"` URI string `json:"uri,omitempty"` Redirect string `json:"redirect,omitempty"` Param string `json:"param,omitempty"` Goto string `json:"goto,omitempty"` Desc string `json:"desc,omitempty"` Play int `json:"play,omitempty"` Danmaku int `json:"danmaku,omitempty"` Reply int `json:"reply,omitempty"` Fav int `json:"favorite,omitempty"` Coin int `json:"coin,omitempty"` Share int `json:"share,omitempty"` Like int `json:"like,omitempty"` Dislike int `json:"dislike,omitempty"` Duration int64 `json:"duration,omitempty"` Count int `json:"count,omitempty"` Status int8 `json:"status,omitempty"` Type int8 `json:"type,omitempty"` Badge string `json:"badge,omitempty"` StatType int8 `json:"stat_type,omitempty"` RcmdReason *RcmdReason `json:"rcmd_reason,omitempty"` Item []*Item `json:"item,omitempty"` // sortedset index Idx int64 `json:"idx,omitempty"` // av Cid int64 `json:"cid,omitempty"` Rid int32 `json:"tid,omitempty"` TName string `json:"tname,omitempty"` Tag *Tag `json:"tag,omitempty"` Button *Button `json:"button,omitempty"` DisklikeReasons []*dislike.DisklikeReason `json:"dislike_reasons,omitempty"` CTime xtime.Time `json:"ctime,omitempty"` Autoplay int32 `json:"autoplay,omitempty"` // upper Mid int64 `json:"mid,omitempty"` Name string `json:"name,omitempty"` Face string `json:"face,omitempty"` IsAtten int8 `json:"is_atten,omitempty"` Fans int64 `json:"fans,omitempty"` RecCnt int `json:"recent_count,omitempty"` Recent []*Item `json:"recent,omitempty"` Official *OfficialInfo `json:"official,omitempty"` // live Online int `json:"online,omitempty"` Area string `json:"area,omitempty"` AreaID int `json:"area_id,omitempty"` Area2 *Area2 `json:"area2,omitempty"` // bangumi Index string `json:"index,omitempty"` IndexTitle string `json:"index_title,omitempty"` CoverMark string `json:"cover_mark,omitempty"` Finish bool `json:"finish,omitempty"` LatestIndex string `json:"last_index,omitempty"` // bangumi ai Updates int `json:"updates,omitempty"` // live or bangumi From int8 `json:"from,omitempty"` // adviertisement RequestID string `json:"request_id,omitempty"` CreativeID int64 `json:"creative_id,omitempty"` SrcID int `json:"src_id,omitempty"` IsAd bool `json:"is_ad,omitempty"` IsAdLoc bool `json:"is_ad_loc,omitempty"` AdCb string `json:"ad_cb,omitempty"` ShowURL string `json:"show_url,omitempty"` ClickURL string `json:"click_url,omitempty"` ClientIP string `json:"client_ip,omitempty"` CmMark int64 `json:"cm_mark,omitempty"` AdIndex int `json:"ad_index,omitempty"` Extra json.RawMessage `json:"extra,omitempty"` CardIndex int `json:"card_index,omitempty"` // tag Tags []*tag.Tag `json:"tags,omitempty"` // rank Cover1 string `json:"cover1,omitempty"` Cover2 string `json:"cover2,omitempty"` Cover3 string `json:"cover3,omitempty"` // banner BannerItem []*banner.Banner `json:"banner_item,omitempty"` Hash string `json:"hash,omitempty"` // article Covers []string `json:"covers,omitempty"` Template int `json:"template,omitempty"` Temple int `json:"temple,omitempty"` Category *Category `json:"category,omitempty"` BannerURL string `json:"banner_url,omitempty"` // game download Download int32 `json:"download,omitempty"` BigCover string `json:"big_cover,omitempty"` // special HideBadge bool `json:"hide_badge,omitempty"` Ratio float64 `json:"ratio,omitempty"` // shopping City string `json:"city,omitempty"` PType string `json:"ptype,omitempty"` Price string `json:"price,omitempty"` Square string `json:"square,omitempty"` STime string `json:"stime,omitempty"` ETime string `json:"etime,omitempty"` // news Content string `json:"content,omitempty"` // subscribe Kind string `json:"kind,omitempty"` // audio SongTitle string `json:"song_title,omitempty"` // bigdata source Source string `json:"-"` AvFeature json.RawMessage `json:"-"` // common GotoOrg string `json:"-"` // rank score Score string `json:"score,omitempty"` // ai recommend AI *ai.Item `json:"-"` // abtest AutoplayCard int `json:"autoplay_card,omitempty"` } type Dimension struct { Width int64 `json:"width,omitempty"` Height int64 `json:"height,omitempty"` Rotate int64 `json:"rotate,omitempty"` } type Button struct { Name string `json:"name,omitempty"` URI string `json:"uri,omitempty"` RedirectURI string `json:"redirect_uri,omitempty"` } type RcmdReason struct { ID int `json:"id,omitempty"` Content string `json:"content,omitempty"` BgColor string `json:"bg_color,omitempty"` IconLocation string `json:"icon_location,omitempty"` Message string `json:"message,omitempty"` } type Category struct { ID int64 `json:"id,omitempty"` Name string `json:"name,omitempty"` Children *Category `json:"children,omitempty"` } type Area2 struct { ID int64 `json:"id,omitempty"` Name string `json:"name,omitempty"` Children *Area2 `json:"children,omitempty"` } type Tag struct { // new ID int64 `json:"id,omitempty"` Name string `json:"name,omitempty"` Face string `json:"face,omitempty"` Fans int `json:"fans,omitempty"` IsAtten int8 `json:"is_atten,omitempty"` URI string `json:"uri,omitempty"` // old TagID int64 `json:"tag_id,omitempty"` TagName string `json:"tag_name,omitempty"` Count *TagCount `json:"count,omitempty"` } type TagCount struct { Atten int `json:"atten,omitempty"` } type OfficialInfo struct { Role int8 `json:"role,omitempty"` Title string `json:"title,omitempty"` Desc string `json:"desc,omitempty"` } type IndexParam struct { Build int `form:"build"` Platform string `form:"platform"` MobiApp string `form:"mobi_app"` Device string `form:"device"` Network string `form:"network"` // idx, err := strconv.ParseInt(idxStr, 10, 64) // if err != nil || idx < 0 { // idx = 0 // } Idx int64 `form:"idx" default:"0"` // pull, err := strconv.ParseBool(pullStr) // if err != nil { // pull = true // } Pull bool `form:"pull" default:"true"` Column cdm.ColumnStatus `form:"column"` // loginEvent, err := strconv.Atoi(loginEventStr) // if err != nil { // loginEvent = 0 // } LoginEvent int `form:"login_event" default:"0"` OpenEvent string `form:"open_event"` BannerHash string `form:"banner_hash"` AdExtra string `form:"ad_extra"` Qn int `form:"qn" default:"0"` Interest string `form:"interest"` Flush int `form:"flush"` AutoPlayCard int `form:"autoplay_card"` Fnver int `form:"fnver" default:"0"` Fnval int `form:"fnval" default:"0"` DeviceType int `form:"device_type"` ParentMode int `form:"parent_mode"` ForceHost int `form:"force_host"` RecsysMode int `form:"recsys_mode"` } type ConvergeParam struct { ID int64 `form:"id" validate:"required,min=1"` MobiApp string `form:"mobi_app"` Device string `form:"device"` Build int `form:"build"` Qn int `form:"qn" default:"0"` Fnver int `form:"fnver" default:"0"` Fnval int `form:"fnval" default:"0"` ForceHost int `form:"force_host"` } func (i *Item) FromRcmd(r *ai.Item) { i.Title = r.Name i.Param = strconv.FormatInt(r.ID, 10) if r.Goto == "" { r.Goto = model.GotoAv } i.From = r.From i.Source = r.Source i.AvFeature = r.AvFeature if r.Config != nil { i.Title = r.Config.Title i.Cover = r.Config.Cover i.URI = r.Config.URI } i.StatType = r.StatType i.GotoOrg = r.Goto } type Infoc struct { UserFeature json.RawMessage IsRcmd bool NewUser bool Code int AutoPlayInfoc string } type Config struct { Column cdm.ColumnStatus `json:"column"` AutoplayCard int8 `json:"autoplay_card"` FeedCleanAbtest int8 `json:"feed_clean_abtest"` FollowMode *FollowMode `json:"follow_mode,omitempty"` } type FollowMode struct { Title string `json:"title,omitempty"` Option []*Option `json:"option,omitempty"` Card *Card `json:"-"` ToastMessage string `json:"toast_message,omitempty"` } type Option struct { Title string `json:"title,omitempty"` Desc string `json:"desc,omitempty"` Value int8 `json:"value"` } type Card struct { Title string `json:"-"` Desc string `json:"-"` Button []string `json:"-"` } func (i *Item) FromAv(a *archive.ArchiveWithPlayer) { if i.Title == "" { i.Title = a.Title } if i.Cover == "" { i.Cover = model.CoverURLHTTPS(a.Pic) } else { i.Cover = model.CoverURLHTTPS(i.Cover) } i.Param = strconv.FormatInt(a.Aid, 10) i.Goto = model.GotoAv i.URI = model.FillURI(i.Goto, i.Param, 0, 0, model.AvPlayHandler(a.Archive3, a.PlayerInfo)) i.Cid = a.FirstCid i.Rid = a.TypeID i.TName = a.TypeName i.Desc = strconv.Itoa(int(a.Stat.Danmaku)) + "弹幕" i.fillArcStat(a.Archive3) i.Duration = a.Duration i.Mid = a.Author.Mid i.Name = a.Author.Name i.Face = a.Author.Face i.CTime = a.PubDate i.Autoplay = a.Rights.Autoplay } func (i *Item) FromLive(r *live.Room) { if r.LiveStatus != 1 || r.Title == "" || r.Cover == "" { return } i.Title = r.Title i.Cover = r.Cover i.Goto = model.GotoLive i.Param = strconv.FormatInt(r.RoomID, 10) i.URI = model.FillURI(i.Goto, i.Param, 0, 0, model.LiveRoomHandler(r)) i.Name = r.Uname i.Mid = r.UID i.Face = r.Face i.Online = int(r.Online) i.Autoplay = 1 // i.Area = r.Area // i.AreaID = r.AreaID i.Area2 = &Area2{ID: r.AreaV2ParentID, Name: r.AreaV2ParentName, Children: &Area2{ID: r.AreaV2ID, Name: r.AreaV2Name}} i.Autoplay = 1 } func (i *Item) FromSeason(b *bangumi.Season) { if i.Title == "" { i.Title = b.Title } if i.Cover == "" { i.Cover = b.Cover } i.Goto = model.GotoBangumi i.Param = b.EpisodeID i.URI = model.FillURI(i.Goto, i.Param, 0, 0, nil) i.Play = int(b.PlayCount) i.Fav = int(b.Favorites) i.Type = b.SeasonType i.Badge = b.TypeBadge i.Desc = b.UpdateDesc i.Face = b.SeasonCover i.Square = b.SeasonCover } func (i *Item) FromPGCSeason(s *episodegrpc.EpisodeCardsProto) { if i.Title == "" { i.Title = s.Season.Title } if i.Cover == "" { i.Cover = s.Cover } i.Goto = model.GotoBangumi i.Param = strconv.Itoa(int(s.EpisodeId)) i.URI = model.FillURI(model.GotoBangumi, i.Param, 0, 0, nil) i.Index = s.Title i.IndexTitle = s.LongTitle i.Status = int8(s.Season.SeasonStatus) i.CoverMark = s.Season.Badge i.Play = int(s.Season.Stat.View) i.Fav = int(s.Season.Stat.Follow) i.Type = int8(s.Season.SeasonType) i.Badge = s.Season.SeasonTypeName if s.Season.IsFinish == 1 { i.Finish = true } i.Count = int(s.Season.TotalCount) i.LatestIndex = s.Title i.Desc = s.Season.NewEpShow i.Face = s.Season.Cover i.Square = s.Season.Cover } func (i *Item) FromLogin() { if i.Param == "0" { i.Param = "1" } i.Goto = model.GotoLogin } func (i *Item) FromAdAv(adInfo *cm.AdInfo, a *archive.ArchiveWithPlayer) { // ad i.RequestID = adInfo.RequestID i.CreativeID = adInfo.CreativeID i.SrcID = adInfo.Source i.IsAdLoc = adInfo.IsAdLoc i.IsAd = adInfo.IsAd i.AdCb = adInfo.AdCb i.CmMark = adInfo.CmMark i.AdIndex = adInfo.Index c := adInfo.CreativeContent i.Title = c.Title i.Desc = c.Desc i.Cover = c.ImageURL i.Goto = model.GotoAdAv i.Name = a.Author.Name i.Face = c.LogURL i.ShowURL = c.ShowURL i.ClickURL = c.ClickURL // archive i.Param = strconv.FormatInt(a.Aid, 10) i.URI = model.FillURI(i.Goto, i.Param, 0, 0, model.AvPlayHandler(a.Archive3, a.PlayerInfo)) if a.TypeName != "广告" { i.TName = a.TypeName } i.fillArcStat(a.Archive3) i.Duration = a.Duration i.Mid = a.Author.Mid if i.Name == "" { i.Name = a.Author.Name } if i.Face == "" { i.Face = a.Author.Face } i.CTime = a.Ctime i.Extra = adInfo.Extra i.CardIndex = adInfo.CardIndex } func (i *Item) FromAdWeb(adInfo *cm.AdInfo) { i.RequestID = adInfo.RequestID i.CreativeID = adInfo.CreativeID i.SrcID = adInfo.Source i.IsAdLoc = adInfo.IsAdLoc i.IsAd = adInfo.IsAd i.AdCb = adInfo.AdCb i.CmMark = adInfo.CmMark i.AdIndex = adInfo.Index c := adInfo.CreativeContent i.Title = c.Title i.Desc = c.Desc i.Cover = c.ImageURL i.Goto = model.GotoAdWeb i.URI = model.FillURI(i.Goto, c.URL, 0, 0, nil) i.ShowURL = c.ShowURL i.ClickURL = c.ClickURL i.Extra = adInfo.Extra i.CardIndex = adInfo.CardIndex } func (i *Item) FromAdLarge(adInfo *cm.AdInfo) { i.RequestID = adInfo.RequestID i.CreativeID = adInfo.CreativeID i.SrcID = adInfo.Source i.IsAdLoc = adInfo.IsAdLoc i.IsAd = adInfo.IsAd i.AdCb = adInfo.AdCb i.CmMark = adInfo.CmMark i.AdIndex = adInfo.Index c := adInfo.CreativeContent i.Title = c.Title i.Desc = c.Desc i.Goto = model.GotoAdLarge i.URI = model.FillURI(i.Goto, c.URL, 0, 0, nil) i.ShowURL = c.ShowURL i.ClickURL = c.ClickURL i.Extra = adInfo.Extra i.CardIndex = adInfo.CardIndex } func (i *Item) FromAdWebS(adInfo *cm.AdInfo) { i.RequestID = adInfo.RequestID i.CreativeID = adInfo.CreativeID i.SrcID = adInfo.Source i.IsAdLoc = adInfo.IsAdLoc i.IsAd = adInfo.IsAd i.AdCb = adInfo.AdCb i.CmMark = adInfo.CmMark i.AdIndex = adInfo.Index c := adInfo.CreativeContent i.Title = c.Title i.Desc = c.Desc i.Cover = c.ImageURL i.Goto = model.GotoAdWebS i.URI = model.FillURI(i.Goto, c.URL, 0, 0, nil) i.ShowURL = c.ShowURL i.ClickURL = c.ClickURL i.Extra = adInfo.Extra i.CardIndex = adInfo.CardIndex } func (i *Item) FromSpecial(id int64, title, cover, desc, url string, typ int, badge string, size string) { if title == "" || cover == "" { return } i.Title = title i.Cover = cover i.Goto = model.GotoSpecial i.URI = model.FillURI(model.OperateType[typ], url, 0, 0, nil) i.Redirect = model.FillRedirect(i.Goto, typ) i.Desc = desc i.Param = strconv.FormatInt(id, 10) i.HideBadge = true i.Badge = badge var ratio float64 if size == "1020x300" { ratio = 34 } else if size == "1020x378" { ratio = 27 } i.Ratio = ratio } func (i *Item) FromSpecialS(id int64, title, cover, square, desc, url string, typ int, badge string) { if title == "" || cover == "" { return } i.Title = title i.Cover = cover // 活不过一个版的单列封面 if square != "" { i.Square = square } else { i.Square = cover } i.Goto = model.GotoSpecialS i.URI = model.FillURI(model.OperateType[typ], url, 0, 0, nil) i.Redirect = model.FillRedirect(i.Goto, typ) i.Desc = desc i.Param = strconv.FormatInt(id, 10) i.Badge = badge } func (i *Item) FromRank(ranks []*rank.Rank, am map[int64]*archive.ArchiveWithPlayer) { if len(ranks) < _rankCount { return } if a, ok := am[ranks[0].Aid]; ok { i.Cover1 = a.Pic } else { return } if a, ok := am[ranks[1].Aid]; ok { i.Cover2 = a.Pic } else { return } if a, ok := am[ranks[2].Aid]; ok { i.Cover3 = a.Pic } else { return } ris := make([]*Item, 0, _rankCount) for _, rank := range ranks[:_rankCount] { if a, ok := am[rank.Aid]; ok { ri := &Item{ Title: a.Title, Cover: a.Pic, Goto: model.GotoAv, Param: strconv.FormatInt(a.Aid, 10), } ri.fillArcStat(a.Archive3) ri.Duration = a.Duration ri.URI = model.FillURI(ri.Goto, ri.Param, 0, 0, model.AvPlayHandler(a.Archive3, a.PlayerInfo)) score := int64(rank.Score) if score < 10000 { ri.Score = model.Rounding(score, 0) } else if score >= 10000 && score < 100000000 { ri.Score = model.Rounding(score, 10000) + "万" } else if score >= 100000000 { ri.Score = model.Rounding(score, 100000000) + "亿" } if ri.Score != "" { ri.Score = "综合评分:" + ri.Score } else { ri.Score = "综合评分:-" } ris = append(ris, ri) } else { return } } i.Title = _rankTitle i.Goto = model.GotoRank i.Item = ris i.Param = "0" i.URI = model.FillURI(i.Goto, _rankURI, 0, 0, nil) } func (i *Item) FromBangumiRcmd(u *bangumi.Update) { i.Cover = u.SquareCover i.Goto = model.GotoBangumiRcmd i.Desc = u.Title if u.Updates > _bangumiRcmdUpdates { i.Updates = _bangumiRcmdUpdates } else { i.Updates = u.Updates } } func (i *Item) FromBanner(bs []*banner.Banner, hash string) { i.Goto = model.GotoBanner i.Hash = hash i.BannerItem = bs } func (i *Item) FromPlayer(a *archive.ArchiveWithPlayer) { if a.Archive3 == nil || !a.IsNormal() { return } title := i.Title if title == "" { title = a.Title } cover := i.Cover if cover == "" { cover = a.Pic } item := &Item{Title: title, Cover: cover, Param: strconv.FormatInt(a.Aid, 10), Goto: model.GotoAv} item.URI = model.FillURI(item.Goto, item.Param, 0, 0, model.AvPlayHandler(a.Archive3, a.PlayerInfo)) item.fillArcStat(a.Archive3) i.Item = []*Item{item} i.Cid = a.FirstCid i.Rid = a.TypeID i.TName = a.TypeName i.Mid = a.Author.Mid i.Goto = model.GotoPlayer i.Name = a.Author.Name i.Face = a.Author.Face i.Duration = a.Duration i.Autoplay = a.Rights.Autoplay } func (i *Item) FromPlayerLive(r *live.Room) { if r.LiveStatus != 1 || r.Title == "" || r.Cover == "" { return } i.Name = r.Uname i.Mid = r.UID i.Face = r.Face item := &Item{Title: r.Title, Cover: r.Cover, Param: strconv.FormatInt(r.RoomID, 10), Goto: model.GotoLive} item.URI = model.FillURI(item.Goto, item.Param, 0, 0, model.LiveRoomHandler(r)) item.Online = int(r.Online) item.Area2 = &Area2{ID: r.AreaV2ParentID, Name: r.AreaV2ParentName, Children: &Area2{ID: r.AreaV2ID, Name: r.AreaV2Name}} i.Item = []*Item{item} i.Goto = model.GotoPlayer i.Autoplay = 1 } func (i *Item) FromRcmdReason(r *ai.RcmdReason) { if r != nil { if r.Style != 0 { i.RcmdReason = &RcmdReason{ID: r.Style, Content: r.Content, BgColor: r.Grounding, IconLocation: r.Position} if r.Style == 3 { i.RcmdReason.Message = i.Name } } else { i.RcmdReason = &RcmdReason{ID: r.ID, Content: r.Content} } } } func (i *Item) FromGameDownloadS(d *operate.Download, plat int8, build int) { i.Title = d.Title i.Cover = d.DoubleCover i.BigCover = d.Cover i.Goto = model.GotoGameDownloadS i.Desc = d.Desc // TODO fuck game i.URI = model.FillURI(model.OperateType[d.URLType], d.URLValue, plat, build, nil) i.Redirect = model.FillRedirect(i.Goto, d.URLType) i.Face = d.Icon i.Param = strconv.FormatInt(d.ID, 10) i.Download = d.Number if d.Icon != "" { i.Square = d.Icon } else { i.Square = d.Cover } } func (i *Item) FromShoppingS(c *show.Shopping) { if c.Name == "" || c.URL == "" { return } i.Title = c.Name i.STime = c.STime i.ETime = c.ETime i.City = c.CityName if len(c.Tags) != 0 { i.PType = c.Tags[0].TagName } i.Param = strconv.FormatInt(c.ID, 10) // 双列封面 if strings.HasPrefix(c.PerformanceImage, "http:") || strings.HasPrefix(c.PerformanceImage, "https:") { i.Cover = c.PerformanceImage } else { i.Cover = "http:" + c.PerformanceImage } // 单列封面 if strings.HasPrefix(c.PerformanceImageP, "http:") || strings.HasPrefix(c.PerformanceImageP, "https:") { i.Square = c.PerformanceImageP } else { i.Square = "http:" + c.PerformanceImageP } if i.Cover == "" { i.Cover = i.Square } if i.Cover == "" { return } i.Goto = model.GotoShoppingS i.URI = model.FillURI(i.Goto, c.URL, 0, 0, nil) i.Type = c.Type i.Subtitle = c.Subname // 漫展需加羊角符 if i.Type == 1 { i.Price = "¥" + c.Pricelt } else { i.Price = c.Pricelt } i.Desc = c.Want } func (i *Item) FromAudio(a *audio.Audio) { i.Title = a.Title i.Cover = a.CoverURL i.Param = strconv.FormatInt(a.MenuID, 10) i.Goto = model.GotoAudio i.URI = model.FillURI(i.Goto, i.Param, 0, 0, model.AudioHandler) i.Play = int(a.PlayNum) i.Count = a.RecordNum i.Fav = int(a.FavoriteNum) i.Face = a.Face // old titles := make([]string, 0, len(a.Songs)) for index, song := range a.Songs { if song != nil || song.Title != "" { titles = append(titles, song.Title) if index == 0 { i.SongTitle = song.Title } } } i.Desc = strings.Join(titles, ",") // new for _, ctg := range a.Ctgs { tag := &tag.Tag{ID: ctg.ItemID, Name: ctg.ItemVal} i.Tags = append(i.Tags, tag) if len(i.Tags) == 2 { break } } // new if len(a.Ctgs) != 0 { id := a.Ctgs[0].ItemID name := a.Ctgs[0].ItemVal if len(a.Ctgs) > 1 { id = a.Ctgs[1].ItemID name += "·" + a.Ctgs[1].ItemVal } i.Tag = &Tag{Name: name, URI: model.FillURI(model.GotoAudioTag, strconv.FormatInt(id, 10), 0, 0, model.AudioHandler)} } if a.Type == 5 { i.Badge = "专辑" i.Type = 2 } else { i.Badge = "歌单" i.Type = 1 } i.CTime = xtime.Time(a.PaTime) } func (i *Item) FromConverge(c *operate.Converge, am map[int64]*archive.ArchiveWithPlayer, rm map[int64]*live.Room, artm map[int64]*article.Meta) { if len(c.Items) < _convergeMinCount { return } cis := make([]*Item, 0, len(c.Items)) for _, content := range c.Items { ci := &Item{Title: content.Title} switch content.Goto { case model.GotoAv: if a, ok := am[content.Pid]; ok && a.Archive3 != nil && a.IsNormal() { if ci.Title == "" { ci.Title = a.Title } ci.Cover = a.Pic ci.Goto = model.GotoAv ci.Param = strconv.FormatInt(a.Aid, 10) ci.URI = model.FillURI(ci.Goto, ci.Param, 0, 0, model.AvPlayHandler(a.Archive3, a.PlayerInfo)) ci.fillArcStat(a.Archive3) ci.Duration = a.Duration cis = append(cis, ci) } case model.GotoLive: if r, ok := rm[content.Pid]; ok { if r.LiveStatus == 0 || r.Title == "" || r.Cover == "" { continue } if ci.Title == "" { ci.Title = r.Title } ci.Cover = r.Cover ci.Goto = model.GotoLive ci.Param = strconv.FormatInt(r.RoomID, 10) ci.Online = int(r.Online) ci.URI = model.FillURI(ci.Goto, ci.Param, 0, 0, model.LiveRoomHandler(r)) ci.Badge = "直播" cis = append(cis, ci) } case model.GotoArticle: if art, ok := artm[content.Pid]; ok { ci.Title = art.Title ci.Desc = art.Summary if len(art.ImageURLs) != 0 { ci.Cover = art.ImageURLs[0] } ci.Goto = model.GotoArticle ci.Param = strconv.FormatInt(art.ID, 10) ci.URI = model.FillURI(ci.Goto, ci.Param, 0, 0, nil) if art.Stats != nil { ci.fillArtStat(art) } ci.Badge = "文章" cis = append(cis, ci) } } } if len(cis) < _convergeMinCount { return } i.Item = cis i.Goto = model.GotoConverge i.URI = model.FillURI(model.OperateType[c.ReType], c.ReValue, 0, 0, nil) i.Redirect = model.FillRedirect(i.Goto, c.ReType) i.Title = c.Title i.Cover = c.Cover i.Param = strconv.FormatInt(c.ID, 10) } func (i *Item) FromUpBangumi(p *feed.Bangumi) { i.Title = p.Title i.Cover = p.NewEp.Cover i.Goto = model.GotoUpBangumi i.Param = strconv.FormatInt(p.SeasonID, 10) i.URI = model.FillURI(i.Goto, i.Param, 0, 0, nil) i.Status = int8(p.IsFinish) i.Index = p.NewEp.Index i.IndexTitle = p.NewEp.IndexTitle i.Play = int(p.NewEp.Play) i.Danmaku = int(p.NewEp.Dm) i.Type = int8(p.BgmType) i.Count = int(p.TotalCount) i.Updates = int(p.NewEp.EpisodeID) i.CTime = xtime.Time(p.Ts) } func (i *Item) FromUpLive(f *livemdl.Feed) { i.Cover = f.Face i.Param = strconv.FormatInt(f.RoomID, 10) i.URI = model.FillURI(model.GotoLive, i.Param, 0, 0, nil) } func (i *Item) FromUpArticle(m *article.Meta) { i.Title = m.Title i.Desc = m.Summary i.Covers = m.ImageURLs i.Goto = model.GotoUpArticle i.Param = strconv.FormatInt(m.ID, 10) i.URI = model.FillURI(i.Goto, i.Param, 0, 0, nil) if m.Author != nil { i.Mid = m.Author.Mid i.Name = m.Author.Name i.Face = m.Author.Face } if m.Category != nil { i.Category = &Category{ID: m.Category.ID, Name: m.Category.Name} } if m.Stats != nil { i.fillArtStat(m) } i.Temple = int(m.TemplateID) if i.Temple == 4 { i.Temple = 1 } i.Template = int(m.TemplateID) i.BannerURL = m.BannerURL i.CTime = m.PublishTime } func (i *Item) FromArticleS(m *article.Meta) { if m.State < 0 { return } i.Title = m.Title i.Desc = m.Summary i.Covers = m.ImageURLs i.Goto = model.GotoArticleS i.Param = strconv.FormatInt(m.ID, 10) i.URI = model.FillURI(i.Goto, i.Param, 0, 0, nil) if m.Author != nil { i.Mid = m.Author.Mid i.Name = m.Author.Name i.Face = m.Author.Face } if len(m.Categories) >= 2 && m.Categories[0] != nil && m.Categories[1] != nil { i.Category = &Category{ID: m.Categories[0].ID, Name: m.Categories[0].Name} i.Category.Children = &Category{ID: m.Categories[1].ID, Name: m.Categories[1].Name} } if m.Stats != nil { i.fillArtStat(m) } i.Temple = int(m.TemplateID) if i.Temple == 4 { i.Temple = 1 } i.Template = int(m.TemplateID) i.BannerURL = m.BannerURL i.CTime = m.PublishTime } func (i *Item) FromLiveUpRcmd(id int64, cs []*live.Card, card map[int64]*account.Card) { if len(cs) < 2 { return } is := make([]*Item, 0, 2) for _, c := range cs[:2] { if c.LiveStatus != 1 { return } it := &Item{} it.Title = c.Title it.Cover = c.ShowCover it.Goto = model.GotoLive it.Param = strconv.FormatInt(c.RoomID, 10) it.URI = model.FillURI(it.Goto, it.Param, 0, 0, model.LiveUpHandler(c)) it.Fans = int64(c.Online) it.Mid = c.UID it.Name = c.Uname it.Badge = "直播" if card, ok := card[it.Mid]; ok { if card.Official.Role != 0 { it.Official = &OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc} } } is = append(is, it) } i.Item = is i.Goto = model.GotoLiveUpRcmd i.Param = strconv.FormatInt(id, 10) } func (i *Item) FromWeb(title, cover, uri string) { i.Title = title i.Cover = cover i.Goto = model.GotoWeb i.URI = model.FillURI(i.Goto, uri, 0, 0, nil) i.Redirect = model.FillRedirect(i.Goto, 0) } func (i *Item) FromDislikeReason(plat int8, build int) { const ( _seasonNoSeason = 1 _seasonRegion = 2 _seasonTag = 3 _seasonUpper = 4 _channelIPhone = 6720 _channelAndroid = 5270000 ) var reasonName string if (plat == model.PlatIPhone && build > _channelIPhone) || (plat == model.PlatAndroid && build >= _channelAndroid) || plat == model.PlatIPhoneB { reasonName = "频道:" } else { reasonName = "标签:" } if i.Tag != nil { i.DisklikeReasons = []*dislike.DisklikeReason{ &dislike.DisklikeReason{ReasonID: _seasonUpper, ReasonName: "UP主:" + i.Name}, &dislike.DisklikeReason{ReasonID: _seasonRegion, ReasonName: "分区:" + i.TName}, &dislike.DisklikeReason{ReasonID: _seasonTag, ReasonName: reasonName + i.Tag.TagName}, &dislike.DisklikeReason{ReasonID: _seasonNoSeason, ReasonName: "不感兴趣"}, } } else { i.DisklikeReasons = []*dislike.DisklikeReason{ &dislike.DisklikeReason{ReasonID: _seasonUpper, ReasonName: "UP主:" + i.Name}, &dislike.DisklikeReason{ReasonID: _seasonRegion, ReasonName: "分区:" + i.TName}, &dislike.DisklikeReason{ReasonID: _seasonNoSeason, ReasonName: "不感兴趣"}, } } } func (i *Item) fillArcStat(a *archive.Archive3) { if a == nil { return } if a.Access == 0 { i.Play = int(a.Stat.View) } i.Danmaku = int(a.Stat.Danmaku) i.Reply = int(a.Stat.Reply) i.Fav = int(a.Stat.Fav) i.Coin = int(a.Stat.Coin) i.Share = int(a.Stat.Share) i.Like = int(a.Stat.Like) i.Dislike = int(a.Stat.DisLike) } func (i *Item) fillArtStat(m *article.Meta) { if m == nil { return } i.Play = int(m.Stats.View) i.Reply = int(m.Stats.Reply) } func (i *Item) FromTabCards(r *operate.Active, am map[int64]*archive.ArchiveWithPlayer, downm map[int64]*operate.Download, sm map[int64]*bangumi.Season, rm map[int64]*live.Room, metam map[int64]*article.Meta, spm map[int64]*operate.Special) { items := make([]*Item, 0, len(r.Items)) for _, r := range r.Items { item := &Item{} switch r.Goto { case model.GotoWeb: item.FromWeb(r.Title, r.Cover, model.FillURI(model.GotoWeb, r.Param, 0, 0, nil)) case model.GotoGame: if d, ok := downm[r.Pid]; ok { item.FromGameDownloadS(d, 0, 0) } case model.GotoAv: if a, ok := am[r.Pid]; ok { item.FromAv(a) } case model.GotoBangumi: if b, ok := sm[r.Pid]; ok { item.FromSeason(b) } case model.GotoLive: if r, ok := rm[r.Pid]; ok { item.FromLive(r) } case model.GotoArticle: if m, ok := metam[r.Pid]; ok { item.FromArticleS(m) } case model.GotoSpecial: if sc, ok := spm[r.Pid]; ok { item.FromSpecialS(sc.ID, sc.Title, sc.Cover, sc.SingleCover, sc.Desc, sc.ReValue, sc.ReType, sc.Badge) } } if item.Goto != "" { items = append(items, item) } } if len(items) == 0 { return } i.Item = items i.Title = r.Title i.URI = model.FillURI(model.GotoWeb, r.Param, 0, 0, nil) i.Subtitle = r.Subtitle i.Goto = r.Type } func (i *Item) FromTabTags(r *operate.Active, am map[int64]*archive.ArchiveWithPlayer, tagm map[int64]*tag.Tag) { items := make([]*Item, 0, len(r.Items)) for _, r := range r.Items { if r == nil { continue } item := &Item{} switch r.Goto { case model.GotoAv: if a, ok := am[r.Pid]; ok { item.FromAv(a) } } if item.Goto != "" { items = append(items, item) } } if len(items) == 0 { return } i.Item = items i.Param = strconv.FormatInt(r.Pid, 10) if t, ok := tagm[r.Pid]; ok { i.Title = t.Name } i.Goto = r.Type } func (i *Item) FromTabBanner(r *operate.Active) { i.BannerItem = make([]*banner.Banner, 0, len(r.Items)) for _, item := range r.Items { banner := &banner.Banner{ID: item.Pid, Title: item.Title, Image: item.Cover, URI: cdm.FillURI(item.Goto, item.Param, nil)} i.BannerItem = append(i.BannerItem, banner) } i.Goto = model.GotoBanner } func (i *Item) FromNews(r *operate.Active) { if r.Desc == "" { return } i.Title = r.Title i.Content = r.Desc i.Goto = model.GotoTabNews i.URI = model.FillURI(model.GotoWeb, r.Param, 0, 0, nil) } //最多配10张卡片 取3个未关注的 不足则不显示该卡片 func (i *Item) FromSubscribe(r *operate.Follow, card map[int64]*account.Card, follow map[int64]bool, statm map[int64]*relation.Stat, tagm map[int64]*bustag.Tag) { if r == nil { return } is := make([]*Item, 0, 3) switch r.Type { case "upper": for _, r := range r.Items { item := &Item{} if card, ok := card[r.Pid]; ok { if follow[r.Pid] { continue } item.Name = card.Name item.Face = card.Face item.Mid = card.Mid if card.Official.Role != 0 { item.Official = &OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc} } item.IsAtten = 0 if stat, ok := statm[r.Pid]; ok { item.Fans = stat.Follower } is = append(is, item) } } i.Kind = "upper" case "channel_three": for _, r := range r.Items { item := &Item{} tg, ok := tagm[r.Pid] if !ok || tg.IsAtten == 1 { continue } item.Name = tg.Name item.Face = tg.Cover item.Fans = int64(tg.Count.Atten) item.IsAtten = tg.IsAtten item.Param = strconv.FormatInt(tg.ID, 10) if item.Face != "" { is = append(is, item) } } i.Kind = "channel" } if len(is) < 3 { return } i.Item = is[:3] i.Title = r.Title i.Param = strconv.FormatInt(r.ID, 10) i.Goto = model.GotoSubscribe } func (i *Item) FromChannelRcmd(r *operate.Follow, am map[int64]*archive.ArchiveWithPlayer, tagm map[int64]*bustag.Tag) { if r == nil { return } if a, ok := am[r.Pid]; ok { i.Goto = model.GotoChannelRcmd i.URI = model.FillURI(model.GotoAv, strconv.FormatInt(a.Aid, 10), 0, 0, model.AvPlayHandler(a.Archive3, a.PlayerInfo)) i.Title = a.Title i.Cover = a.Pic if tag, ok := tagm[r.Tid]; ok { i.Tag = &Tag{ID: tag.ID, Name: tag.Name, Face: tag.Cover, Fans: tag.Count.Atten, IsAtten: tag.IsAtten} } i.Cid = a.FirstCid i.Autoplay = a.Rights.Autoplay i.fillArcStat(a.Archive3) i.Duration = a.Duration // TODO 等待开启 // percent := i.Like / (i.Like + i.Dislike) * 100 // if percent != 0 { // i.Desc = strconv.Itoa(percent) + "%的人推荐" // } i.Param = strconv.FormatInt(r.ID, 10) } }