Browse Source

lots of work

master
mortie 5 years ago
parent
commit
e2b477c31a
11 changed files with 240 additions and 17 deletions
  1. 1
    0
      .gitignore
  2. 3
    0
      conf.json.example
  3. 61
    0
      js/play/http-stream.js
  4. 26
    3
      js/play/index.js
  5. 8
    5
      js/play/player.js
  6. 56
    0
      js/play/torrent-streamer.js
  7. 3
    1
      package.json
  8. 25
    5
      server.js
  9. 15
    3
      web/index.html
  10. 10
    0
      web/playback/script.js
  11. 32
    0
      web/style.css

+ 1
- 0
.gitignore View File

@@ -1 +1,2 @@
node_modules
conf.json

+ 3
- 0
conf.json.example View File

@@ -0,0 +1,3 @@
{
"tmpdir": "tmp"
}

+ 61
- 0
js/play/http-stream.js View File

@@ -0,0 +1,61 @@
var mime = require("mime");

/*
* Must be set to a function which takes an options argument with
* 'start' and 'end', and returns a readStream.
* The readStream must have the additional properties
* 'filesize' and 'filename' set.
*/
exports.readStreamCreator = null;

exports.httpPath = "/http-stream";

exports.init = function(app) {
app.get("/http-stream", (req, res) => {
if (exports.readStreamCreator == null) {
res.writeHead(500);
throw "readStreamCreator not set!";
}

var rs;
var parts = [];
if (req.headers.range) {
parts = req.headers.range.replace("bytes=", "").split("-");
var start = parseInt(parts[0]);
var end = parseInt(parts[1]);
var options = {};
if (!isNaN(start)) options.start = start;
if (!isNaN(end)) options.end = end;
rs = exports.readStreamCreator(options);
} else {
rs = exports.readStreamCreator();
}

var start = parseInt(parts[0]) || 0;
var end = parts[1] ? parseInt(parts[1]) : rs.filesize - 1;
var chunksize = end - start + 1;

if (chunksize > rs.filesize || start > end || end >= rs.filesize) {
res.writeHead(416);
res.end("Range not satisfiable. Start: "+start+", end: "+end);
return;
}

res.writeHead(req.headers.range ? 206 : 200, {
"icy-name": rs.filename,
"content-length": chunksize,
"content-type": mime.lookup(rs.filename),
"content-range": "bytes "+start+"-"+end+"/"+rs.filesize,
"accept-ranges": "bytes"
});

if (req.method === "HEAD")
return res.end();

rs.pipe(res);
});
}

exports.stop = function() {
exports.readStreamCreator = null;
}

+ 26
- 3
js/play/index.js View File

@@ -1,11 +1,34 @@
var player = require("./player.js");
var player = require("./player");
var httpStream = require("./http-stream");
var torrentStreamer = require("./torrent-streamer");

exports.init = function(app) {
player.init(app);
exports.httpPath = player.httpPath;

var app;

exports.init = function(_app, conf) {
app = _app;
player.init(app, conf);
httpStream.init(app, conf);
torrentStreamer.init(app, conf);
}

exports.playFile = function(path, cb) {
player.play(path, cb);
}

exports.playTorrent = function(magnet, cb) {
torrentStreamer.stream(magnet, err => {
if (err)
return cb(err);

player.play(app.getAddress()+httpStream.httpPath, cb);
});
}

exports.isPlaying = player.isPlaying;

player.onstop = function() {
torrentStreamer.stop();
httpStream.stop();
}

+ 8
- 5
js/play/player.js View File

@@ -3,6 +3,8 @@ var fs = require("fs");
var net = require("net");
var Queue = require("../queue");

exports.httpPath = "/playback";

var child = null;

var ipcServer = process.cwd()+"/mpv-ipc-socket";
@@ -86,13 +88,15 @@ exports.play = function(path, cb) {

var lchild = spawn("mpv", [
path,
"--input-ipc-server", ipcServer
"--no-cache-pause",
"--input-unix-socket", ipcServer
], { 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()));
@@ -100,9 +104,7 @@ exports.play = function(path, cb) {
lchild.state = {};
lchild.msgqueue = Queue();

console.log("lchild is "+lchild);
lchild.initTimeout = setTimeout(() => {
console.log("lchild still is "+lchild);
// Create socket
lchild.sock = net.connect(ipcServer, () => {

@@ -118,7 +120,6 @@ exports.play = function(path, cb) {

lchild.sock.on("error", err => console.trace(err));

// Set fullscreen
cmd(["set_property", "fullscreen", "yes"]);

cb();
@@ -132,6 +133,8 @@ exports.stop = function() {
child.kill("SIGKILL");
clearTimeout(child.initTimeout);
child = null;

exports.onstop();
}
try {
fs.unlinkSync(ipcServer);
@@ -140,7 +143,7 @@ exports.stop = function() {

exports.init = function(app) {
function evt(p, cb) {
app.post("/playback/"+p, (req, res) => cb(req, res));
app.post(exports.httpPath+"/"+p, (req, res) => cb(req, res));
}

evt("exit", (req, res) => { res.end(); exports.stop() });

+ 56
- 0
js/play/torrent-streamer.js View File

@@ -0,0 +1,56 @@
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;

exports.init = function(app, _conf) {
conf = _conf;
}

exports.stream = function(magnet, cb) {
if (engine)
return engine.destroy(() =>
{ engine = null; exports.stream(magnet, cb) });

engine = torrentStream(magnet, {
tmp: conf.tmpdir
});

engine.on("ready", () => {
var file = null;

engine.files.forEach(f => {
if (mediarx.test(f.name) &&
(file == null || f.length > file.length)) {
file = f;
}
});

if (file == null)
return cb("No media file in the torrent");

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();
});
}

exports.stop = function() {
if (engine != null)
engine.destroy();
}

+ 3
- 1
package.json View File

@@ -10,6 +10,8 @@
"author": "Martin Dørum Nygaard <martid0311@gmail.com> (http://mort.coffee)",
"license": "ISC",
"dependencies": {
"webstuff": "^1.3.0"
"mime": "^1.3.4",
"torrent-stream": "^1.0.3",
"webstuff": "^1.4.0"
}
}

+ 25
- 5
server.js View File

@@ -1,21 +1,41 @@
var fs = require("fs");
var web = require("webstuff");
var play = require("./js/play");

var magnet = "magnet:?xt=urn:btih:13241fe16a2797b2a41b7822bde970274d6b687c&dn=Mad+Max%3A+Fury+Road+%282015%29+1080p+BrRip+x264+-+YIFY&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Fzer0day.ch%3A1337&tr=udp%3A%2F%2Fopen.demonii.com%3A1337&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Fexodus.desync.com%3A6969";

var conf = JSON.parse(fs.readFileSync("conf.json"));

var app = web();
play.init(app);
play.init(app, conf);

app.express.use((req, res, next) => {
if (req.url === "/" && play.isPlaying())
res.redirect("/playback");
res.redirect(play.httpPath);
else
next();
});

app.static("web");

app.post("/plaything", (req, res) => {
play.playFile("/home/martin/Documents/Assassination Classroom - E01.mkv", () => {
res.redirect("/playback");
app.post("/play/link", (req, res) => {
req.parseBody((err, fields) => {
if (!fields.url)
return res.redirect("/");

play.playFile(fields.url, () => {
res.redirect(play.httpPath);
});
});
});

app.post("/play/magnet", (req, res) => {
req.parseBody((err, fields) => {
if (!fields.magnet)
return res.redirect("/");

play.playTorrent(fields.magnet, () => {
res.redirect(play.httpPath);
});
});
});

+ 15
- 3
web/index.html View File

@@ -3,12 +3,24 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<title>Mort's Media PC</title>
</head>
<body>
<form method="post" action="/plaything">
<button>Hello There!</button>
</form>
<div id="parts">
<form class="part" method="post" action="/play/link">
<div class="name">URL:</div>
<input type="url" name="url" autocomplete="off">
<button>Play</button>
</form>

<form class="part" method="post" action="/play/magnet">
<div class="name">Magnet Link:</div>
<input type="url" name="magnet" autocomplete="off">
<button>Play</button>
</form>
</div>

<script src="/webstuff.js"></script>
</body>
</html>

+ 10
- 0
web/playback/script.js View File

@@ -120,3 +120,13 @@ elems.exit.addEventListener("click", function(evt) {
elems.volume.addEventListener("change", function(evt) {
playerset("volume", evt.target.value);
});
elems.volume.addEventListener("keydown", function(evt) {
if (evt.keyCode === 37 || evt.keyCode === 40)
playerset("volume", parseInt(evt.target.value) - parseInt(evt.target.step));
else if (evt.keyCode === 38 || evt.keyCode === 39)
playerset("volume", parseInt(evt.target.value) + parseInt(evt.target.step));
});

window.addEventListener("keydown", function(evt) {
console.log(evt.keyCode);
});

+ 32
- 0
web/style.css View File

@@ -0,0 +1,32 @@
* {
box-sizing: border-box;
}

#parts {
max-width: 600px;
margin: auto;
}

#parts .part {
border-bottom: 1px solid #eee;
padding: 20px 10px;
padding-top: 17px;
}
#parts .part:last-child {
border-bottom: none;
}
#parts .part .name {
margin-bottom: 6px;
}
#parts .part input {
width: calc(100% - 80px);
}
#parts .part button {
width: 75px;
float: right;
}

#parts .part input,
#parts .part button {
height: 25px;
}

Loading…
Cancel
Save