10 changed files with 168 additions and 46 deletions
@ -1,3 +1,17 @@
@@ -1,3 +1,17 @@
|
||||
import { usePlayerStore } from "@/stores/player/store"; |
||||
|
||||
export function Pause() { |
||||
return <button type="button" />; |
||||
const display = usePlayerStore((s) => s.display); |
||||
const { isPaused } = usePlayerStore((s) => s.mediaPlaying); |
||||
|
||||
const toggle = () => { |
||||
if (isPaused) display?.play(); |
||||
else display?.pause(); |
||||
}; |
||||
|
||||
return ( |
||||
<button type="button" onClick={toggle}> |
||||
play/pause |
||||
</button> |
||||
); |
||||
} |
||||
|
@ -1 +1,45 @@
@@ -1 +1,45 @@
|
||||
export {}; |
||||
import { |
||||
DisplayInterface, |
||||
DisplayInterfaceEvents, |
||||
} from "@/components/player/display/displayInterface"; |
||||
import { Source } from "@/components/player/hooks/usePlayer"; |
||||
import { makeEmitter } from "@/utils/events"; |
||||
|
||||
export function makeVideoElementDisplayInterface(): DisplayInterface { |
||||
const { emit, on, off } = makeEmitter<DisplayInterfaceEvents>(); |
||||
let source: Source | null = null; |
||||
let videoElement: HTMLVideoElement | null = null; |
||||
|
||||
function setSource() { |
||||
if (!videoElement || !source) return; |
||||
videoElement.src = source.url; |
||||
videoElement.addEventListener("play", () => emit("play", undefined)); |
||||
videoElement.addEventListener("pause", () => emit("pause", undefined)); |
||||
} |
||||
|
||||
return { |
||||
on, |
||||
off, |
||||
|
||||
// no need to destroy anything
|
||||
destroy: () => {}, |
||||
|
||||
load(newSource) { |
||||
source = newSource; |
||||
setSource(); |
||||
}, |
||||
|
||||
processVideoElement(video) { |
||||
videoElement = video; |
||||
setSource(); |
||||
}, |
||||
|
||||
pause() { |
||||
videoElement?.pause(); |
||||
}, |
||||
|
||||
play() { |
||||
videoElement?.play(); |
||||
}, |
||||
}; |
||||
} |
||||
|
@ -1,36 +1,46 @@
@@ -1,36 +1,46 @@
|
||||
import { RefObject, useEffect, useRef } from "react"; |
||||
import { useEffect, useRef } from "react"; |
||||
|
||||
import { MWStreamType } from "@/backend/helpers/streams"; |
||||
import { SourceSliceSource } from "@/stores/player/slices/source"; |
||||
import { AllSlices } from "@/stores/player/slices/types"; |
||||
import { makeVideoElementDisplayInterface } from "@/components/player/display/base"; |
||||
import { playerStatus } from "@/stores/player/slices/source"; |
||||
import { usePlayerStore } from "@/stores/player/store"; |
||||
|
||||
// should this video container show right now?
|
||||
function useShouldShow(source: SourceSliceSource | null): boolean { |
||||
if (!source) return false; |
||||
if (source.type !== MWStreamType.MP4) return false; |
||||
// initialize display interface
|
||||
function useDisplayInterface() { |
||||
const display = usePlayerStore((s) => s.display); |
||||
const setDisplay = usePlayerStore((s) => s.setDisplay); |
||||
|
||||
useEffect(() => { |
||||
if (!display) { |
||||
setDisplay(makeVideoElementDisplayInterface()); |
||||
} |
||||
}, [display, setDisplay]); |
||||
} |
||||
|
||||
function useShouldShowVideoElement() { |
||||
const status = usePlayerStore((s) => s.status); |
||||
|
||||
if (status !== playerStatus.PLAYING) return false; |
||||
return true; |
||||
} |
||||
|
||||
// make video element up to par with the state
|
||||
function useRestoreVideo( |
||||
videoRef: RefObject<HTMLVideoElement>, |
||||
player: AllSlices |
||||
) { |
||||
function VideoElement() { |
||||
const videoEl = useRef<HTMLVideoElement>(null); |
||||
const display = usePlayerStore((s) => s.display); |
||||
|
||||
// report video element to display interface
|
||||
useEffect(() => { |
||||
const el = videoRef.current; |
||||
const src = player.source?.url ?? ""; |
||||
if (!el) return; |
||||
if (el.src !== src) el.src = src; |
||||
}, [player.source?.url, videoRef]); |
||||
if (display && videoEl.current) { |
||||
display.processVideoElement(videoEl.current); |
||||
} |
||||
}, [display, videoEl]); |
||||
|
||||
return <video autoPlay ref={videoEl} />; |
||||
} |
||||
|
||||
export function VideoContainer() { |
||||
const videoEl = useRef<HTMLVideoElement>(null); |
||||
const player = usePlayerStore(); |
||||
useRestoreVideo(videoEl, player); |
||||
const show = useShouldShow(player.source); |
||||
const show = useShouldShowVideoElement(); |
||||
useDisplayInterface(); |
||||
|
||||
if (!show) return null; |
||||
return <video autoPlay ref={videoEl} />; |
||||
return <VideoElement />; |
||||
} |
||||
|
@ -1,9 +1,30 @@
@@ -1,9 +1,30 @@
|
||||
import { MWStreamType } from "@/backend/helpers/streams"; |
||||
import { Player } from "@/components/player"; |
||||
import { usePlayer } from "@/components/player/hooks/usePlayer"; |
||||
import { playerStatus } from "@/stores/player/slices/source"; |
||||
|
||||
export function PlayerView() { |
||||
const { status, playMedia } = usePlayer(); |
||||
|
||||
function scrape() { |
||||
playMedia({ |
||||
type: MWStreamType.MP4, |
||||
url: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", |
||||
}); |
||||
} |
||||
|
||||
return ( |
||||
<Player.Container> |
||||
<Player.Pause /> |
||||
|
||||
{status === playerStatus.IDLE ? ( |
||||
<div> |
||||
<p>Its now scraping</p> |
||||
<button type="button" onClick={scrape}> |
||||
Finish scraping |
||||
</button> |
||||
</div> |
||||
) : null} |
||||
</Player.Container> |
||||
); |
||||
} |
||||
|
@ -1,18 +1,5 @@
@@ -1,18 +1,5 @@
|
||||
import { useEffect } from "react"; |
||||
|
||||
import { MWStreamType } from "@/backend/helpers/streams"; |
||||
import { usePlayer } from "@/components/player/hooks/usePlayer"; |
||||
import { PlayerView } from "@/pages/PlayerView"; |
||||
|
||||
export default function VideoTesterView() { |
||||
const player = usePlayer(); |
||||
|
||||
useEffect(() => { |
||||
player.playMedia({ |
||||
type: MWStreamType.MP4, |
||||
url: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", |
||||
}); |
||||
}); |
||||
|
||||
return <PlayerView />; |
||||
} |
||||
|
Loading…
Reference in new issue