feat: add deep fallback for latest anime episodes
This commit is contained in:
@@ -107,6 +107,12 @@ type searchResult struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type AvailableEpisodes struct {
|
||||
Sub int
|
||||
Dub int
|
||||
Raw int
|
||||
}
|
||||
|
||||
type allAnimeClient struct {
|
||||
httpClient *http.Client
|
||||
extractor *providerExtractor
|
||||
@@ -487,6 +493,47 @@ func (c *allAnimeClient) GetEpisodes(ctx context.Context, showID string, mode st
|
||||
return episodes, nil
|
||||
}
|
||||
|
||||
func (c *allAnimeClient) GetAvailableEpisodes(ctx context.Context, showID string) (AvailableEpisodes, error) {
|
||||
graphqlQuery := `query($showId: String!) {
|
||||
show(_id: $showId) {
|
||||
availableEpisodesDetail
|
||||
}
|
||||
}`
|
||||
|
||||
result, err := c.graphqlRequest(ctx, graphqlQuery, map[string]any{"showId": showID})
|
||||
if err != nil {
|
||||
return AvailableEpisodes{}, err
|
||||
}
|
||||
|
||||
data, ok := result["data"].(map[string]any)
|
||||
if !ok {
|
||||
return AvailableEpisodes{}, fmt.Errorf("invalid response")
|
||||
}
|
||||
|
||||
show, ok := data["show"].(map[string]any)
|
||||
if !ok || show == nil {
|
||||
return AvailableEpisodes{}, fmt.Errorf("show not found")
|
||||
}
|
||||
|
||||
detail, ok := show["availableEpisodesDetail"].(map[string]any)
|
||||
if !ok {
|
||||
return AvailableEpisodes{}, fmt.Errorf("invalid detail")
|
||||
}
|
||||
|
||||
var count AvailableEpisodes
|
||||
if sub, ok := detail["sub"].([]any); ok {
|
||||
count.Sub = len(sub)
|
||||
}
|
||||
if dub, ok := detail["dub"].([]any); ok {
|
||||
count.Dub = len(dub)
|
||||
}
|
||||
if raw, ok := detail["raw"].([]any); ok {
|
||||
count.Raw = len(raw)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func buildStreamSource(url, sourceType, provider string) StreamSource {
|
||||
return StreamSource{
|
||||
URL: url,
|
||||
|
||||
@@ -2,6 +2,7 @@ package playback
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -107,6 +108,31 @@ func (h *Handler) HandleWatchPage(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("watch data error: %v", err)
|
||||
}
|
||||
|
||||
// Update episodes list if fallback has more
|
||||
if watchData.FallbackEpisodes != nil {
|
||||
maxCount := 0
|
||||
for _, count := range watchData.FallbackEpisodes {
|
||||
if count > maxCount {
|
||||
maxCount = count
|
||||
}
|
||||
}
|
||||
|
||||
if maxCount > len(episodes.Data) {
|
||||
// Add dummy episodes for the ones Jikan is missing
|
||||
start := len(episodes.Data) + 1
|
||||
for i := start; i <= maxCount; i++ {
|
||||
dummy := jikan.Episode{
|
||||
MalID: i,
|
||||
Episode: fmt.Sprintf("Episode %d", i),
|
||||
Title: fmt.Sprintf("Episode %d", i),
|
||||
Images: &jikan.EpisodeImages{},
|
||||
}
|
||||
dummy.Images.Jpg.ImageURL = dummy.GetFallbackImage(id)
|
||||
episodes.Data = append(episodes.Data, dummy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := templates.GetRenderer().ExecuteTemplate(w, "watch.gohtml", map[string]any{
|
||||
"Anime": anime,
|
||||
"Episodes": episodes.Data,
|
||||
|
||||
@@ -59,10 +59,11 @@ type playbackDataCacheItem struct {
|
||||
}
|
||||
|
||||
type playbackBaseData struct {
|
||||
Title string
|
||||
AvailableModes []string
|
||||
ModeSources map[string]ModeSource
|
||||
Segments []SkipSegment
|
||||
Title string
|
||||
AvailableModes []string
|
||||
ModeSources map[string]ModeSource
|
||||
Segments []SkipSegment
|
||||
FallbackEpisodes map[string]int
|
||||
}
|
||||
|
||||
type modeSourceResult struct {
|
||||
@@ -140,6 +141,13 @@ func (s *Service) BuildWatchPageData(ctx context.Context, malID int, titleCandid
|
||||
return WatchPageData{}, errors.New("no direct playable sources available")
|
||||
}
|
||||
|
||||
fallbackEpisodes := make(map[string]int)
|
||||
if counts, err := s.allAnimeClient.GetAvailableEpisodes(ctx, showID); err == nil {
|
||||
fallbackEpisodes["sub"] = counts.Sub
|
||||
fallbackEpisodes["dub"] = counts.Dub
|
||||
fallbackEpisodes["raw"] = counts.Raw
|
||||
}
|
||||
|
||||
watchTitle := strings.TrimSpace(resolvedTitle)
|
||||
if watchTitle == "" {
|
||||
watchTitle = firstNonEmptyTitle(titleCandidates)
|
||||
@@ -149,10 +157,11 @@ func (s *Service) BuildWatchPageData(ctx context.Context, malID int, titleCandid
|
||||
}
|
||||
|
||||
baseData = playbackBaseData{
|
||||
Title: watchTitle,
|
||||
AvailableModes: availableModes(modeSources),
|
||||
ModeSources: modeSources,
|
||||
Segments: segments,
|
||||
Title: watchTitle,
|
||||
AvailableModes: availableModes(modeSources),
|
||||
ModeSources: modeSources,
|
||||
Segments: segments,
|
||||
FallbackEpisodes: fallbackEpisodes,
|
||||
}
|
||||
|
||||
s.setPlaybackBaseDataCache(cacheKey, baseData)
|
||||
@@ -189,6 +198,7 @@ func (s *Service) BuildWatchPageData(ctx context.Context, malID int, titleCandid
|
||||
AvailableModes: cloneSlice(baseData.AvailableModes),
|
||||
ModeSources: clientModeSources,
|
||||
Segments: cloneSlice(segments),
|
||||
FallbackEpisodes: baseData.FallbackEpisodes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -342,10 +352,11 @@ func (s *Service) fetchPlaybackSourcesAndSegments(ctx context.Context, showID st
|
||||
|
||||
func clonePlaybackBaseData(data playbackBaseData) playbackBaseData {
|
||||
return playbackBaseData{
|
||||
Title: data.Title,
|
||||
AvailableModes: cloneSlice(data.AvailableModes),
|
||||
ModeSources: cloneModeSources(data.ModeSources),
|
||||
Segments: cloneSlice(data.Segments),
|
||||
Title: data.Title,
|
||||
AvailableModes: cloneSlice(data.AvailableModes),
|
||||
ModeSources: cloneModeSources(data.ModeSources),
|
||||
Segments: cloneSlice(data.Segments),
|
||||
FallbackEpisodes: data.FallbackEpisodes,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,13 +35,14 @@ type SkipSegment struct {
|
||||
}
|
||||
|
||||
type WatchPageData struct {
|
||||
MalID int
|
||||
Title string
|
||||
CurrentEpisode string
|
||||
StartTimeSeconds float64
|
||||
CurrentStatus string
|
||||
InitialMode string
|
||||
AvailableModes []string
|
||||
ModeSources map[string]ModeSource
|
||||
Segments []SkipSegment
|
||||
MalID int
|
||||
Title string
|
||||
CurrentEpisode string
|
||||
StartTimeSeconds float64
|
||||
CurrentStatus string
|
||||
InitialMode string
|
||||
AvailableModes []string
|
||||
ModeSources map[string]ModeSource
|
||||
Segments []SkipSegment
|
||||
FallbackEpisodes map[string]int
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user