Skip to content

Commit

Permalink
chore(.): init
Browse files Browse the repository at this point in the history
  • Loading branch information
jwerle committed Apr 29, 2019
1 parent e922351 commit 6423992
Show file tree
Hide file tree
Showing 6 changed files with 333 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ typings/

# next.js build output
.next
package-lock.json
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
tag-version-prefix=""
message="chore(release): %s :tada:"
72 changes: 70 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,70 @@
# dat-shaka-plugin
A plugin for 'shaka-player' to load 'dat://' URI
dat-shaka-player-plugin
================

A plugin for 'shaka-player' to load `dat://` URI schemes.

## Installation

```sh
$ npm install dat-shaka-player-plugin
```

## Usage

```js
const register = require('dat-shaka-player-plugin')
const shaka = require('shaka-player')

register(shaka)
```

## API

### `plugin = require('dat-shaka-player-plugin')(shaka, options)`

A factory function to create and register the plugin with
`shaka-player` to handle `dat://` URI requests for MPEG-DASH
manifests (`*.mpd`) or HLS playlists (`*.m3u8`). `shaka` is the
`shaka-player` module and `options` can be:

```js
{
archive: Object, // an optional 'hyperdrive' instance that represents a DAT archive. This will override the resolved archive from the DAT link.
swarm: Object, // an optional 'discovery-swarm-web' or 'discovery-swarm' object to swarm for resources
discovery: Object, // an optional object passed to the swarm constructor
}
```

#### `plugin.requests`

A `Set` that represents the currently active requests being made by the
`shaka-player` for resources stored in a DAT archive.

#### `plugin.options`

The options passed to the plugin factory.

#### `plugin.swarms`

A `Map` of swarms corresponding to a DAT link.

#### `plugin.drives`

A `Map` of hyperdrives corresponding to a DAT link.

#### `plugin.destroyed`

A boolean indicating that the plugin is destroyed.

#### `plugin.unregister()`

An alias to `plugin.destroy()`

#### `plugin.destroy()`

Destroy the plugin, close the underlying resources, and unregister as a
scheme for `shaka-player`.

## License

MIT
38 changes: 38 additions & 0 deletions example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const register = require('./')
const shaka = require('shaka-player')

const BIG_BUCK_BUNNY_URI = 'dat://b2e7f16be8cf431310db322c2b8f8c97a39872de364295f251b76f40002f62bc/bunny.mpd'

const video = document.createElement('video')
const player = new shaka.Player(video)

Object.assign(video, {
crossOrigin: 'anonymous',
autoplay: true,
controls: true,
preload: true,
})

Object.assign(document.body.style, {
background: '#151515',
padding: 0,
})

Object.assign(video.style, {
display: 'block',
position: 'relative',
margin: '0 auto',
width: '98%',
})

const plugin = register(shaka, {})
const uri = window.location.hash.slice(1) || BIG_BUCK_BUNNY_URI

player.load(uri)

video.addEventListener('ended', () => {
// you most likely never need to do this
plugin.destroy()
})

document.body.appendChild(video)
192 changes: 192 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
const resolveLink = require('dat-link-resolve')
const parseRange = require('range-parser')
const hyperdrive = require('hyperdrive')
const Discovery = require('discovery-swarm-web')
const collect = require('collect-stream')
const debug = require('debug')('dat-shaka-plugin')
const mime = require('mime')
const ram = require('random-access-memory')
const url = require('url')

const SCHEME = 'dat'

function createPlugin(shaka, options) {
if (!options || 'object' !== typeof options) {
options = {}
}

const requests = new Set()
const swarms = new Map()
const drives = new Map()

let destroyed = false

shaka.net.NetworkingEngine.registerScheme(SCHEME, onscheme)

return {
requests,
options,
swarms,
drives,

get destroyed() {
return destroyed
},

unregister() {
return this.destroy()
},

destroy() {
for (const key of swarms.keys()) {
swarms.get(key).close()
swarms.delete(key)
}

for (const key of drives.keys()) {
drives.get(key).close()
drives.delete(key)
}

for (const request of requests.values()) {
request.abort()
requests.delete(request)
}

shaka.net.NetworkingEngine.unregisterScheme(SCHEME)
},
}

function onscheme(uri, req, type, onprogress) {
if (destroyed) {
throw new Error("'dat-shaka-plugin' is destroyed. Please unregister.")
}

let filename = null
let drive = null
let abort = null
let key = null

const request = new Promise((resolve, reject) => {
abort = reject

resolveLink(uri, onresolvelink)

function onresolvelink(err, link) {
if (err) {
return reject(err)
}

key = Buffer.from(link, 'hex')
drive = drives.get(link) || options.archive || options.drive
swarm = swarms.get(link) || options.swarm

if (!drive) {
drive = hyperdrive(ram, key, Object.assign({}, options, {
latest: true,
sparse: true,
}))
}

if (!swarm) {
swarm = new Discovery(Object.assign({}, options.discovery, {
stream: onstream
}))
}

drives.set(link, drive)
swarms.set(link, swarm)

const { pathname } = url.parse(uri.replace(link, 'dat.local'))

filename = pathname

drive.ready(() => {
swarm.join(drive.discoveryKey)
drive.download(filename)
drive.stat(filename, onstat)
})
}

function onread(err, buffer) {
if (err) {
return reject(err)
}

resolve(makeResponse(uri, buffer, type))
}

function onstat(err, stats) {
if (err) {
return drive.once('update', () => {
drive.stat(filename, onstat)
})
}

if ('HEAD' === req.method) {
resolve(makeResponse(uri, stats, type))
}

if ('GET' === req.method) {
const { headers } = req
const { size } = stats
const range = headers.Range
? parseRange(size, headers.Range)[0]
: null

collect(drive.createReadStream(filename, range), onread)
}
}
})

const operation = new shaka.util.AbortableOperation(request, onabort)

requests.add(request)
request.then(ondone).catch(ondone)
request.operation = operation
request.abort = () => operation.abort()

return operation

function ondone(err) {
if (err) {
debug(err)
}
requests.delete(request)
}

function onabort() {
requests.delete(request)
return abort(new shaka.util.Error(
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.NETWORK,
shaka.util.Error.Code.OPERATION_ABORTED
))
}

function onstream() {
return drive.replicate({ timeout: 0 })
}
}

function makeResponse(uri, file, type) {
const contentType = mime.getType(uri)
const headers = {}
const status = 200
const data = file && file.buffer ? file.buffer : null

headers['content-type'] = contentType

if (file && file.size) {
headers['content-length'] = file.size
} else if (file && file.buffer) {
headers['content-length'] = file.length
}

const response = { uri, data, status, headers, fromCache: false }

return response
}
}

module.exports = createPlugin
30 changes: 30 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "dat-shaka-plugin",
"version": "0.0.1",
"description": "A plugin for 'shaka-player' to load 'dat://' URIs",
"main": "index.js",
"scripts": {
"test": ":"
},
"keywords": [
"dat",
"shaka",
"player",
"plugin"
],
"author": "Joseph Werle <joseph.werle@gmail.com>",
"license": "MIT",
"devDependencies": {
"discovery-swarm-web": "^1.0.6",
"shaka-player": "^2.5.0-beta3"
},
"dependencies": {
"collect-stream": "^1.2.1",
"dat-link-resolve": "^2.3.0",
"debug": "^4.1.1",
"hyperdrive": "^9.14.5",
"mime": "^2.4.2",
"random-access-memory": "^3.1.1",
"range-parser": "^1.2.0"
}
}

0 comments on commit 6423992

Please sign in to comment.