Files
yunkong2.sayit/admin/index.html
2018-11-11 13:26:55 +08:00

651 lines
32 KiB
HTML

<html>
<head>
<link rel="stylesheet" type="text/css" href="../../lib/css/themes/jquery-ui/redmond/jquery-ui.min.css"/>
<script type="text/javascript" src="../../lib/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../lib/js/jquery-ui-1.10.3.full.min.js"></script>
<link rel="stylesheet" type="text/css" href="../../css/adapter.css"/>
<script type="text/javascript" src="engines.js"></script>
<script type="text/javascript" src="../../js/translate.js"></script>
<script type="text/javascript" src="../../js/adapter-settings.js"></script>
<script type="text/javascript" src="words.js"></script>
<style>
#drop_zone {
border: 2px dashed #bbb;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
padding: 25px;
text-align: center;
font-size: 20pt;
font-weight: bold;
font-family: 'Arial';
color: #bbb;
width: 90%;
height: 60px;
}
.error {
border: 2px solid red;
}
</style>
<script type="text/javascript">
var webServers = null;
var gOnChange;
function showHideSettings() {
$('.variable').hide();
var type = $('#type').val();
for (var j = 0; j < sayitOptions[type].params.length; j++) {
$('#tr_' + sayitOptions[type].params[j]).show();
}
if (type === 'googleHome') {
$('.announceValue').hide();
$('.announce').hide();
$('.system').hide();
$('.googleHome').show();
getIsAdapterAlive(function (isAlive) {
if (isAlive || common.enabled) {
$('#search').button('enable');
fillGoogleHome();
}
});
} else {
$('.googleHome').hide();
$('.announceValue').show();
$('.announce').show();
}
$('.engine').hide();
var engine = $('#engine').val();
if (!sayitEngines[engine] || sayitOptions[type].params.indexOf('engine') === -1) return;
for (var i = 0; i < sayitEngines[engine].params.length; i++) {
$('#tr_' + sayitEngines[engine].params[i]).show();
if (sayitEngines[engine][sayitEngines[engine].params[i]]) {
var arr = sayitEngines[engine][sayitEngines[engine].params[i]];
var param = sayitEngines[engine].params[i];
var $val = $('#' + param);
if (arr.length) {
var val = $val.val();
var text = '';
for (var p = 0; p < arr.length; p++) {
text += '<option value="' + arr[p] + '">' + arr[p] + '</option>';
}
$val.html(text);
if (val) {
$val.val(val);
} else {
$val.val(arr[0]);
}
} else {
$val.html('');
}
}
}
if (type !== 'system') {
$('.system').hide();
} else {
$('.system').show();
}
if (!$('#announce').val()) {
$('.announce').hide();
$('#play').button('disable');
} else {
$('.announce').show();
$('#play').button('enable');
}
}
function fillSonosDevices(elem, current) {
socket.emit('getObjectView', 'system', 'channel', {startkey: 'sonos.', endkey: 'sonos.\u9999'}, function (err, res) {
if (!err && res) {
for (var i = 0; i < res.rows.length; i++) {
$(elem).append('<option value="' + res.rows[i].id + '">' + res.rows[i].id + '</option>');
}
}
$(elem).val(current);
});
}
function fillMpdDevice(elem, current) {
socket.emit('getObjectView', 'system', 'instance', {startkey: 'system.adapter.mpd.', endkey: 'system.adapter.mpd.\u9999'}, function (err, res) {
if (!err && res) {
for (var i = 0; i < res.rows.length; i++) {
var n = res.rows[i].id.replace('system.adapter.', '');
$(elem).append('<option value="' + n + '">' + n + '</option>');
}
}
$(elem).val(current);
});
}
function fillChromecastDevices(elem, current) {
socket.emit('getObjectView', 'system', 'device', {startkey: 'chromecast.', endkey: 'chromecast.\u9999'}, function (err, res) {
if (!err && res) {
for (var i = 0; i < res.rows.length; i++) {
$(elem).append('<option value="' + res.rows[i].id + '">' + res.rows[i].id + '</option>');
}
}
$(elem).val(current);
});
}
function fillGoogleHome() {
$('#search').button('disable');
sendTo(null, 'browseChromecast', null, function (list) {
var text = '<option value="">' + _('select') + '</option>';
if (list) {
for (var i = 0; i < list.length; i++) {
text += '<option value="' + list[i].ip + '">' + list[i].name + '[' + list[i].ip + ']</option>';
}
$('#googleHome').html(text).prop('disabled', false).off('change').on('change', function () {
$('#server').val($(this).val()).trigger('change');
});
} else {
$('#googleHome').html('<option value="">' + _('error') + '</option>').prop('disabled', true);
}
$('#search').button('enable');
});
}
function ip2hex(ip) {
var octets = ip.split('.');
if (octets.length !== 4) {
return 0;
}
var result = 0;
for (var i = 0; i < octets.length; ++i) {
var octet = parseInt(octets[i], 10);
if (Number.isNaN(octet) || octet < 0 || octet > 255) {
throw new Error("Each octet must be between 0 and 255");
}
result |= octet << ((octets.length - i) * 8);
}
return result;
}
function checkWeb(elem, current) {
var web = $('#web').val();
for (var i = 0; i < webServers.length; i++) {
if (webServers[i].id === 'system.adapter.' + web) {
if (webServers[i].value.native.auth) {
showMessage(_('Cannot use web server with authentication'), null, 'info');
}
if (webServers[i].value.native.bind === 'localhost' || webServers[i].value.native.bind === '127.0.0.1' || webServers[i].value.native.bind === '::1') {
showMessage(_('Cannot use web server only on localhost'), null, 'info');
}
if (webServers[i].value.native.bind === '0.0.0.0') {
$('#tr_webServer').show();
var $webServer = $('#webServer');
$webServer.html('');
// read all ipv4 addresses of host
socket.emit('getObject', 'system.host.' + webServers[i].value.common.host, function (err, obj) {
var text = '';
if (!err && obj && obj.native) {
for (var iface in obj.native.hardware.networkInterfaces) {
if (obj.native.hardware.networkInterfaces.hasOwnProperty(iface)) {
for (var i = 0; i < obj.native.hardware.networkInterfaces[iface].length; i++) {
if (obj.native.hardware.networkInterfaces[iface][i].family === 'IPv4' && !obj.native.hardware.networkInterfaces[iface][i].internal) {
text += '<option value="' + obj.native.hardware.networkInterfaces[iface][i].address + '" data-mask="' + obj.native.hardware.networkInterfaces[iface][i].netmask + '">[IPv4] ' + obj.native.hardware.networkInterfaces[iface][i].address + ' - ' + iface + '</option>';
}
}
}
}
}
$webServer.html(text);
if (current) {
$webServer.val(current);
}
$webServer.off('change').on('change', function () {
gOnChange();
var ip = $(this).val();
var netmask = $(this).find('option[value="' + ip + '"]').data('mask');
var server;
var type = $('#type').val();
if (type === 'googleHome') {
server = $('#server').val();
} else if (type === 'sonos') {
server = $('#device').val();
} else if (type === 'chromecast') {
server = $('#cDevice').val();
} else if (type === 'mpd') {
server = $('#mpd_device').val();
}
if (server && ip && netmask && server.indexOf(':') === -1 && ip.indexOf(':') === -1) {
ip = ip2hex(ip);
netmask = ip2hex(netmask);
server = ip2hex(server);
if ((ip & netmask) !== (server & netmask)) {
$webServer.addClass('error').attr('title', _('IP not accessible for server'));
} else {
$webServer.removeClass('error').attr('title', '');
}
} else {
$webServer.removeClass('error');
}
});
});
} else if (webServers[i].value.native.bind === '::') {
// read all ipv6 addresses of host
socket.emit('getObject', 'system.host.' + webServers[i].value.common.host, function (err, obj) {
if (!err && obj && obj.native) {
for (var iface in obj.native.hardware.networkInterfaces) {
if (obj.native.hardware.networkInterfaces.hasOwnProperty(iface)) {
for (var i = 0; i < obj.native.hardware.networkInterfaces[iface].length; i++) {
if (obj.native.hardware.networkInterfaces[iface][i].family === 'IPv6' && !obj.native.hardware.networkInterfaces[iface][i].internal) {
$('#webServer').append('<option value="' + obj.native.hardware.networkInterfaces[iface][i].address + '">[IPv6] ' + obj.native.hardware.networkInterfaces[iface][i].address + ' - ' + iface + '</option>');
}
}
}
}
}
if (current) {
$('#webServer').val(current);
}
});
} else {
$('#tr_webServer').hide();
}
}
}
}
function fillWebServices(elem, current, type, webServer) {
socket.emit('getObjectView', 'system', 'instance', {startkey: 'system.adapter.web.', endkey: 'system.adapter.web.\u9999'}, function (err, res) {
if (!err && res) {
webServers = res.rows;
for (var i = 0; i < res.rows.length; i++) {
var n = res.rows[i].id.replace('system.adapter.', '');
var auth = res.rows[i].value.native.auth ? 'data-auth="true"' : '';
$(elem).append('<option value="' + n + '" ' + auth + '>' + n + '</option>');
}
}
$(elem).val(current);
if ((type === 'sonos') ||
(type === 'chromecast') ||
(type === 'googleHome')) {
checkWeb('#web', webServer);
}
});
}
function uploadFile(file, callback) {
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = function(e) {
socket.emit('writeFile', 'sayit.' + instance, 'tts.userfiles/' + file.name, e.target.result, function () {
if (callback) callback(file.name);
});
};
// Read in the image file as a data URL.
reader.readAsArrayBuffer(file);
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
$('#drop_indcator').hide();
// files is a FileList of File objects. List some properties.
var count = 0;
for (var i = 0, f; f = files[i]; i++) {
if (f.size > 1024 * 1024) {
showMessage(_('File %s is too big. Maximum 1MB', escape(f.name)));
$('#files').val('');
return;
}
if (f.name === 'say.mp3') {
showMessage(_('Name say.mp3 is reserved'));
$('#files').val('');
return;
}
count++;
uploadFile(f, function (name) {
count--;
if (!count) {
// Read names of files for gong
socket.emit('readDir', 'sayit.' + instance, 'tts.userfiles', function (err, dir) {
var text = '<option value="">' + _('none') + '</option>';
if (dir) {
for (var i = 0; i < dir.length; i++) {
if (dir[i].isDir) continue;
text += '<option value="' + dir[i].file + '">' + dir[i].file + '</option>';
}
}
$('#announce').html(text).val(name).trigger('change');
$('#files').val('');
});
}
});
}
}
function handleFileSelectDrop(evt) {
$('#drop_indcator').hide();
evt.stopPropagation();
evt.preventDefault();
var files = evt.dataTransfer.files; // FileList object.
// files is a FileList of File objects. List some properties.
var output = [];
for (var i = 0, f; f = files[i]; i++) {
if (f.size > 1024 * 1024) {
showMessage(_('File %s is too big. Maximum 1MB', escape(f.name)));
return;
}
console.log(escape(f.name));
}
}
function handleDragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
$('#drop_indcator').show();
}
// the function loadSettings has to exist ...
function load(settings, onChange) {
if (!settings) return;
if (!settings.player) {
settings.player = 'mpg321';
}
gOnChange = onChange;
var $play = $('#play');
var $announce = $('#announce');
$play.button({icons: {primary: 'ui-icon-play'}, text: false}).css({width: 22, height: 22}).click(function () {
socket.emit('readFile', 'sayit.' + instance, 'tts.userfiles/' + $('#announce').val(), function (err, data) {
if (typeof AudioContext !== 'undefined') {
context = new AudioContext();
context.decodeAudioData(data, function(buffer) {
//console.log(buffer);
var source = context.createBufferSource(); // creates a sound source
source.buffer = buffer; // tell the source which sound to play
source.connect(context.destination); // connect the source to the context's destination (the speakers)
source.start(0);
}, function(err) {
console.log(err);
});
}
});
});
var $type = $('#type');
for (name in sayitOptions) {
if (sayitOptions.hasOwnProperty(name)) {
$type.append('<option value="' + name + '">' + sayitOptions[name].name + '</option>');
}
}
$('#search').button({
label: _('Browse')
}).click(function () {
fillGoogleHome();
});
getIsAdapterAlive(function (isAlive) {
if (isAlive || common.enabled) {
$('#search').button('enable');
if (settings.type === 'googleHome') {
fillGoogleHome();
}
} else {
$('#search').button('disable').title(_('Adapter must be enabled'));
}
});
var $engine = $('#engine');
for (var name in sayitEngines) {
if (sayitEngines.hasOwnProperty(name)) {
$engine.append('<option value="' + name + '">' + sayitEngines[name].name + '</option>');
}
}
for (var key in settings) {
if (settings.hasOwnProperty(key)) {
var $val = $('#' + key + '.value');
if ($val.attr('type') === 'checkbox') {
$val.prop('checked', settings[key]);
} else {
$val.val(settings[key]);
}
}
}
$('.value').on('change', function () {
var key = $(this).attr('id');
if (key === 'auth') {
if ($('#auth').prop('checked')) {
$('#secure').prop('checked', true);
}
} else
if (key === 'type' || key === 'engine') {
showHideSettings();
var type = $('#type').val();
if ((type === 'sonos') ||
(type === 'chromecast') ||
(type === 'mpd') ||
(type === 'googleHome')) {
checkWeb('#web');
$('#announce').val('').trigger('change');
showHideSettings();
} else {
showHideSettings();
}
} else
if (key === 'announce') {
showHideSettings();
if ($(this).val()) {
socket.emit('readFile', 'sayit.' + instance, 'tts.userfiles/' + $('#announce').val(), function (err, data) {
if (typeof AudioContext !== 'undefined') {
context = new AudioContext();
context.decodeAudioData(data, function (buffer) {
$('#annoDuration').val(Math.ceil(buffer.duration));
}, function (err) {
console.log(err);
});
}
});
}
}
if (key === 'web') {
checkWeb('#web');
}
onChange();
}).keyup(function() {
$(this).trigger('change');
});
if (!settings.engine) {
settings.engine = systemLang;
$engine.val(systemLang).trigger('change');
}
if (!settings.instance) {
settings.instance = 'FFFFFFFF';
$('#instance').val(settings.instance).trigger('change');
}
fillSonosDevices('#device', settings.device);
fillChromecastDevices('#cDevice', settings.cDevice);
fillWebServices('#web', settings.web, settings.type, settings.webServer);
fillMpdDevice('#mpd_device', settings.mpd_device);
if ((settings.type === 'sonos') ||
(settings.type === 'chromecast') ||
(settings.type === 'mpd') ||
(settings.type === 'googleHome')){
$('.announce').hide();
if (settings.announce) {
$announce.val('').trigger('change');
}
}
// Read names of files for gong
socket.emit('readDir', 'sayit.' + instance, 'tts.userfiles', function (err, dir) {
var text = '<option value="">' + _('none') + '</option>';
if (dir) {
for (var i = 0; i < dir.length; i++) {
if (dir[i].isDir) continue;
text += '<option value="' + dir[i].file + '">' + dir[i].file + '</option>';
}
}
$('#announce').html(text).val(settings.announce);
showHideSettings();
});
getAdapterInstances('cloud', function (list) {
if (list) {
var text = '';
for (var i = 0; i < list.length; i++) {
var id = list[i]._id.substring('system.adapter.'.length);
text += '<option value="' + id + '" ' + (id === settings.cloud ? 'selected' : '') + '>' + id + '</option>';
}
if (text) $('#cloud').html(text);
}
});
$('#voice').val(settings.voice);
var w = $engine.width();
$announce.css({width: w});
$type.css({width: w});
document.getElementById('files').addEventListener('change', handleFileSelect, false);
showHideSettings();
if (!$announce.val()) {
$play.button('disable');
}
if (typeof AudioContext === 'undefined') {
$play.hide();
}
var dropZone = document.getElementById('drop_zone');
if (dropZone) {
dropZone.addEventListener('dragover', handleDragOver, false);
dropZone.addEventListener('drop', handleFileSelect, false);
/*dropZone.addEventListener('dragend', function () {
$(this).css({background: 'white'});
console.log('dragend');
return false;
}, false);
dropZone.addEventListener('dragstart', function () {
console.log('dragstart');
}, false);
dropZone.addEventListener('dragleave', function () {
$(this).css({background: 'white'});
}, false);
dropZone.addEventListener('dragenter', function () {
$(this).css({background: 'gray'});
}, false);*/
}
onChange(false);
}
function save(callback) {
var obj = {};
$('.value').each(function () {
var $this = $(this);
if ($this.attr('type') === 'checkbox') {
obj[$this.attr('id')] = $this.prop('checked');
} else {
obj[$this.attr('id')] = $this.val();
}
});
if (obj.engine && sayitEngines[obj.engine].engine === 'yandex') {
if (!obj.key) {
showMessage(_('API Key is not set!'));
return;
}
} else
if (obj.engine && sayitEngines[obj.engine].engine === 'polly') {
if (!obj.accessKey || !obj.secretKey) {
showMessage(_('API Key is not set!'));
return;
}
if (!obj.region) {
showMessage(_('AWS Region is not set!'));
return;
}
}
if (obj.type === 'googleHome') {
obj.cache = false;
}
callback(obj);
}
</script>
</head>
<body>
<!-- you have to put your config page in a div with id adapter-container -->
<div id="adapter-container" ondragover="return false" ondrop="return false">
<table><tr><td><img src="sayit.png"></td><td><h3 class="translate">sayIt adapter settings</h3></td></tr></table>
<div>
<table>
<tr><td><label class="translate" for="type">Type:</label></td><td class="admin-icon" data-link="configuration"></td><td><select class="value" id="type"></select></td></tr>
<tr id="tr_engine" class="variable"><td style="width: 150px"><label class="translate" for="engine">Language:</label></td><td class="admin-icon" data-link="tts-engines"></td><td><select class="value" id="engine"></select></td></tr>
<tr class="system"><td><label for="player" class="translate">Linux player:</label></td><td class="admin-icon"></td><td>
<select class="value" id="player" >
<option value="mpg321">mpg321</option>
<option value="omxplayer">omxplayer</option>
</select><span class="translate" style="padding-left: 10px; font-size: 10px">Ignore for non linux OS</span>
</td>
</tr>
<tr class="system"><td><label class="translate" for="command">System command:</label></td><td class="admin-icon" data-link="system-command"></td><td><input class="value" id="command"/></td></tr>
<tr class="announceValue"><td><label class="translate" for="announce">Announce:</label></td><td class="admin-icon"></td><td><select class="value" id="announce"></select>&nbsp;<button id="play"></button></td></tr>
<tr class="announceValue"><td></td><td></td><td><input type="file" accept=".mp3,audio/*" id="files" name="files[]" multiple /></td></tr>
<tr class="announce"><td><label class="translate" for="annoTimeout">Announce timeout (sec):</label></td><td class="admin-icon"></td><td><input class="value" id="annoTimeout" size="8" /></td></tr>
<tr class="announce"><td><label class="translate" for="annoDuration">Announce length (sec):</label></td><td class="admin-icon"></td><td><input class="value" id="annoDuration" size="2" /></td></tr>
<tr class="announce"><td><label class="translate" for="annoVolume">Announce volume (%):</label></td><td class="admin-icon"></td><td><input class="value" id="annoVolume" size="3" /></td></tr>
<tr><td>&nbsp;</td><td></td><td>&nbsp;</td></tr>
<tr id="tr_cache" class="variable"><td><label class="translate" for="cache">Cache:</label></td><td class="admin-icon"></td> <td><input class="value" id="cache" type="checkbox" /></td></tr>
<tr id="tr_cacheExpiryDays" class="variable"><td><label class="translate" for="tr_cacheExpiryDays">Cache-Expiry:</label></td><td class="admin-icon"></td> <td><input class="value" id="cacheExpiryDays" size="5" /></td></tr>
<tr id="tr_server" class="variable"><td><label class="translate" for="server">Server:</label></td><td class="admin-icon"></td> <td><input class="value" id="server"/><select class="value googleHome" id="googleHome"><option value="" class="translate">none</option></select><button style="font-size: 10px; margin-left: 10px;" id="search" class="translate googleHome"></button></td></tr>
<tr id="tr_port" class="variable"><td><label class="translate" for="port">Port:</label></td><td class="admin-icon"></td> <td><input class="value" id="port" size="5" maxlength="5"/></td></tr>
<tr id="tr_user" class="variable"><td><label class="translate" for="user">User:</label></td><td class="admin-icon"></td> <td><input class="value" id="user" /></td></tr>
<tr id="tr_pass" class="variable"><td><label class="translate" for="pass">Password:</label></td><td class="admin-icon"></td><td><input class="value" id="pass" /></td></tr>
<tr id="tr_instance" class="variable"><td><label class="translate" for="instance">Browser instance:</label></td><td class="admin-icon"></td><td><input class="value" id="instance" /></td></tr>
<tr id="tr_device" class="variable"><td><label class="translate" for="device">Device:</label></td><td class="admin-icon"></td><td><select class="value" id="device"><option value="" class="translate">All</option></select></td></tr>
<tr id="tr_cDevice" class="variable"><td><label class="translate" for="cDevice">Device:</label></td><td class="admin-icon"></td><td><select class="value" id="cDevice"><option value="" class="translate">All</option></select></td></tr>
<tr id="tr_mpd_device" class="variable"><td><label class="translate" for="mpd_device">Device:</label></td><td class="admin-icon"></td><td><select class="value" id="mpd_device"><option value="" class="translate">All</option></select></td></tr>
<tr id="tr_web" class="variable"><td><label class="translate" for="web">Web instance:</label></td><td class="admin-icon"></td><td><select class="value" id="web"></select></td></tr>
<tr id="tr_webServer" class="variable"><td><label class="translate" for="webServer">Web server IP:</label></td><td class="admin-icon"></td><td><select class="value" id="webServer"></select></td></tr>
<tr id="tr_voice" class="engine" ><td><label class="translate" for="voice">Voice:</label></td><td class="admin-icon"></td><td><select class="value" id="voice"></select></td></tr>
<tr id="tr_key" class="engine" ><td><label class="translate" for="key">API Key:</label></td><td class="admin-icon"></td><td><input class="value" id="key" type="text"/></td></tr>
<tr id="tr_emotion" class="engine" ><td><label class="translate" for="emotion">Emotion:</label></td><td class="admin-icon"></td><td><select class="value" id="emotion"></select></td></tr>
<tr id="tr_drunk" class="engine" ><td><label class="translate" for="drunk">Drunk:</label></td><td class="admin-icon"></td><td><input type="checkbox" class="value" id="drunk"/></td></tr>
<tr id="tr_ill" class="engine" ><td><label class="translate" for="ill">Ill:</label></td><td class="admin-icon"></td><td><input type="checkbox" class="value" id="ill"/></td></tr>
<tr id="tr_robot" class="engine" ><td><label class="translate" for="robot">Robot:</label></td><td class="admin-icon"></td><td><input type="checkbox" class="value" id="robot"/></td></tr>
<tr id="tr_accessKey" class="engine" ><td><label class="translate" for="accessKey">Access Key:</label></td><td class="admin-icon"></td><td><input class="value" id="accessKey" type="text"/></td></tr>
<tr id="tr_secretKey" class="engine" ><td><label class="translate" for="secretKey">Secret Key:</label></td><td class="admin-icon"></td><td><input class="value" id="secretKey" type="password"/></td></tr>
<tr id="tr_region" class="engine" ><td><label class="translate" for="region">AWS Region:</label></td><td class="admin-icon"></td><td><input class="value" id="region" type="text"/></td></tr>
<tr id="tr_cloud" class="engine" ><td><label class="translate" for="cloud">Cloud instance:</label></td><td class="admin-icon"></td><td><select class="value" id="cloud"><option value="" class="translate">Install first cloud adapter</option></select></td></tr>
</table>
</div>
<div id="drop_zone" style="display: none" class="translate">place here</div>
</div>
</body>
</html>