<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<link rel="manifest" href="ir.webmanifest">
<link rel="icon" href="ir.png">
<title>IR</title>
<script>
"use strict"
function fillTemplate(template,values) {
const newElement=template.content.cloneNode(true);
newElement.querySelectorAll('*').forEach(
(e) => {
if (e.tagName == 'SLOT') {
const valueKey=e.textContent.trim();
e.replaceWith(e.ownerDocument.createTextNode(values[valueKey]));
return;
}
const attrs = e.attributes;
for (var i = attrs.length - 1; i >= 0; i--) {
if (attrs[i].name.startsWith('slot:')) {
const name=attrs[i].name.replace('slot:','');
const valueKey=attrs[i].value;
e.removeAttribute(attrs[i].name);
if (values.hasOwnProperty(valueKey)) {
e.setAttribute(name,values[valueKey]);
}
}
}
},
);
return newElement;
}
function call(method,path,args) {
args ||= {};
let url=new URL(path,location.href);
for (let a in args) {
url.searchParams.set(a,args[a]);
}
return fetch(url, {
method: method,
mode: 'no-cors',
});
}
function vlcCommand(command,args) {
call('post',`vlc/${command}`,args);
}
function irCommand(thing,arg) {
call('post',`ir/${thing}/${arg}`);
}
async function updateView() {
const status = (await (await call('get','vlc/status')).json()).status;
const currentPos = document.querySelector('#current-pos');
if (status.state != 'playing') {
currentPos.disabled=true;
return;
}
currentPos.disabled=false;
const length = parseInt(status.length);
if (!length) { return }
const time = parseInt(status.time);
currentPos.max=length;
currentPos.value=time;
}
async function browseTo(id) {
const fileList = document.querySelector('ul#file-list');
const pathList = document.querySelector('ul#path-list');
const itemTemplate = document.querySelector('#file-item');
const browseData = await (await call('get',`media${ id ? '/' + id : ''}`)).json();
const fileItems = browseData.children.map(f => {
return fillTemplate(itemTemplate, {
'url': f.is_dir ? `javascript:browseTo(${f.id})` : `javascript:vlcCommand('play/${f.id}')`,
'name': f.name,
'dirclass': f.is_dir ? 'dir' : 'file',
});
});
fileList.replaceChildren(...fileItems);
const parents = browseData.parents ? [{id:null,name:'(root)'},...browseData.parents] : [];
const pathItems = parents.map(f => {
return fillTemplate(itemTemplate, {
'url': `javascript:browseTo(${f.id})`,
'name': f.name,
});
});
pathList.replaceChildren(...pathItems);
}
async function fillFiles() {
const filesDetails = document.querySelector('#files');
if (document.querySelector('#files').open) {
filesDetails.removeEventListener('toggle',fillFiles);
await browseTo(null);
}
}
document.addEventListener('readystatechange', (event) => {
if (document.readyState == 'complete') {
document.querySelector('#files').addEventListener('toggle', fillFiles);
}
})
</script>
<style>
body { font-size: 12vw }
* { font-size: inherit }
div.thing {
padding: 0.2em 0.1em 0.2em 0.1em;
margin-top: 0.5em;
margin-bottom: 1em;
border: solid 0.05em black;
border-radius: 0.5em;
position: relative;
}
div.thing label {
font-size: 0.8em;
line-height: 0.5em;
position: absolute;
top: -0.5em;
left: 1em;
background-color: white;
}
div.thing table {
width: 100%;
table-layout: fixed;
}
td button {
border-radius: 0.5em;
width: calc(100% - 0.2em);
margin: 0.1em;
}
.power { color: white }
.power.on { background-color: green }
.power.off { background-color: red }
.volume { background-color: lightblue }
.input { background-color: darkorange }
.control { background-color: black; color: white; filter: grayscale(100%) }
.bd-red { background-color: red }
.bd-yellow { background-color: yellow }
.bd-blue { background-color: blue; color: white }
input[type=range] {
width: 100%;
transform: scaleY(5);
}
#files {
width: 100%;
overflow-x: scroll;
}
#files ul {
width: min-content;
margin-top: 0;
font-size: 50%;
list-style-type: none;
}
#files li {
white-space: pre;
}
#path-list {
display: flex;
}
#path-list li::before {
content: '/';
}
#path-list li:first-child::before {
content: '';
}
li.dir::marker { content: '📁' }
li.dir::after { content: '/' }
li.file::marker { content: '🎞️' }
</style>
</head>
<body>
<div class="thing">
<label>Vlc</label>
<table>
<tr>
<td><button onclick="irCommand('start','hdmi3')" class="power on">start</button></td>
<td><button onclick="irCommand('stop','hdmi')" class="power off">stop</button></td>
</tr>
</table>
<details>
<summary>controls</summary>
<button onclick="vlcCommand('play')">play</button>
<button onclick="vlcCommand('pause')">pause</button>
<button onclick="vlcCommand('stop')">stop</button>
<button onclick="updateView()">update</button>
<input type="range" id="current-pos">
</details>
<details id="files">
<summary>files</summary>
<ul id="path-list">
</ul>
<ul id="file-list">
</ul>
</details>
</div>
<div class="thing">
<label>Laptop</label>
<table>
<tr>
<td><button onclick="irCommand('start','hdmi1')" class="power on">start</button></td>
<td><button onclick="irCommand('stop','hdmi')" class="power off">stop</button></td>
</tr>
</table>
</div>
<div class="thing">
<label>Bluray</label>
<table>
<tr>
<td><button onclick="irCommand('start','bluray')" class="power on">start</button></td>
<td><button onclick="irCommand('stop','bluray')" class="power off">stop</button></td>
</tr>
</table>
<details>
<summary>controls</summary>
<table>
<tr>
<td><button onclick="irCommand('bluray','ejectcd')" class="input">⏏</button></td>
<td> </td>
<td><button onclick="irCommand('bluray','power')" class="power on">ON</button></td>
</tr>
<tr>
<td><button onclick="irCommand('bluray','yellow')" class="bd-yellow">a</button></td>
<td><button onclick="irCommand('bluray','blue')" class="bd-blue">b</button></td>
<td><button onclick="irCommand('bluray','red')" class="bd-red">c</button></td>
</tr>
<tr>
<td><button onclick="irCommand('bluray','menu')" class="control">🔝</button></td>
<td><button onclick="irCommand('bluray','up')" class="control">↑</button></td>
<td><button onclick="irCommand('bluray','context_menu')" class="control">⎙</button></td>
</tr>
<tr>
<td><button onclick="irCommand('bluray','left')" class="control">←</button></td>
<td><button onclick="irCommand('bluray','ok')" class="control">ok</button></td>
<td><button onclick="irCommand('bluray','right')" class="control">→</button></td>
</tr>
<tr>
<td><button onclick="irCommand('bluray','back')" class="control">🔙</button></td>
<td><button onclick="irCommand('bluray','down')" class="control">↓</button></td>
<td><button onclick="irCommand('bluray','option')" class="control">opt</button></td>
</tr>
<tr>
<td colspan="3"><button onclick="irCommand('bluray','home')" class="control">home</button></td>
</tr>
<tr>
<td><button onclick="irCommand('bluray','previous')" class="control">⏪</button></td>
<td><button onclick="irCommand('bluray','play')" class="control">▶</button></td>
<td><button onclick="irCommand('bluray','next')" class="control">⏩</button></td>
</tr>
<tr>
<td><button onclick="irCommand('bluray','review')" class="control">⏮</button></td>
<td><button onclick="irCommand('bluray','pause')" class="control">⏸</button></td>
<td><button onclick="irCommand('bluray','fastforward')" class="control">⏭</button></td>
</tr>
<tr>
<td><button onclick="irCommand('bluray','subtitle')" class="control">sub</button></td>
<td><button onclick="irCommand('bluray','stop')" class="control">⏹</button></td>
<td><button onclick="irCommand('bluray','audio')" class="control">aud</button></td>
</tr>
<tr>
<td> </td>
<td> </td>
<td><button onclick="irCommand('bluray','displaytoggle')" class="control">disp</button></td>
</tr>
</table>
</details>
</div>
<div class="thing">
<label>Amp</label>
<table>
<tr>
<td><button onclick="irCommand('amplifier','power')" class="power on">ON</button></td>
<td><button onclick="irCommand('amplifier','power')" class="power off">OFF</button></td>
<td><button onclick="irCommand('amplifier','volumeup')" class="volume">↑</button></td>
</tr>
<tr>
<td> </td>
<td> </td>
<td><button onclick="irCommand('amplifier','volumedown')" class="volume">↓</button></td>
</tr>
<tr>
<td><button onclick="irCommand('amplifier','hdmi1')" class="input">Lap</button></td>
<td><button onclick="irCommand('amplifier','hdmi2')" class="input">BD</button></td>
<td><button onclick="irCommand('amplifier','hdmi3')" class="input">NAS</button></td>
</tr>
</table>
</div>
<div class="thing">
<label>Projector</label>
<table>
<tr>
<td><button onclick="irCommand('projector','power')" class="power on">ON</button></td>
<td><button onclick="irCommand('projector','suspend')" class="power off">OFF</button></td>
</tr>
</table>
</div>
<template id="file-item">
<li slot:class="dirclass"><a slot:href="url"><span><slot>name</slot></span></a></li>
</template>
</body>
</html>