This repository has been archived by the owner on Feb 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: Add browser example for ReadableStreams
feat: Allows for byte offsets when using ipfs.files.cat and friends to request slices of files
- Loading branch information
1 parent
8e3ea44
commit 9f5b933
Showing
8 changed files
with
286 additions
and
6 deletions.
There are no files selected for viewing
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
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,16 @@ | ||
# Streaming video from IPFS using ReadableStreams | ||
|
||
We can use the execllent [`videostream`](https://www.npmjs.com/package/videostream) to stream video from IPFS to the browser. All we need to do is return a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)-like object that contains the requested byte ranges. | ||
|
||
Take a look at [`index.js`](./index.js) to see a working example. | ||
|
||
## Running the demo | ||
|
||
In this directory: | ||
|
||
``` | ||
$ npm install | ||
$ npm start | ||
``` | ||
|
||
Then open [http://localhost:8888](http://localhost:8888) in your browser. |
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,62 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/> | ||
<title><%= htmlWebpackPlugin.options.title %></title> | ||
<style type="text/css"> | ||
|
||
body { | ||
margin: 0; | ||
padding: 0; | ||
} | ||
|
||
#container { | ||
display: flex; | ||
height: 100vh; | ||
} | ||
|
||
pre { | ||
flex-grow: 2; | ||
padding: 10px; | ||
height: calc(100vh - 45px); | ||
overflow: auto; | ||
} | ||
|
||
#form-wrapper { | ||
padding: 20px; | ||
} | ||
|
||
form { | ||
padding-bottom: 10px; | ||
display: flex; | ||
} | ||
|
||
#hash { | ||
display: inline-block; | ||
margin: 0 10px 10px 0; | ||
font-size: 16px; | ||
flex-grow: 2; | ||
padding: 5px; | ||
} | ||
|
||
button { | ||
display: inline-block; | ||
font-size: 16px; | ||
height: 32px; | ||
} | ||
|
||
</style> | ||
</head> | ||
<body> | ||
<div id="container"> | ||
<div id="form-wrapper"> | ||
<form> | ||
<input type="text" id="hash" placeholder="Hash" value="QmZ8dHcccdqNBNgEHKnSMCVjAAhLc293tmhDZZcptfF5eD" disabled /> | ||
<button id="gobutton" disabled>Go!</button> | ||
</form> | ||
<video id="video" controls></video> | ||
</div> | ||
<pre id="output" style="display: inline-block"></pre> | ||
</div> | ||
</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,131 @@ | ||
'use strict' | ||
|
||
/* eslint-env browser */ | ||
|
||
const Ipfs = require('../../') | ||
const videoStream = require('videostream') | ||
const ipfs = new Ipfs({ repo: 'ipfs-' + Math.random() }) | ||
|
||
const log = (line) => { | ||
document.getElementById('output').appendChild(document.createTextNode(`${line}\r\n`)) | ||
} | ||
|
||
log('IPFS: Initialising') | ||
|
||
const timeouts = [] | ||
|
||
ipfs.on('ready', () => { | ||
// Set up event listeners on the <video> element from index.html | ||
const videoElement = createVideoElement() | ||
const hashInput = document.getElementById('hash') | ||
const goButton = document.getElementById('gobutton') | ||
let stream | ||
|
||
goButton.onclick = function (event) { | ||
event.preventDefault() | ||
|
||
log(`IPFS: Playing ${hashInput.value.trim()}`) | ||
|
||
// Set up the video stream an attach it to our <video> element | ||
videoStream({ | ||
createReadStream: function (opts) { | ||
const start = opts.start | ||
|
||
// The videostream library does not always pass an end byte but when | ||
// it does, it wants bytes between start & end inclusive. | ||
// catReadableStream returns the bytes exclusive so increment the end | ||
// byte if it's been requested | ||
const end = opts.end ? start + opts.end + 1 : undefined | ||
|
||
log(`Stream: Asked for data starting at byte ${start} and ending at byte ${end}`) | ||
|
||
// If we've streamed before, clean up the existing stream | ||
if (stream && stream.destroy) { | ||
stream.destroy() | ||
} | ||
|
||
// This stream will contain the requested bytes | ||
stream = ipfs.files.catReadableStream(hashInput.value.trim(), start, end) | ||
|
||
// Log error messages | ||
stream.on('error', log) | ||
|
||
if (start === 0) { | ||
// Show the user some messages while we wait for the data stream to start | ||
statusMessages(stream) | ||
} | ||
|
||
return stream | ||
} | ||
}, videoElement) | ||
} | ||
|
||
log('IPFS: Ready') | ||
log('IPFS: Press the "Go!" button to start playing a video') | ||
|
||
hashInput.disabled = false | ||
goButton.disabled = false | ||
}) | ||
|
||
const createVideoElement = () => { | ||
const videoElement = document.getElementById('video') | ||
videoElement.addEventListener('loadedmetadata', () => { | ||
videoElement.play() | ||
.then(() => log('Video: Playing')) | ||
.catch(log) | ||
}) | ||
|
||
const events = [ | ||
'playing', | ||
'waiting', | ||
'seeking', | ||
'seeked', | ||
'ended', | ||
'loadedmetadata', | ||
'loadeddata', | ||
'canplay', | ||
'canplaythrough', | ||
'durationchange', | ||
'play', | ||
'pause', | ||
'suspend', | ||
'emptied', | ||
'stalled', | ||
'error', | ||
'abort' | ||
] | ||
events.forEach(event => { | ||
videoElement.addEventListener(event, () => { | ||
log(`Video: ${event}`) | ||
}) | ||
}) | ||
|
||
videoElement.addEventListener('error', () => { | ||
if (videoElement.error) { | ||
log('Error:', videoElement.error.message) | ||
} | ||
}) | ||
|
||
return videoElement | ||
} | ||
|
||
const statusMessages = (stream) => { | ||
let time = 0 | ||
const timeouts = [ | ||
'Stream: Still loading data from IPFS...', | ||
'Stream: This can take a while depending on content availability', | ||
'Stream: Hopefully not long now', | ||
'Stream: *Whistles absentmindedly*', | ||
'Stream: *Taps foot*', | ||
'Stream: *Looks at watch*', | ||
'Stream: *Stares at floor*', | ||
'Stream: *Checks phone*', | ||
'Stream: *Stares at ceiling*', | ||
'Stream: Got anything nice planned for the weekend?' | ||
].map(message => setTimeout(() => log(message), time += 5000)) | ||
|
||
stream.once('data', () => { | ||
log('Stream: Here we go') | ||
timeouts.forEach(clearTimeout) | ||
}) | ||
} |
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,22 @@ | ||
{ | ||
"name": "browser-videostream", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1", | ||
"build": "webpack", | ||
"start": "npm run build && http-server dist -a 127.0.0.1 -p 8888" | ||
}, | ||
"author": "", | ||
"license": "ISC", | ||
"devDependencies": { | ||
"html-webpack-plugin": "^2.30.1", | ||
"http-server": "^0.11.1", | ||
"uglifyjs-webpack-plugin": "^1.2.0", | ||
"webpack": "^3.11.0" | ||
}, | ||
"dependencies": { | ||
"videostream": "^2.4.2" | ||
} | ||
} |
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,29 @@ | ||
'use strict' | ||
|
||
const path = require('path') | ||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin') | ||
const HtmlWebpackPlugin = require('html-webpack-plugin') | ||
|
||
module.exports = { | ||
devtool: 'source-map', | ||
entry: [ | ||
'./index.js' | ||
], | ||
plugins: [ | ||
new UglifyJsPlugin({ | ||
sourceMap: true, | ||
uglifyOptions: { | ||
mangle: false, | ||
compress: false | ||
} | ||
}), | ||
new HtmlWebpackPlugin({ | ||
title: 'IPFS Videostream example', | ||
template: 'index.html' | ||
}) | ||
], | ||
output: { | ||
path: path.join(__dirname, 'dist'), | ||
filename: 'bundle.js' | ||
} | ||
} |
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
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