feat: use canonical episodes in watch data and wire episode service

This commit is contained in:
2026-05-17 21:16:50 +02:00
parent c5ea265c46
commit 9414d3b51e
2 changed files with 28 additions and 45 deletions

View File

@@ -22,10 +22,10 @@ var Module = fx.Options(
fx.Provide(
repository.NewPlaybackRepository,
fx.Annotate(
func(repo domain.PlaybackRepository, providers []domain.Provider, jikan *jikan.Client, proxyTokenKey string) domain.PlaybackService {
return service.NewPlaybackService(repo, providers, jikan, proxyTokenKey)
func(repo domain.PlaybackRepository, providers []domain.Provider, jikan *jikan.Client, episodeSvc domain.EpisodeService, proxyTokenKey string) domain.PlaybackService {
return service.NewPlaybackService(repo, providers, jikan, episodeSvc, proxyTokenKey)
},
fx.ParamTags(``, ``, ``, ``),
fx.ParamTags(``, ``, ``, ``, ``),
),
func(svc domain.PlaybackService, animeSvc domain.AnimeService) *handler.PlaybackHandler {
return handler.NewPlaybackHandler(svc, animeSvc)

View File

@@ -26,6 +26,7 @@ type playbackService struct {
repo domain.PlaybackRepository
providers []domain.Provider
jikan *jikan.Client
episodes domain.EpisodeService
httpClient *http.Client
proxyTokenKey string
}
@@ -43,8 +44,8 @@ type proxyTokenPayload struct {
ExpiresAt int64 `json:"exp"`
}
func NewPlaybackService(repo domain.PlaybackRepository, providers []domain.Provider, jikan *jikan.Client, proxyTokenKey string) domain.PlaybackService {
return &playbackService{repo: repo, providers: providers, jikan: jikan, httpClient: &http.Client{Timeout: 10 * time.Second}, proxyTokenKey: proxyTokenKey}
func NewPlaybackService(repo domain.PlaybackRepository, providers []domain.Provider, jikan *jikan.Client, episodes domain.EpisodeService, proxyTokenKey string) domain.PlaybackService {
return &playbackService{repo: repo, providers: providers, jikan: jikan, episodes: episodes, httpClient: &http.Client{Timeout: 10 * time.Second}, proxyTokenKey: proxyTokenKey}
}
func (s *playbackService) SignProxyToken(targetURL, referer, scope string) (string, error) {
@@ -127,6 +128,23 @@ func (s *playbackService) BuildWatchData(ctx context.Context, animeID int, title
}
}
canonicalEpisodes, err := s.episodes.GetCanonicalEpisodes(ctx, anime, false)
if err != nil {
return nil, fmt.Errorf("failed to fetch episodes: %w", err)
}
requestedMode := mode
modeSwitchedFrom := ""
if epNum, parseErr := strconv.Atoi(episode); parseErr == nil && requestedMode == "dub" {
for _, ep := range canonicalEpisodes.Episodes {
if ep.Number == epNum && !ep.HasDub && ep.HasSub {
mode = "sub"
modeSwitchedFrom = requestedMode
break
}
}
}
type SubtitleItem struct {
Lang string `json:"lang"`
URL string `json:"url,omitempty"`
@@ -213,42 +231,6 @@ func (s *playbackService) BuildWatchData(ctx context.Context, animeID int, title
}
}
// 4. Get Episodes list
jikanEpisodes, err := s.jikan.GetAllEpisodes(ctx, animeID)
if err != nil {
}
// Fallback/Fill episodes if needed
totalCount := anime.Episodes
if len(jikanEpisodes) < totalCount {
epMap := make(map[int]jikan.Episode)
for _, ep := range jikanEpisodes {
epMap[ep.MalID] = ep
}
for i := 1; i <= totalCount; i++ {
if _, ok := epMap[i]; !ok {
jikanEpisodes = append(jikanEpisodes, jikan.Episode{
MalID: i,
Episode: fmt.Sprintf("Episode %d", i),
Title: fmt.Sprintf("Episode %d", i),
})
}
}
}
sort.Slice(jikanEpisodes, func(i, j int) bool {
return jikanEpisodes[i].MalID < jikanEpisodes[j].MalID
})
domainEpisodes := make([]domain.EpisodeData, len(jikanEpisodes))
for i, ep := range jikanEpisodes {
domainEpisodes[i] = domain.EpisodeData{
MalID: ep.MalID,
Title: ep.Title,
IsFiller: ep.Filler,
IsRecap: ep.Recap,
}
}
// 5. Build provider data
streams := []domain.ProviderStream{
{
@@ -295,12 +277,13 @@ func (s *playbackService) BuildWatchData(ctx context.Context, animeID int, title
"Title": anime.DisplayTitle(),
"CurrentEpisode": episode,
"StartTimeSeconds": startTime,
"Episodes": domainEpisodes,
"Episodes": canonicalEpisodes.Episodes,
"Providers": []domain.ProviderData{
{Streams: streams},
},
"ModeSources": modeSources,
"InitialMode": mode,
"ModeSources": modeSources,
"InitialMode": mode,
"ModeSwitchedFrom": modeSwitchedFrom,
"AvailableModes": func() []string {
var modes []string
for m := range modeSources {
@@ -315,7 +298,7 @@ func (s *playbackService) BuildWatchData(ctx context.Context, animeID int, title
return map[string]any{
"WatchData": watchData,
"Anime": anime,
"Episodes": domainEpisodes,
"Episodes": canonicalEpisodes.Episodes,
"CurrentEpID": episode,
"WatchlistStatus": watchlistStatus,
"WatchlistIDs": watchlistIDs,