Permalink
Browse files

Refactor/cleanup

  • Loading branch information...
pldubouilh committed Mar 7, 2018
1 parent 5ad807a commit c0e15fdb23fe007a6aacb3fe1736965036e11301
Showing with 69 additions and 45 deletions.
  1. +6 −2 README.md
  2. +14 −20 cli.js
  3. +8 −4 client/fake-video.html
  4. +38 −16 lib/wtManifest.js
  5. +3 −3 package.json
View
@@ -18,8 +18,8 @@ Yes please ! Live demo with sintel at [live.computer](https://live.computer)
# Install
npm i -g live-torrent
# Start with example live-feed. Manifest fetched from https://live.computer/manifest.m3u8
live-torrent -v -u https://live.computer -p manifest.m3u8
# Start with example live-feed
live-torrent -v -u https://live.computer/manifest.m3u8
# ... or Create a Webtorrent enabled feed from a folder with .ts files
live-torrent -v -l -f feed
@@ -42,6 +42,10 @@ Have a look in the [feed directory](https://github.com/pldubouilh/live-torrent/t
Just host the script, serviceworker and videoplayer on your site and you're good to go. Also, there are some limitations to the use of SW ; the site hosting the videoplayer needs to be served from HTTPS, and serviceworker should be located at the root of the domain (e.g. `https://live.computer/sw.js`). Also feel free to open an issue if something's acting weird :)
> Do I need CORS ?
Yes ! [But it's easy to enable](https://enable-cors.org/server.html). It's enabled by default using the "create from a folder" option.
### How is it working ?
TLDR(ish); A server script parses the video manifest and generates torrent magnet links from the video chunks. The magnets are pushed on the manifest.
View
34 cli.js
@@ -10,21 +10,19 @@ const help = `🛰 Live-torrent 🛰
# To convert an existing stream to a live-torrent feed
-u manifest location
-p playlist name
-c video chunk location - default same as -u
# To create a stream from a folder with HLS chunks
-f folder with chunks location
-l start from beggining and loop - default false
-l start from beggining and loop - default false
# Misc
-s add simple testpage to server - default true
-v display manifest when generated - default false
-r manifest refresh rate (in seconds) - default 2
-s add simple testpage to server - default true
-v display manifest when generated - default false
-r manifest refresh rate (in sec.) - default 2
eg. from existing feed
live-torrent -v -u https://live.computer -p manifest.m3u8
live-torrent -v -u https://live.computer/manifest.m3u8
eg. from local folder with ts files
live-torrent -v -l -f feed/
@@ -35,7 +33,7 @@ function die (msg, code) {
process.exit(code)
}
if (argv.h || argv.help || !((argv.p && argv.u) || argv.f)) {
if (argv.h || argv.help || !(argv.u || argv.f)) {
die(help, 0)
}
@@ -44,26 +42,22 @@ const sampleWebserver = typeof argv.s === 'undefined' ? true : (argv.s === 'true
const delay = parseInt(argv.r || 10)
const manifestLocation = argv.u
const playlistName = argv.p
const chunksLocation = argv.c || argv.u
const makeFromFolder = argv.f
const loop = !!argv.l
const wtm = new WtManifest(chunksLocation, manifestLocation, playlistName, makeFromFolder, delay, loop)
const wtm = new WtManifest(manifestLocation, makeFromFolder, delay, loop)
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
next()
})
app.get('*.m3u8', (req, res) => res.send(wtm.manifest))
if (sampleWebserver) app.use(express.static('client'))
if (makeFromFolder) {
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
next()
})
app.use(express.static(makeFromFolder))
}
if (makeFromFolder) app.use(express.static(makeFromFolder))
const makeManifest = async (cb) => {
try {
View
@@ -11,14 +11,18 @@
<script src="./build.js"></script>
<script>
const chunksDone = {}
const manifestName = 'manifest.m3u8'
const manifestURL = 'http://127.0.0.1:8008/'
const chunksLocation = 'https://live.computer/'
const dlManifest = async () => {
const m = await fetch('manifest.m3u8')
const m = await fetch(manifestURL + manifestName)
const t = await m.text()
const last = t.split('\n').filter(l => l.endsWith('.ts'))[2]
if (chunksDone[last] || !last) return
const chunks = t.split('\n').filter(l => l.endsWith('.ts'))
const last = chunks[2] || chunks[0]
if (chunksDone[last]) return
chunksDone[last] = true
fetch(last)
fetch(chunksLocation + last)
}
dlManifest()
View
@@ -6,15 +6,28 @@ const StreamMaker = require('./streamMaker')
const chunkName = url => url.match(/\d+(\.ts)/g)[0]
function wtManifest (chunksLoc = '', manifestLoc = '', manifestName = '', makeFromFolder = '', delay, loop = false) {
this.chunksLoc = chunksLoc.endsWith('/') ? chunksLoc : chunksLoc + '/'
this.manifestLoc = manifestLoc.endsWith('/') ? manifestLoc : manifestLoc + '/'
this.manifestName = manifestName
const removeDanglingSlash = u => u.startsWith('/') ? u.substring(1) : u
this.localPath = makeFromFolder.endsWith('/') ? makeFromFolder : makeFromFolder + '/'
this.isLocalStream = this.localPath.length > 1
const addTrailingSlash = u => u.endsWith('/') ? u : u + '/'
this.sm = makeFromFolder ? new StreamMaker(makeFromFolder, delay, loop) : null
const isUrl = f => f.startsWith('http://') || f.startsWith('https://')
function wtManifest (fullManifestPath = '', makeFromFolder = '', delay, loop = false) {
this.isLocalStream = makeFromFolder.length > 1
// Torrent from local folder
if (this.isLocalStream) {
this.localPath = addTrailingSlash(makeFromFolder)
this.sm = makeFromFolder ? new StreamMaker(makeFromFolder, delay, loop) : null
} else {
// Manifest location is splitted ; Filename goes in manifestname, rest of the path in manifestLoc
this.manifestLoc = fullManifestPath.split('/').slice(0, -1).join('/')
this.manifestLoc = addTrailingSlash(this.manifestLoc)
this.manifestName = fullManifestPath.split('/').pop()
// If no full path in manifest, chunk location is the manifest path minus the manifest name
this.chunksLoc = addTrailingSlash(fullManifestPath.split('/').slice(0, -1).join('/'))
}
this.announceList = [['wss://tracker.openwebtorrent.com']]
this.fileToMagnet = {}
@@ -29,7 +42,7 @@ wtManifest.prototype.computeMagnet = function (file, cn) {
createTorrent(file, { announceList: this.announceList }, (err, t) => {
if (err) return console.log(err)
const magnet = parseTorrent.toMagnetURI(parseTorrent(t))
resolve('###' + magnet)
resolve('###' + magnet + '\n')
})
})
}
@@ -41,7 +54,15 @@ wtManifest.prototype.makeMagnet = async function (f) {
if (self.fileToMagnet[cn]) return
// Fetch payload and compute magnet
const payload = self.isLocalStream ? await fs.readFile(self.localPath + f) : await request(self.chunksLoc + f, { encoding: null })
let payload
if (self.isLocalStream) {
payload = await fs.readFile(self.localPath + f)
} else {
// Use url if one's been provided. Otherwise use chunksLoc
const url = isUrl(f) ? f : self.chunksLoc + removeDanglingSlash(f)
payload = await request(url, { encoding: null })
}
const magnet = await self.computeMagnet(payload, cn)
// Store magnet computed
@@ -63,7 +84,7 @@ wtManifest.prototype.makeManifest = async function (manifest) {
// Split manifest and get sequenece number
let split = manifest.split('\n')
const sequence = split.filter(l => l.includes(`#EXT-X-MEDIA-SEQUENCE:`))[0].replace('#EXT-X-MEDIA-SEQUENCE:', '')
const sequence = split.find(l => l.includes(`#EXT-X-MEDIA-SEQUENCE:`))
if (sequence === self.sequence) {
return self.manifest
}
@@ -76,26 +97,27 @@ wtManifest.prototype.makeManifest = async function (manifest) {
await self.makeAllMagnets(files)
// Pop manifest back, inject magnet links alongside TS files
self.manifest = split.map(l => l.includes('.ts') ? self.fileToMagnet[chunkName(l)] + '\n' + self.chunksLoc + l : l).join('\n')
self.manifest = split.map(l => l.includes('.ts') ? self.fileToMagnet[chunkName(l)] + l : l).join('\n')
self.sequence = sequence
return self.manifest
}
wtManifest.prototype.doManifest = async function (extraManifestName) {
let manifest
if (this.isLocalStream) {
const manifest = await this.sm.makeLiveStream()
return this.makeManifest(manifest)
manifest = await this.sm.makeLiveStream()
} else {
const manifest = await request(this.manifestLoc + (extraManifestName || this.manifestName))
manifest = await request(this.manifestLoc + (extraManifestName || this.manifestName))
// Head over to the playlist, if what we got was a link to a playlist. Taking only last link for now.
if (manifest.replace(/\n$/, '').endsWith('.m3u8')) {
const m3u8 = manifest.split('\n').find(l => l.includes('.m3u8'))
return this.doManifest(m3u8)
}
return this.makeManifest(manifest)
}
return this.makeManifest(manifest)
}
module.exports = wtManifest
View
@@ -5,9 +5,9 @@
"main": "index.js",
"scripts": {
"build": "browserify client/loader.js -o client/build.js",
"cliRemote": "npm run build && node cli.js -v -u https://live.computer -p manifest.m3u8",
"cliLocal": "npm run build && node cli.js -v -f feed",
"startTest": "node cli.js -u https://live.computer -p manifest.m3u8 &",
"cliRemote": "npm run build && node cli.js -v -u https://live.computer/manifest.m3u8",
"cliLocal": "npm run build && node cli.js -v -l -f feed",
"startTest": "node cli.js -u https://live.computer/manifest.m3u8 &",
"stopTest": "kill `lsof -ti tcp:8008`",
"lint": "standard .",
"test": "npm run startTest && sleep 5 && node tests/test.js && npm run stopTest"

0 comments on commit c0e15fd

Please sign in to comment.