{ | { | ||||
"tmpdir": "tmp" | |||||
"tmpdir": "tmp", | |||||
"subtitles": "en" | |||||
} | } |
var fs = require("fs"); | |||||
var pathlib = require("path"); | |||||
var player = require("./player"); | var player = require("./player"); | ||||
var httpStream = require("./http-stream"); | var httpStream = require("./http-stream"); | ||||
var torrentStreamer = require("./torrent-streamer"); | var torrentStreamer = require("./torrent-streamer"); | ||||
var subtitleFinder = require("./subtitle-finder"); | |||||
exports.httpPath = player.httpPath; | exports.httpPath = player.httpPath; | ||||
player.init(app, conf); | player.init(app, conf); | ||||
httpStream.init(app, conf); | httpStream.init(app, conf); | ||||
torrentStreamer.init(app, conf); | torrentStreamer.init(app, conf); | ||||
subtitleFinder.init(app, conf); | |||||
} | } | ||||
exports.playFile = function(path, cb) { | exports.playFile = function(path, cb) { | ||||
player.play(path, cb); | |||||
// Find file's length | |||||
fs.stat(path, (err, stat) => { | |||||
// Find subtitles | |||||
subtitleFinder.find(stat.size, pathlib.basename(path), subFile => { | |||||
// Play! | |||||
player.play(path, subFile, cb); | |||||
}); | |||||
}); | |||||
} | |||||
exports.playUrl = function(path, cb) { | |||||
// Just play, we won't bother finding subtitles | |||||
player.play(path, null, cb); | |||||
} | } | ||||
exports.playTorrent = function(magnet, cb) { | exports.playTorrent = function(magnet, cb) { | ||||
torrentStreamer.stream(magnet, err => { | |||||
// Stream torrent | |||||
torrentStreamer.stream(magnet, (err, filesize, filename) => { | |||||
if (err) | if (err) | ||||
return cb(err); | return cb(err); | ||||
player.play(app.getAddress()+httpStream.httpPath, cb); | |||||
// Find subtitles | |||||
subtitleFinder.find(filesize, filename, subFile => { | |||||
// Play! | |||||
player.play(app.getAddress()+httpStream.httpPath, subFile, cb); | |||||
}); | |||||
}); | }); | ||||
} | } | ||||
return child != null; | return child != null; | ||||
} | } | ||||
exports.play = function(path, cb) { | |||||
exports.play = function(path, subFile, cb) { | |||||
exports.stop(); | exports.stop(); | ||||
var lchild = spawn("mpv", [ | |||||
var args = [ | |||||
path, | path, | ||||
"--no-cache-pause", | "--no-cache-pause", | ||||
"--no-resume-playback", | |||||
"--input-unix-socket", ipcServer | "--input-unix-socket", ipcServer | ||||
], { stdio: "inherit" }); | |||||
]; | |||||
if (subFile) { | |||||
args.push("--sub-file"); | |||||
args.push(subFile); | |||||
} | |||||
var lchild = spawn("mpv", args, { stdio: "inherit" }); | |||||
child = lchild; | child = lchild; | ||||
lchild.running = true; | lchild.running = true; | ||||
lchild.once("close", () => { | lchild.once("close", () => { | ||||
console.log("child closed"); | |||||
if (lchild.running) exports.stop(); | if (lchild.running) exports.stop(); | ||||
}); | }); | ||||
lchild.on("error", err => console.error(err.toString())); | lchild.on("error", err => console.error(err.toString())); |
var OpenSubtitles = require("opensubtitles-api"); | |||||
var fs = require("fs"); | |||||
var http = require("http"); | |||||
var urllib = require("url"); | |||||
var subs = new OpenSubtitles({ useragent: "mmpc-media-streamer" }); | |||||
var conf; | |||||
exports.init = function(app, _conf) { | |||||
conf = _conf; | |||||
} | |||||
exports.find = function(filesize, filename, cb) { | |||||
if (!conf.subtitles) | |||||
return cb(); | |||||
var subFile = conf.tmpdir+"/subs.srt"; | |||||
try { | |||||
fs.unlinkSync(subFile); | |||||
} catch (err) {} | |||||
subs.search({ | |||||
sublanguageid: conf.subtitles, | |||||
filesize: filesize, | |||||
filename: filename | |||||
}).then(subtitles => { | |||||
var sub = subtitles[conf.subtitles]; | |||||
if (!sub || !sub.url) | |||||
return cb(); | |||||
try { | |||||
var ws = fs.createWriteStream(subFile); | |||||
} catch (err) { | |||||
console.trace(err); | |||||
cb(); | |||||
} | |||||
http.request(urllib.parse(sub.url), res => { | |||||
res.pipe(ws); | |||||
res | |||||
.on("error", err => { | |||||
console.trace(err); | |||||
cb(); | |||||
}) | |||||
.on("end", () => { | |||||
ws.close(); | |||||
cb(subFile); | |||||
}); | |||||
}).end(); | |||||
}); | |||||
} |
var torrentStream = require("torrent-stream"); | var torrentStream = require("torrent-stream"); | ||||
var mediarx = /\.(mp4|mkv|mov|avi|ogv)$/; | var mediarx = /\.(mp4|mkv|mov|avi|ogv)$/; | ||||
var tmpdir = process.cwd()+"/tmp"; | |||||
var engine; | var engine; | ||||
var conf; | var conf; | ||||
return engine.destroy(() => | return engine.destroy(() => | ||||
{ engine = null; exports.stream(magnet, cb) }); | { engine = null; exports.stream(magnet, cb) }); | ||||
engine = torrentStream(magnet, { | |||||
tmp: conf.tmpdir | |||||
}); | |||||
try { | |||||
engine = torrentStream(magnet, { | |||||
tmp: conf.tmpdir | |||||
}); | |||||
} catch (err) { | |||||
return cb(err.toString()); | |||||
} | |||||
engine.on("ready", () => { | engine.on("ready", () => { | ||||
var file = null; | var file = null; | ||||
file.select(); | file.select(); | ||||
httpStream.readStreamCreator = function(options) { | httpStream.readStreamCreator = function(options) { | ||||
console.log("creating stream with", options); | |||||
var rs = file.createReadStream(options); | var rs = file.createReadStream(options); | ||||
rs.filesize = file.length; | rs.filesize = file.length; | ||||
rs.filename = file.name; | rs.filename = file.name; | ||||
rs.on("close", () => console.log("stream closing")); | |||||
return rs; | return rs; | ||||
} | } | ||||
cb(); | |||||
cb(null, file.length, file.name); | |||||
}); | }); | ||||
} | } | ||||
"license": "ISC", | "license": "ISC", | ||||
"dependencies": { | "dependencies": { | ||||
"mime": "^1.3.4", | "mime": "^1.3.4", | ||||
"opensubtitles-api": "^3.2.0", | |||||
"torrent-stream": "^1.0.3", | "torrent-stream": "^1.0.3", | ||||
"webstuff": "^1.4.0" | "webstuff": "^1.4.0" | ||||
} | } |
app.static("web"); | app.static("web"); | ||||
app.post("/play/link", (req, res) => { | |||||
app.post("/play/url", (req, res) => { | |||||
req.parseBody((err, fields) => { | req.parseBody((err, fields) => { | ||||
if (!fields.url) | if (!fields.url) | ||||
return res.redirect("/"); | return res.redirect("/"); | ||||
play.playFile(fields.url, () => { | |||||
play.playUrl(fields.url, () => { | |||||
res.redirect(play.httpPath); | res.redirect(play.httpPath); | ||||
}); | }); | ||||
}); | }); |
</head> | </head> | ||||
<body> | <body> | ||||
<div id="parts"> | <div id="parts"> | ||||
<form class="part" method="post" action="/play/link"> | |||||
<form class="part" method="post" action="/play/url"> | |||||
<div class="name">URL:</div> | <div class="name">URL:</div> | ||||
<input type="url" name="url" autocomplete="off"> | <input type="url" name="url" autocomplete="off"> | ||||
<button>Play</button> | <button>Play</button> |
} | } | ||||
#parts .part { | #parts .part { | ||||
border-bottom: 1px solid #eee; | |||||
border-bottom: 1px solid #ccc; | |||||
padding: 20px 10px; | padding: 20px 10px; | ||||
padding-top: 17px; | padding-top: 17px; | ||||
} | } | ||||
#parts .part input, | #parts .part input, | ||||
#parts .part button { | #parts .part button { | ||||
height: 25px; | |||||
height: 28px; | |||||
} | } |