diff --git a/src/components/VideoPlayer/index.tsx b/src/components/VideoPlayer/index.tsx
index 8b7c1c38..3b67933b 100644
--- a/src/components/VideoPlayer/index.tsx
+++ b/src/components/VideoPlayer/index.tsx
@@ -1,4 +1,4 @@
-import { cn } from '@/lib/utils'
+import { cn, isInViewport } from '@/lib/utils'
import videoManager from '@/services/video-manager.service'
import { useEffect, useRef } from 'react'
import NsfwOverlay from '../NsfwOverlay'
@@ -25,11 +25,17 @@ export default function VideoPlayer({
const observer = new IntersectionObserver(
([entry]) => {
- if (!entry.isIntersecting && !video.paused) {
- videoManager.enterPiP(video)
+ if (entry.isIntersecting) {
+ setTimeout(() => {
+ if (isInViewport(container)) {
+ videoManager.autoPlay(video)
+ }
+ }, 200)
+ } else {
+ videoManager.pause(video)
}
},
- { threshold: 0.5 }
+ { threshold: 1 }
)
observer.observe(container)
@@ -39,13 +45,6 @@ export default function VideoPlayer({
}
}, [])
- const handlePlay = async () => {
- const video = videoRef.current
- if (!video) return
-
- await videoManager.playVideo(video)
- }
-
return (
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index c7eeeb61..4faf2c55 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -11,3 +11,13 @@ export function isSafari() {
const vendor = window.navigator.vendor
return /Safari/.test(ua) && /Apple Computer/.test(vendor) && !/Chrome/.test(ua)
}
+
+export function isInViewport(el: HTMLElement) {
+ const rect = el.getBoundingClientRect()
+ return (
+ rect.top >= 0 &&
+ rect.left >= 0 &&
+ rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
+ rect.right <= (window.innerWidth || document.documentElement.clientWidth)
+ )
+}
diff --git a/src/services/video-manager.service.ts b/src/services/video-manager.service.ts
index 32c80b29..ab5f0bc0 100644
--- a/src/services/video-manager.service.ts
+++ b/src/services/video-manager.service.ts
@@ -6,59 +6,59 @@ class VideoManagerService {
constructor() {
if (!VideoManagerService.instance) {
VideoManagerService.instance = this
- document.addEventListener('leavepictureinpicture', (e) => {
- ;(e.target as HTMLVideoElement).pause()
- })
}
return VideoManagerService.instance
}
- enterPiP(video: HTMLVideoElement) {
- if (this.currentVideo && this.currentVideo !== video) {
- this.exitPiP(this.currentVideo)
+ pause(video: HTMLVideoElement) {
+ if (isPipElement(video)) {
+ return
}
-
- if (
- (video as any).webkitSupportsPresentationMode &&
- typeof (video as any).webkitSetPresentationMode === 'function'
- ) {
- ;(video as any).webkitSetPresentationMode('picture-in-picture')
- setTimeout(() => {
- if ((video as any).webkitPresentationMode !== 'picture-in-picture') {
- video.pause()
- }
- }, 10)
- } else {
- video.requestPictureInPicture().catch(() => {
- video.pause()
- })
- }
- }
-
- private exitPiP(video: HTMLVideoElement) {
- video.pause()
- if (
- (video as any).webkitSupportsPresentationMode &&
- typeof (video as any).webkitSetPresentationMode === 'function'
- ) {
- ;(video as any).webkitSetPresentationMode('inline')
- } else {
- document.exitPictureInPicture()
- }
-
if (this.currentVideo === video) {
this.currentVideo = null
}
+ video.pause()
}
- async playVideo(video: HTMLVideoElement) {
+ autoPlay(video: HTMLVideoElement) {
+ if (
+ document.pictureInPictureElement &&
+ isVideoPlaying(document.pictureInPictureElement as HTMLVideoElement)
+ ) {
+ return
+ }
+ this.play(video)
+ }
+
+ play(video: HTMLVideoElement) {
+ if (document.pictureInPictureElement && document.pictureInPictureElement !== video) {
+ ;(document.pictureInPictureElement as HTMLVideoElement).pause()
+ }
if (this.currentVideo && this.currentVideo !== video) {
- this.exitPiP(this.currentVideo)
+ this.currentVideo.pause()
}
this.currentVideo = video
- video.play()
+ if (isVideoPlaying(video)) {
+ return
+ }
+
+ this.currentVideo.play().catch((error) => {
+ console.error('Error playing video:', error)
+ this.currentVideo = null
+ })
}
}
const instance = new VideoManagerService()
export default instance
+
+function isVideoPlaying(video: HTMLVideoElement) {
+ return video.currentTime > 0 && !video.paused && !video.ended && video.readyState >= 2
+}
+
+function isPipElement(video: HTMLVideoElement) {
+ if (document.pictureInPictureElement === video) {
+ return true
+ }
+ return (video as any).webkitPresentationMode === 'picture-in-picture'
+}