From daeebed9e49833f6c840957ec8c0211f314e3e2e Mon Sep 17 00:00:00 2001 From: Kylart Date: Wed, 21 Mar 2018 06:01:59 +0100 Subject: [PATCH] [wip] Can now extract subtitles from local video and torrents into streaming feature --- components/videoModal.vue | 2 +- pages/localPage.vue | 30 ++++--- pages/releases.vue | 1 - server/index.js | 2 +- server/local/local.js | 10 +-- server/{torrent => video}/index.js | 2 +- server/video/local.js | 25 ++++++ .../torrent.js => video/streaming.js} | 82 ++++++++++++------- 8 files changed, 103 insertions(+), 51 deletions(-) rename server/{torrent => video}/index.js (78%) create mode 100644 server/video/local.js rename server/{torrent/torrent.js => video/streaming.js} (56%) diff --git a/components/videoModal.vue b/components/videoModal.vue index 6a5b861..a3ab763 100644 --- a/components/videoModal.vue +++ b/components/videoModal.vue @@ -18,7 +18,7 @@ export default { }, methods: { close () { - if (document.fullscreenElement == null) { + if (document.fullscreenElement === null) { this.$store.commit('videoPlayer/close') } } diff --git a/pages/localPage.vue b/pages/localPage.vue index d7a4cd8..f10af75 100644 --- a/pages/localPage.vue +++ b/pages/localPage.vue @@ -129,19 +129,25 @@ console.log(`[${(new Date()).toLocaleTimeString()}]: Requested to play ${item.name} - ${item.ep}. Sending...`) // No need to get through store. - this.$axios.get(`openThis`, { - params: { - type: 'video', - path: item.path, - dir: this.$store.state.localFiles.dir - } - }).then((res) => { - if (res.status !== 200) { console.log('An error occurred: request to open file ended with a status ' + res.status + '.') } + // this.$axios.get(`openThis`, { + // params: { + // type: 'video', + // path: item.path, + // dir: this.$store.state.localFiles.dir + // } + // }).then((res) => { + // if (res.status !== 200) { console.log('An error occurred: request to open file ended with a status ' + res.status + '.') } - this.$store.dispatch('history/append', { - type: 'Play', - text: `${item.name} - ${item.ep}` - }).catch(err => { void (err) }) + // this.$store.dispatch('history/append', { + // type: 'Play', + // text: `${item.name} - ${item.ep}` + // }).catch(err => { void (err) }) + // }) + this.$store.commit('videoPlayer/play', { + show: true, + link: { + link: item.path + } }) }, delThis (item) { diff --git a/pages/releases.vue b/pages/releases.vue index 8ac57eb..003c517 100644 --- a/pages/releases.vue +++ b/pages/releases.vue @@ -190,7 +190,6 @@ : '' }, watch (item) { - console.log(item) this.$store.commit('videoPlayer/play', { show: true, link: { diff --git a/server/index.js b/server/index.js index ee933f6..e918c35 100644 --- a/server/index.js +++ b/server/index.js @@ -20,7 +20,7 @@ const setup = (app) => { nyaa: require('./nyaa'), openExternal: require('./openExternal'), seasons: require('./seasons'), - torrent: require('./torrent'), + video: require('./video'), search: require('./search'), wl: require('./watchList') } diff --git a/server/local/local.js b/server/local/local.js index 4875dbc..5598c3b 100644 --- a/server/local/local.js +++ b/server/local/local.js @@ -37,7 +37,7 @@ const getUniques = (files) => { return result } -const sendFiles = (json, files, res) => { +const sendFiles = (json, dir, files, res) => { const result = [] logger.info('Sending files.') @@ -47,7 +47,7 @@ const sendFiles = (json, files, res) => { if (name) { const tmp = JSON.parse(JSON.stringify(json[minifyName(name)])) tmp.ep = getEp(file) - tmp.path = file + tmp.path = join(dir, file) result.push(tmp) } }) @@ -67,7 +67,7 @@ const searchLocalFiles = ({query}, res) => { let counter = 0 if (!files.length) { - sendFiles(json, [], res) + sendFiles(json, DIR, [], res) } else { uniqueNames.forEach((elem) => { // Search MAL for each name if not in json @@ -96,7 +96,7 @@ const searchLocalFiles = ({query}, res) => { logger.info('Successfully saved data.') - sendFiles(json, files, res) + sendFiles(json, DIR, files, res) } }).catch(/* istanbul ignore next */(err) => { logger.error('An error occurred.', err) @@ -105,7 +105,7 @@ const searchLocalFiles = ({query}, res) => { } else { ++counter /* istanbul ignore next */ - if (counter === uniqueNames.length) sendFiles(json, files, res) + if (counter === uniqueNames.length) sendFiles(json, DIR, files, res) } }) } diff --git a/server/torrent/index.js b/server/video/index.js similarity index 78% rename from server/torrent/index.js rename to server/video/index.js index cef7b54..490ca38 100644 --- a/server/torrent/index.js +++ b/server/video/index.js @@ -1,4 +1,4 @@ -const {stream, tracks} = require('./torrent.js') +const {stream, tracks} = require('./streaming.js') const sseExpress = require('sse-express') const routes = [ diff --git a/server/video/local.js b/server/video/local.js new file mode 100644 index 0000000..df4f2ee --- /dev/null +++ b/server/video/local.js @@ -0,0 +1,25 @@ +const fs = require('fs') +const MatroskaSubtitles = require('matroska-subtitles') +const { Logger } = require('../utils') + +const logger = new Logger('Local') + +const getSubtitles = (req, res) => { + const { query: { path } } = req + logger.info('Extracting subtitles from file:', path) + + const parser = new MatroskaSubtitles() + + parser.once('tracks', tracks => res.sse('tracks', tracks)) + + parser.on('subtitle', (subtitle, trackNumber) => res.sse('subtitle', { + subtitle, + trackNumber + })) + + fs.createReadStream(path).pipe(parser) +} + +module.exports = { + getSubtitles +} diff --git a/server/torrent/torrent.js b/server/video/streaming.js similarity index 56% rename from server/torrent/torrent.js rename to server/video/streaming.js index cfb4942..c1d008b 100644 --- a/server/torrent/torrent.js +++ b/server/video/streaming.js @@ -1,3 +1,4 @@ +const fs = require('fs') const WebTorrent = require('webtorrent') const parseRange = require('range-parser') const mime = require('mime') @@ -6,9 +7,10 @@ const logger = new Logger('Torrent Streamer') const decode = require('urldecode') const MatroskaSubtitles = require('matroska-subtitles') -const client = new WebTorrent() +const client = process.torrentClient = new WebTorrent() const stream = (req, res) => { + // Getting magnet hash const magnet = decode(req.url.slice('/stream/'.length)) logger.info(`Streaming magnet: ${magnet}`) @@ -21,19 +23,24 @@ const stream = (req, res) => { }) let range = parseRange(torrent.length, req.headers.range || '') + if (Array.isArray(range)) { range = range[0] - res.status(206) - .set({ - 'Content-Range': `bytes ${range.start}-${range.end}/${torrent.length}`, - 'Content-Length': range.end - range.start + 1 - }) + res + .status(206) + .set({ + 'Content-Range': `bytes ${range.start}-${range.end}/${torrent.length}`, + 'Content-Length': range.end - range.start + 1 + }) } else { + // Means that parseRange a parsing error occurred. res.setHeader('Content-Length', torrent.length) } + if (req.method === 'HEAD') { res.end() } else { let stream = torrent.createReadStream(range) + const close = () => { if (stream) { logger.info(`Closing stream of range: ${JSON.stringify(range)} for magnet: ${magnet}`) @@ -50,27 +57,38 @@ const stream = (req, res) => { } } - const torrent = client.get(magnet) + // const torrent = client.get(magnet) - if (torrent) { - if (torrent.ready) { - processTorrent(torrent) - } else { - torrent.once('ready', () => processTorrent(torrent)) - } - } else { - client.add(magnet, processTorrent) - } + // if (torrent) { + // if (torrent.ready) { + // processTorrent(torrent) + // } else { + // torrent.once('ready', () => processTorrent(torrent)) + // } + // } else { + // client.add(magnet, processTorrent) + // } } -const tracks = (req, res) => { - const magnet = decode(req.url.slice('/tracks/'.length)) - logger.info(`Tracks for Magnet magnet: ${magnet}`) - const processTorrent = ({ files: [torrent] }) => { - const mimeType = mime.getType(torrent.name) +const tracks = (req, res) => { + const info = decode(req.url.slice('/tracks/'.length)) + const isMagnet = /^magnet:\?/.test(info) + const type = isMagnet ? 'magnet' : 'file' + + const path = isMagnet ? null : info + const magnet = isMagnet ? info : null + + logger.info(`Tracks for ${type}: ${isMagnet ? magnet : path}`) + + const processFile = (obj = {files: []}) => { + const { files: [torrent] } = obj + const mimeType = mime.getType(path || torrent.name) + if (mimeType === 'video/x-matroska') { const parser = new MatroskaSubtitles() - let stream = torrent.createReadStream() + let stream = isMagnet + ? torrent.createReadStream() + : fs.createReadStream(path) parser.once('tracks', tracks => res.sse('tracks', tracks)) @@ -81,9 +99,9 @@ const tracks = (req, res) => { const close = () => { if (stream) { - logger.info(`Closing stream for magnet tracks: ${magnet}`) + logger.info(`Closing stream for ${type} tracks: ${magnet}`) stream.destroy() - torrent.deselect() + torrent && torrent.deselect() stream = null } } @@ -97,16 +115,20 @@ const tracks = (req, res) => { } } - const torrent = client.get(magnet) + if (magnet) { + const torrent = client.get(magnet) - if (torrent) { - if (torrent.ready) { - processTorrent(torrent) + if (torrent) { + if (torrent.ready) { + processFile(torrent) + } else { + torrent.once('ready', () => processFile(torrent)) + } } else { - torrent.once('ready', () => processTorrent(torrent)) + client.add(magnet, processFile) } } else { - client.add(magnet, processTorrent) + processFile() } }