stremio-addon-client
Version:
Client library for using stremio addons (v3 protocol)
160 lines (137 loc) • 3.89 kB
JavaScript
var URL = require('url')
var errors = require('../errors')
var IPFS = require('ipfs')
var Room = require('ipfs-pubsub-room')
var thunky = require('thunky')
var qs = require('querystring')
var URL = require('url')
var IPFSRepo = require('ipfs-repo')
// Potentially for IPFSRepo
//var path = require('path')
//var os = require('os')
// @TODO: retry logic
var TIME_TO_RETR_MISSING = 6 * 1000
var setupIPFS = thunky(function(cb) {
var node = new IPFS({
EXPERIMENTAL: {
pubsub: true
},
// takes path; the impl can take memory
repo: new IPFSRepo('./.jsipfs', { lock: 'memory' }),
// overload the default IPFS node config, find defaults at https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime
config: {
Addresses: {
Swarm: [
// @todo: sockets, webrtc, web browser compatible
'/ip4/127.0.0.1/tcp/1339',
//'/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star',
]
},
Bootstrap: [
'/ip4/127.0.0.1/tcp/4001/ipfs/QmYRaTC2DqsgXaRUJzGFagLy725v1QyYwt66kvpifPosgj',
],
Discovery: {
MDNS: {
Enabled: false
}
}
},
})
node.once('ready', function() {
cb(null, node)
})
// @TODO
node.on('error', function(err) { console.error(err) })
})
function ipfsTransport(url)
{
var manifestUrl = url.replace('ipfs://', '/ipfs/').replace('ipns://', '/ipns/')
var base = manifestUrl.replace('/manifest.json', '/')
this.url = url
this.manifest = function(cb)
{
setupIPFS(function(err, node) {
if (err) return cb(err)
retrFile(node, manifestUrl, function(err, resp) {
if (err) return cb(err)
if (!resp || typeof(resp.id) !== 'string')
return cb(errors.ERR_MANIFEST_INVALID)
// Find the add-on creator peer
// https://github.com/ipfs/js-ipfs/issues/870
//node1.peerRouting.findPeer(node3.peerInfo.id, function(err, peer) {
node.addonRoom = Room(node, resp.id)
node.addonRoom.on('subscribed', function() {
cb(null, resp)
})
// @TODO TEMP
//node.addonRoom.on 'peer joined' 'peer left' 'subscribed'
})
})
}
this.get = function(args, cb)
{
setupIPFS(function(err, node) {
if (err) return cb(err)
if (! node.addonRoom)
return cb(errors.ERR_MANIFEST_CALL_FIRST)
var p = args.map(mapArg).join('/')
retrFile(node, base+p+'.json', function(err, res) {
if (err && err.message.match('No such file')) {
node.addonRoom.broadcast(p)
setTimeout(function() {
retrFile(node, base+p+'.json', cb)
}, TIME_TO_RETR_MISSING)
return
}
cb(err, res)
})
})
}
this.destroy = function(cb)
{
// @XXX: if you call this without calling manifest/get before, it will create a instance and then kill it
setupIPFS(function(err, node) {
if (err) return cb(err)
node.stop(cb)
})
}
function mapArg(arg)
{
if (typeof(arg) === 'object') {
return qs.stringify(arg, '&', '=', {
encodeURIComponent: function(x) { return x }
})
} else {
return arg
}
}
function retrFile(node, p, cb)
{
node.files.cat(p, function(err, res) {
if (err)
return cb(err)
try {
res = JSON.parse(res.toString())
} catch(e) {
return cb(err)
}
cb(null, res)
})
}
function requestMissing(url, cb)
{
// https://github.com/ipfs/js-ipfs/issues/870
// node1.peerRouting.findPeer(node3.peerInfo.id, function(err, peer) {
// https://github.com/ipfs/js-libp2p-ipfs-nodejs/tree/master/examples/echo
// https://github.com/libp2p/go-libp2p/tree/master/examples/echo
// https://github.com/libp2p/js-libp2p/tree/master/examples/transports
}
// @TODO ipns, or otherwise do not open a pubsub
// @TODO: anti-spam on the pubsub, and research whether all clients have to listen
return this
}
ipfsTransport.isValidURL = function(url) {
var parsed = URL.parse(url)
return parsed.protocol === 'ipfs:' || parsed.protocol === 'ipns:'
}
module.exports = ipfsTransport