From 77be47297f7a203fdfafa9b08eaad39a4aec6da2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 28 Apr 2026 06:25:40 +0000
Subject: [PATCH] Add autoplay toggle button to video player
Agent-Logs-Url: https://github.com/mkelvers/mal/sessions/72375034-49a9-451f-8e30-ee26c9c20eab
Co-authored-by: melosh101 <59763532+melosh101@users.noreply.github.com>
---
static/player.ts | 23 ++++++++++++++++++++++-
web/components/watch/video_player.templ | 14 ++++++++++++++
2 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/static/player.ts b/static/player.ts
index 6984e65..fd40cb3 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 = container.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,15 @@ 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()
+ autoplayBtn.title = enabled ? 'Autoplay: On' : 'Autoplay: Off'
+ autoplayBtn.classList.toggle('opacity-40', !enabled)
+ }
+
const timelineBounds = (): { start: number, end: number, duration: number } => {
const duration = Number.isFinite(video.duration) && video.duration > 0 ? video.duration : 0
@@ -803,6 +813,8 @@ const goToNextEpisode = (): void => {
return
}
+ if (!isAutoplayEnabled()) return
+
const nextEpisode = currentEpisodeNumber + 1
markEpisodeTransition(nextEpisode)
@@ -862,7 +874,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 +1050,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 +1162,7 @@ const loadNextEpisodeInPlace = async (animeID: number, nextEpisode: number): Pro
updatePlayPauseIcons(false)
syncVolumeUI()
updateSkipButton(0)
+ updateAutoplayButton()
showControls()
playerInitialized = true
diff --git a/web/components/watch/video_player.templ b/web/components/watch/video_player.templ
index f2c5434..4788076 100644
--- a/web/components/watch/video_player.templ
+++ b/web/components/watch/video_player.templ
@@ -251,6 +251,20 @@ templ VideoPlayer(data shared.WatchPageData, displayTitle string) {
+