GitHub PullRequestReviewCommentEvent at 2014-12-15 12:37:49
Repository: mpv
Actor: avih
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/mpv-player/mpv/pulls/comments/21820803","id":21820803,"diff_hunk":"@@ -0,0 +1,1340 @@\n+/*\n+ * This file is part of mpv.\n+ *\n+ * mpv is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License as published by\n+ * the Free Software Foundation; either version 2 of the License, or\n+ * (at your option) any later version.\n+ *\n+ * mpv is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License along\n+ * with mpv.  If not, see <http://www.gnu.org/licenses/>.\n+ */\n+\n+#include <assert.h>\n+#include <string.h>\n+#include <strings.h>\n+#include <sys/types.h>\n+#include <sys/stat.h>\n+#include <unistd.h>\n+#include <dirent.h>\n+#include <math.h>\n+\n+#include \"osdep/io.h\"\n+#include \"osdep/atomics.h\"\n+\n+#include <mujs.h>\n+\n+#include \"talloc.h\"\n+\n+#include \"common/common.h\"\n+#include \"options/m_property.h\"\n+#include \"common/msg.h\"\n+#include \"common/msg_control.h\"\n+#include \"options/m_option.h\"\n+#include \"input/input.h\"\n+#include \"options/path.h\"\n+#include \"misc/bstr.h\"\n+#include \"misc/json.h\"\n+#include \"osdep/subprocess.h\"\n+#include \"osdep/timer.h\"\n+#include \"osdep/threads.h\"\n+#include \"stream/stream.h\"\n+#include \"sub/osd.h\"\n+#include \"core.h\"\n+#include \"command.h\"\n+#include \"client.h\"\n+#include \"libmpv/client.h\"\n+\n+#define MAX_LENGTH_COMMANDV 50\n+\n+// List of builtin modules and their contents as strings.\n+// All these are generated from player/javascript/*.js\n+static const char *const builtin_files[][3] = {\n+    {\"@defaults.js\",\n+#   include \"player/javascript/defaults.js.inc\"\n+    },\n+    {0}\n+};\n+\n+// Represents a loaded script. Each has its own js state.\n+struct script_ctx {\n+    const char *name;\n+    const char *filename;\n+    js_State *jsstate;\n+    struct mp_log *log;\n+    struct mpv_handle *client;\n+    struct MPContext *mpctx;\n+    int suspended;\n+};\n+\n+static struct script_ctx *get_ctx(js_State *J)\n+{\n+    return (struct script_ctx *)js_getcontext(J);\n+}\n+\n+static struct MPContext *get_mpctx(js_State *J)\n+{\n+    return get_ctx(J)->mpctx;\n+}\n+\n+static mpv_handle *client_js(js_State *J)\n+{\n+    return get_ctx(J)->client;\n+}\n+\n+\n+/**********************************************************************\n+ *  error handling\n+ *********************************************************************/\n+// sets mp.last_error_string from str, or if doesn't exist, from err\n+static void set_last_error(js_State *J, int err, const char *str)\n+{\n+    js_getglobal(J, \"mp\");\n+    js_pushstring(J, str ? str : mpv_error_string(err));\n+    js_setproperty(J, -2, \"last_error_string\");\n+    js_pop(J, 1);\n+}\n+\n+// if err < 0: sets mp.last_error_string, pushes undefined and returns 1\n+// else: does nothing and returns 0\n+static int handledAsError(js_State *J, int err)\n+{\n+    if (err >= 0)\n+        return 0;\n+\n+    set_last_error(J, err, NULL);\n+    js_pushundefined(J);\n+    return 1;\n+}\n+\n+// assumes idx 2 exists on the stack (even if undefined).\n+// identical to handledAsError if idx 2 is undefined. otherwise:\n+// - always sets mp.last_error_string (\"success\" on success)\n+// - on error, pushes idx 2 as the result.\n+static int handledAsErrDef(js_State *J, int err)\n+{\n+    if (js_isundefined(J, 2))\n+        return handledAsError(J, err);\n+\n+    set_last_error(J, err, NULL);\n+    if (err >= 0)\n+        return 0;\n+\n+    js_copy(J, 2);\n+    return 1;\n+}\n+\n+// pushes true/false and handles error if required\n+static void pushStatus(js_State *J, int err)\n+{\n+    if (!handledAsError(J, err))\n+        js_pushboolean(J, 1);\n+}\n+\n+\n+/**********************************************************************\n+ *  Initialization and files reading/loading/running\n+ *********************************************************************/\n+\n+enum FILE_COMMAND {\n+    FILE_READ, // pushes the file's content as (null terminated) string\n+    FILE_LOAD, // compiles the file to js and pushes it as a js function\n+    FILE_RUN   // compiles + runs the file and pushes the result\n+};\n+\n+static void push_file_result(js_State *J, enum FILE_COMMAND command,\n+                             const char *filename);\n+\n+static void script_read_file(js_State *J)\n+{\n+    push_file_result(J, FILE_READ, js_tostring(J, 1));\n+}\n+\n+static void script_load_file(js_State *J)\n+{\n+    push_file_result(J, FILE_LOAD, js_tostring(J, 1));\n+}\n+\n+static void script_run_file(js_State *J)\n+{\n+    push_file_result(J, FILE_RUN, js_tostring(J, 1));\n+}\n+\n+/// file functions helper:\n+static const char *get_builtin_file(const char *name)\n+{\n+    for (int n = 0; builtin_files[n][0]; n++)\n+        if (!strcmp(builtin_files[n][0], name))\n+            return builtin_files[n][1];\n+    return NULL;\n+}\n+\n+// executes the command and pushes the result or throws an error\n+// filename is taken as is, unless it starts with '@', at which case it's read from builtin_files.\n+static void push_file_result(js_State *J, enum FILE_COMMAND command,\n+                             const char *filename)\n+{\n+    if (!filename || !filename[0])\n+        js_error(J, \"invalid file name\");\n+\n+    FILE *f;\n+    char *s = NULL;\n+    const char *data;\n+\n+    if (filename[0] == '@') {\n+        data = get_builtin_file(filename);\n+        if (!data)\n+            js_error(J, \"cannot find built in file '%s'\", filename);\n+\n+    } else {\n+        int n, t;\n+\n+        f = fopen(filename, \"rb\");\n+        if (!f)\n+            js_error(J, \"cannot open file: '%s'\", filename);\n+\n+        if (fseek(f, 0, SEEK_END) < 0) {\n+            fclose(f);\n+            js_error(J, \"cannot seek in file: '%s'\", filename);\n+        }\n+        n = ftell(f);\n+        fseek(f, 0, SEEK_SET);\n+\n+        s = talloc_array(NULL, char, n + 1);\n+        if (!s) {\n+            fclose(f);\n+            js_error(J, \"cannot allocate storage for file contents: '%s'\",\n+                     filename);\n+        }\n+\n+        t = fread(s, 1, n, f);\n+        if (t != n) {\n+            talloc_free(s);\n+            fclose(f);\n+            js_error(J, \"cannot read data from file: '%s'\", filename);\n+        }\n+\n+        s[n] = 0;\n+        data = s;\n+    }\n+\n+    int err;\n+    switch (command) {\n+    case FILE_READ:\n+        js_pushstring(J, data);\n+        err = 0;\n+        break;\n+    case FILE_LOAD:\n+        err = js_ploadstring(J, filename, data);\n+        break;\n+    case FILE_RUN:\n+        if (err = js_ploadstring(J, filename, data))\n+            break;\n+        js_pushglobal(J);\n+        err = js_pcall(J, 0);\n+        break;\n+    default:\n+        js_newerror(J, \"unknown file command\");\n+        err = -1;\n+    }\n+\n+    if (s) {\n+        talloc_free(s);\n+        fclose(f);\n+    }\n+\n+    if (err)\n+        js_throw(J);\n+}\n+\n+static void add_functions(struct script_ctx *ctx);\n+\n+// called directly, doesn't modify stack's depth, runs the file or throws an error\n+static void run_file(js_State *J, const char *fname)\n+{\n+    struct script_ctx *ctx = get_ctx(J);\n+    char *res_name = mp_get_user_path(NULL, ctx->mpctx->global, fname);\n+    const char *name = fname[0] == '@' ? fname : res_name;\n+    MP_VERBOSE(ctx, \"loading file %s\\n\", name);\n+\n+    js_newcfunction(J, script_run_file, \"run_file\", 1);\n+    js_pushglobal(J);\n+    js_pushstring(J, name);\n+    int r = js_pcall(J, 1);\n+    talloc_free(res_name); // careful to not leak this on errors\n+    if (r)\n+        js_throw(J);  // the error object is at the top of the stack\n+    js_pop(J, 1);\n+}\n+\n+// called as script, leaves result on stack or throws\n+static void script_run_scripts(js_State *J)\n+{\n+    add_functions(get_ctx(J));\n+    run_file(J, \"@defaults.js\");\n+    //run_file(J, \"player/javascript/defaults.js\"); // useful for development to read from the filesystem instead of embedded.\n+    run_file(J, get_ctx(J)->filename);  // the main file for this script\n+\n+    js_getglobal(J, \"mp_event_loop\"); // fn\n+    if (!js_iscallable(J, -1))\n+        js_error(J, \"no event loop function\");\n+\n+    js_pushglobal(J);\n+    js_call(J, 0); // mp_event_loop\n+}\n+\n+// load_javascript - entry point for mpv to run this script, and exit point for any uncaught js errors beyond it.\n+// script_run_scripts - loads the built in functions into the vm\n+//                   - runs the default file[s] and the main script file\n+//                   - calls mp_event_loop, returns on shutdown/error/script-exit.\n+// run_file        - loads and runs a single js file\n+// - names starting with script_ are js functions (take arguments from the vm's stack and push the result back)\n+// - names starting with push take c args and push a value to the js stack\n+// - js calle's stack index 0 is \"this\", and the rest (1, 2, 3, ...) are the args\n+static int load_javascript(struct mpv_handle *client, const char *fname)\n+{\n+    struct script_ctx ctx = (struct script_ctx) {\n+        .mpctx = mp_client_get_core(client),\n+        .client = client,\n+        .name = mpv_client_name(client),\n+        .log = mp_client_get_log(client),\n+        .filename = fname,\n+    };\n+\n+    int r = -1;\n+    js_State *J = ctx.jsstate = js_newstate(NULL, NULL);\n+    if (!J)\n+        goto error_out;\n+\n+    // store ctx as the vm's context\n+    // later used by functions called from js with get_ctx(J);\n+    js_setcontext(J, &ctx);\n+\n+    js_newcfunction(J, script_run_scripts, \"run_scripts\", 0);\n+    js_pushglobal(J);\n+    if (js_pcall(J, 0)) {\n+        MP_FATAL(&ctx, \"JS error: %s\\n\", js_tostring(J, -1));\n+        goto error_out;\n+    }\n+\n+    r = 0;\n+\n+error_out:\n+    if (ctx.suspended)\n+        mpv_resume(ctx.client);\n+    if (J)\n+        js_freestate(J);\n+    return r;\n+}\n+\n+/**********************************************************************\n+ *  functions exposed to javascript and helpers\n+ *********************************************************************/\n+static int check_loglevel(js_State *J, int idx)\n+{\n+    const char *level = js_tostring(J, idx);\n+    for (int n = 0; n < MSGL_MAX; n++) {\n+        if (mp_log_levels[n] && strcasecmp(mp_log_levels[n], level) == 0)\n+            return n;\n+    }\n+    js_error(J, \"Invalid log level '%s'\", level);\n+}\n+\n+// assumes fromIdx is not negative\n+static void finalize_log(int msgl, js_State *J, int fromIdx)\n+{\n+    struct script_ctx *ctx = get_ctx(J);\n+    int last = js_gettop(J) - 1;\n+    for (int i = fromIdx; i <= last; i++)\n+        mp_msg(ctx->log, msgl, \"%s%s\", (i > fromIdx ? \" \" : \"\"),\n+               js_tostring(J, i));\n+\n+    mp_msg(ctx->log, msgl, \"\\n\");\n+    pushStatus(J, 1);\n+}\n+\n+// All the log functions are at mp.msg\n+\n+// args: level as string and the rest are strings to log\n+static void script_log(js_State *J)\n+{\n+    finalize_log(check_loglevel(J, 1), J, 2);\n+}\n+\n+#define LOG_BODY(mlevel) { finalize_log(mlevel, J, 1); }\n+// args: strings to log\n+static void script_fatal(js_State *J)   LOG_BODY(MSGL_FATAL)\n+static void script_error(js_State *J)   LOG_BODY(MSGL_ERR)\n+static void script_warn(js_State *J)    LOG_BODY(MSGL_WARN)\n+static void script_info(js_State *J)    LOG_BODY(MSGL_INFO)\n+static void script_verbose(js_State *J) LOG_BODY(MSGL_V)\n+static void script_debug(js_State *J)   LOG_BODY(MSGL_DEBUG)\n+\n+static void script_find_config_file(js_State *J)\n+{\n+    struct MPContext *mpctx = get_mpctx(J);\n+    const char *s = js_tostring(J, 1);\n+    char *path = mp_find_config_file(NULL, mpctx->global, s);\n+    if (path)\n+        js_pushstring(J, path);\n+    else\n+        js_pushnull(J);","path":"player/javascript.c","position":386,"original_position":386,"commit_id":"7b7906c062c93517ee3ff1683d012818b0d9b283","original_commit_id":"7b7906c062c93517ee3ff1683d012818b0d9b283","user":{"login":"avih","id":2164962,"avatar_url":"https://avatars.githubusercontent.com/u/2164962?v=3","gravatar_id":"","url":"https://api.github.com/users/avih","html_url":"https://github.com/avih","followers_url":"https://api.github.com/users/avih/followers","following_url":"https://api.github.com/users/avih/following{/other_user}","gists_url":"https://api.github.com/users/avih/gists{/gist_id}","starred_url":"https://api.github.com/users/avih/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/avih/subscriptions","organizations_url":"https://api.github.com/users/avih/orgs","repos_url":"https://api.github.com/users/avih/repos","events_url":"https://api.github.com/users/avih/events{/privacy}","received_events_url":"https://api.github.com/users/avih/received_events","type":"User","site_admin":false},"body":"I did read the formatting notes and ran it through uncrustify before the PR, and rejected some of uncrustify's suggestion (not on this case though).\r\n\r\nSo you prefer that every if/else use braces?","created_at":"2014-12-15T12:37:49Z","updated_at":"2014-12-15T12:37:49Z","html_url":"https://github.com/mpv-player/mpv/pull/1351#discussion_r21820803","pull_request_url":"https://api.github.com/repos/mpv-player/mpv/pulls/1351","_links":{"self":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/comments/21820803"},"html":{"href":"https://github.com/mpv-player/mpv/pull/1351#discussion_r21820803"},"pull_request":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/1351"}}},"pull_request":{"url":"https://api.github.com/repos/mpv-player/mpv/pulls/1351","id":26060492,"html_url":"https://github.com/mpv-player/mpv/pull/1351","diff_url":"https://github.com/mpv-player/mpv/pull/1351.diff","patch_url":"https://github.com/mpv-player/mpv/pull/1351.patch","issue_url":"https://api.github.com/repos/mpv-player/mpv/issues/1351","number":1351,"state":"open","locked":false,"title":"Add Javascript scripting support using MuJS","user":{"login":"avih","id":2164962,"avatar_url":"https://avatars.githubusercontent.com/u/2164962?v=3","gravatar_id":"","url":"https://api.github.com/users/avih","html_url":"https://github.com/avih","followers_url":"https://api.github.com/users/avih/followers","following_url":"https://api.github.com/users/avih/following{/other_user}","gists_url":"https://api.github.com/users/avih/gists{/gist_id}","starred_url":"https://api.github.com/users/avih/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/avih/subscriptions","organizations_url":"https://api.github.com/users/avih/orgs","repos_url":"https://api.github.com/users/avih/repos","events_url":"https://api.github.com/users/avih/events{/privacy}","received_events_url":"https://api.github.com/users/avih/received_events","type":"User","site_admin":false},"body":"Implements JS with almost identical API to the Lua support, except:\r\n- The global mp, mp.msg and mp.utils are always available\r\n- On error return undefined and the error is at mp.last_error_string\r\n- Timers are JS \"standard\" global set/clear Timeout/Interval/Immediate\r\n- Supports CommonJS style modules/require\r\n- At mp.utils: read_file (any), and load_file, run_file (for js files)\r\n- Global print and dump functions\r\n- mp.options currently not supported\r\n\r\nRequires MuJS (clone, make, make install):\r\n- http://mujs.com/ - git://git.ghostscript.com/mujs.git\r\n- Mirror - https://github.com/ccxvii/mujs\r\n\r\nNote: MuJS is a new project and this is probably its first major use outside\r\nof MuPDF. Please keep an eye open for bugs.","created_at":"2014-12-15T10:49:33Z","updated_at":"2014-12-15T12:37:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"dfb73489e6db09e163181f260b048bb884c61c0b","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/mpv-player/mpv/pulls/1351/commits","review_comments_url":"https://api.github.com/repos/mpv-player/mpv/pulls/1351/comments","review_comment_url":"https://api.github.com/repos/mpv-player/mpv/pulls/comments/{number}","comments_url":"https://api.github.com/repos/mpv-player/mpv/issues/1351/comments","statuses_url":"https://api.github.com/repos/mpv-player/mpv/statuses/7b7906c062c93517ee3ff1683d012818b0d9b283","head":{"label":"avih:js-merge","ref":"js-merge","sha":"7b7906c062c93517ee3ff1683d012818b0d9b283","user":{"login":"avih","id":2164962,"avatar_url":"https://avatars.githubusercontent.com/u/2164962?v=3","gravatar_id":"","url":"https://api.github.com/users/avih","html_url":"https://github.com/avih","followers_url":"https://api.github.com/users/avih/followers","following_url":"https://api.github.com/users/avih/following{/other_user}","gists_url":"https://api.github.com/users/avih/gists{/gist_id}","starred_url":"https://api.github.com/users/avih/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/avih/subscriptions","organizations_url":"https://api.github.com/users/avih/orgs","repos_url":"https://api.github.com/users/avih/repos","events_url":"https://api.github.com/users/avih/events{/privacy}","received_events_url":"https://api.github.com/users/avih/received_events","type":"User","site_admin":false},"repo":{"id":17341571,"name":"mpv","full_name":"avih/mpv","owner":{"login":"avih","id":2164962,"avatar_url":"https://avatars.githubusercontent.com/u/2164962?v=3","gravatar_id":"","url":"https://api.github.com/users/avih","html_url":"https://github.com/avih","followers_url":"https://api.github.com/users/avih/followers","following_url":"https://api.github.com/users/avih/following{/other_user}","gists_url":"https://api.github.com/users/avih/gists{/gist_id}","starred_url":"https://api.github.com/users/avih/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/avih/subscriptions","organizations_url":"https://api.github.com/users/avih/orgs","repos_url":"https://api.github.com/users/avih/repos","events_url":"https://api.github.com/users/avih/events{/privacy}","received_events_url":"https://api.github.com/users/avih/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/avih/mpv","description":"Video player based on MPlayer/mplayer2","fork":true,"url":"https://api.github.com/repos/avih/mpv","forks_url":"https://api.github.com/repos/avih/mpv/forks","keys_url":"https://api.github.com/repos/avih/mpv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/avih/mpv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/avih/mpv/teams","hooks_url":"https://api.github.com/repos/avih/mpv/hooks","issue_events_url":"https://api.github.com/repos/avih/mpv/issues/events{/number}","events_url":"https://api.github.com/repos/avih/mpv/events","assignees_url":"https://api.github.com/repos/avih/mpv/assignees{/user}","branches_url":"https://api.github.com/repos/avih/mpv/branches{/branch}","tags_url":"https://api.github.com/repos/avih/mpv/tags","blobs_url":"https://api.github.com/repos/avih/mpv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/avih/mpv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/avih/mpv/git/refs{/sha}","trees_url":"https://api.github.com/repos/avih/mpv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/avih/mpv/statuses/{sha}","languages_url":"https://api.github.com/repos/avih/mpv/languages","stargazers_url":"https://api.github.com/repos/avih/mpv/stargazers","contributors_url":"https://api.github.com/repos/avih/mpv/contributors","subscribers_url":"https://api.github.com/repos/avih/mpv/subscribers","subscription_url":"https://api.github.com/repos/avih/mpv/subscription","commits_url":"https://api.github.com/repos/avih/mpv/commits{/sha}","git_commits_url":"https://api.github.com/repos/avih/mpv/git/commits{/sha}","comments_url":"https://api.github.com/repos/avih/mpv/comments{/number}","issue_comment_url":"https://api.github.com/repos/avih/mpv/issues/comments/{number}","contents_url":"https://api.github.com/repos/avih/mpv/contents/{+path}","compare_url":"https://api.github.com/repos/avih/mpv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/avih/mpv/merges","archive_url":"https://api.github.com/repos/avih/mpv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/avih/mpv/downloads","issues_url":"https://api.github.com/repos/avih/mpv/issues{/number}","pulls_url":"https://api.github.com/repos/avih/mpv/pulls{/number}","milestones_url":"https://api.github.com/repos/avih/mpv/milestones{/number}","notifications_url":"https://api.github.com/repos/avih/mpv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/avih/mpv/labels{/name}","releases_url":"https://api.github.com/repos/avih/mpv/releases{/id}","created_at":"2014-03-02T16:37:09Z","updated_at":"2014-12-15T11:13:15Z","pushed_at":"2014-12-15T11:13:15Z","git_url":"git://github.com/avih/mpv.git","ssh_url":"git@github.com:avih/mpv.git","clone_url":"https://github.com/avih/mpv.git","svn_url":"https://github.com/avih/mpv","homepage":"http://mpv.io","size":64012,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"mpv-player:master","ref":"master","sha":"fcec3df700386139c1fcd11bdd7c501ce26b6913","user":{"login":"mpv-player","id":2550273,"avatar_url":"https://avatars.githubusercontent.com/u/2550273?v=3","gravatar_id":"","url":"https://api.github.com/users/mpv-player","html_url":"https://github.com/mpv-player","followers_url":"https://api.github.com/users/mpv-player/followers","following_url":"https://api.github.com/users/mpv-player/following{/other_user}","gists_url":"https://api.github.com/users/mpv-player/gists{/gist_id}","starred_url":"https://api.github.com/users/mpv-player/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mpv-player/subscriptions","organizations_url":"https://api.github.com/users/mpv-player/orgs","repos_url":"https://api.github.com/users/mpv-player/repos","events_url":"https://api.github.com/users/mpv-player/events{/privacy}","received_events_url":"https://api.github.com/users/mpv-player/received_events","type":"Organization","site_admin":false},"repo":{"id":6201092,"name":"mpv","full_name":"mpv-player/mpv","owner":{"login":"mpv-player","id":2550273,"avatar_url":"https://avatars.githubusercontent.com/u/2550273?v=3","gravatar_id":"","url":"https://api.github.com/users/mpv-player","html_url":"https://github.com/mpv-player","followers_url":"https://api.github.com/users/mpv-player/followers","following_url":"https://api.github.com/users/mpv-player/following{/other_user}","gists_url":"https://api.github.com/users/mpv-player/gists{/gist_id}","starred_url":"https://api.github.com/users/mpv-player/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mpv-player/subscriptions","organizations_url":"https://api.github.com/users/mpv-player/orgs","repos_url":"https://api.github.com/users/mpv-player/repos","events_url":"https://api.github.com/users/mpv-player/events{/privacy}","received_events_url":"https://api.github.com/users/mpv-player/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mpv-player/mpv","description":"Video player based on MPlayer/mplayer2","fork":false,"url":"https://api.github.com/repos/mpv-player/mpv","forks_url":"https://api.github.com/repos/mpv-player/mpv/forks","keys_url":"https://api.github.com/repos/mpv-player/mpv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mpv-player/mpv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mpv-player/mpv/teams","hooks_url":"https://api.github.com/repos/mpv-player/mpv/hooks","issue_events_url":"https://api.github.com/repos/mpv-player/mpv/issues/events{/number}","events_url":"https://api.github.com/repos/mpv-player/mpv/events","assignees_url":"https://api.github.com/repos/mpv-player/mpv/assignees{/user}","branches_url":"https://api.github.com/repos/mpv-player/mpv/branches{/branch}","tags_url":"https://api.github.com/repos/mpv-player/mpv/tags","blobs_url":"https://api.github.com/repos/mpv-player/mpv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mpv-player/mpv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mpv-player/mpv/git/refs{/sha}","trees_url":"https://api.github.com/repos/mpv-player/mpv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mpv-player/mpv/statuses/{sha}","languages_url":"https://api.github.com/repos/mpv-player/mpv/languages","stargazers_url":"https://api.github.com/repos/mpv-player/mpv/stargazers","contributors_url":"https://api.github.com/repos/mpv-player/mpv/contributors","subscribers_url":"https://api.github.com/repos/mpv-player/mpv/subscribers","subscription_url":"https://api.github.com/repos/mpv-player/mpv/subscription","commits_url":"https://api.github.com/repos/mpv-player/mpv/commits{/sha}","git_commits_url":"https://api.github.com/repos/mpv-player/mpv/git/commits{/sha}","comments_url":"https://api.github.com/repos/mpv-player/mpv/comments{/number}","issue_comment_url":"https://api.github.com/repos/mpv-player/mpv/issues/comments/{number}","contents_url":"https://api.github.com/repos/mpv-player/mpv/contents/{+path}","compare_url":"https://api.github.com/repos/mpv-player/mpv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mpv-player/mpv/merges","archive_url":"https://api.github.com/repos/mpv-player/mpv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mpv-player/mpv/downloads","issues_url":"https://api.github.com/repos/mpv-player/mpv/issues{/number}","pulls_url":"https://api.github.com/repos/mpv-player/mpv/pulls{/number}","milestones_url":"https://api.github.com/repos/mpv-player/mpv/milestones{/number}","notifications_url":"https://api.github.com/repos/mpv-player/mpv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mpv-player/mpv/labels{/name}","releases_url":"https://api.github.com/repos/mpv-player/mpv/releases{/id}","created_at":"2012-10-13T08:08:44Z","updated_at":"2014-12-15T12:32:32Z","pushed_at":"2014-12-15T05:24:19Z","git_url":"git://github.com/mpv-player/mpv.git","ssh_url":"git@github.com:mpv-player/mpv.git","clone_url":"https://github.com/mpv-player/mpv.git","svn_url":"https://github.com/mpv-player/mpv","homepage":"http://mpv.io","size":83890,"stargazers_count":1393,"watchers_count":1393,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":189,"mirror_url":null,"open_issues_count":136,"forks":189,"open_issues":136,"watchers":1393,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/1351"},"html":{"href":"https://github.com/mpv-player/mpv/pull/1351"},"issue":{"href":"https://api.github.com/repos/mpv-player/mpv/issues/1351"},"comments":{"href":"https://api.github.com/repos/mpv-player/mpv/issues/1351/comments"},"review_comments":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/1351/comments"},"review_comment":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/mpv-player/mpv/pulls/1351/commits"},"statuses":{"href":"https://api.github.com/repos/mpv-player/mpv/statuses/7b7906c062c93517ee3ff1683d012818b0d9b283"}}}}