207 lines
7.8 KiB
JavaScript
207 lines
7.8 KiB
JavaScript
|
/*
|
||
|
* OPTIONS.JS (MODULE)
|
||
|
*
|
||
|
* Description: JavaScript implementation of mpv's Lua API's config file system,
|
||
|
* via "mp.options.read_options()". See official Lua docs for help.
|
||
|
* https://github.com/mpv-player/mpv/blob/master/DOCS/man/lua.rst#mpoptions-functions
|
||
|
* Version: 2.1.0
|
||
|
* Author: VideoPlayerCode
|
||
|
* URL: https://github.com/VideoPlayerCode/mpv-tools
|
||
|
* License: Apache License, Version 2.0
|
||
|
*/
|
||
|
|
||
|
/* jshint -W097 */
|
||
|
/* global mp, exports, require */
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/* This is a slightly modified version of the Options module from VideoPlayerCode
|
||
|
that has been changed to better work with the mpvDLNA plugin.
|
||
|
|
||
|
Specifically it checks an additional location for the config file (/script-opts)
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
var ScriptConfig = function(options, identifier)
|
||
|
{
|
||
|
if (!options)
|
||
|
throw 'Options table parameter is missing.';
|
||
|
this.options = options;
|
||
|
this.scriptName = typeof identifier === 'string' ? identifier : mp.get_script_name();
|
||
|
this.configFile = null;
|
||
|
|
||
|
// Converts string "val" to same primitive type as "destTypeVal".
|
||
|
var typeConv = function(destTypeVal, val)
|
||
|
{
|
||
|
switch (typeof destTypeVal) {
|
||
|
case 'object':
|
||
|
if (!Array.isArray(destTypeVal))
|
||
|
val = undefined; // Unknown "object" target variable.
|
||
|
else if (typeof val !== 'string')
|
||
|
val = String(val); // Target is array, so use string values.
|
||
|
break;
|
||
|
case 'string':
|
||
|
if (typeof val !== 'string')
|
||
|
val = String(val);
|
||
|
break;
|
||
|
case 'boolean':
|
||
|
if (val === 'yes')
|
||
|
val = true;
|
||
|
else if (val === 'no')
|
||
|
val = false;
|
||
|
else {
|
||
|
mp.msg.error('Error: Can\'t convert '+JSON.stringify(val)+' to boolean!');
|
||
|
val = undefined;
|
||
|
}
|
||
|
break;
|
||
|
case 'number':
|
||
|
var num = parseFloat(val);
|
||
|
if (!isNaN(num))
|
||
|
val = num;
|
||
|
else {
|
||
|
mp.msg.error('Error: Can\'t convert '+JSON.stringify(val)+' to number!');
|
||
|
val = undefined;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
val = undefined;
|
||
|
}
|
||
|
return val;
|
||
|
};
|
||
|
|
||
|
// Find config file.
|
||
|
if (this.scriptName && this.scriptName.length) {
|
||
|
mp.msg.debug('Reading options for '+this.scriptName+'.');
|
||
|
this.configFile = mp.find_config_file('script-settings/'+this.scriptName+'.conf');
|
||
|
if (!this.configFile) // Try legacy settings location as fallback.
|
||
|
this.configFile = mp.find_config_file('script-opts/'+this.scriptName+'.conf');
|
||
|
if (!this.configFile) // Try legacy settings location as fallback.
|
||
|
this.configFile = mp.find_config_file('lua-settings/'+this.scriptName+'.conf');
|
||
|
}
|
||
|
|
||
|
// Read and parse configuration if found.
|
||
|
var i, len, pos, key, val, isArrayVal, convVal;
|
||
|
if (this.configFile && this.configFile.length) {
|
||
|
try {
|
||
|
var line, configLines = mp.utils.read_file(this.configFile).split(/[\r\n]+/);
|
||
|
for (i = 0, len = configLines.length; i < len; ++i) {
|
||
|
line = configLines[i].replace(/^\s+/, '');
|
||
|
if (!line.length || line.charAt(0) === '#')
|
||
|
continue;
|
||
|
pos = line.indexOf('=');
|
||
|
if (pos < 0) {
|
||
|
mp.msg.warn('"'+this.configFile+'": Ignoring malformatted config line "'+line.replace(/\s+$/, '')+'".');
|
||
|
continue;
|
||
|
}
|
||
|
key = line.substring(0, pos);
|
||
|
val = line.substring(pos + 1);
|
||
|
isArrayVal = false;
|
||
|
if ('[]' === line.substring(pos - 2, pos)) {
|
||
|
key = key.substring(0, key.length - 2);
|
||
|
isArrayVal = true;
|
||
|
}
|
||
|
if (this.options.hasOwnProperty(key)) {
|
||
|
convVal = typeConv(this.options[key], val);
|
||
|
if (typeof convVal !== 'undefined') {
|
||
|
if (Array.isArray(this.options[key])) {
|
||
|
if (isArrayVal)
|
||
|
this.options[key].push(convVal);
|
||
|
else
|
||
|
mp.msg.error('"'+this.configFile+'": Ignoring non-array value for array-based option key "'+key+'".');
|
||
|
}
|
||
|
else
|
||
|
this.options[key] = convVal;
|
||
|
}
|
||
|
else
|
||
|
mp.msg.error('"'+this.configFile+'": Unable to convert value "'+val+'" for key "'+key+'".');
|
||
|
}
|
||
|
else
|
||
|
mp.msg.warn('"'+this.configFile+'": Ignoring unknown key "'+key+'".');
|
||
|
}
|
||
|
} catch (e) {
|
||
|
mp.msg.error('Unable to read configuration file "'+this.configFile+'".');
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
mp.msg.verbose('Unable to find configuration file for '+this.scriptName+'.');
|
||
|
|
||
|
// Parse command-line options.
|
||
|
if (this.scriptName && this.scriptName.length) {
|
||
|
var cmdOpts = mp.get_property_native('options/script-opts'), rawOpt,
|
||
|
prefix = this.scriptName+'-', keyLen;
|
||
|
len = prefix.length;
|
||
|
for (rawOpt in cmdOpts) {
|
||
|
if (!cmdOpts.hasOwnProperty(rawOpt))
|
||
|
continue;
|
||
|
pos = rawOpt.indexOf(prefix);
|
||
|
if (pos !== 0)
|
||
|
continue;
|
||
|
key = rawOpt.substring(len);
|
||
|
keyLen = key.length;
|
||
|
isArrayVal = false;
|
||
|
if ('[]' === key.substring(keyLen - 2)) {
|
||
|
key = key.substring(0, keyLen - 2);
|
||
|
isArrayVal = true;
|
||
|
}
|
||
|
if (key.length && this.options.hasOwnProperty(key)) {
|
||
|
val = cmdOpts[rawOpt];
|
||
|
convVal = typeConv(this.options[key], val);
|
||
|
if (typeof convVal !== 'undefined') {
|
||
|
if (Array.isArray(this.options[key])) {
|
||
|
if (isArrayVal)
|
||
|
this.options[key].push(convVal);
|
||
|
else
|
||
|
mp.msg.error('script-opts: Ignoring non-array value for array-based option key "'+key+'".');
|
||
|
}
|
||
|
else
|
||
|
this.options[key] = convVal;
|
||
|
}
|
||
|
else
|
||
|
mp.msg.error('script-opts: Unable to convert value "'+val+'" for key "'+key+'".');
|
||
|
}
|
||
|
else
|
||
|
mp.msg.warn('script-opts: Ignoring unknown key "'+key+'".');
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
ScriptConfig.prototype.getValue = function(key)
|
||
|
{
|
||
|
if (!this.options.hasOwnProperty(key))
|
||
|
throw 'Invalid option "'+key+'"';
|
||
|
return this.options[key];
|
||
|
};
|
||
|
|
||
|
ScriptConfig.prototype.getMultiValue = function(key)
|
||
|
{
|
||
|
// Multi-value format: `{one}+{two}+{three}`.
|
||
|
var i, len,
|
||
|
val = this.getValue(key), // Throws.
|
||
|
result = [];
|
||
|
if (typeof val !== 'string')
|
||
|
throw 'Invalid non-string value in multi-value option "'+key+'"';
|
||
|
len = val.length;
|
||
|
if (len) {
|
||
|
if (val.charAt(0) !== '{' || val.charAt(len - 1) !== '}')
|
||
|
throw 'Missing surrounding "{}" brackets in multi-value option "'+key+'"';
|
||
|
val = val.substring(1, len - 1).split('}+{');
|
||
|
len = val.length;
|
||
|
for (i = 0; i < len; ++i) {
|
||
|
result.push(val[i]);
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
};
|
||
|
|
||
|
// Class `advanced_options()`: Offers extended features such as multi-values.
|
||
|
exports.advanced_options = ScriptConfig;
|
||
|
|
||
|
// Function `read_options()`: Behaves like Lua API (returns plain list of opts).
|
||
|
exports.read_options = function(table, identifier) {
|
||
|
// NOTE: "table" will be modified by reference, just as the Lua version.
|
||
|
var config = new ScriptConfig(table, identifier);
|
||
|
return config.options; // This is the same object as "table".
|
||
|
};
|