summaryrefslogtreecommitdiffstats
path: root/player/javascript/defaults.js
diff options
context:
space:
mode:
Diffstat (limited to 'player/javascript/defaults.js')
-rw-r--r--player/javascript/defaults.js231
1 files changed, 211 insertions, 20 deletions
diff --git a/player/javascript/defaults.js b/player/javascript/defaults.js
index c91228087d..6493e38680 100644
--- a/player/javascript/defaults.js
+++ b/player/javascript/defaults.js
@@ -126,10 +126,17 @@ function dispatch_message(ev) {
var hooks = []; // array of callbacks, id is index+1
function run_hook(ev) {
+ var state = 0; // 0:initial, 1:deferred, 2:continued
+ function do_cont() { return state = 2, mp._hook_continue(ev.hook_id) }
+
+ function err() { return mp.msg.error("hook already continued"), undefined }
+ function usr_defer() { return state == 2 ? err() : (state = 1, true) }
+ function usr_cont() { return state == 2 ? err() : do_cont() }
+
var cb = ev.id > 0 && hooks[ev.id - 1];
if (cb)
- cb();
- mp._hook_continue(ev.hook_id);
+ cb({ defer: usr_defer, cont: usr_cont });
+ return state == 0 ? do_cont() : true;
}
mp.add_hook = function add_hook(name, pri, fn) {
@@ -170,6 +177,68 @@ mp.abort_async_command = function abort_async_command(id) {
mp._abort_async_command(id);
}
+// osd-ass
+var next_assid = 1;
+mp.create_osd_overlay = function create_osd_overlay(format) {
+ return {
+ format: format || "ass-events",
+ id: next_assid++,
+ data: "",
+ res_x: 0,
+ res_y: 720,
+ z: 0,
+
+ update: function ass_update() {
+ var cmd = {}; // shallow clone of `this', excluding methods
+ for (var k in this) {
+ if (typeof this[k] != "function")
+ cmd[k] = this[k];
+ }
+
+ cmd.name = "osd-overlay";
+ cmd.res_x = Math.round(this.res_x);
+ cmd.res_y = Math.round(this.res_y);
+
+ return mp.command_native(cmd);
+ },
+
+ remove: function ass_remove() {
+ mp.command_native({
+ name: "osd-overlay",
+ id: this.id,
+ format: "none",
+ data: "",
+ });
+ return mp.last_error() ? undefined : true;
+ },
+ };
+}
+
+// osd-ass legacy API
+mp.set_osd_ass = function set_osd_ass(res_x, res_y, data) {
+ if (!mp._legacy_overlay)
+ mp._legacy_overlay = mp.create_osd_overlay("ass-events");
+
+ var lo = mp._legacy_overlay;
+ if (lo.res_x == res_x && lo.res_y == res_y && lo.data == data)
+ return true;
+
+ mp._legacy_overlay.res_x = res_x;
+ mp._legacy_overlay.res_y = res_y;
+ mp._legacy_overlay.data = data;
+ return mp._legacy_overlay.update();
+}
+
+// the following return undefined on error, null passthrough, or legacy object
+mp.get_osd_size = function get_osd_size() {
+ var d = mp.get_property_native("osd-dimensions");
+ return d && {width: d.w, height: d.h, aspect: d.aspect};
+}
+mp.get_osd_margins = function get_osd_margins() {
+ var d = mp.get_property_native("osd-dimensions");
+ return d && {left: d.ml, right: d.mr, top: d.mt, bottom: d.mb};
+}
+
/**********************************************************************
* key bindings
*********************************************************************/
@@ -177,16 +246,26 @@ mp.abort_async_command = function abort_async_command(id) {
// {cb: fn, forced: bool, maybe input: str, repeatable: bool, complex: bool}
var binds = new_cache();
-function dispatch_key_binding(name, state) {
+function dispatch_key_binding(name, state, key_name, key_text) {
var cb = binds[name] ? binds[name].cb : false;
if (cb) // "script-binding [<script_name>/]<name>" command was invoked
- cb(state);
+ cb(state, key_name, key_text);
}
-function update_input_sections() {
+var binds_tid = 0; // flush timer id. actual id's are always true-thy
+mp.flush_key_bindings = function flush_key_bindings() {
+ function prioritized_inputs(arr) {
+ return arr.sort(function(a, b) { return a.id - b.id })
+ .map(function(bind) { return bind.input });
+ }
+
var def = [], forced = [];
- for (var n in binds) // Array.join() will later skip undefined .input
- (binds[n].forced ? forced : def).push(binds[n].input);
+ for (var n in binds)
+ if (binds[n].input)
+ (binds[n].forced ? forced : def).push(binds[n]);
+ // newer bindings for the same key override/hide older ones
+ def = prioritized_inputs(def);
+ forced = prioritized_inputs(forced);
var sect = "input_" + mp.script_name;
mp.commandv("define-section", sect, def.join("\n"), "default");
@@ -195,6 +274,14 @@ function update_input_sections() {
sect = "input_forced_" + mp.script_name;
mp.commandv("define-section", sect, forced.join("\n"), "force");
mp.commandv("enable-section", sect, "allow-hide-cursor+allow-vo-dragging");
+
+ clearTimeout(binds_tid); // cancel future flush if called directly
+ binds_tid = 0;
+}
+
+function sched_bindings_flush() {
+ if (!binds_tid)
+ binds_tid = setTimeout(mp.flush_key_bindings, 0); // fires on idle
}
// name/opts maybe omitted. opts: object with optional bool members: repeatable,
@@ -204,23 +291,28 @@ function add_binding(forced, key, name, fn, opts) {
if (typeof name == "function") { // as if "name" is not part of the args
opts = fn;
fn = name;
- name = "__keybinding" + next_bid++; // new unique binding name
+ name = false;
}
var key_data = {forced: forced};
switch (typeof opts) { // merge opts into key_data
case "string": key_data[opts] = true; break;
case "object": for (var o in opts) key_data[o] = opts[o];
}
+ key_data.id = next_bid++;
+ if (!name)
+ name = "__keybinding" + key_data.id; // new unique binding name
if (key_data.complex) {
mp.register_script_message(name, function msg_cb() {
fn({event: "press", is_mouse: false});
});
var KEY_STATES = { u: "up", d: "down", r: "repeat", p: "press" };
- key_data.cb = function key_cb(state) {
+ key_data.cb = function key_cb(state, key_name, key_text) {
fn({
event: KEY_STATES[state[0]] || "unknown",
- is_mouse: state[1] == "m"
+ is_mouse: state[1] == "m",
+ key_name: key_name || undefined,
+ key_text: key_text || undefined
});
}
} else {
@@ -238,7 +330,7 @@ function add_binding(forced, key, name, fn, opts) {
if (key)
key_data.input = key + " script-binding " + mp.script_name + "/" + name;
binds[name] = key_data; // used by user and/or our (key) script-binding
- update_input_sections();
+ sched_bindings_flush();
}
mp.add_key_binding = add_binding.bind(null, false);
@@ -247,7 +339,7 @@ mp.add_forced_key_binding = add_binding.bind(null, true);
mp.remove_key_binding = function(name) {
mp.unregister_script_message(name);
delete binds[name];
- update_input_sections();
+ sched_bindings_flush();
}
/**********************************************************************
@@ -372,6 +464,10 @@ function process_timers() {
- Module id supports mpv path enhancements, e.g. ~/foo, ~~/bar, ~~desktop/baz
*********************************************************************/
+mp.module_paths = []; // global modules search paths
+if (mp.script_path !== undefined) // loaded as a directory
+ mp.module_paths.push(mp.utils.join_path(mp.script_path, "modules"));
+
// Internal meta top-dirs. Users should not rely on these names.
var MODULES_META = "~~modules",
SCRIPTDIR_META = "~~scriptdir", // relative script path -> meta absolute id
@@ -386,10 +482,14 @@ function resolve_module_file(id) {
return mp.utils.join_path(main_script[0], rest);
if (base == MODULES_META) {
- var path = mp.find_config_file("scripts/modules.js/" + rest);
- if (!path)
- throw(Error("Cannot find module file '" + rest + "'"));
- return path;
+ for (var i = 0; i < mp.module_paths.length; i++) {
+ try {
+ var f = mp.utils.join_path(mp.module_paths[i], rest);
+ mp.utils.read_file(f, 1); // throws on any error
+ return f;
+ } catch (e) {}
+ }
+ throw(Error("Cannot find module file '" + rest + "'"));
}
return id + ".js";
@@ -480,13 +580,13 @@ g.require = new_require(SCRIPTDIR_META + "/" + main_script[1]);
/**********************************************************************
* mp.options
*********************************************************************/
-function read_options(opts, id) {
- id = String(typeof id != "undefined" ? id : mp.get_script_name());
+function read_options(opts, id, on_update, conf_override) {
+ id = String(id ? id : mp.get_script_name());
mp.msg.debug("reading options for " + id);
var conf, fname = "~~/script-opts/" + id + ".conf";
try {
- conf = mp.utils.read_file(fname);
+ conf = arguments.length > 3 ? conf_override : mp.utils.read_file(fname);
} catch (e) {
mp.msg.verbose(fname + " not found.");
}
@@ -525,18 +625,101 @@ function read_options(opts, id) {
else
mp.msg.error(info, "Error: can't convert '" + val + "' to " + type);
});
+
+ if (on_update) {
+ mp.observe_property("options/script-opts", "native", function(_n, _v) {
+ var saved = JSON.parse(JSON.stringify(opts)); // clone
+ var changelist = {}, changed = false;
+ read_options(opts, id, 0, conf); // re-apply orig-file + script-opts
+ for (var key in opts) {
+ if (opts[key] != saved[key]) // type always stays the same
+ changelist[key] = changed = true;
+ }
+ if (changed)
+ on_update(changelist);
+ });
+ }
}
mp.options = { read_options: read_options };
/**********************************************************************
+* input
+*********************************************************************/
+function register_event_handler(t) {
+ mp.register_script_message("input-event", function (type, args) {
+ if (t[type]) {
+ args = JSON.parse(args)
+ var result = t[type](args[0], args[1]);
+
+ if (type == "complete" && result) {
+ mp.commandv("script-message-to", "console", "complete",
+ JSON.stringify(result[0]), result[1]);
+ }
+ }
+
+ if (type == "closed")
+ mp.unregister_script_message("input-event");
+ })
+}
+
+mp.input = {
+ get: function(t) {
+ mp.commandv("script-message-to", "console", "get-input", mp.script_name,
+ JSON.stringify({
+ prompt: t.prompt,
+ default_text: t.default_text,
+ cursor_position: t.cursor_position,
+ id: t.id,
+ }));
+
+ register_event_handler(t)
+ },
+ select: function (t) {
+ mp.commandv("script-message-to", "console", "get-input", mp.script_name,
+ JSON.stringify({
+ prompt: t.prompt,
+ items: t.items,
+ default_item: t.default_item,
+ }));
+
+ register_event_handler(t);
+ },
+ terminate: function () {
+ mp.commandv("script-message-to", "console", "disable");
+ },
+ log: function (message, style, terminal_style) {
+ mp.commandv("script-message-to", "console", "log", JSON.stringify({
+ text: message,
+ style: style,
+ terminal_style: terminal_style,
+ }));
+ },
+ log_error: function (message) {
+ mp.commandv("script-message-to", "console", "log",
+ JSON.stringify({ text: message, error: true }));
+ },
+ set_log: function (log) {
+ mp.commandv("script-message-to", "console", "set-log",
+ JSON.stringify(log));
+ }
+}
+
+/**********************************************************************
* various
*********************************************************************/
g.print = mp.msg.info; // convenient alias
mp.get_script_name = function() { return mp.script_name };
mp.get_script_file = function() { return mp.script_file };
+mp.get_script_directory = function() { return mp.script_path };
mp.get_time = function() { return mp.get_time_ms() / 1000 };
mp.utils.getcwd = function() { return mp.get_property("working-directory") };
+mp.utils.getpid = function() { return mp.get_property_number("pid") }
+mp.utils.get_user_path =
+ function(p) { return mp.command_native(["expand-path", String(p)]) };
+mp.get_mouse_pos = function() { return mp.get_property_native("mouse-pos") };
+mp.utils.write_file = mp.utils._write_file.bind(null, false);
+mp.utils.append_file = mp.utils._write_file.bind(null, true);
mp.dispatch_event = dispatch_event;
mp.process_timers = process_timers;
mp.notify_idle_observers = notify_idle_observers;
@@ -621,7 +804,7 @@ g.mp_event_loop = function mp_event_loop() {
wait = 0; // poll the next one
} else {
wait = process_timers() / 1000;
- if (wait != 0) {
+ if (wait != 0 && iobservers.length) {
notify_idle_observers(); // can add timers -> recalculate wait
wait = peek_timers_wait() / 1000;
}
@@ -629,4 +812,12 @@ g.mp_event_loop = function mp_event_loop() {
} while (mp.keep_running);
};
+
+// let the user extend us, e.g. by adding items to mp.module_paths
+var initjs = mp.find_config_file("init.js"); // ~~/init.js
+if (initjs)
+ require(initjs.slice(0, -3)); // remove ".js"
+else if ((initjs = mp.find_config_file(".init.js")))
+ mp.msg.warn("Use init.js instead of .init.js (ignoring " + initjs + ")");
+
})(this)