160 lines
5.7 KiB
Go
160 lines
5.7 KiB
Go
package recipes
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
// TrendingParams configures trending_on_server.
|
|
type TrendingParams struct {
|
|
Window string `json:"window"` // 24h | 7d | 30d
|
|
}
|
|
|
|
type trendingRecipe struct{}
|
|
|
|
func (trendingRecipe) Type() string { return "trending_on_server" }
|
|
func (trendingRecipe) NewParams() any { return &TrendingParams{} }
|
|
func (trendingRecipe) DefaultCacheTTL() time.Duration { return time.Hour }
|
|
func (trendingRecipe) Resolve(rc ResolverContext) (ResolvedItems, error) {
|
|
return delegateResolve("trending_on_server", rc)
|
|
}
|
|
func (trendingRecipe) Validate(raw json.RawMessage) error {
|
|
if len(raw) == 0 {
|
|
return nil
|
|
}
|
|
var p TrendingParams
|
|
if err := json.Unmarshal(raw, &p); err != nil {
|
|
return err
|
|
}
|
|
switch p.Window {
|
|
case "", "24h", "7d", "30d":
|
|
return nil
|
|
default:
|
|
return errors.New("trending_on_server: window must be 24h, 7d, or 30d")
|
|
}
|
|
}
|
|
func (trendingRecipe) Definition() RecipeDefinition {
|
|
return RecipeDefinition{
|
|
Type: "trending_on_server",
|
|
Category: CategorySocial,
|
|
Presets: []GalleryPreset{
|
|
{Key: "tr_24h", DisplayName: "Trending Now (24h)", Icon: "📈", DescriptionShort: "Most-played in the last day.", DefaultParams: json.RawMessage(`{"window":"24h"}`)},
|
|
{Key: "tr_7d", DisplayName: "Trending This Week", Icon: "📈", DescriptionShort: "Most-played in the last week.", DefaultParams: json.RawMessage(`{"window":"7d"}`)},
|
|
{Key: "tr_30d", DisplayName: "Trending This Month", Icon: "📈", DescriptionShort: "Most-played in the last month.", DefaultParams: json.RawMessage(`{"window":"30d"}`)},
|
|
},
|
|
}
|
|
}
|
|
|
|
// ProfileActivityFeedParams configures profile_activity_feed.
|
|
//
|
|
// RecentCount is reserved for future use — it will eventually let admins cap
|
|
// the number of activity entries per profile (e.g., "show at most N from each
|
|
// other profile"). Currently the fetcher honors only the section ItemLimit;
|
|
// persisting RecentCount in the section config is forward-compatible.
|
|
type ProfileActivityFeedParams struct {
|
|
ProfileID string `json:"profile_id"` // empty = all-other-profiles
|
|
RecentCount int `json:"recent_count,omitempty"`
|
|
}
|
|
|
|
type profileActivityRecipe struct{}
|
|
|
|
func (profileActivityRecipe) Type() string { return "profile_activity_feed" }
|
|
func (profileActivityRecipe) NewParams() any { return &ProfileActivityFeedParams{} }
|
|
func (profileActivityRecipe) DefaultCacheTTL() time.Duration { return 5 * time.Minute }
|
|
func (profileActivityRecipe) Resolve(rc ResolverContext) (ResolvedItems, error) {
|
|
return delegateResolve("profile_activity_feed", rc)
|
|
}
|
|
func (profileActivityRecipe) Validate(raw json.RawMessage) error {
|
|
if len(raw) == 0 {
|
|
return nil
|
|
}
|
|
var p ProfileActivityFeedParams
|
|
return json.Unmarshal(raw, &p)
|
|
}
|
|
func (profileActivityRecipe) Definition() RecipeDefinition {
|
|
return RecipeDefinition{
|
|
Type: "profile_activity_feed",
|
|
Category: CategorySocial,
|
|
Presets: []GalleryPreset{
|
|
{Key: "pa_household", DisplayName: "What Others Just Watched", Icon: "👪", DescriptionShort: "Recent watches across all other profiles.", DefaultParams: json.RawMessage(`{"profile_id":""}`)},
|
|
},
|
|
}
|
|
}
|
|
|
|
// NewToLibraryParams configures new_to_library.
|
|
type NewToLibraryParams struct {
|
|
LookbackDays int `json:"lookback_days,omitempty"` // default 30
|
|
}
|
|
|
|
type newToLibraryRecipe struct{}
|
|
|
|
func (newToLibraryRecipe) Type() string { return "new_to_library" }
|
|
func (newToLibraryRecipe) NewParams() any { return &NewToLibraryParams{} }
|
|
func (newToLibraryRecipe) DefaultCacheTTL() time.Duration { return time.Hour }
|
|
func (newToLibraryRecipe) Resolve(rc ResolverContext) (ResolvedItems, error) {
|
|
return delegateResolve("new_to_library", rc)
|
|
}
|
|
func (newToLibraryRecipe) Validate(raw json.RawMessage) error {
|
|
if len(raw) == 0 {
|
|
return nil
|
|
}
|
|
var p NewToLibraryParams
|
|
return json.Unmarshal(raw, &p)
|
|
}
|
|
func (newToLibraryRecipe) Definition() RecipeDefinition {
|
|
return RecipeDefinition{
|
|
Type: "new_to_library",
|
|
Category: CategorySocial,
|
|
Presets: []GalleryPreset{
|
|
{Key: "ntl_30", DisplayName: "New to Library This Month", Icon: "🆕", DescriptionShort: "Added in the last 30 days.", DefaultParams: json.RawMessage(`{"lookback_days":30}`)},
|
|
},
|
|
}
|
|
}
|
|
|
|
// MostWatchedParams configures most_watched.
|
|
type MostWatchedParams struct {
|
|
Window string `json:"window"` // week | month
|
|
}
|
|
|
|
type mostWatchedRecipe struct{}
|
|
|
|
func (mostWatchedRecipe) Type() string { return "most_watched" }
|
|
func (mostWatchedRecipe) NewParams() any { return &MostWatchedParams{} }
|
|
func (mostWatchedRecipe) DefaultCacheTTL() time.Duration { return time.Hour }
|
|
func (mostWatchedRecipe) Resolve(rc ResolverContext) (ResolvedItems, error) {
|
|
return delegateResolve("most_watched", rc)
|
|
}
|
|
func (mostWatchedRecipe) Validate(raw json.RawMessage) error {
|
|
if len(raw) == 0 {
|
|
return nil
|
|
}
|
|
var p MostWatchedParams
|
|
if err := json.Unmarshal(raw, &p); err != nil {
|
|
return err
|
|
}
|
|
switch p.Window {
|
|
case "", "week", "month":
|
|
return nil
|
|
default:
|
|
return errors.New("most_watched: window must be week or month")
|
|
}
|
|
}
|
|
func (mostWatchedRecipe) Definition() RecipeDefinition {
|
|
return RecipeDefinition{
|
|
Type: "most_watched",
|
|
Category: CategorySocial,
|
|
Presets: []GalleryPreset{
|
|
{Key: "mw_week", DisplayName: "Most Watched This Week", Icon: "🏆", DescriptionShort: "Top plays in the last 7 days.", DefaultParams: json.RawMessage(`{"window":"week"}`)},
|
|
{Key: "mw_month", DisplayName: "Most Watched This Month", Icon: "🏆", DescriptionShort: "Top plays in the last 30 days.", DefaultParams: json.RawMessage(`{"window":"month"}`)},
|
|
},
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
Register(trendingRecipe{})
|
|
Register(profileActivityRecipe{})
|
|
Register(newToLibraryRecipe{})
|
|
Register(mostWatchedRecipe{})
|
|
}
|