watch: sync watchlist status between watch and details page
This commit is contained in:
@@ -11,7 +11,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"mal/internal/database"
|
||||||
"mal/internal/jikan"
|
"mal/internal/jikan"
|
||||||
|
"mal/internal/shared/middleware"
|
||||||
"mal/internal/templates"
|
"mal/internal/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -75,7 +77,8 @@ func (h *Handler) HandleWatchPage(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
title := anime.DisplayTitle()
|
title := anime.DisplayTitle()
|
||||||
data, err := h.svc.BuildWatchPageData(ctx, malID, title, episode, mode)
|
userID := watchlistUserIDFromRequest(r)
|
||||||
|
data, err := h.svc.BuildWatchPageData(ctx, malID, title, episode, mode, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("watch page error for mal_id=%d: %v", malID, err)
|
log.Printf("watch page error for mal_id=%d: %v", malID, err)
|
||||||
http.Error(w, "Failed to load playback", http.StatusBadGateway)
|
http.Error(w, "Failed to load playback", http.StatusBadGateway)
|
||||||
@@ -87,6 +90,7 @@ func (h *Handler) HandleWatchPage(w http.ResponseWriter, r *http.Request) {
|
|||||||
MalID: data.MalID,
|
MalID: data.MalID,
|
||||||
Title: data.Title,
|
Title: data.Title,
|
||||||
CurrentEpisode: data.CurrentEpisode,
|
CurrentEpisode: data.CurrentEpisode,
|
||||||
|
CurrentStatus: data.CurrentStatus,
|
||||||
InitialMode: data.InitialMode,
|
InitialMode: data.InitialMode,
|
||||||
AvailableModes: data.AvailableModes,
|
AvailableModes: data.AvailableModes,
|
||||||
ModeSources: convertModeSources(data.ModeSources),
|
ModeSources: convertModeSources(data.ModeSources),
|
||||||
@@ -96,6 +100,15 @@ func (h *Handler) HandleWatchPage(w http.ResponseWriter, r *http.Request) {
|
|||||||
templates.WatchPage(anime, pageData).Render(r.Context(), w)
|
templates.WatchPage(anime, pageData).Render(r.Context(), w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func watchlistUserIDFromRequest(r *http.Request) string {
|
||||||
|
user, ok := r.Context().Value(middleware.UserContextKey).(*database.User)
|
||||||
|
if !ok || user == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return user.ID
|
||||||
|
}
|
||||||
|
|
||||||
func convertModeSources(sources map[string]ModeSource) map[string]templates.ModeSource {
|
func convertModeSources(sources map[string]ModeSource) map[string]templates.ModeSource {
|
||||||
result := make(map[string]templates.ModeSource, len(sources))
|
result := make(map[string]templates.ModeSource, len(sources))
|
||||||
for k, v := range sources {
|
for k, v := range sources {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"mal/internal/database"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -22,6 +23,7 @@ type Service struct {
|
|||||||
allAnimeClient *allAnimeClient
|
allAnimeClient *allAnimeClient
|
||||||
jikanClient *jikan.Client
|
jikanClient *jikan.Client
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
|
db database.Querier
|
||||||
}
|
}
|
||||||
|
|
||||||
type sourceScore struct {
|
type sourceScore struct {
|
||||||
@@ -33,15 +35,16 @@ type sourceScore struct {
|
|||||||
refererScore int
|
refererScore int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(jikanClient *jikan.Client) *Service {
|
func NewService(jikanClient *jikan.Client, db database.Querier) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
allAnimeClient: newAllAnimeClient(),
|
allAnimeClient: newAllAnimeClient(),
|
||||||
jikanClient: jikanClient,
|
jikanClient: jikanClient,
|
||||||
httpClient: &http.Client{Timeout: 12 * time.Second},
|
httpClient: &http.Client{Timeout: 12 * time.Second},
|
||||||
|
db: db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) BuildWatchPageData(ctx context.Context, malID int, title string, episode string, mode string) (WatchPageData, error) {
|
func (s *Service) BuildWatchPageData(ctx context.Context, malID int, title string, episode string, mode string, userID string) (WatchPageData, error) {
|
||||||
if malID <= 0 {
|
if malID <= 0 {
|
||||||
return WatchPageData{}, errors.New("invalid mal id")
|
return WatchPageData{}, errors.New("invalid mal id")
|
||||||
}
|
}
|
||||||
@@ -94,6 +97,17 @@ func (s *Service) BuildWatchPageData(ctx context.Context, malID int, title strin
|
|||||||
|
|
||||||
segments := s.fetchSkipSegments(ctx, malID, normalizedEpisode)
|
segments := s.fetchSkipSegments(ctx, malID, normalizedEpisode)
|
||||||
|
|
||||||
|
currentStatus := ""
|
||||||
|
if userID != "" && s.db != nil {
|
||||||
|
entry, err := s.db.GetWatchListEntry(ctx, database.GetWatchListEntryParams{
|
||||||
|
UserID: userID,
|
||||||
|
AnimeID: int64(malID),
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
currentStatus = entry.Status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watchTitle := strings.TrimSpace(resolvedTitle)
|
watchTitle := strings.TrimSpace(resolvedTitle)
|
||||||
if watchTitle == "" {
|
if watchTitle == "" {
|
||||||
watchTitle = strings.TrimSpace(title)
|
watchTitle = strings.TrimSpace(title)
|
||||||
@@ -106,6 +120,7 @@ func (s *Service) BuildWatchPageData(ctx context.Context, malID int, title strin
|
|||||||
MalID: malID,
|
MalID: malID,
|
||||||
Title: watchTitle,
|
Title: watchTitle,
|
||||||
CurrentEpisode: normalizedEpisode,
|
CurrentEpisode: normalizedEpisode,
|
||||||
|
CurrentStatus: currentStatus,
|
||||||
InitialMode: initialMode,
|
InitialMode: initialMode,
|
||||||
AvailableModes: availableModes,
|
AvailableModes: availableModes,
|
||||||
ModeSources: modeSources,
|
ModeSources: modeSources,
|
||||||
|
|||||||
@@ -41,12 +41,13 @@ type SkipSegment struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WatchPageData struct {
|
type WatchPageData struct {
|
||||||
MalID int
|
MalID int
|
||||||
Title string
|
Title string
|
||||||
CurrentEpisode string
|
CurrentEpisode string
|
||||||
InitialMode string
|
CurrentStatus string
|
||||||
AvailableModes []string
|
InitialMode string
|
||||||
ModeSources map[string]ModeSource
|
AvailableModes []string
|
||||||
Episodes []EpisodeListItem
|
ModeSources map[string]ModeSource
|
||||||
Segments []SkipSegment
|
Episodes []EpisodeListItem
|
||||||
|
Segments []SkipSegment
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func NewRouter(cfg Config) http.Handler {
|
|||||||
|
|
||||||
animeSvc := anime.NewService(cfg.JikanClient, cfg.DB)
|
animeSvc := anime.NewService(cfg.JikanClient, cfg.DB)
|
||||||
animeHandler := anime.NewHandler(animeSvc)
|
animeHandler := anime.NewHandler(animeSvc)
|
||||||
playbackSvc := playback.NewService(cfg.JikanClient)
|
playbackSvc := playback.NewService(cfg.JikanClient, cfg.DB)
|
||||||
playbackHandler := playback.NewHandler(playbackSvc, cfg.JikanClient)
|
playbackHandler := playback.NewHandler(playbackSvc, cfg.JikanClient)
|
||||||
|
|
||||||
// Serve static files
|
// Serve static files
|
||||||
|
|||||||
Reference in New Issue
Block a user