├── .gitignore ├── LICENSE ├── README.md ├── autoload-sub-in-mka.lua ├── autoloop.lua ├── exit-fullscreen.lua ├── save-sub-delay.lua └── screenshot-to-clipboard.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 cczzhh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mpv-scripts 2 | Scripts for [mpv](https://github.com/mpv-player/mpv) 3 | 4 | ## ~~autoloop.lua~~ 5 | ~~Work on issue [mpv-player/mpv#5222](https://github.com/mpv-player/mpv/issues/5222).~~ 6 | ~~Automatically loops files that are under a given duration (default 5 seconds).~~ 7 | 8 | Probably you would like to use [`auto-profiles.lua`](https://github.com/wiiaboo/mpv-scripts/blob/master/auto-profiles.lua), which is general-purpose and can achieve the same goal easily. 9 | 10 | ## screenshot-to-clipboard.js 11 | Work on issue [mpv-player/mpv#5330](https://github.com/mpv-player/mpv/issues/5330). 12 | Generates a temp screenshot file on desktop then copy to clipboard. (Windows only) 13 | 14 | ## save-sub-delay.lua 15 | This script saves the sub-delay quantity for each file. When next time the file is opened, sub-delay is automatically restored. 16 | 17 | ## exit-fullscreen.lua 18 | If you use `--keep-open=yes`, this script exits fullscreen mode when the playback reaches the end of file/playlist. 19 | -------------------------------------------------------------------------------- /autoload-sub-in-mka.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | _ _ _ _ _ _ _ _ 3 | | \ | | ___ | | ___ _ __ __ _ ___ _ __ | \ | | ___ ___ __| | ___ __| | | | 4 | | \| | / _ \ | | / _ \ | '_ \ / _` | / _ \ | '__| | \| | / _ \ / _ \ / _` | / _ \ / _` | | | 5 | | |\ | | (_) | | |___ | (_) | | | | | | (_| | | __/ | | | |\ | | __/ | __/ | (_| | | __/ | (_| | |_| 6 | |_| \_| \___/ |_____| \___/ |_| |_| \__, | \___| |_| |_| \_| \___| \___| \__,_| \___| \__,_| (_) 7 | |___/ 8 | No longer needed after: https://github.com/mpv-player/mpv/commit/80d43ee4e692f13358f134c906ba2c5439ecde5f 9 | ]] 10 | --[[ 11 | -- Load mka files as sub files. 12 | -- Respect sub-auto, audio-auto, sub-file-paths and audio-file-paths options. 13 | -- Issue 5132 14 | 15 | mputils = require 'mp.utils' 16 | 17 | -- Followings are from: 18 | -- https://github.com/mpv-player/mpv/blob/master/TOOLS/lua/autoload.lua 19 | function get_extension(path) 20 | match = string.match(path, "%.([^%.]+)$" ) 21 | if match == nil then 22 | return "nomatch" 23 | else 24 | return match 25 | end 26 | end 27 | 28 | table.filter = function(t, iter) 29 | for i = #t, 1, -1 do 30 | if not iter(t[i]) then 31 | table.remove(t, i) 32 | end 33 | end 34 | end 35 | 36 | -- splitbynum and alnumcomp from alphanum.lua (C) Andre Bogus 37 | -- Released under the MIT License 38 | -- http://www.davekoelle.com/files/alphanum.lua 39 | 40 | -- split a string into a table of number and string values 41 | function splitbynum(s) 42 | local result = {} 43 | for x, y in (s or ""):gmatch("(%d*)(%D*)") do 44 | if x ~= "" then table.insert(result, tonumber(x)) end 45 | if y ~= "" then table.insert(result, y) end 46 | end 47 | return result 48 | end 49 | 50 | function clean_key(k) 51 | k = (' '..k..' '):gsub("%s+", " "):sub(2, -2):lower() 52 | return splitbynum(k) 53 | end 54 | 55 | -- compare two strings 56 | function alnumcomp(x, y) 57 | local xt, yt = clean_key(x), clean_key(y) 58 | for i = 1, math.min(#xt, #yt) do 59 | local xe, ye = xt[i], yt[i] 60 | if type(xe) == "string" then ye = tostring(ye) 61 | elseif type(ye) == "string" then xe = tostring(xe) end 62 | if xe ~= ye then return xe < ye end 63 | end 64 | return #xt < #yt 65 | end 66 | ------------------------------------------------------------------ END 67 | 68 | function match_filename(path, sub_auto, filename_wo_ext) 69 | match = string.match(path, "^(.+)%.") 70 | if match == filename_wo_ext then 71 | return true 72 | elseif sub_auto == "exact" then 73 | return false 74 | else -- fuzzy. When sub_auto=all, return identical value to fuzzy 75 | while match ~= nil do 76 | match = string.match(match, "^(.+)%.") 77 | if match == filename_wo_ext then 78 | return true 79 | end 80 | end 81 | end 82 | return false 83 | end 84 | 85 | function autoload_sub_in_mka() 86 | local sub_auto = mp.get_property("options/sub-auto", "") 87 | if sub_auto == "no" then 88 | return 89 | end 90 | 91 | local path = mp.get_property("path", "") 92 | local dir, filename = mputils.split_path(path) 93 | if #dir == 0 then 94 | return 95 | end 96 | local filename_wo_ext = mp.get_property("filename/no-ext", "") 97 | 98 | local files = mputils.readdir(dir, "files") 99 | if files == nil then 100 | return 101 | end 102 | 103 | -- in current dir 104 | table.filter(files, function (v, k) 105 | if string.match(v, "^%.") then 106 | return false 107 | end 108 | if sub_auto ~= "all" and not match_filename(v, sub_auto, filename_wo_ext) then 109 | return false 110 | end 111 | local ext = get_extension(v) 112 | if string.lower(ext) ~= "mka" then 113 | return false 114 | end 115 | return true 116 | end) 117 | table.sort(files, alnumcomp) 118 | 119 | for i = 1, #files do 120 | local file = mputils.join_path(dir, files[i]) 121 | mp.commandv("sub-add", file, "auto") 122 | mp.msg.info("Adding as subtitle files: " .. file) 123 | end 124 | 125 | local sub_file_paths = mp.get_property_native("options/sub-file-paths", {}) 126 | 127 | -- in sub-file-paths 128 | for i = 1, #sub_file_paths do 129 | local sub_file_path = mputils.join_path(dir, sub_file_paths[i]) 130 | local files = mputils.readdir(sub_file_path, "files") 131 | if files ~= nil then 132 | table.filter(files, function (v, k) 133 | if string.match(v, "^%.") then 134 | return false 135 | end 136 | if sub_auto ~= "all" and not match_filename(v, sub_auto, filename_wo_ext) then 137 | return false 138 | end 139 | local ext = get_extension(v) 140 | if string.lower(ext) ~= "mka" then 141 | return false 142 | end 143 | return true 144 | end) 145 | table.sort(files, alnumcomp) 146 | 147 | for j = 1, #files do 148 | local file = mputils.join_path(sub_file_path, files[j]) 149 | mp.commandv("sub-add", file, "auto") 150 | mp.msg.info("Adding as subtitle files: " .. file) 151 | end 152 | end 153 | end 154 | 155 | local audio_file_paths = mp.get_property_native("options/audio-file-paths", {}) 156 | 157 | -- in audio-file-paths, this script respects sub-auto=all as sub-auto=fuzzy 158 | for i = 1, #audio_file_paths do 159 | local audio_file_path = mputils.join_path(dir, audio_file_paths[i]) 160 | local files = mputils.readdir(audio_file_path, "files") 161 | if files ~= nil then 162 | table.filter(files, function (v, k) 163 | if string.match(v, "^%.") then 164 | return false 165 | end 166 | -- no sub_auto ~= "all" here because even if sub-auto=all, 167 | -- one should not expect all mka files in audio-file-paths to be 168 | -- loaded as subtitle files 169 | if not match_filename(v, sub_auto, filename_wo_ext) then 170 | return false 171 | end 172 | local ext = get_extension(v) 173 | if string.lower(ext) ~= "mka" then 174 | return false 175 | end 176 | return true 177 | end) 178 | table.sort(files, alnumcomp) 179 | 180 | for j = 1, #files do 181 | local file = mputils.join_path(audio_file_path, files[j]) 182 | mp.commandv("sub-add", file, "auto") 183 | mp.msg.info("Adding as subtitle files: " .. file) 184 | end 185 | end 186 | end 187 | end 188 | 189 | mp.register_event("file-loaded", autoload_sub_in_mka) 190 | ]] 191 | -------------------------------------------------------------------------------- /autoloop.lua: -------------------------------------------------------------------------------- 1 | -- mpv issue 5222 2 | -- Automatically set loop-file=inf for duration <= given length. Default is 5s 3 | -- Use autoloop_duration=n in script-opts/autoloop.conf to set your preferred length 4 | -- Alternatively use script-opts=autoloop-autoloop_duration=n in mpv.conf (takes priority) 5 | -- Also disables the save-position-on-quit for this file, if it qualifies for looping. 6 | 7 | 8 | require 'mp.options' 9 | 10 | function getOption() 11 | -- Use recommended way to get options 12 | local options = {autoloop_duration = 5} 13 | read_options(options) 14 | autoloop_duration = options.autoloop_duration 15 | 16 | 17 | -- Keep old way just for compatibility (remove lines 15-27 soon) 18 | if autoloop_duration ~= 5 then 19 | return 20 | end 21 | 22 | local opt = tonumber(mp.get_opt("autoloop-duration")) 23 | if not opt then 24 | return 25 | end 26 | print("Depracted configuration! Please use script-opts directory to set auto_loop duration") 27 | print("Or use 'script-opts=autoloop-autoloop_duration' in mpv.conf") 28 | autoloop_duration = opt 29 | -- Remove lines 15-27 soon 30 | end 31 | 32 | function set_loop() 33 | local duration = mp.get_property_native("duration") 34 | 35 | -- Checks whether the loop status was changed for the last file 36 | was_loop = mp.get_property_native("loop-file") 37 | 38 | -- Cancel operation if there is no file duration 39 | if not duration then 40 | return 41 | end 42 | 43 | -- Loops file if was_loop is false, and file meets requirements 44 | if not was_loop and duration <= autoloop_duration then 45 | mp.set_property_native("loop-file", true) 46 | mp.set_property_bool("file-local-options/save-position-on-quit", false) 47 | -- Unloops file if was_loop is true, and file does not meet requirements 48 | elseif was_loop and duration > autoloop_duration then 49 | mp.set_property_native("loop-file", false) 50 | end 51 | end 52 | 53 | 54 | getOption() 55 | mp.register_event("file-loaded", set_loop) 56 | -------------------------------------------------------------------------------- /exit-fullscreen.lua: -------------------------------------------------------------------------------- 1 | -- Exit fullscreen when playback ends, if keep-open=yes 2 | 3 | mp.observe_property("eof-reached", "bool", function(name, value) 4 | if value then 5 | local pause = mp.get_property_native("pause") 6 | if pause then 7 | local fullscreen = mp.get_property_native("fullscreen") 8 | if fullscreen then 9 | mp.set_property_native("fullscreen", false) 10 | end 11 | end 12 | end 13 | end) 14 | -------------------------------------------------------------------------------- /save-sub-delay.lua: -------------------------------------------------------------------------------- 1 | -- This script saves the sub-delay quantity for each file. 2 | -- When next time the file is opened, sub-delay is automatically restored. 3 | -- Using `--sub-delay=` or `x` and `z` key-bindings both work. 4 | -- But keep in mind that this script distinguishes different files by reading 5 | -- their 'path' properties. If you use in command line: 6 | -- `mpv --sub-delay=0.1 example.mkv` 7 | -- this delay value won't be applied to the same file you open by 8 | -- double-clicking it. 9 | 10 | local mputils = require "mp.utils" 11 | 12 | local JSON = (os.getenv('APPDATA') or os.getenv('HOME')..'/.config')..'/mpv/mpv_sub-delay.json' 13 | local jsonFile = io.open(JSON, 'a+') 14 | local sub_delay_table = mputils.parse_json(jsonFile:read("*all")) 15 | jsonFile:close() 16 | 17 | function read_sub_delay() 18 | local sub_delay = mp.get_property_native("sub-delay") 19 | local path = mp.get_property_native("path") 20 | if sub_delay_table == nil then 21 | sub_delay_table = {} 22 | end 23 | if sub_delay == 0 then 24 | if sub_delay_table[path] ~= nil then 25 | sub_delay = sub_delay_table[path] 26 | if sub_delay > 0.000999 or sub_delay < -0.000999 then 27 | mp.command("add sub-delay " .. sub_delay) 28 | end 29 | end 30 | else 31 | sub_delay_table[path] = sub_delay 32 | write_sub_delay() 33 | end 34 | end 35 | 36 | function write_sub_delay() 37 | local jsonFile = io.open(JSON, 'w+') 38 | local path = mp.get_property_native("path") 39 | sub_delay_table[path] = mp.get_property_native("sub-delay") 40 | local jsonContent, ret = mputils.format_json(sub_delay_table) 41 | if ret ~= error and jsonContent ~= nil then 42 | jsonFile:write(jsonContent) 43 | end 44 | jsonFile:close() 45 | end 46 | 47 | function sub_delay_pos() 48 | mp.command("add sub-delay 0.1") 49 | write_sub_delay() 50 | end 51 | 52 | function sub_delay_neg() 53 | mp.command("add sub-delay -0.1") 54 | write_sub_delay() 55 | end 56 | 57 | mp.register_event("file-loaded", read_sub_delay) 58 | 59 | mp.add_key_binding("x", "sub-delay+", sub_delay_pos) 60 | mp.add_key_binding("z", "sub-delay-", sub_delay_neg) 61 | -------------------------------------------------------------------------------- /screenshot-to-clipboard.js: -------------------------------------------------------------------------------- 1 | // Issue mpv-player/mpv#5330 2 | // Generate a temp screenshot file on desktop then copy to clipboard. (Windows only) 3 | // nircmd is required: http://www.nirsoft.net/utils/nircmd.html 4 | // 5 | // script option: ss-2-cb-nircmdc="nircmdc" 6 | // Path to nircmdc executable file. 7 | // If not set, nircmdc will be searched in Windows PATH variable. 8 | // 9 | // Example: mpv file.mkv --script-opts=ss-2-cb-nircmdc="C:\nircmd\nircmdc.exe" 10 | 11 | var nircmdc = "nircmdc"; 12 | 13 | function getOption() 14 | { 15 | var opt = mp.get_opt("ss-2-cb-nircmdc"); 16 | if (opt) 17 | nircmdc = opt; 18 | } 19 | getOption(); 20 | 21 | var ss_file = mp.utils.get_user_path("~~desktop/mpv-ss-2-cb.png"); 22 | 23 | function ss_2_cb() 24 | { 25 | mp.commandv("osd-msg", "screenshot-to-file", ss_file); 26 | mp.utils.subprocess_detached({"args" : [nircmdc, "clipboard", "copyimage", 27 | ss_file]}); 28 | } 29 | 30 | function ss_2_cb_video() 31 | { 32 | mp.commandv("osd-msg", "screenshot-to-file", ss_file, "video"); 33 | mp.utils.subprocess_detached({"args" : [nircmdc, "clipboard", "copyimage", 34 | ss_file]}); 35 | } 36 | 37 | function ss_2_cb_window() 38 | { 39 | mp.commandv("osd-msg", "screenshot-to-file", ss_file, "window"); 40 | mp.utils.subprocess_detached({"args" : [nircmdc, "clipboard", "copyimage", 41 | ss_file]}); 42 | } 43 | 44 | mp.add_key_binding("s", "screenshot-to-clipboard", ss_2_cb); 45 | mp.add_key_binding("S", "screenshot-to-clipboard_video", ss_2_cb_video); 46 | mp.add_key_binding("ctrl+s", "screenshot-to-clipboard_window", ss_2_cb_window); 47 | --------------------------------------------------------------------------------