summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--old-makefile1
-rw-r--r--options/options.c2
-rw-r--r--player/client.c5
-rw-r--r--player/client.h1
-rw-r--r--player/command.c1
-rw-r--r--player/core.h7
-rw-r--r--player/lua.c125
-rw-r--r--player/lua.h10
-rw-r--r--player/main.c7
-rw-r--r--player/scripting.c179
-rw-r--r--wscript_build.py1
11 files changed, 209 insertions, 130 deletions
diff --git a/old-makefile b/old-makefile
index 1d3b45a8cc..b54c147eb6 100644
--- a/old-makefile
+++ b/old-makefile
@@ -227,6 +227,7 @@ SOURCES = audio/audio.c \
player/osd.c \
player/playloop.c \
player/screenshot.c \
+ player/scripting.c \
player/sub.c \
player/video.c \
player/timeline/tl_matroska.c \
diff --git a/options/options.c b/options/options.c
index 5674c7dc0d..d3a10457a8 100644
--- a/options/options.c
+++ b/options/options.c
@@ -680,7 +680,9 @@ const struct MPOpts mp_default_opts = {
.osd_bar_h = 3.125,
.osd_scale = 1,
.osd_scale_by_window = 1,
+#if HAVE_LUA
.lua_load_osc = 1,
+#endif
.auto_load_scripts = 1,
.loop_times = -1,
.ordered_chapters = 1,
diff --git a/player/client.c b/player/client.c
index 45ba680e84..31a32b5616 100644
--- a/player/client.c
+++ b/player/client.c
@@ -205,6 +205,11 @@ struct mp_log *mp_client_get_log(struct mpv_handle *ctx)
return ctx->log;
}
+struct MPContext *mp_client_get_core(struct mpv_handle *ctx)
+{
+ return ctx->mpctx;
+}
+
static void wakeup_client(struct mpv_handle *ctx)
{
pthread_cond_signal(&ctx->wakeup);
diff --git a/player/client.h b/player/client.h
index 6e078e9d7b..e9d41d8c09 100644
--- a/player/client.h
+++ b/player/client.h
@@ -21,5 +21,6 @@ void mp_client_property_change(struct MPContext *mpctx, const char **list);
struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name);
struct mp_log *mp_client_get_log(struct mpv_handle *ctx);
+struct MPContext *mp_client_get_core(struct mpv_handle *ctx);
#endif
diff --git a/player/command.c b/player/command.c
index ec05e8cc40..512f2b478a 100644
--- a/player/command.c
+++ b/player/command.c
@@ -75,7 +75,6 @@
#include "osdep/io.h"
#include "core.h"
-#include "lua.h"
struct command_ctx {
double last_seek_time;
diff --git a/player/core.h b/player/core.h
index 18a749376b..891c14792d 100644
--- a/player/core.h
+++ b/player/core.h
@@ -449,6 +449,13 @@ void idle_loop(struct MPContext *mpctx);
void handle_force_window(struct MPContext *mpctx, bool reconfig);
void add_frame_pts(struct MPContext *mpctx, double pts);
+// scripting.c
+struct mp_scripting {
+ const char *file_ext; // e.g. "lua"
+ int (*load)(struct mpv_handle *client, const char *filename);
+};
+void mp_load_scripts(struct MPContext *mpctx);
+
// sub.c
void reset_subtitles(struct MPContext *mpctx, int order);
void uninit_subs(struct demuxer *demuxer);
diff --git a/player/lua.c b/player/lua.c
index 0c8c415630..1c8a5c6cb5 100644
--- a/player/lua.c
+++ b/player/lua.c
@@ -44,7 +44,6 @@
#include "command.h"
#include "client.h"
#include "libmpv/client.h"
-#include "lua.h"
// List of builtin modules and their contents as strings.
// All these are generated from player/lua/*.lua
@@ -55,7 +54,7 @@ static const char *builtin_lua_scripts[][2] = {
{"mp.assdraw",
# include "player/lua/assdraw.inc"
},
- {"@osc",
+ {"@osc.lua",
# include "player/lua/osc.inc"
},
{0}
@@ -140,27 +139,6 @@ static int run_event_loop(lua_State *L)
static void add_functions(struct script_ctx *ctx);
-static char *script_name_from_filename(void *talloc_ctx, const char *fname)
-{
- fname = mp_basename(fname);
- if (fname[0] == '@')
- fname += 1;
- char *name = talloc_strdup(talloc_ctx, fname);
- // Drop .lua extension
- char *dot = strrchr(name, '.');
- if (dot)
- *dot = '\0';
- // Turn it into a safe identifier - this is used with e.g. dispatching
- // input via: "send scriptname ..."
- for (int n = 0; name[n]; n++) {
- char c = name[n];
- if (!(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') &&
- !(c >= '0' && c <= '9'))
- name[n] = '_';
- }
- return talloc_asprintf(talloc_ctx, "%s", name);
-}
-
static int load_file(struct script_ctx *ctx, const char *fname)
{
int r = 0;
@@ -206,20 +184,10 @@ static bool require(lua_State *L, const char *name)
return true;
}
-struct thread_arg {
- struct MPContext *mpctx;
- mpv_handle *client;
- const char *fname;
-};
-
-static void *lua_thread(void *p)
+static int load_lua(struct mpv_handle *client, const char *fname)
{
- pthread_detach(pthread_self());
-
- struct thread_arg *arg = p;
- struct MPContext *mpctx = arg->mpctx;
- const char *fname = arg->fname;
- mpv_handle *client = arg->client;
+ struct MPContext *mpctx = mp_client_get_core(client);
+ int r = -1;
struct script_ctx *ctx = talloc_ptrtype(NULL, ctx);
*ctx = (struct script_ctx) {
@@ -301,40 +269,15 @@ static void *lua_thread(void *p)
if (mp_cpcall(L, run_event_loop, 0) != 0)
report_error(L);
+ r = 0;
+
error_out:
- MP_VERBOSE(ctx, "exiting.\n");
if (ctx->suspended)
mpv_resume(ctx->client);
if (ctx->state)
lua_close(ctx->state);
- mpv_destroy(ctx->client);
talloc_free(ctx);
- talloc_free(arg);
- return NULL;
-}
-
-static void mp_lua_load_script(struct MPContext *mpctx, const char *fname)
-{
- struct thread_arg *arg = talloc_ptrtype(NULL, arg);
- char *name = script_name_from_filename(arg, fname);
- *arg = (struct thread_arg){
- .mpctx = mpctx,
- .fname = talloc_strdup(arg, fname),
- // Create the client before creating the thread; otherwise a race
- // condition could happen, where MPContext is destroyed while the
- // thread tries to create the client.
- .client = mp_new_client(mpctx->clients, name),
- };
- if (!arg->client) {
- talloc_free(arg);
- return;
- }
-
- pthread_t thread;
- if (pthread_create(&thread, NULL, lua_thread, arg))
- talloc_free(arg);
-
- return;
+ return r;
}
static int check_loglevel(lua_State *L, int arg)
@@ -1099,53 +1042,7 @@ static void add_functions(struct script_ctx *ctx)
lua_setfield(L, -2, "get_property_osd");
}
-static int compare_filename(const void *pa, const void *pb)
-{
- char *a = (char *)pa;
- char *b = (char *)pb;
- return strcmp(a, b);
-}
-
-static char **list_lua_files(void *talloc_ctx, char *path)
-{
- char **files = NULL;
- int count = 0;
- DIR *dp = opendir(path);
- if (!dp)
- return NULL;
- struct dirent *ep;
- while ((ep = readdir(dp))) {
- char *ext = mp_splitext(ep->d_name, NULL);
- if (!ext || strcasecmp(ext, "lua") != 0)
- continue;
- char *fname = mp_path_join(talloc_ctx, bstr0(path), bstr0(ep->d_name));
- MP_TARRAY_APPEND(talloc_ctx, files, count, fname);
- }
- closedir(dp);
- qsort(files, count, sizeof(char *), compare_filename);
- MP_TARRAY_APPEND(talloc_ctx, files, count, NULL);
- return files;
-}
-
-void mp_lua_init(struct MPContext *mpctx)
-{
- // Load scripts from options
- if (mpctx->opts->lua_load_osc)
- mp_lua_load_script(mpctx, "@osc");
- char **files = mpctx->opts->lua_files;
- for (int n = 0; files && files[n]; n++) {
- if (files[n][0])
- mp_lua_load_script(mpctx, files[n]);
- }
- if (!mpctx->opts->auto_load_scripts)
- return;
- // Load ~/.mpv/lua/*
- void *tmp = talloc_new(NULL);
- char *lua_path = mp_find_user_config_file(tmp, mpctx->global, "lua");
- if (lua_path) {
- files = list_lua_files(tmp, lua_path);
- for (int n = 0; files && files[n]; n++)
- mp_lua_load_script(mpctx, files[n]);
- }
- talloc_free(tmp);
-}
+const struct mp_scripting mp_scripting_lua = {
+ .file_ext = "lua",
+ .load = load_lua,
+};
diff --git a/player/lua.h b/player/lua.h
deleted file mode 100644
index cb4a7f95ec..0000000000
--- a/player/lua.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef MP_LUA_H
-#define MP_LUA_H
-
-#include <stdbool.h>
-
-struct MPContext;
-
-void mp_lua_init(struct MPContext *mpctx);
-
-#endif
diff --git a/player/main.c b/player/main.c
index 097e51e11c..8c5e90a0d7 100644
--- a/player/main.c
+++ b/player/main.c
@@ -61,7 +61,6 @@
#include "core.h"
#include "client.h"
-#include "lua.h"
#include "command.h"
#include "screenshot.h"
@@ -450,11 +449,9 @@ int mp_initialize(struct MPContext *mpctx)
mpctx->initialized_flags |= INITIALIZED_VO;
}
-#if HAVE_LUA
- // Lua user scripts can call arbitrary functions. Load them at a point
+ // Lua user scripts (etc.) can call arbitrary functions. Load them at a point
// where this is safe.
- mp_lua_init(mpctx);
-#endif
+ mp_load_scripts(mpctx);
if (opts->shuffle)
playlist_shuffle(mpctx->playlist);
diff --git a/player/scripting.c b/player/scripting.c
new file mode 100644
index 0000000000..ed7a1c2d3b
--- /dev/null
+++ b/player/scripting.c
@@ -0,0 +1,179 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <math.h>
+#include <pthread.h>
+#include <assert.h>
+
+#include "config.h"
+
+#include "osdep/io.h"
+
+#include "common/common.h"
+#include "common/msg.h"
+#include "options/path.h"
+#include "bstr/bstr.h"
+#include "core.h"
+#include "client.h"
+#include "libmpv/client.h"
+
+extern const struct mp_scripting mp_scripting_lua;
+
+static const struct mp_scripting *scripting_backends[] = {
+#if HAVE_LUA
+ &mp_scripting_lua,
+#endif
+ NULL
+};
+
+static char *script_name_from_filename(void *talloc_ctx, const char *fname)
+{
+ fname = mp_basename(fname);
+ if (fname[0] == '@')
+ fname += 1;
+ char *name = talloc_strdup(talloc_ctx, fname);
+ // Drop file extension
+ char *dot = strrchr(name, '.');
+ if (dot)
+ *dot = '\0';
+ // Turn it into a safe identifier - this is used with e.g. dispatching
+ // input via: "send scriptname ..."
+ for (int n = 0; name[n]; n++) {
+ char c = name[n];
+ if (!(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') &&
+ !(c >= '0' && c <= '9'))
+ name[n] = '_';
+ }
+ return talloc_asprintf(talloc_ctx, "%s", name);
+}
+
+struct thread_arg {
+ const struct mp_scripting *backend;
+ mpv_handle *client;
+ const char *fname;
+};
+
+static void *script_thread(void *p)
+{
+ pthread_detach(pthread_self());
+
+ struct thread_arg *arg = p;
+ struct mp_log *log = mp_client_get_log(arg->client);
+
+ mp_verbose(log, "Loading script...\n");
+
+ if (arg->backend->load(arg->client, arg->fname) < 0)
+ mp_err(log, "Could not load script %s\n", arg->fname);
+
+ mp_verbose(log, "Exiting...\n");
+
+ mpv_destroy(arg->client);
+ talloc_free(arg);
+ return NULL;
+}
+
+static void mp_load_script(struct MPContext *mpctx, const char *fname)
+{
+ char *ext = mp_splitext(fname, NULL);
+ const struct mp_scripting *backend = NULL;
+ for (int n = 0; scripting_backends[n]; n++) {
+ const struct mp_scripting *b = scripting_backends[n];
+ if (ext && strcasecmp(ext, b->file_ext) == 0) {
+ backend = b;
+ break;
+ }
+ }
+
+ if (!backend) {
+ MP_WARN(mpctx, "Can't load unknown script: %s\n", fname);
+ return;
+ }
+
+ struct thread_arg *arg = talloc_ptrtype(NULL, arg);
+ char *name = script_name_from_filename(arg, fname);
+ *arg = (struct thread_arg){
+ .fname = talloc_strdup(arg, fname),
+ .backend = backend,
+ // Create the client before creating the thread; otherwise a race
+ // condition could happen, where MPContext is destroyed while the
+ // thread tries to create the client.
+ .client = mp_new_client(mpctx->clients, name),
+ };
+ if (!arg->client) {
+ talloc_free(arg);
+ return;
+ }
+
+ pthread_t thread;
+ if (pthread_create(&thread, NULL, script_thread, arg))
+ talloc_free(arg);
+
+ return;
+}
+
+static int compare_filename(const void *pa, const void *pb)
+{
+ char *a = (char *)pa;
+ char *b = (char *)pb;
+ return strcmp(a, b);
+}
+
+static char **list_script_files(void *talloc_ctx, char *path)
+{
+ char **files = NULL;
+ int count = 0;
+ DIR *dp = opendir(path);
+ if (!dp)
+ return NULL;
+ struct dirent *ep;
+ while ((ep = readdir(dp))) {
+ char *fname = mp_path_join(talloc_ctx, bstr0(path), bstr0(ep->d_name));
+ struct stat s;
+ if (!mp_stat(fname, &s) && S_ISREG(s.st_mode))
+ MP_TARRAY_APPEND(talloc_ctx, files, count, fname);
+ }
+ closedir(dp);
+ qsort(files, count, sizeof(char *), compare_filename);
+ MP_TARRAY_APPEND(talloc_ctx, files, count, NULL);
+ return files;
+}
+
+void mp_load_scripts(struct MPContext *mpctx)
+{
+ // Load scripts from options
+ if (mpctx->opts->lua_load_osc)
+ mp_load_script(mpctx, "@osc.lua");
+ char **files = mpctx->opts->lua_files;
+ for (int n = 0; files && files[n]; n++) {
+ if (files[n][0])
+ mp_load_script(mpctx, files[n]);
+ }
+ if (!mpctx->opts->auto_load_scripts)
+ return;
+ // Load ~/.mpv/lua/*
+ void *tmp = talloc_new(NULL);
+ char *script_path = mp_find_user_config_file(tmp, mpctx->global, "lua");
+ if (script_path) {
+ files = list_script_files(tmp, script_path);
+ for (int n = 0; files && files[n]; n++)
+ mp_load_script(mpctx, files[n]);
+ }
+ talloc_free(tmp);
+}
diff --git a/wscript_build.py b/wscript_build.py
index c8058b6fe6..fe711b554f 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -216,6 +216,7 @@ def build(ctx):
( "player/osd.c" ),
( "player/playloop.c" ),
( "player/screenshot.c" ),
+ ( "player/scripting.c" ),
( "player/sub.c" ),
( "player/timeline/tl_cue.c" ),
( "player/timeline/tl_mpv_edl.c" ),