Files
MeBox/internal/handler/discover_extra.go
T

125 lines
4.3 KiB
Go

// Package handler — multi-section discover endpoints.
//
// The Vue DiscoverView paginates a configurable list of "sections"
// (trending day/week, popular movies, top rated, etc.) and asks the
// backend for a feed keyed by section name. We mirror that surface so
// the React DiscoverPage can render the same rails without a rewrite.
package handler
import (
"context"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/ShukeBta/MediaStationGo/internal/service"
)
type discoverSectionDef struct {
Key string
Label string
Provider string
}
var discoverSectionCatalog = []discoverSectionDef{
{Key: "tmdb_trending_day", Label: "TMDb 今日趋势", Provider: "tmdb"},
{Key: "tmdb_trending_week", Label: "TMDb 本周热门", Provider: "tmdb"},
{Key: "tmdb_popular_movie", Label: "TMDb 热门电影", Provider: "tmdb"},
{Key: "tmdb_popular_tv", Label: "TMDb 热门剧集", Provider: "tmdb"},
{Key: "tmdb_top_rated_movie", Label: "TMDb 高分电影", Provider: "tmdb"},
{Key: "douban_hot_movie", Label: "豆瓣热门电影", Provider: "douban"},
{Key: "douban_hot_tv", Label: "豆瓣热门剧集", Provider: "douban"},
{Key: "douban_top_movie", Label: "豆瓣高分电影", Provider: "douban"},
{Key: "bangumi_calendar", Label: "Bangumi 每日放送", Provider: "bangumi"},
}
// discoverSectionsHandler returns the catalog of sections the UI can
// pick from. The names match the upstream Vue UI so existing settings
// keep working.
func discoverSectionsHandler(svc *service.Container) gin.HandlerFunc {
return func(c *gin.Context) {
sections := make([]gin.H, 0, len(discoverSectionCatalog))
for _, section := range discoverSectionCatalog {
if !discoverProviderEnabled(c.Request.Context(), svc, section.Provider) {
continue
}
sections = append(sections, gin.H{"key": section.Key, "label": section.Label, "provider": section.Provider})
}
c.JSON(http.StatusOK, gin.H{"sections": sections})
}
}
// discoverFeedHandler resolves one or more section keys (?sections=a,b)
// to TMDb / Douban / Bangumi rails and returns the joined results keyed by
// section name. Unknown keys are silently dropped so URL typos don't break
// the page.
func discoverFeedHandler(svc *service.Container) gin.HandlerFunc {
return func(c *gin.Context) {
keys := strings.Split(c.DefaultQuery("sections", "tmdb_trending_day,tmdb_popular_movie,douban_hot_movie,bangumi_calendar"), ",")
out := gin.H{}
artworkItems := []service.ExternalMediaResult{}
for _, raw := range keys {
k := strings.TrimSpace(raw)
if provider := discoverSectionProvider(k); provider != "" && !discoverProviderEnabled(c.Request.Context(), svc, provider) {
out[k] = []service.ExternalMediaResult{}
continue
}
items, err := discoverSectionItems(c.Request.Context(), svc, k)
if err != nil {
svc.Log.Debug("discover fetch failed")
items = nil
}
artworkItems = append(artworkItems, items...)
out[k] = items
}
svc.Discover.WarmExternalArtwork(artworkItems)
c.JSON(http.StatusOK, out)
}
}
func discoverSectionProvider(key string) string {
for _, section := range discoverSectionCatalog {
if section.Key == key {
return section.Provider
}
}
switch key {
case "trending_day", "trending_week", "popular_movie", "popular_tv", "top_rated_movie", "upcoming_movie":
return "tmdb"
default:
return ""
}
}
func discoverProviderEnabled(ctx context.Context, svc *service.Container, provider string) bool {
if svc == nil || svc.APIConfig == nil || strings.TrimSpace(provider) == "" {
return true
}
cfg, err := svc.APIConfig.Get(ctx, provider)
if err != nil || cfg == nil {
return true
}
return cfg.Enabled
}
func discoverSectionItems(ctx context.Context, svc *service.Container, k string) ([]service.ExternalMediaResult, error) {
switch k {
case "tmdb_trending_day", "tmdb_trending_week", "tmdb_popular_movie", "tmdb_popular_tv", "tmdb_top_rated_movie",
"trending_day", "trending_week", "popular_movie", "popular_tv", "top_rated_movie", "upcoming_movie":
return svc.Discover.TMDbSection(ctx, k)
case "douban_hot_movie", "douban_hot_tv", "douban_top_movie":
if svc.Douban == nil {
return []service.ExternalMediaResult{}, nil
}
return svc.Douban.Discover(ctx, k)
case "bangumi_calendar":
if svc.Bangumi == nil {
return []service.ExternalMediaResult{}, nil
}
return svc.Bangumi.Calendar(ctx)
default:
return []service.ExternalMediaResult{}, nil
}
}