From 19481caeecfff003a01426aa194835cb22377754 Mon Sep 17 00:00:00 2001 From: mkelvers Date: Sun, 26 Apr 2026 23:10:23 +0200 Subject: [PATCH] feat: show video overlay with episode info in fullscreen --- api/playback/handler.go | 13 ++++- integrations/jikan/episodes.go | 9 ++++ integrations/jikan/types.go | 4 ++ web/components/watch/video_player.templ | 63 +++++++++++++++---------- web/shared/watch.go | 1 + web/templates/watch.templ | 2 +- 6 files changed, 65 insertions(+), 27 deletions(-) diff --git a/api/playback/handler.go b/api/playback/handler.go index 7e6ea5e..97017d6 100644 --- a/api/playback/handler.go +++ b/api/playback/handler.go @@ -99,6 +99,16 @@ func (h *Handler) HandleWatchPage(w http.ResponseWriter, r *http.Request) { return } + // Fetch episode title for the overlay + episodeTitle := "" + epNum, epErr := strconv.Atoi(episode) + if epErr == nil && epNum > 0 { + episodeData, epErr := h.jikanClient.GetEpisode(ctx, malID, epNum) + if epErr == nil && episodeData.Data.Title != "" { + episodeTitle = episodeData.Data.Title + } + } + // Convert playback.WatchPageData to shared.WatchPageData pageData := shared.WatchPageData{ MalID: data.MalID, @@ -115,10 +125,11 @@ func (h *Handler) HandleWatchPage(w http.ResponseWriter, r *http.Request) { AvailableModes: data.AvailableModes, ModeSources: convertModeSources(data.ModeSources), Segments: convertSegments(data.Segments), + EpisodeTitle: episodeTitle, } if r.Header.Get("HX-Request") == "true" { - if err := watch.VideoPlayer(pageData).Render(r.Context(), w); err != nil { + if err := watch.VideoPlayer(pageData, anime.DisplayTitle()).Render(r.Context(), w); err != nil { log.Printf("render error: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } diff --git a/integrations/jikan/episodes.go b/integrations/jikan/episodes.go index 238db69..2565ea7 100644 --- a/integrations/jikan/episodes.go +++ b/integrations/jikan/episodes.go @@ -18,3 +18,12 @@ func (c *Client) GetEpisodes(ctx context.Context, animeID int, page int) (Episod err := c.getWithCache(ctx, cacheKey, 12*time.Hour, reqURL, &result) return result, err } + +func (c *Client) GetEpisode(ctx context.Context, animeID int, episode int) (EpisodeResponse, error) { + cacheKey := fmt.Sprintf("anime:%d:episode:%d", animeID, episode) + var result EpisodeResponse + reqURL := fmt.Sprintf("%s/anime/%d/episodes/%d", c.baseURL, animeID, episode) + + err := c.getWithCache(ctx, cacheKey, 24*time.Hour, reqURL, &result) + return result, err +} diff --git a/integrations/jikan/types.go b/integrations/jikan/types.go index 7e85827..77324a4 100644 --- a/integrations/jikan/types.go +++ b/integrations/jikan/types.go @@ -168,6 +168,10 @@ type EpisodesResponse struct { Pagination Pagination `json:"pagination"` } +type EpisodeResponse struct { + Data Episode `json:"data"` +} + type JikanRelationEntry struct { MalID int `json:"mal_id"` Type string `json:"type"` diff --git a/web/components/watch/video_player.templ b/web/components/watch/video_player.templ index f473254..79c0d84 100644 --- a/web/components/watch/video_player.templ +++ b/web/components/watch/video_player.templ @@ -5,12 +5,13 @@ import ( "mal/web/shared" ) -templ VideoPlayer(data shared.WatchPageData) { +templ VideoPlayer(data shared.WatchPageData, displayTitle string) { {{ streamToken := shared.ModeToken(data.InitialMode, data.ModeSources) }} {{ hasDub := shared.ModeAvailable(data.AvailableModes, "dub") }} {{ hasSub := shared.ModeAvailable(data.AvailableModes, "sub") }} + {{ episodeTitle := data.EpisodeTitle }}
-
+
+
+

{ displayTitle }

+

+ if episodeTitle != "" { + { fmt.Sprintf("Episode %s, %s", data.CurrentEpisode, episodeTitle) } + } else { + { fmt.Sprintf("Episode %s", data.CurrentEpisode) } + } +

+
diff --git a/web/shared/watch.go b/web/shared/watch.go index 629c764..4429729 100644 --- a/web/shared/watch.go +++ b/web/shared/watch.go @@ -23,6 +23,7 @@ type WatchPageData struct { AvailableModes []string ModeSources map[string]ModeSource Segments []SkipSegment + EpisodeTitle string } // ModeSource represents a stream source for a specific mode (dub/sub) diff --git a/web/templates/watch.templ b/web/templates/watch.templ index 965222d..530fd48 100644 --- a/web/templates/watch.templ +++ b/web/templates/watch.templ @@ -38,7 +38,7 @@ templ WatchPage(anime jikan.Anime, data shared.WatchPageData) { class="order-1 flex min-w-0 flex-1 flex-col gap-4 sm:gap-5 lg:order-2" hx-boost="true" > - @watch.VideoPlayer(data) + @watch.VideoPlayer(data, anime.DisplayTitle())
if shared.CanGoPrevEpisode(data.CurrentEpisode) {