feat: PiP on mobile

feat: better mobile seeking
This commit is contained in:
ThaUnknown 2024-07-12 01:05:53 +02:00
parent 84b0a557a7
commit ccb05122ec
5 changed files with 32 additions and 9 deletions

View file

@ -14,6 +14,8 @@
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBarLaunch"
android:launchMode="singleTask"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View file

@ -37,6 +37,7 @@
"capacitor-plugin-safe-area": "^2.0.5",
"common": "workspace:*",
"cordova-plugin-navigationbar": "^1.0.31",
"cordova-plugin-pip": "^0.0.2",
"cordova-plugin-screen-orientation": "^3.0.4",
"es6-promise-plugin": "^4.2.2"
}

View file

@ -1,4 +1,4 @@
/* globals navigationbar */
/* globals navigationbar, PictureInPicture */
import { StatusBar, Style } from '@capacitor/status-bar'
import { SafeArea } from 'capacitor-plugin-safe-area'
import { App } from '@capacitor/app'
@ -91,3 +91,17 @@ StatusBar.setOverlaysWebView({ overlay: true })
navigationbar.setUp(true)
// cordova screen orientation plugin is also used, and it patches global screen.orientation.lock
// hook into pip request, and use our own pip implementation, then instantly report exit pip
// this is more like DOM PiP, rather than video PiP
HTMLVideoElement.prototype.requestPictureInPicture = function () {
PictureInPicture.enter(this.videoWidth, this.videoHeight, success => {
this.dispatchEvent(new Event('leavepictureinpicture'))
if (success) document.querySelector('.content-wrapper').requestFullscreen()
}, err => {
this.dispatchEvent(new Event('leavepictureinpicture'))
console.error(err)
})
return Promise.resolve({})
}

View file

@ -414,6 +414,7 @@
if (!subs?.renderer) {
if (video !== document.pictureInPictureElement) {
video.requestPictureInPicture()
resetImmerse()
pip = true
} else {
document.exitPictureInPicture()
@ -433,6 +434,7 @@
canvasVideo.remove()
}
pip = true
resetImmerse()
canvasVideo.srcObject = stream
canvasVideo.onloadedmetadata = () => {
canvasVideo.play()
@ -612,7 +614,7 @@
if (!noSubs) subs.renderer.resize(video.videoWidth, video.videoHeight)
const renderFrame = () => {
context.drawImage(deband ? deband.canvas : video, 0, 0)
if (!noSubs) context.drawImage(subs.renderer?._canvas, 0, 0, canvas.width, canvas.height)
if (!noSubs && canvas.width && canvas.height) context.drawImage(subs.renderer?._canvas, 0, 0, canvas.width, canvas.height)
loop = video.requestVideoFrameCallback(renderFrame)
}
renderFrame()
@ -1107,11 +1109,9 @@
<!-- eslint-disable-next-line svelte/valid-compile -->
<div class='w-full h-full position-absolute toggle-immerse d-none' on:dblclick={toggleFullscreen} on:click|self={toggleImmerse} />
<div class='w-full h-full position-absolute mobile-focus-target d-none' use:click={() => { page = 'player' }} />
<span class='material-symbols-outlined ctrl' class:text-muted={!hasLast} class:disabled={!hasLast} use:click={playLast}> skip_previous </span>
<span class='material-symbols-outlined ctrl' use:click={rewind}> fast_rewind </span>
<span class='material-symbols-outlined ctrl h-full align-items-center justify-content-end w-150 mw-full mr-auto' use:click={rewind}> fast_rewind </span>
<span class='material-symbols-outlined ctrl' data-name='playPause' use:click={playPause}> {ended ? 'replay' : paused ? 'play_arrow' : 'pause'} </span>
<span class='material-symbols-outlined ctrl' use:click={forward}> fast_forward </span>
<span class='material-symbols-outlined ctrl' class:text-muted={!hasNext} class:disabled={!hasNext} use:click={playNext}> skip_next </span>
<span class='material-symbols-outlined ctrl h-full align-items-center w-150 mw-full ml-auto' use:click={forward}> fast_forward </span>
<div class='position-absolute bufferingDisplay' />
{#if currentSkippable}
<button class='skip btn text-dark position-absolute bottom-0 right-0 mr-20 mb-5 font-weight-bold' use:click={skip}>
@ -1136,10 +1136,10 @@
<div class='d-flex'>
<span class='material-symbols-outlined ctrl' title='Play/Pause [Space]' data-name='playPause' use:click={playPause}> {ended ? 'replay' : paused ? 'play_arrow' : 'pause'} </span>
{#if hasLast}
<span class='material-symbols-outlined ctrl' title='Last [B]' data-name='playLast' use:click={playLast}> skip_previous </span>
<span class='material-symbols-outlined ctrl' title='Last [B]' use:click={playLast}> skip_previous </span>
{/if}
{#if hasNext}
<span class='material-symbols-outlined ctrl' title='Next [N]' data-name='playNext' use:click={playNext}> skip_next </span>
<span class='material-symbols-outlined ctrl' title='Next [N]' use:click={playNext}> skip_next </span>
{/if}
<div class='d-flex w-auto volume'>
<span class='material-symbols-outlined ctrl' title='Mute [M]' data-name='toggleMute' use:click={toggleMute}> {muted ? 'volume_off' : 'volume_up'} </span>
@ -1526,7 +1526,6 @@
@media (pointer: none), (pointer: coarse) {
.bottom .ctrl[data-name='playPause'],
.bottom .ctrl[data-name='playNext'],
.bottom .volume,
.bottom .keybinds {
display: none !important;

View file

@ -99,6 +99,9 @@ importers:
cordova-plugin-navigationbar:
specifier: ^1.0.31
version: 1.0.31
cordova-plugin-pip:
specifier: ^0.0.2
version: 0.0.2
cordova-plugin-screen-orientation:
specifier: ^3.0.4
version: 3.0.4
@ -3282,6 +3285,10 @@ packages:
engines: {'0': {name: cordova, version: '>=3.0.0'}}
dev: false
/cordova-plugin-pip@0.0.2:
resolution: {integrity: sha512-Wy9aK7rQX3kif6+kUl7RwTG/+arixvW5Sphyr0bPxsVrkVIliseW/S/JhO7BVX25/HGU0lGMvQe8a134yvI9iQ==}
dev: false
/cordova-plugin-screen-orientation@3.0.4:
resolution: {integrity: sha512-AswRuUKJ8J3HycUilTJsIB50aa9TLrwndPNBFG+wfAPhHEqIBF0HaD0q3HbAK7ypgmaj0cvbzk84qP51IcTfrQ==}
engines: {cordovaDependencies: {4.0.0: {cordova: '>100'}}}