UNPKG

dcent-feeds

Version:

peer to peer camera feeds

180 lines (143 loc) 5.61 kB
#!/usr/bin/env node const path = require('path') const os = require('os') const { once } = require('events') const fs = require('fs') const fsProm = fs.promises const { pipelinePromise } = require('streamx') const { program } = require('commander') const { ProxyClient } = require('p2proxy') const IdEnc = require('hypercore-id-encoding') const goodbye = require('graceful-goodbye') const Corestore = require('corestore') const Hyperswarm = require('hyperswarm') const Hyperdrive = require('hyperdrive') const DEFAULT_STORAGE = path.join(os.homedir(), '.dcent-feeds') const DEFAULT_DOWNLOAD_DIR = path.join(os.homedir(), 'Downloads') async function main () { program.name('dcent-feeds').description('CLI for managing dcent feeds') program .command('stream') .description('Stream a video feed') .argument('<secret>', 'The secret giving access to the video feed') .option('-p, --port <int>', 'Local port to serve the stream from', 4619) .option('-h, --host <string>', 'Host to serve from', '127.0.0.1') .action(runStream) program .command('list-feeds') .description('List timestamps for which feeds are available') .argument('<key>', 'The key of the hyperdrive containing the archived feeds') .option('-s, --storage <string>', 'Corestore storage location', DEFAULT_STORAGE) .action(runListFeeds) program .command('download') .description('Download a feed') .argument('<key>', 'The key of the hyperdrive containing the archived feeds') .argument('<name>', 'The name of the feed (use the list-feeds commands to see the available names)') .option('-s, --storage <string>', 'Corestore storage location', DEFAULT_STORAGE) .option('-d, --download-dir <string>', 'Download folder location', DEFAULT_DOWNLOAD_DIR) .action(runDownload) await program.parseAsync(process.argv) } async function runStream (secret, { port, host }) { secret = IdEnc.decode(secret) const client = new ProxyClient(secret, port, host) client.on('connection', ({ remoteAddress, remotePort, id }) => { console.info(`Opened connection to ${remoteAddress}:${remotePort} (${id})`) }) client.on('connection-close', ({ remoteAddress, remotePort, id }) => { console.info(`Closed connection to ${remoteAddress}:${remotePort} (${id})`) }) console.info('Setting up...') goodbye(async () => { console.info('Shutting down...') if (client.opened) await client.close() console.info('Shut down') }) await client.ready() const { address, port: listenPort } = client.address console.info(`The video stream is served at http://${address}:${listenPort} (open this link in your browser)`) } async function runListFeeds (key, { storage }) { key = IdEnc.decode(key) const logger = console const { store, drive, swarm } = setup(storage, key, logger) goodbye(async () => { console.info('Shutting down...') await swarm.destroy() await store.close() console.info('Shut down') }) console.info('Setting up...') await drive.ready() console.log(IdEnc.normalize(drive.key)) swarm.join(drive.discoveryKey, { client: true, server: false }) if (drive.db.core.peers.length === 0) { logger.log('Waiting for connection to other peers...') await once(drive.db.core, 'peer-add') await drive.update({ wait: true }) } for await (const entry of drive.list()) { console.log(entry.key) } goodbye.exit() } async function runDownload (key, name, { storage, downloadDir }) { key = IdEnc.decode(key) const logger = console const { store, drive, swarm } = setup(storage, key, logger) const outputLoc = path.join(downloadDir, name) // Imperfect check, but at least we avoid 99% of cases (checked again when opening the file) if (fs.existsSync(outputLoc)) { logger.error(`A file already exists at location: ${outputLoc}`) process.exit(1) } logger.info(`Writing feed to: ${outputLoc}`) let done = false goodbye(async () => { if (!done) console.info('Shutting down...') await swarm.destroy() await store.close() if (!done) console.info('Shut down') }) console.info('Setting up...') await drive.ready() console.log(IdEnc.normalize(drive.key)) swarm.join(drive.discoveryKey, { client: true, server: false }) if (drive.db.core.peers.length === 0) { logger.log('Waiting for connection to other peers...') await once(drive.db.core, 'peer-add') await drive.update({ wait: true }) } let file try { file = await fsProm.open(outputLoc, 'wx') } catch (e) { if (e.code === 'EEXIST') { logger.error(`A file already exists at location: ${outputLoc}. Cancelling download.`) process.exit(1) } throw e } const writeStream = file.createWriteStream() const readStream = drive.createReadStream(name) logger.info('Downloading feed...') await pipelinePromise(readStream, writeStream) logger.info(`Downloaded at: ${outputLoc}`) done = true goodbye.exit() } function setup (storage, key, logger) { logger.info(`Using storage: ${storage}`) const store = new Corestore(storage) const drive = new Hyperdrive(store.namespace('feeds-drive'), key) const swarm = new Hyperswarm() swarm.on('connection', (conn, peer) => { store.replicate(conn) const address = `${conn.rawStream.remoteHost}:${conn.rawStream.remotePort}` logger.info(`Opened connection to ${IdEnc.normalize(peer.publicKey)} (${address})`) conn.on('close', () => { logger.info(`Closed connection to ${IdEnc.normalize(peer.publicKey)} (${address})`) }) }) return { store, drive, swarm } } main()