-
-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Player #135
Merged
Merged
Player #135
Changes from 14 commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
39c10a7
player
matvp91 a68b72c
Merge branch 'main' of github.com:matvp91/mixwave into feature/player…
matvp91 01c553c
Multiple video elements
matvp91 ca3e053
New player
matvp91 98e094b
Fixture for media-chrome
matvp91 45dde29
Prettier
matvp91 d69fdf1
Rendition menu
matvp91 c464967
Added mapping for audio and quality
matvp91 df5fe0d
Added expiry
matvp91 cbebc18
Removed unused log
matvp91 f6a7e6a
Added temp code
matvp91 fa5d6ac
Added textTracks support
matvp91 c99d615
Ignore error
matvp91 0e92824
Interstitial demo asset
matvp91 621f243
Fix tags
matvp91 8ef5324
Seeking
matvp91 61b32ae
Added support for multiple load calls
matvp91 bfd4a8d
Added interstitial asset
matvp91 c8f7c6d
Hide seekbar
matvp91 03ab89c
Merge branch 'main' of github.com:matvp91/mixwave into feature/player…
matvp91 a0f4ae7
Added object dump
matvp91 73fc260
Added context viewer
matvp91 f2cf130
Added json viewer
matvp91 da06384
Support for text files
matvp91 158412f
Merge branch 'main' of github.com:matvp91/mixwave into feature/player…
matvp91 197018c
Added interstitial instead of solely asset
matvp91 b58975a
Added player controls
matvp91 930d5fb
Added controls to bottom
matvp91 62a4146
Show cuepoints
matvp91 bb23744
Added player view
matvp91 b18b0b1
Added more player
matvp91 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,57 @@ | ||||||
<!doctype html> | ||||||
<html> | ||||||
<head> | ||||||
<meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
<link | ||||||
rel="stylesheet" | ||||||
href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css" | ||||||
> | ||||||
<style> | ||||||
media-controller { | ||||||
display: block; | ||||||
width: 100%; | ||||||
aspect-ratio: 16 / 9; | ||||||
background: #000; | ||||||
} | ||||||
</style> | ||||||
</head> | ||||||
<body> | ||||||
<script async src="https://cdn.jsdelivr.net/npm/es-module-shims"></script> | ||||||
<script type="importmap"> | ||||||
{ | ||||||
"imports": { | ||||||
"super-media-element": "https://cdn.jsdelivr.net/npm/super-media-element@1.3/+esm", | ||||||
"media-tracks": "https://cdn.jsdelivr.net/npm/media-tracks@0.2/+esm", | ||||||
"@superstreamer/player": "/packages/player/dist/index.js", | ||||||
"hls.js": "https://cdn.jsdelivr.net/npm/hls.js@1.6.0-beta.1/dist/hls.mjs" | ||||||
} | ||||||
} | ||||||
</script> | ||||||
<script type="module" src="./superstreamer-video-element.js"></script> | ||||||
<script type="module" src="https://cdn.jsdelivr.net/npm/media-chrome/+esm"></script> | ||||||
<script type="module" src="https://cdn.jsdelivr.net/npm/media-chrome/menu/+esm"></script> | ||||||
|
||||||
<media-controller> | ||||||
<superstreamer-video | ||||||
slot="media" | ||||||
src="https://stitcher.superstreamer.xyz/session/8121e4cc-2024-45b5-bfc8-323dec368819/master.m3u8"> | ||||||
</superstreamer-video> | ||||||
<media-loading-indicator slot="centered-chrome" no-auto-hide></media-loading-indicator> | ||||||
<media-rendition-menu hidden anchor="auto"></media-rendition-menu> | ||||||
<media-audio-track-menu hidden anchor="auto"></media-audio-track-menu> | ||||||
<media-captions-menu hidden anchor="auto"></media-captions-menu> | ||||||
<media-control-bar> | ||||||
<media-play-button></media-play-button> | ||||||
<media-seek-forward-button></media-seek-forward-button> | ||||||
<media-mute-button></media-mute-button> | ||||||
<media-volume-range></media-volume-range> | ||||||
<media-time-range></media-time-range> | ||||||
<media-time-display show-duration></media-time-display> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
<media-rendition-menu-button></media-rendition-menu-button> | ||||||
<media-audio-track-menu-button></media-audio-track-menu-button> | ||||||
<media-captions-menu-button></media-captions-menu-button> | ||||||
<media-fullscreen-button></media-fullscreen-button> | ||||||
</media-control-bar> | ||||||
</media-controller> | ||||||
</body> | ||||||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
import { Events, HlsPlayer } from "@superstreamer/player"; | ||
import { MediaTracksMixin } from "media-tracks"; | ||
|
||
function getTemplateHTML() { | ||
return ` | ||
<style> | ||
:host { | ||
width: 100%; | ||
height: 100%; | ||
} | ||
</style> | ||
<div class="container"></div> | ||
`; | ||
} | ||
|
||
const SymbolTrackId = Symbol("superstreamer.trackId"); | ||
|
||
class SuperstreamerVideoElement extends MediaTracksMixin( | ||
globalThis.HTMLElement, | ||
) { | ||
static getTemplateHTML = getTemplateHTML; | ||
|
||
static shadowRootOptions = { | ||
mode: "open", | ||
}; | ||
|
||
static observedAttributes = ["src"]; | ||
|
||
#player; | ||
|
||
#readyState = 0; | ||
|
||
constructor() { | ||
super(); | ||
|
||
const video = document.createElement("video"); | ||
|
||
this.textTracks = video.textTracks; | ||
this.addTextTrack = video.addTextTrack.bind(video); | ||
} | ||
|
||
get src() { | ||
return this.getAttribute("src"); | ||
} | ||
|
||
set src(val) { | ||
if (this.src === val) { | ||
return; | ||
} | ||
this.setAttribute("src", val); | ||
} | ||
|
||
attributeChangedCallback(attrName, oldValue, newValue) { | ||
if (attrName === "src" && oldValue !== newValue) { | ||
this.load(); | ||
} | ||
} | ||
|
||
load() { | ||
if (!this.shadowRoot) { | ||
this.attachShadow({ | ||
mode: "open", | ||
}); | ||
this.shadowRoot.innerHTML = getTemplateHTML(); | ||
} | ||
|
||
this.#readyState = 0; | ||
this.dispatchEvent(new Event("emptied")); | ||
|
||
if (!this.#player) { | ||
const container = this.shadowRoot.querySelector(".container"); | ||
const player = (this.#player = new HlsPlayer(container)); | ||
|
||
// TODO: For debug purposes. | ||
Object.assign(window, { player }); | ||
|
||
player.on(Events.PLAYHEAD_CHANGE, () => { | ||
switch (player.playhead) { | ||
case "play": | ||
this.dispatchEvent(new Event("play")); | ||
break; | ||
case "playing": | ||
this.dispatchEvent(new Event("playing")); | ||
break; | ||
case "pause": | ||
this.dispatchEvent(new Event("pause")); | ||
break; | ||
} | ||
}); | ||
|
||
player.on(Events.TIME_CHANGE, () => { | ||
this.dispatchEvent(new Event("timeupdate")); | ||
}); | ||
|
||
player.on(Events.VOLUME_CHANGE, () => { | ||
this.dispatchEvent(new Event("volumechange")); | ||
}); | ||
|
||
player.on(Events.READY, async () => { | ||
this.dispatchEvent(new Event("loadedmetadata")); | ||
this.dispatchEvent(new Event("durationchange")); | ||
this.dispatchEvent(new Event("volumechange")); | ||
this.dispatchEvent(new Event("loadcomplete")); | ||
|
||
this.#createVideoTracks(); | ||
this.#createAudioTracks(); | ||
this.#createTextTracks(); | ||
|
||
this.#readyState = 1; | ||
}); | ||
|
||
player.on(Events.STARTED, () => { | ||
this.#readyState = 3; | ||
}); | ||
} | ||
|
||
this.dispatchEvent(new Event("loadstart")); | ||
|
||
this.#player.load(this.src); | ||
} | ||
|
||
get currentTime() { | ||
return this.#player.time; | ||
} | ||
|
||
set currentTime(val) { | ||
this.#player.seekTo(val); | ||
} | ||
|
||
get duration() { | ||
return this.#player.duration; | ||
} | ||
|
||
get paused() { | ||
const { playhead } = this.#player; | ||
if (playhead === "play" || playhead === "playing") { | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
get readyState() { | ||
return this.#readyState; | ||
} | ||
|
||
get muted() { | ||
return this.#player.volume === 0; | ||
} | ||
|
||
set muted(val) { | ||
this.#player.setVolume(val ? 0 : 1); | ||
} | ||
|
||
get volume() { | ||
return this.#player.volume; | ||
} | ||
|
||
set volume(val) { | ||
this.#player.setVolume(val); | ||
} | ||
|
||
async play() { | ||
this.#player.playOrPause(); | ||
} | ||
|
||
pause() { | ||
this.#player.playOrPause(); | ||
} | ||
|
||
#createVideoTracks() { | ||
let videoTrack = this.videoTracks.getTrackById("main"); | ||
|
||
if (!videoTrack) { | ||
videoTrack = this.addVideoTrack("main"); | ||
videoTrack.id = "main"; | ||
videoTrack.selected = true; | ||
} | ||
|
||
this.#player.qualities.forEach((quality) => { | ||
videoTrack.addRendition( | ||
undefined, | ||
quality.height, | ||
quality.height, | ||
undefined, | ||
undefined, | ||
); | ||
}); | ||
|
||
this.videoRenditions.addEventListener("change", (event) => { | ||
if (event.target.selectedIndex < 0) { | ||
this.#player.setQuality(null); | ||
} else { | ||
const rendition = this.videoRenditions[event.target.selectedIndex]; | ||
this.#player.setQuality(rendition.height); | ||
} | ||
}); | ||
} | ||
|
||
#createAudioTracks() { | ||
this.#player.audioTracks.forEach((a) => { | ||
const audioTrack = this.addAudioTrack("main", a.label, a.label); | ||
audioTrack[SymbolTrackId] = a.id; | ||
audioTrack.enabled = a.active; | ||
}); | ||
|
||
this.audioTracks.addEventListener("change", () => { | ||
const id = [...this.audioTracks].find((a) => a.enabled)?.[SymbolTrackId]; | ||
this.#player.setAudioTrack(id); | ||
}); | ||
} | ||
|
||
#createTextTracks() { | ||
this.#player.subtitleTracks.forEach((s) => { | ||
const textTrack = this.addTextTrack("subtitles", s.label, s.track.lang); | ||
textTrack[SymbolTrackId] = s.id; | ||
}); | ||
|
||
this.textTracks.addEventListener("change", () => { | ||
const id = | ||
[...this.textTracks].find((t) => t.mode === "showing")?.[SymbolTrackId] ?? | ||
null; | ||
this.#player.setSubtitleTrack(id); | ||
}); | ||
} | ||
} | ||
|
||
if (!globalThis.customElements?.get("superstreamer-video")) { | ||
globalThis.customElements.define( | ||
"superstreamer-video", | ||
SuperstreamerVideoElement, | ||
); | ||
} | ||
|
||
export default SuperstreamerVideoElement; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,29 @@ | ||
import { | ||
ControllerProvider, | ||
Controls, | ||
useController, | ||
} from "@superstreamer/player/react"; | ||
import Hls from "hls.js"; | ||
import { useEffect, useState } from "react"; | ||
import type { Lang, Metadata } from "@superstreamer/player/react"; | ||
import { HlsPlayer } from "@superstreamer/player"; | ||
import { useEffect, useRef, useState } from "react"; | ||
|
||
interface PlayerProps { | ||
url?: string | null; | ||
metadata: Metadata; | ||
lang: Lang; | ||
} | ||
|
||
export function Player({ url, lang, metadata }: PlayerProps) { | ||
const [hls] = useState(() => new Hls()); | ||
const controller = useController(hls, { | ||
multipleVideoElements: false, | ||
}); | ||
export function Player({ url }: PlayerProps) { | ||
const ref = useRef<HTMLDivElement>(null); | ||
const [player, setPlayer] = useState<HlsPlayer | null>(null); | ||
|
||
useEffect(() => { | ||
if (url) { | ||
hls.loadSource(url); | ||
} | ||
}, [url]); | ||
const player = new HlsPlayer(ref.current!); | ||
Object.assign(window, { player }); | ||
setPlayer(player); | ||
}, []); | ||
|
||
useEffect(() => { | ||
Object.assign(window, { | ||
facade: controller.facade, | ||
}); | ||
}, [controller]); | ||
if (!player || !url) { | ||
return; | ||
} | ||
player.load(url); | ||
return () => { | ||
player.unload(); | ||
}; | ||
}, [player, url]); | ||
|
||
return ( | ||
<ControllerProvider controller={controller}> | ||
<div | ||
className="relative aspect-video bg-black overflow-hidden rounded-md" | ||
data-sprs-container | ||
> | ||
<video | ||
ref={controller.mediaRef} | ||
className="absolute inset-O w-full h-full" | ||
/> | ||
<Controls lang={lang} metadata={metadata} /> | ||
</div> | ||
</ControllerProvider> | ||
); | ||
return <div className="relative aspect-video bg-black" ref={ref} />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering where you got this markup? seems to have the old way of kebab cased attributes.
the new attributes in Media Chrome are all without dashes in them.