mirror of
https://github.com/seigler/HLS-over-IPFS-video-player
synced 2025-07-27 01:36:14 +00:00
feat: context menu, link to current time
This commit is contained in:
parent
38d372a603
commit
1b49b532dc
3 changed files with 107 additions and 38 deletions
|
@ -7,6 +7,7 @@ Accepts three query parameters:
|
||||||
- `hash`: required. The IPFS hash of a folder containing an HLS playlist and its files.
|
- `hash`: required. The IPFS hash of a folder containing an HLS playlist and its files.
|
||||||
- `source`: optional, defaults to `master.m3u8`.
|
- `source`: optional, defaults to `master.m3u8`.
|
||||||
- `title`: optional, allows overriding the browser tab title.
|
- `title`: optional, allows overriding the browser tab title.
|
||||||
|
- `time`: optional, start video at this many seconds
|
||||||
|
|
||||||
## Usage Examples:
|
## Usage Examples:
|
||||||
|
|
||||||
|
|
34
index.html
34
index.html
|
@ -55,13 +55,44 @@
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
#contextMenu {
|
||||||
|
position: sticky;
|
||||||
|
background-color: #2f2f2faf;
|
||||||
|
color: white;
|
||||||
|
width: 20em;
|
||||||
|
}
|
||||||
|
#contextMenu > div {
|
||||||
|
border: 1px solid #0000007f;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
#contextMenu > div + div {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
#contextBackground {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.is-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script type="module" defer="" src="./src/index.js"></script>
|
<script type="module" defer="" src="./src/index.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<video id="video" controls="" autoplay=""></video>
|
<video id="video" controls="" controlsList="nodownload" autoplay=""></video>
|
||||||
<div id="status"></div>
|
<div id="status"></div>
|
||||||
|
</div>
|
||||||
|
<div class="is-hidden" id="contextBackground">
|
||||||
|
<div id="contextMenu">
|
||||||
|
<div id="contextMenu-url">Copy video URL</div>
|
||||||
|
<div id="contextMenu-urlWithTime">Copy video URL at current time</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<section id="help">
|
<section id="help">
|
||||||
<h1>HLS over IPFS video player</h1>
|
<h1>HLS over IPFS video player</h1>
|
||||||
<h2 id="how-to-use">
|
<h2 id="how-to-use">
|
||||||
|
@ -79,6 +110,7 @@
|
||||||
<li>
|
<li>
|
||||||
<code>title</code>: optional, allows overriding the browser tab title.
|
<code>title</code>: optional, allows overriding the browser tab title.
|
||||||
</li>
|
</li>
|
||||||
|
<li><code>time</code>: optional, start the video at this many seconds</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h2 id="usage-examples">
|
<h2 id="usage-examples">
|
||||||
<a class="anchor" href="#usage-examples"></a>Usage Examples:
|
<a class="anchor" href="#usage-examples"></a>Usage Examples:
|
||||||
|
|
110
src/index.js
110
src/index.js
|
@ -4,7 +4,77 @@ import { create } from 'ipfs-core'
|
||||||
import Hls from 'hls.js'
|
import Hls from 'hls.js'
|
||||||
import HlsjsIpfsLoader from 'hlsjs-ipfs-loader'
|
import HlsjsIpfsLoader from 'hlsjs-ipfs-loader'
|
||||||
|
|
||||||
let node
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
setBodyHeight()
|
||||||
|
window.addEventListener('resize', setBodyHeight)
|
||||||
|
const hash = getUrlParameter('hash')
|
||||||
|
const source = getUrlParameter('source')
|
||||||
|
const title = getUrlParameter('title')
|
||||||
|
const time = getUrlParameter('time')
|
||||||
|
setUpContextMenu() // todo move back inside if(hash)
|
||||||
|
if (title) {
|
||||||
|
document.title = title
|
||||||
|
}
|
||||||
|
if (hash) {
|
||||||
|
document.getElementById('help').style.display = 'none'
|
||||||
|
const video = document.getElementById('video')
|
||||||
|
video.style.display = 'block'
|
||||||
|
const repoPath = 'ipfs-' + Math.random()
|
||||||
|
showStatus('Connecting to IPFS')
|
||||||
|
const node = await create({ repo: repoPath })
|
||||||
|
showStatus('Connected')
|
||||||
|
Hls.DefaultConfig.loader = HlsjsIpfsLoader
|
||||||
|
Hls.DefaultConfig.debug = false
|
||||||
|
if (Hls.isSupported()) {
|
||||||
|
const hls = new Hls()
|
||||||
|
hls.config.ipfs = node
|
||||||
|
hls.config.ipfsHash = hash
|
||||||
|
showStatus('Video loading')
|
||||||
|
hls.loadSource(source || 'master.m3u8')
|
||||||
|
hls.attachMedia(video)
|
||||||
|
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||||
|
showStatus('Video loaded', true)
|
||||||
|
if (time) {
|
||||||
|
video.currentTime = time
|
||||||
|
}
|
||||||
|
video.play()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUpContextMenu() {
|
||||||
|
const video = document.getElementById('video')
|
||||||
|
const background = document.getElementById('contextBackground')
|
||||||
|
const menu = document.getElementById('contextMenu')
|
||||||
|
video.oncontextmenu = e => {
|
||||||
|
background.classList.toggle('is-hidden')
|
||||||
|
menu.style.left = e.pageX + 'px'
|
||||||
|
menu.style.top = e.pageY + 'px'
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
background.onClick = e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
background.classList.toggle('is-hidden')
|
||||||
|
}
|
||||||
|
const url = `http://ipfsvideo.cc?hash=${
|
||||||
|
hash
|
||||||
|
}${
|
||||||
|
title && '&title=' + encodeURIComponent(title)
|
||||||
|
}${
|
||||||
|
source && '&source=' + encodeURIComponent(source)
|
||||||
|
}`
|
||||||
|
document.getElementById('contextMenu-url').onClick = e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
navigator.clipboard.writeText(url)
|
||||||
|
background.classList.toggle('is-hidden')
|
||||||
|
}
|
||||||
|
document.getElementById('contextMenu-urlWithTime').onClick = e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
navigator.clipboard.writeText(`${url}&time=${Math.round(video.currentTime)}`)
|
||||||
|
background.classList.toggle('is-hidden')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
function getUrlParameter(name) {
|
function getUrlParameter(name) {
|
||||||
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]')
|
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]')
|
||||||
|
@ -15,44 +85,10 @@ function getUrlParameter(name) {
|
||||||
|
|
||||||
function showStatus(message, hide = false) {
|
function showStatus(message, hide = false) {
|
||||||
const status = document.getElementById('status')
|
const status = document.getElementById('status')
|
||||||
status.classList.toggle("is-hiding", hide)
|
status.classList.toggle('is-hiding', hide)
|
||||||
status.innerText = message
|
status.innerText = message
|
||||||
}
|
}
|
||||||
|
|
||||||
function setBodyHeight() {
|
function setBodyHeight() {
|
||||||
document.body.style.height = window.innerHeight + "px"
|
document.body.style.height = window.innerHeight + 'px'
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
|
||||||
setBodyHeight()
|
|
||||||
window.addEventListener('resize', setBodyHeight)
|
|
||||||
const hash = getUrlParameter("hash")
|
|
||||||
const source = getUrlParameter("source") || 'master.m3u8'
|
|
||||||
const title = getUrlParameter("title")
|
|
||||||
if (title) {
|
|
||||||
document.title = title
|
|
||||||
}
|
|
||||||
if (hash) {
|
|
||||||
document.getElementById("help").style.display = "none"
|
|
||||||
const video = document.getElementById('video')
|
|
||||||
video.style.display = "block"
|
|
||||||
const repoPath = 'ipfs-' + Math.random()
|
|
||||||
showStatus("Connecting to IPFS")
|
|
||||||
node = await create({ repo: repoPath })
|
|
||||||
showStatus("Connected")
|
|
||||||
Hls.DefaultConfig.loader = HlsjsIpfsLoader
|
|
||||||
Hls.DefaultConfig.debug = false
|
|
||||||
if (Hls.isSupported()) {
|
|
||||||
const hls = new Hls()
|
|
||||||
hls.config.ipfs = node
|
|
||||||
hls.config.ipfsHash = hash
|
|
||||||
showStatus("Video loading")
|
|
||||||
hls.loadSource(source)
|
|
||||||
hls.attachMedia(video)
|
|
||||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
||||||
showStatus("Video loaded", true)
|
|
||||||
video.play()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue