diff options
Diffstat (limited to 'player/scripting.c')
-rw-r--r-- | player/scripting.c | 179 |
1 files changed, 179 insertions, 0 deletions
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); +} |