diff --git a/api/playback/handler.go b/api/playback/handler.go index 01452d1..9040b7e 100644 --- a/api/playback/handler.go +++ b/api/playback/handler.go @@ -17,6 +17,7 @@ import ( "mal/integrations/jikan" "mal/internal/db" "mal/internal/middleware" + "mal/web/components/watch" "mal/web/shared" "mal/web/templates" ) @@ -115,6 +116,14 @@ func (h *Handler) HandleWatchPage(w http.ResponseWriter, r *http.Request) { Segments: convertSegments(data.Segments), } + if r.Header.Get("HX-Request") == "true" { + if err := watch.VideoPlayer(pageData).Render(r.Context(), w); err != nil { + log.Printf("render error: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + } + return + } + if err := templates.WatchPage(anime, pageData).Render(r.Context(), w); err != nil { log.Printf("render error: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) diff --git a/static/player.ts b/static/player.ts index 78a697e..98c3e91 100644 --- a/static/player.ts +++ b/static/player.ts @@ -1,3 +1,7 @@ +declare const htmx: { + ajax(verb: string, path: string, target: HTMLElement): Promise +} + export {} interface ModeSource { @@ -16,10 +20,17 @@ interface SkipSegment { end: number } +let playerInitialized = false + const initPlayer = (): void => { const container = document.querySelector('[data-video-player]') if (!container) return + if (playerInitialized) return + + const shouldAutoPlay = sessionStorage.getItem('mal:autoplay-next') === 'true' + sessionStorage.removeItem('mal:autoplay-next') + const video = container.querySelector('video') as HTMLVideoElement const loading = container.querySelector('[data-loading]') as HTMLElement const playPause = container.querySelector('[data-play-pause]') as HTMLButtonElement @@ -686,6 +697,9 @@ const initPlayer = (): void => { } catch {} pendingSeekTime = null } + if (shouldAutoPlay) { + video.play().catch(() => {}) + } updateTimeline(video.currentTime) updateSkipButton(video.currentTime) }) @@ -727,26 +741,27 @@ const initPlayer = (): void => { }) } - const goToNextEpisode = (): void => { - const pathParts = window.location.pathname.split('/') - if (pathParts.length < 4) return +const goToNextEpisode = (): void => { + const pathParts = window.location.pathname.split('/') + if (pathParts.length < 4) return - const animeID = pathParts[2] - const currentEpisodeNumber = Number.parseInt(pathParts[3], 10) - if (Number.isNaN(currentEpisodeNumber)) return + const animeID = pathParts[2] + const currentEpisodeNumber = Number.parseInt(pathParts[3], 10) + if (Number.isNaN(currentEpisodeNumber)) return - if (Number.isInteger(totalEpisodes) && totalEpisodes > 0 && currentEpisodeNumber >= totalEpisodes) { - completeAnime(currentEpisodeNumber) - return - } - - const nextEpisode = currentEpisodeNumber + 1 - markEpisodeTransition(nextEpisode) - const nextUrl = `/watch/${animeID}/${nextEpisode}` - - window.location.href = nextUrl + if (Number.isInteger(totalEpisodes) && totalEpisodes > 0 && currentEpisodeNumber >= totalEpisodes) { + completeAnime(currentEpisodeNumber) + return } + const nextEpisode = currentEpisodeNumber + 1 + markEpisodeTransition(nextEpisode) + const nextUrl = `/watch/${animeID}/${nextEpisode}` + + sessionStorage.setItem('mal:autoplay-next', 'true') + window.location.href = nextUrl +} + const completeAnime = async (episodeNumber: number): Promise => { if (completionSent) return if (!Number.isInteger(malID) || malID <= 0) return @@ -995,6 +1010,15 @@ const initPlayer = (): void => { syncVolumeUI() updateSkipButton(0) showControls() + + playerInitialized = true } document.addEventListener('DOMContentLoaded', initPlayer) +document.body.addEventListener('htmx:afterSwap', (e: Event) => { + const target = (e as CustomEvent).detail?.target as HTMLElement | null + if (!target) return + if (target.querySelector('[data-video-player]')) { + initPlayer() + } +}) diff --git a/web/templates/watch.templ b/web/templates/watch.templ index f85490e..1d665fb 100644 --- a/web/templates/watch.templ +++ b/web/templates/watch.templ @@ -33,7 +33,10 @@ templ WatchPage(anime jikan.Anime, data shared.WatchPageData) { -
+
@watch.VideoPlayer(data)
if shared.CanGoPrevEpisode(data.CurrentEpisode) {