diff --git a/static/player.ts b/static/player.ts index 6984e65..6fedbb3 100644 --- a/static/player.ts +++ b/static/player.ts @@ -81,6 +81,7 @@ const initPlayer = (): void => { const forwardBtn = container.querySelector('[data-forward]') as HTMLButtonElement const fullscreenBtn = container.querySelector('[data-fullscreen]') as HTMLButtonElement const skipSegmentBtn = container.querySelector('[data-skip]') as HTMLButtonElement + const autoplayBtn = document.querySelector('[data-autoplay]') as HTMLButtonElement const subtitleText = container.querySelector('[data-subtitle-text]') as HTMLElement const streamURL = container.getAttribute('data-stream-url') || '/watch/proxy/stream' @@ -176,6 +177,21 @@ const initPlayer = (): void => { const skipLabel = (segmentType: string): string => segmentType === 'ed' ? 'Skip outro' : 'Skip intro' + const isAutoplayEnabled = (): boolean => localStorage.getItem('mal:autoplay-enabled') !== 'false' + + const updateAutoplayButton = (): void => { + if (!autoplayBtn) return + const enabled = isAutoplayEnabled() + const label = enabled ? 'Autoplay: On' : 'Autoplay: Off' + autoplayBtn.title = label + autoplayBtn.classList.remove('opacity-40', 'opacity-50') + if (!enabled) autoplayBtn.classList.add('opacity-50') + const lastChild = autoplayBtn.lastChild + if (lastChild && lastChild.nodeType === Node.TEXT_NODE) { + lastChild.textContent = label + } + } + const timelineBounds = (): { start: number, end: number, duration: number } => { const duration = Number.isFinite(video.duration) && video.duration > 0 ? video.duration : 0 @@ -803,6 +819,8 @@ const goToNextEpisode = (): void => { return } + if (!isAutoplayEnabled()) return + const nextEpisode = currentEpisodeNumber + 1 markEpisodeTransition(nextEpisode) @@ -862,7 +880,9 @@ const loadNextEpisodeInPlace = async (animeID: number, nextEpisode: number): Pro } video.load() - video.play().catch(() => {}) + if (isAutoplayEnabled()) { + video.play().catch(() => {}) + } parsedSegments = (data.segments || []) .map((segment: SkipSegment) => { @@ -1036,6 +1056,12 @@ const loadNextEpisodeInPlace = async (animeID: number, nextEpisode: number): Pro modeDub?.addEventListener('click', toggleDub) modeSub?.addEventListener('click', toggleSub) + autoplayBtn?.addEventListener('click', () => { + localStorage.setItem('mal:autoplay-enabled', isAutoplayEnabled() ? 'false' : 'true') + updateAutoplayButton() + showControls() + }) + subtitleSelect?.addEventListener('change', async () => { const selected = subtitleSelect.value if (selected === 'none') { @@ -1142,6 +1168,7 @@ const loadNextEpisodeInPlace = async (animeID: number, nextEpisode: number): Pro updatePlayPauseIcons(false) syncVolumeUI() updateSkipButton(0) + updateAutoplayButton() showControls() playerInitialized = true diff --git a/web/templates/watch.templ b/web/templates/watch.templ index 530fd48..acdf74e 100644 --- a/web/templates/watch.templ +++ b/web/templates/watch.templ @@ -39,7 +39,19 @@ templ WatchPage(anime jikan.Anime, data shared.WatchPageData) { hx-boost="true" > @watch.VideoPlayer(data, anime.DisplayTitle()) -
+
+ +
if shared.CanGoPrevEpisode(data.CurrentEpisode) { +