diff --git a/package.json b/package.json index 939b136..872239c 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "@snort/system-wasm": "^1.0.4", "@snort/wallet": "^0.1.7", "@snort/worker-relay": "^1.1.0", - "@sqlite.org/sqlite-wasm": "^3.45.1-build1", "@szhsin/react-menu": "^4.1.0", "@types/webscopeio__react-textarea-autocomplete": "^4.7.5", "@webscopeio/react-textarea-autocomplete": "^4.9.2", @@ -22,9 +21,9 @@ "dayjs": "^1.11.11", "emoji-mart": "^5.6.0", "flag-icons": "^7.2.1", - "hls.js": "^1.5.8", + "hls-video-element": "^1.2.7", "marked": "^12.0.2", - "media-chrome": "^3.2.2", + "media-chrome": "^3.2.4", "qr-code-styling": "^1.6.0-rc.1", "react": "^18.3.1", "react-confetti": "^6.1.0", @@ -36,8 +35,6 @@ "react-intl": "^6.6.8", "react-router-dom": "^6.23.1", "react-tag-input-component": "^2.0.2", - "react-use-gesture": "^9.1.3", - "react-use-pip": "^1.5.0", "recharts": "^2.12.7", "usehooks-ts": "^3.1.0", "uuid": "^9.0.1", diff --git a/src/element/stream/live-video-player.tsx b/src/element/stream/live-video-player.tsx index 4ef6668..eabce85 100644 --- a/src/element/stream/live-video-player.tsx +++ b/src/element/stream/live-video-player.tsx @@ -1,287 +1,64 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import Hls from "hls.js"; -import { HTMLProps, useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { FormattedMessage } from "react-intl"; -import { Icon } from "../icon"; -import { ProgressBar } from "../progress-bar"; -import { Menu, MenuItem } from "@szhsin/react-menu"; -import { StreamState } from "@/const"; +import { CSSProperties, HTMLProps } from "react"; import classNames from "classnames"; -import usePictureInPicture, { VideoRefType } from "react-use-pip"; - -export enum VideoStatus { - Online = "online", - Offline = "offline", -} +import { + MediaControlBar, + MediaController, + MediaFullscreenButton, + MediaMuteButton, + MediaPipButton, + MediaPlayButton, + MediaTimeRange, + MediaVolumeRange, + MediaCastButton, + MediaLiveButton, + MediaRenditionMenu, + MediaRenditionMenuButton, + MediaPosterImage, + MediaTimeDisplay, + MediaPlaybackRateButton, +} from "media-chrome/react"; +import "hls-video-element"; +import { StreamState } from "@/const"; type VideoPlayerProps = { title?: string; - stream?: string; status?: StreamState; + stream?: string; poster?: string; muted?: boolean; } & HTMLProps; -export default function LiveVideoPlayer({ - title, - stream, - status: pStatus, - poster, - muted: pMuted, - ...props -}: VideoPlayerProps) { - const video = useRef(null); - const hlsObj = useRef(null); - const streamCached = useMemo(() => stream, [stream]); - const [status, setStatus] = useState(); - const [src, setSrc] = useState(); - const [levels, setLevels] = useState>(); - const [level, setLevel] = useState(-1); - const [playState, iSetPlayState] = useState<"loading" | "playing" | "paused">("paused"); - const [volume, setVolume] = useState(1); - const [muted, setMuted] = useState(pMuted ?? false); - const [position, setPosition] = useState(); - const [maxPosition, setMaxPosition] = useState(); - - function setPlayState(s: typeof playState) { - console.debug("PLAY STATE", s); - iSetPlayState(s); - } - - useEffect(() => { - if (streamCached && video.current) { - if (Hls.isSupported() && streamCached.endsWith(".m3u8")) { - try { - const hls = new Hls({ - enableWorker: true, - lowLatencyMode: true, - backBufferLength: 90, - }); - hls.loadSource(streamCached); - hls.attachMedia(video.current); - hls.on(Hls.Events.ERROR, (event, data) => { - console.debug(event, data); - const errorType = data.type; - if (errorType === Hls.ErrorTypes.NETWORK_ERROR && data.fatal) { - hls.stopLoad(); - hls.detachMedia(); - setStatus(VideoStatus.Offline); - } - }); - hls.on(Hls.Events.MANIFEST_PARSED, () => { - setStatus(VideoStatus.Online); - setLevels([ - { - level: -1, - height: 0, - }, - ...hls.levels.map((a, i) => ({ - level: i, - height: a.height, - })), - ]); - }); - hls.on(Hls.Events.LEVEL_SWITCHING, (_, l) => { - console.debug("HLS Level Switch", l); - setMaxPosition(l.details?.totalduration); - }); - // @ts-ignore Can write anyway - hlsObj.current = hls; - return () => { - // @ts-ignore Can write anyway - hlsObj.current = null; - hls.destroy(); - }; - } catch (e) { - console.error(e); - setStatus(VideoStatus.Offline); - } - } else { - setSrc(streamCached); - setStatus(VideoStatus.Online); - video.current.muted = true; - video.current.load(); - } - } - }, [video, streamCached, pStatus]); - - useEffect(() => { - if (hlsObj.current) { - hlsObj.current.nextLevel = level; - } - }, [hlsObj, level]); - - useEffect(() => { - if (video.current) { - video.current.onplaying = () => setPlayState("playing"); - video.current.onpause = () => setPlayState("paused"); - video.current.onseeking = () => { - if (video.current?.paused) { - setPlayState("paused"); - } else { - setPlayState("loading"); - } - }; - video.current.onplay = () => setPlayState("loading"); - video.current.onvolumechange = () => setVolume(video.current?.volume ?? 1); - video.current.ontimeupdate = () => setPosition(video.current?.currentTime); - } - }, [video.current]); - - useEffect(() => { - if (video.current) { - if (video.current.volume !== volume) { - video.current.volume = volume; - } - if (video.current.muted !== muted) { - video.current.muted = muted; - } - } - }, [video, volume, muted]); - - const { isPictureInPictureActive, isPictureInPictureAvailable, togglePictureInPicture } = usePictureInPicture( - video as VideoRefType, - ); - - const handlePIPClick = useCallback(async () => { - togglePictureInPicture(!isPictureInPictureActive); - }, [isPictureInPictureActive, togglePictureInPicture]); - - function playStateToIcon() { - switch (playState) { - case "playing": - return "pause"; - case "paused": - return "play"; - case "loading": - return "loading"; - } - } - - function togglePlay() { - if (video.current) { - if (playState === "playing") { - video.current.pause(); - } else if (playState === "paused") { - video.current.play(); - } - } - } - - function toggleMute() { - setMuted(s => !s); - } - - function levelName(l: number) { - if (l === -1) { - return ; - } else { - const h = levels?.find(a => a.level === l)?.height; - return ; - } - } - - function playerOverlay() { - return ( - <> - {status === VideoStatus.Online && ( -
togglePlay()}> - {/* TITLE */} -
-

{title}

-
- {/* CENTER PLAY ICON */} -
- {!isPictureInPictureActive && ( - - )} -
- {/* PLAYER CONTROLS OVERLAY */} -
e.stopPropagation()}> -
-
togglePlay()}> - -
-
{pStatus}
- {pStatus === StreamState.Ended && maxPosition !== undefined && position !== undefined && ( - { - const ct = maxPosition * v; - if (video.current) { - video.current.currentTime = ct; - } - setPosition(ct); - }} - marker={
} - style={{ width: "100%", height: "4px" }} - /> - )} -
-
- - setVolume(v)} style={{ width: "100px", height: "100%" }} /> -
-
- {levelName(level)}
} - menuClassName="bg-primary w-fit"> - {levels?.map(v => ( - setLevel(v.level)} - className="bg-primary px-3 py-2 text-white"> - {levelName(v.level)} - - ))} - -
- {isPictureInPictureAvailable && ( -
- PIP -
- )} -
{ - if (video.current) { - video.current.requestFullscreen(); - } - }}> - -
-
- - )} - {status === VideoStatus.Offline && ( -
- -
- )} - - ); - } +export default function LiveVideoPlayer({ title, stream, status, poster, ...props }: VideoPlayerProps) { return ( -
- {playerOverlay()} -
+ +
+ {title} +
+ {/* @ts-ignore Web Componenet */} + +
); } diff --git a/src/lang.json b/src/lang.json index 6f248d4..211844f 100644 --- a/src/lang.json +++ b/src/lang.json @@ -593,9 +593,6 @@ "defaultMessage": "@ {rate}", "description": "Showing zap amount in USD @ rate" }, - "YagVIe": { - "defaultMessage": "{n}p" - }, "YjhNaf": { "defaultMessage": "Create Stream" }, @@ -820,9 +817,6 @@ "nwA8Os": { "defaultMessage": "Add card" }, - "o8pHw3": { - "defaultMessage": "AUTO" - }, "oHPB8Q": { "defaultMessage": "Zap {name}" }, diff --git a/src/translations/en.json b/src/translations/en.json index 7eed13f..ba1349e 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -195,7 +195,6 @@ "Xq2sb0": "To go live, copy and paste your Server URL and Stream Key below into your streaming software settings and press 'Start Streaming'. We recommend OBS.", "Y0DXJb": "Recording URL", "YPh5Nq": "@ {rate}", - "YagVIe": "{n}p", "YjhNaf": "Create Stream", "YwzT/0": "Clip title", "YyXVHf": "Clear Draft", @@ -270,7 +269,6 @@ "nBCvvJ": "Topup", "nOaArs": "Setup Profile", "nwA8Os": "Add card", - "o8pHw3": "AUTO", "oHPB8Q": "Zap {name}", "oZrFyI": "Stream type should be HLS", "p4N05H": "Upload", diff --git a/yarn.lock b/yarn.lock index 2d5fd96..6a0ebd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2730,15 +2730,6 @@ __metadata: languageName: node linkType: hard -"@sqlite.org/sqlite-wasm@npm:^3.45.1-build1": - version: 3.45.1-build1 - resolution: "@sqlite.org/sqlite-wasm@npm:3.45.1-build1" - bin: - sqlite-wasm: bin/index.js - checksum: 10c0/bb774ea390a18087837408b43ab5355dda0867c92092e15591d2bdcdfbfeab90bd6d11413ff98b2d82db48bd565a8a212fe375b53fa1116e45c8dbe912cc374a - languageName: node - linkType: hard - "@sqlite.org/sqlite-wasm@npm:^3.45.3-build3": version: 3.45.3-build3 resolution: "@sqlite.org/sqlite-wasm@npm:3.45.3-build3" @@ -3975,6 +3966,13 @@ __metadata: languageName: node linkType: hard +"custom-media-element@npm:^1.3.2": + version: 1.3.2 + resolution: "custom-media-element@npm:1.3.2" + checksum: 10c0/49b1efa65ecb3dbf2c49f66132496c50bc44fd73dcdb5529bdc601e9642eba1d02400ad18c28d640c1fe0d671cee3613ae26f50575a1c7702a14f656a69e9d10 + languageName: node + linkType: hard + "d3-array@npm:2 - 3, d3-array@npm:2.10.0 - 3, d3-array@npm:^3.1.6": version: 3.2.4 resolution: "d3-array@npm:3.2.4" @@ -5217,10 +5215,21 @@ __metadata: languageName: node linkType: hard -"hls.js@npm:^1.5.8": - version: 1.5.8 - resolution: "hls.js@npm:1.5.8" - checksum: 10c0/feb36dd1c7c6759d78c3b1597e681ded497eba71e883c4bc8b49232f1169cbcf7d6c3214e0a40ef824da47a4262b6ea30d181cb543d0f8912f6588ea08be68e8 +"hls-video-element@npm:^1.2.7": + version: 1.2.7 + resolution: "hls-video-element@npm:1.2.7" + dependencies: + custom-media-element: "npm:^1.3.2" + hls.js: "npm:^1.5.11" + media-tracks: "npm:^0.3.3" + checksum: 10c0/ed1a9839c2119d084fdd89eeb313b7733649e153638735ced985a40ecf79cd5505363982b7583277d98604d5c824e75f7b2f8f161924375905c0cce12087f8de + languageName: node + linkType: hard + +"hls.js@npm:^1.5.11": + version: 1.5.13 + resolution: "hls.js@npm:1.5.13" + checksum: 10c0/58f5b9dda5c0faca72059be51f88180a8cff49bf1011f50c9d6f30cdb21b44ab9f9493f18443bf6861ee68f262adb0e43bbba8dea668108816adff738e2a3f7e languageName: node linkType: hard @@ -6042,10 +6051,17 @@ __metadata: languageName: node linkType: hard -"media-chrome@npm:^3.2.2": - version: 3.2.2 - resolution: "media-chrome@npm:3.2.2" - checksum: 10c0/d3e36629aaf67eae12837be06a874ba1d56c5b6094c26baa8b76b4e420618564447491fd05b28e593306d2025ffd171db6598bab308255dcd69024e9b70c33b6 +"media-chrome@npm:^3.2.4": + version: 3.2.4 + resolution: "media-chrome@npm:3.2.4" + checksum: 10c0/60596bfc8661c5b99c62107774b3227966aab976dbe5ab4cc960512a7cac3dc002ad3f6e8bffa8fc6d7e44603d3dc1df91cbae080516ad781138740b13b97d51 + languageName: node + linkType: hard + +"media-tracks@npm:^0.3.3": + version: 0.3.3 + resolution: "media-tracks@npm:0.3.3" + checksum: 10c0/ecd57e628222b6e0611f21813a291a47b30c96aa1c9d7e13546932b59e8bb206651961a36360cc5584bf76bf890ce01be3bb0c045facf76ab8b1cfacecf9808b languageName: node linkType: hard @@ -6981,24 +6997,6 @@ __metadata: languageName: node linkType: hard -"react-use-gesture@npm:^9.1.3": - version: 9.1.3 - resolution: "react-use-gesture@npm:9.1.3" - peerDependencies: - react: ">= 16.8.0" - checksum: 10c0/36e8991811c9bf97018cda24b84d9010143fbb8c43eec1089580ef69450f4004a60969b662b489f5416a5c45c516a5d718f20ae46b3fbf0c49f7e34320021246 - languageName: node - linkType: hard - -"react-use-pip@npm:^1.5.0": - version: 1.5.0 - resolution: "react-use-pip@npm:1.5.0" - peerDependencies: - react: ^16.9.0 || ^17 || ^18 - checksum: 10c0/448a6d176eada90cafdcc2465dd9c974ba46e80c033168b3ecef87a064dd5f2976a8758f039e8b5c3f4abf15f844ad4096a2db6b1b0465f8a76988bee28aefdc - languageName: node - linkType: hard - "react@npm:^18.2.0": version: 18.2.0 resolution: "react@npm:18.2.0" @@ -7624,7 +7622,6 @@ __metadata: "@snort/system-wasm": "npm:^1.0.4" "@snort/wallet": "npm:^0.1.7" "@snort/worker-relay": "npm:^1.1.0" - "@sqlite.org/sqlite-wasm": "npm:^3.45.1-build1" "@szhsin/react-menu": "npm:^4.1.0" "@testing-library/dom": "npm:^9.3.1" "@types/node": "npm:^20.12.12" @@ -7646,9 +7643,9 @@ __metadata: emoji-mart: "npm:^5.6.0" eslint: "npm:^8.56.0" flag-icons: "npm:^7.2.1" - hls.js: "npm:^1.5.8" + hls-video-element: "npm:^1.2.7" marked: "npm:^12.0.2" - media-chrome: "npm:^3.2.2" + media-chrome: "npm:^3.2.4" postcss: "npm:^8.4.38" prettier: "npm:^3.2.5" prop-types: "npm:^15.8.1" @@ -7663,8 +7660,6 @@ __metadata: react-intl: "npm:^6.6.8" react-router-dom: "npm:^6.23.1" react-tag-input-component: "npm:^2.0.2" - react-use-gesture: "npm:^9.1.3" - react-use-pip: "npm:^1.5.0" recharts: "npm:^2.12.7" rollup-plugin-visualizer: "npm:^5.12.0" tailwindcss: "npm:^3.4.3"