diff --git a/static/app.js b/static/app.js index 5b99457..31f214e 100644 --- a/static/app.js +++ b/static/app.js @@ -446,6 +446,22 @@ async function showAnime(slug_id, opts = {}) { anB.classList.toggle("on", autoNext); }; const ctl = [langBtn, spd, prevB, nextB, fillB, jump, anB]; + const fsB = el("button", "tiny", "\u26f6 full"); + fsB.title = "fullscreen"; + fsB.onclick = () => { + const v = document.querySelector("#player video"); + if (!v) return; + if (document.fullscreenElement || document.webkitFullscreenElement) { + (document.exitFullscreen || document.webkitExitFullscreen).call(document); + return; + } + // native Fullscreen API: the video fills the window (and scales with it); + // iOS Safari has no element fullscreen, so fall back to its in-player one + if (v.requestFullscreen) v.requestFullscreen(); + else if (v.webkitEnterFullscreen) v.webkitEnterFullscreen(); + else if (v.webkitRequestFullscreen) v.webkitRequestFullscreen(); + }; + ctl.push(fsB); if (document.pictureInPictureEnabled) { const pipB = el("button", "tiny", "\u29c9 PiP"); pipB.title = "picture-in-picture"; diff --git a/tests/test_browser.py b/tests/test_browser.py index 2e0d8e3..07026e1 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -490,3 +490,31 @@ async def _stale_history_remap_board(): "location.search.includes('q=')", timeout=15000, polling=POLL) assert not errors, errors await browser.close() + + +def test_fullscreen_toggle(): + asyncio.run(_fullscreen_toggle()) + + +async def _fullscreen_toggle(): + # the player controls get a fullscreen button; in headless chromium the + # native API works, so exercise enter + exit. + async with async_playwright() as p: + browser = await p.chromium.launch(args=LAUNCH_ARGS) + page = await (await browser.new_context()).new_page() + errors = [] + page.on("pageerror", lambda e: errors.append(str(e))) + await _login(page, "admin", "adminpw12345") + await page.goto(BASE + "/?anime=chainsaw-man-922") + btn = page.locator("button:has-text('full')") + await btn.wait_for(timeout=30000) + assert await page.evaluate( + "() => typeof document.querySelector('#player video').requestFullscreen === 'function'") + await btn.click(force=True) + await page.wait_for_function( + "() => !!document.fullscreenElement", timeout=15000, polling=POLL) + await btn.click(force=True) + await page.wait_for_function( + "() => !document.fullscreenElement", timeout=15000, polling=POLL) + assert not errors, errors + await browser.close()