neodot/.config/mpv/scripts/mpvDLNA/modules.js/Options.js
2023-11-29 21:44:29 -05:00

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".
};