@@ -1,3 +1,4 @@ | |||
{ | |||
"tmpdir": "tmp" | |||
"tmpdir": "tmp", | |||
"subtitles": "en" | |||
} |
@@ -1,6 +1,9 @@ | |||
var fs = require("fs"); | |||
var pathlib = require("path"); | |||
var player = require("./player"); | |||
var httpStream = require("./http-stream"); | |||
var torrentStreamer = require("./torrent-streamer"); | |||
var subtitleFinder = require("./subtitle-finder"); | |||
exports.httpPath = player.httpPath; | |||
@@ -11,18 +14,42 @@ exports.init = function(_app, conf) { | |||
player.init(app, conf); | |||
httpStream.init(app, conf); | |||
torrentStreamer.init(app, conf); | |||
subtitleFinder.init(app, conf); | |||
} | |||
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) { | |||
torrentStreamer.stream(magnet, err => { | |||
// Stream torrent | |||
torrentStreamer.stream(magnet, (err, filesize, filename) => { | |||
if (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); | |||
}); | |||
}); | |||
} | |||
@@ -83,20 +83,27 @@ exports.isPlaying = function() { | |||
return child != null; | |||
} | |||
exports.play = function(path, cb) { | |||
exports.play = function(path, subFile, cb) { | |||
exports.stop(); | |||
var lchild = spawn("mpv", [ | |||
var args = [ | |||
path, | |||
"--no-cache-pause", | |||
"--no-resume-playback", | |||
"--input-unix-socket", ipcServer | |||
], { stdio: "inherit" }); | |||
]; | |||
if (subFile) { | |||
args.push("--sub-file"); | |||
args.push(subFile); | |||
} | |||
var lchild = spawn("mpv", args, { stdio: "inherit" }); | |||
child = lchild; | |||
lchild.running = true; | |||
lchild.once("close", () => { | |||
console.log("child closed"); | |||
if (lchild.running) exports.stop(); | |||
}); | |||
lchild.on("error", err => console.error(err.toString())); |
@@ -0,0 +1,53 @@ | |||
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(); | |||
}); | |||
} |
@@ -2,7 +2,6 @@ var httpStream = require("./http-stream"); | |||
var torrentStream = require("torrent-stream"); | |||
var mediarx = /\.(mp4|mkv|mov|avi|ogv)$/; | |||
var tmpdir = process.cwd()+"/tmp"; | |||
var engine; | |||
var conf; | |||
@@ -16,9 +15,13 @@ exports.stream = function(magnet, cb) { | |||
return engine.destroy(() => | |||
{ 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", () => { | |||
var file = null; | |||
@@ -36,17 +39,14 @@ exports.stream = function(magnet, cb) { | |||
file.select(); | |||
httpStream.readStreamCreator = function(options) { | |||
console.log("creating stream with", options); | |||
var rs = file.createReadStream(options); | |||
rs.filesize = file.length; | |||
rs.filename = file.name; | |||
rs.on("close", () => console.log("stream closing")); | |||
return rs; | |||
} | |||
cb(); | |||
cb(null, file.length, file.name); | |||
}); | |||
} | |||
@@ -11,6 +11,7 @@ | |||
"license": "ISC", | |||
"dependencies": { | |||
"mime": "^1.3.4", | |||
"opensubtitles-api": "^3.2.0", | |||
"torrent-stream": "^1.0.3", | |||
"webstuff": "^1.4.0" | |||
} |
@@ -18,12 +18,12 @@ app.express.use((req, res, next) => { | |||
app.static("web"); | |||
app.post("/play/link", (req, res) => { | |||
app.post("/play/url", (req, res) => { | |||
req.parseBody((err, fields) => { | |||
if (!fields.url) | |||
return res.redirect("/"); | |||
play.playFile(fields.url, () => { | |||
play.playUrl(fields.url, () => { | |||
res.redirect(play.httpPath); | |||
}); | |||
}); |
@@ -8,7 +8,7 @@ | |||
</head> | |||
<body> | |||
<div id="parts"> | |||
<form class="part" method="post" action="/play/link"> | |||
<form class="part" method="post" action="/play/url"> | |||
<div class="name">URL:</div> | |||
<input type="url" name="url" autocomplete="off"> | |||
<button>Play</button> |
@@ -8,7 +8,7 @@ | |||
} | |||
#parts .part { | |||
border-bottom: 1px solid #eee; | |||
border-bottom: 1px solid #ccc; | |||
padding: 20px 10px; | |||
padding-top: 17px; | |||
} | |||
@@ -28,5 +28,5 @@ | |||
#parts .part input, | |||
#parts .part button { | |||
height: 25px; | |||
height: 28px; | |||
} |