Browse Source

initial commit

master
mortie 5 years ago
commit
d2f1649557
10 changed files with 452 additions and 0 deletions
  1. 1
    0
      .gitignore
  2. 11
    0
      js/play/index.js
  3. 158
    0
      js/play/player.js
  4. 24
    0
      js/queue.js
  5. 15
    0
      package.json
  6. 21
    0
      server.js
  7. 14
    0
      web/index.html
  8. 35
    0
      web/playback/index.html
  9. 122
    0
      web/playback/script.js
  10. 51
    0
      web/playback/style.css

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
node_modules

+ 11
- 0
js/play/index.js View File

@@ -0,0 +1,11 @@
var player = require("./player.js");

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

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

exports.isPlaying = player.isPlaying;

+ 158
- 0
js/play/player.js View File

@@ -0,0 +1,158 @@
var spawn = require("child_process").spawn;
var fs = require("fs");
var net = require("net");
var Queue = require("../queue");

var child = null;

var ipcServer = process.cwd()+"/mpv-ipc-socket";

function cmd(params, cb) {
if (child == null || child.sock == null)
return;

child.sock.write(JSON.stringify({
command: params
})+"\n");

child.msgqueue.dequeue(obj => {
if (cb)
cb(obj);
});
}

function getState(cb) {
if (child == null) {
cb({
playing: false,
paused: false,
muted: false,
duration: 0,
time_pos: 0,
volume: 0,
volume_max: 0
});
return;
}

var state = {
playing: true
};

var cbs = 6;
function next() {
cbs -= 1;
if (cbs === 0)
cb(state);
}

cmd(["get_property", "pause"], res => {
state.paused = res.data;
next();
});

cmd(["get_property", "mute"], res => {
state.muted = res.data;
next();
});

cmd(["get_property", "duration"], res => {
state.duration = res.data;
next();
});

cmd(["get_property", "time-pos"], res => {
state.time_pos = res.data;
next();
});

cmd(["get_property", "volume"], res => {
state.volume = res.data;
next();
});

cmd(["get_property", "volume-max"], res => {
state.volume_max = res.data;
next();
});
}

exports.isPlaying = function() {
return child != null;
}

exports.play = function(path, cb) {
exports.stop();

var lchild = spawn("mpv", [
path,
"--input-ipc-server", ipcServer
], { stdio: "inherit" });
child = lchild;

lchild.running = true;

lchild.once("close", () => {
if (lchild.running) exports.stop();
});
lchild.on("error", err => console.error(err.toString()));

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, () => {

// Add output from mpv to the queue
lchild.sock.on("data", d => {
d.toString().split("\n").forEach(str => {
if (str == "") return;
var obj = JSON.parse(str);
if (obj.event) return;
lchild.msgqueue.push(obj);
});
});

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

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

cb();
});
}, 1000);
}

exports.stop = function() {
if (child) {
child.running = false;
child.kill("SIGKILL");
clearTimeout(child.initTimeout);
child = null;
}
try {
fs.unlinkSync(ipcServer);
} catch (err) {}
}

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

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

evt("state", (req, res) => {
getState((state) => {
res.json(state);
});
});

evt("set/:key/:val", (req, res) => {
cmd(["set_property", req.params.key, req.params.val]);
res.end();
});
}

+ 24
- 0
js/queue.js View File

@@ -0,0 +1,24 @@
module.exports = function() {
var self = {};

var cbs = [];
var arr = [];

self.push = function(val) {
if (cbs.length > 0) {
cbs.shift()(val);
} else {
arr.push(val);
}
}

self.dequeue = function(cb) {
if (arr.length > 0) {
cb(arr.shift());
} else {
cbs.push(cb);
}
}

return self;
}

+ 15
- 0
package.json View File

@@ -0,0 +1,15 @@
{
"name": "mmpc3",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"author": "Martin Dørum Nygaard <martid0311@gmail.com> (http://mort.coffee)",
"license": "ISC",
"dependencies": {
"webstuff": "^1.3.0"
}
}

+ 21
- 0
server.js View File

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

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

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

app.static("web");

app.post("/plaything", (req, res) => {
play.playFile("/home/martin/Documents/Assassination Classroom - E01.mkv", () => {
res.redirect("/playback");
});
});


+ 14
- 0
web/index.html View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mort's Media PC</title>
</head>
<body>
<form method="post" action="/plaything">
<button>Hello There!</button>
</form>
<script src="/webstuff.js"></script>
</body>
</html>

+ 35
- 0
web/playback/index.html View File

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Playback</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="group-info">
<div id="is-playing">Not Playing</div>
<div id="progress-text"></div>
</div>

<div id="group-bar">
<progress id="progress"></progress>
</div>

<div id="group-buttons">
<button id="mute">Mute</button>
<button id="skip-back">&lt;</button>
<button id="pause">||</button>
<button id="skip-forward">&gt;</button>
<button id="exit">Exit</button>
</div>

<div id="group-volume">
Volume <span id="volume-text"></span><br>
<input id="volume" type="range">
</div>

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

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

@@ -0,0 +1,122 @@
function timeformat(sec) {
var d = new Date(null);
d.setSeconds(Math.floor(sec))
return d.toISOString().substr(11, 8);
}

var elems = {
is_playing: $$("#is-playing"),
progress_text: $$("#progress-text"),
progress: $$("#progress"),

pause: $$("#pause"),
skip_back: $$("#skip-back"),
skip_forward: $$("#skip-forward"),

mute: $$("#mute"),
exit: $$("#exit"),

volume: $$("#volume"),
volume_text: $$("#volume-text"),
};

var state = {};

/*
* Update GUI stuff
*/

function update(state) {

// Playing
if (state.playing)
elems.is_playing.innerHTML = "Playing";
else
location.href = "/";

// Progress text
elems.progress_text.innerHTML =
timeformat(state.time_pos)+"/"+
timeformat(state.duration);

// Progress bar
elems.progress.value = state.time_pos;
elems.progress.max = state.duration;

// Buttons
elems.pause.className = state.paused ? "active" : "";
elems.mute.className = state.muted ? "active" : "";

// Volume
elems.volume.min = 0;
elems.volume.max = state.volume_max;
elems.volume.value = state.volume;
elems.volume.step = 5;
elems.volume_text.innerHTML = state.volume+"%";
}

function checkState() {
post("/playback/state", null, function(err, res) {
if (err)
return;

state = JSON.parse(res);
update(state);
});
}

checkState();
setInterval(checkState, 500);

/*
* React to input
*/

function playerset(key, val) {
post("/playback/set/"+key+"/"+val, null, function() {
checkState();
});
}

// Set time
elems.progress.addEventListener("click", function(evt) {
var pos = (evt.clientX - evt.target.offsetLeft) / evt.target.clientWidth;
pos *= evt.target.max;
playerset("time-pos", pos);
});

// Toggle pause
elems.pause.addEventListener("click", function() {
if (state.paused)
playerset("pause", "no");
else
playerset("pause", "yes");
});

// Back 15 seconds
elems.skip_back.addEventListener("click", function() {
playerset("time-pos", state.time_pos - 15);
});

// Forwards 15 seconds
elems.skip_forward.addEventListener("click", function() {
playerset("time-pos", state.time_pos + 15);
});

// Toggle mute
elems.mute.addEventListener("click", function(evt) {
if (state.muted)
playerset("mute", "no");
else
playerset("mute", "yes");
});

// Exit
elems.exit.addEventListener("click", function(evt) {
post("/playback/exit", null, function() {});
});

// Set volume
elems.volume.addEventListener("change", function(evt) {
playerset("volume", evt.target.value);
});

+ 51
- 0
web/playback/style.css View File

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

body {
margin: 12px;
}

body > div {
margin-bottom: 12px;
}

button {
display: inline-block;
padding: 0px;
border: 1px solid #666;
width: 50px;
height: 32px;
background: #ddd;
margin-left: 2px;
margin-right: 2px;
}

button:active,
button.active {
background: #aaa;
}

progress {
width: 100%;
height: 24px;
}

input[type="range"] {
border: 1px solid rgba(0, 0, 0, 0);
box-sizing: border-box;
margin: 0px;
width: 100%;
}

#is-playing,
#progress-text {
display: inline-block;
}
#progress-text {
float: right;
}

#group-buttons {
text-align: center;
}

Loading…
Cancel
Save