all repos — dotfiles @ 590b5c22aec59db03124692385d8d94cf0e76add

linux dotfiles

add mpv/scripts
Prithu Goswami prithugoswami524@gmail.com
Tue, 26 Nov 2019 08:50:39 +0530
commit

590b5c22aec59db03124692385d8d94cf0e76add

parent

661581003e1a1f8074cf6291b001248439643561

2 files changed, 420 insertions(+), 0 deletions(-)

jump to
A mpv/scripts/slicing.lua

@@ -0,0 +1,145 @@

+local msg = require "mp.msg" +local utils = require "mp.utils" +local options = require "mp.options" + +local cut_pos = nil +local copy_audio = true +local o = { + target_dir = "~", + vcodec = "rawvideo", + acodec = "pcm_s16le", + prevf = "", + vf = "format=yuv444p16$hqvf,scale=in_color_matrix=$matrix,format=bgr24", + hqvf = "", + postvf = "", + opts = "", + ext = "avi", + command_template = [[ + ffmpeg -v warning -y -stats + -ss $shift -i "$in" -t $duration + -c:v $vcodec -c:a $acodec $audio + -vf $prevf$vf$postvf $opts "$out.$ext" + ]], +} +options.read_options(o) + +function timestamp(duration) + local hours = duration / 3600 + local minutes = duration % 3600 / 60 + local seconds = duration % 60 + return string.format("%02d:%02d:%02.03f", hours, minutes, seconds) +end + +function osd(str) + return mp.osd_message(str, 3) +end + +function get_homedir() + -- It would be better to do platform detection instead of fallback but + -- it's not that easy in Lua. + return os.getenv("HOME") or os.getenv("USERPROFILE") or "" +end + +function log(str) + local logpath = utils.join_path( + o.target_dir:gsub("~", get_homedir()), + "mpv_slicing.log") + f = io.open(logpath, "a") + f:write(string.format("# %s\n%s\n", + os.date("%Y-%m-%d %H:%M:%S"), + str)) + f:close() +end + +function escape(str) + -- FIXME(Kagami): This escaping is NOT enough, see e.g. + -- https://stackoverflow.com/a/31413730 + -- Consider using `utils.subprocess` instead. + return str:gsub("\\", "\\\\"):gsub('"', '\\"') +end + +function trim(str) + return str:gsub("^%s+", ""):gsub("%s+$", "") +end + +function get_csp() + local csp = mp.get_property("colormatrix") + if csp == "bt.601" then return "bt601" + elseif csp == "bt.709" then return "bt709" + elseif csp == "smpte-240m" then return "smpte240m" + else + local err = "Unknown colorspace: " .. csp + osd(err) + error(err) + end +end + +function get_outname(shift, endpos) + local name = mp.get_property("filename") + local dotidx = name:reverse():find(".", 1, true) + if dotidx then name = name:sub(1, -dotidx-1) end + name = name:gsub(" ", "_") + name = name:gsub(":", "-") + name = name .. string.format(".%s-%s", timestamp(shift), timestamp(endpos)) + return name +end + +function cut(shift, endpos) + local cmd = trim(o.command_template:gsub("%s+", " ")) + local inpath = escape(utils.join_path( + utils.getcwd(), + mp.get_property("stream-path"))) + local outpath = escape(utils.join_path( + o.target_dir:gsub("~", get_homedir()), + get_outname(shift, endpos))) + + cmd = cmd:gsub("$shift", shift) + cmd = cmd:gsub("$duration", endpos - shift) + cmd = cmd:gsub("$vcodec", o.vcodec) + cmd = cmd:gsub("$acodec", o.acodec) + cmd = cmd:gsub("$audio", copy_audio and "" or "-an") + cmd = cmd:gsub("$prevf", o.prevf) + cmd = cmd:gsub("$vf", o.vf) + cmd = cmd:gsub("$hqvf", o.hqvf) + cmd = cmd:gsub("$postvf", o.postvf) + cmd = cmd:gsub("$matrix", get_csp()) + cmd = cmd:gsub("$opts", o.opts) + -- Beware that input/out filename may contain replacing patterns. + cmd = cmd:gsub("$ext", o.ext) + cmd = cmd:gsub("$out", outpath) + cmd = cmd:gsub("$in", inpath, 1) + + msg.info(cmd) + log(cmd) + os.execute(cmd) +end + +function toggle_mark() + local pos = mp.get_property_number("time-pos") + if cut_pos then + local shift, endpos = cut_pos, pos + if shift > endpos then + shift, endpos = endpos, shift + end + if shift == endpos then + osd("Cut fragment is empty") + else + cut_pos = nil + osd(string.format("Cut fragment: %s - %s", + timestamp(shift), + timestamp(endpos))) + cut(shift, endpos) + end + else + cut_pos = pos + osd(string.format("Marked %s as start position", timestamp(pos))) + end +end + +function toggle_audio() + copy_audio = not copy_audio + osd("Audio capturing is " .. (copy_audio and "enabled" or "disabled")) +end + +mp.add_key_binding("Ctrl+e", "slicing_mark", toggle_mark) +mp.add_key_binding("a", "slicing_audio", toggle_audio)
A mpv/scripts/youtube-quality.lua

@@ -0,0 +1,275 @@

+-- youtube-quality.lua +-- +-- Change youtube video quality on the fly. +-- +-- Diplays a menu that lets you switch to different ytdl-format settings while +-- you're in the middle of a video (just like you were using the web player). +-- +-- Bound to ctrl-f by default. + +local mp = require 'mp' +local utils = require 'mp.utils' +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' + +local opts = { + --key bindings + toggle_menu_binding = "ctrl+f", + up_binding = "UP", + down_binding = "DOWN", + select_binding = "ENTER", + + --formatting / cursors + selected_and_active = "▶ - ", + selected_and_inactive = "● - ", + unselected_and_active = "▷ - ", + unselected_and_inactive = "○ - ", + + --font size scales by window, if false requires larger font and padding sizes + scale_playlist_by_window=false, + + --playlist ass style overrides inside curly brackets, \keyvalue is one field, extra \ for escape in lua + --example {\\fnUbuntu\\fs10\\b0\\bord1} equals: font=Ubuntu, size=10, bold=no, border=1 + --read http://docs.aegisub.org/3.2/ASS_Tags/ for reference of tags + --undeclared tags will use default osd settings + --these styles will be used for the whole playlist. More specific styling will need to be hacked in + -- + --(a monospaced font is recommended but not required) + style_ass_tags = "{\\fnmonospace}", + + --paddings for top left corner + text_padding_x = 5, + text_padding_y = 5, + + --other + menu_timeout = 10, + + --use youtube-dl to fetch a list of available formats (overrides quality_strings) + fetch_formats = true, + + --default menu entries + quality_strings=[[ + [ + {"4320p" : "bestvideo[height<=?4320p]+bestaudio/best"}, + {"2160p" : "bestvideo[height<=?2160]+bestaudio/best"}, + {"1440p" : "bestvideo[height<=?1440]+bestaudio/best"}, + {"1080p" : "bestvideo[height<=?1080]+bestaudio/best"}, + {"720p" : "bestvideo[height<=?720]+bestaudio/best"}, + {"480p" : "bestvideo[height<=?480]+bestaudio/best"}, + {"360p" : "bestvideo[height<=?360]+bestaudio/best"}, + {"240p" : "bestvideo[height<=?240]+bestaudio/best"}, + {"144p" : "bestvideo[height<=?144]+bestaudio/best"} + ] + ]], +} +(require 'mp.options').read_options(opts, "youtube-quality") +opts.quality_strings = utils.parse_json(opts.quality_strings) + +local destroyer = nil + + +function show_menu() + local selected = 1 + local active = 0 + local current_ytdl_format = mp.get_property("ytdl-format") + msg.verbose("current ytdl-format: "..current_ytdl_format) + local num_options = 0 + local options = {} + + + if opts.fetch_formats then + options, num_options = download_formats() + end + + if next(options) == nil then + for i,v in ipairs(opts.quality_strings) do + num_options = num_options + 1 + for k,v2 in pairs(v) do + options[i] = {label = k, format=v2} + if v2 == current_ytdl_format then + active = i + selected = active + end + end + end + end + + --set the cursor to the currently format + for i,v in ipairs(options) do + if v.format == current_ytdl_format then + active = i + selected = active + break + end + end + + function selected_move(amt) + selected = selected + amt + if selected < 1 then selected = num_options + elseif selected > num_options then selected = 1 end + timeout:kill() + timeout:resume() + draw_menu() + end + function choose_prefix(i) + if i == selected and i == active then return opts.selected_and_active + elseif i == selected then return opts.selected_and_inactive end + + if i ~= selected and i == active then return opts.unselected_and_active + elseif i ~= selected then return opts.unselected_and_inactive end + return "> " --shouldn't get here. + end + + function draw_menu() + local ass = assdraw.ass_new() + + ass:pos(opts.text_padding_x, opts.text_padding_y) + ass:append(opts.style_ass_tags) + + for i,v in ipairs(options) do + ass:append(choose_prefix(i)..v.label.."\\N") + end + + local w, h = mp.get_osd_size() + if opts.scale_playlist_by_window then w,h = 0, 0 end + mp.set_osd_ass(w, h, ass.text) + end + + function destroy() + timeout:kill() + mp.set_osd_ass(0,0,"") + mp.remove_key_binding("move_up") + mp.remove_key_binding("move_down") + mp.remove_key_binding("select") + mp.remove_key_binding("escape") + destroyer = nil + end + timeout = mp.add_periodic_timer(opts.menu_timeout, destroy) + destroyer = destroy + + mp.add_forced_key_binding(opts.up_binding, "move_up", function() selected_move(-1) end, {repeatable=true}) + mp.add_forced_key_binding(opts.down_binding, "move_down", function() selected_move(1) end, {repeatable=true}) + mp.add_forced_key_binding(opts.select_binding, "select", function() + destroy() + mp.set_property("ytdl-format", options[selected].format) + reload_resume() + end) + mp.add_forced_key_binding(opts.toggle_menu_binding, "escape", destroy) + + draw_menu() + return +end + +local ytdl = { + path = "youtube-dl", + searched = false, + blacklisted = {} +} + +format_cache={} +function download_formats() + local function exec(args) + local ret = utils.subprocess({args = args}) + return ret.status, ret.stdout, ret + end + + local function table_size(t) + s = 0 + for i,v in ipairs(t) do + s = s+1 + end + return s + end + + local url = mp.get_property("path") + + url = string.gsub(url, "ytdl://", "") -- Strip possible ytdl:// prefix. + + -- don't fetch the format list if we already have it + if format_cache[url] ~= nil then + local res = format_cache[url] + return res, table_size(res) + end + mp.osd_message("fetching available formats with youtube-dl...", 60) + + if not (ytdl.searched) then + local ytdl_mcd = mp.find_config_file("youtube-dl") + if not (ytdl_mcd == nil) then + msg.verbose("found youtube-dl at: " .. ytdl_mcd) + ytdl.path = ytdl_mcd + end + ytdl.searched = true + end + + local command = {ytdl.path, "--no-warnings", "--no-playlist", "-J"} + table.insert(command, url) + local es, json, result = exec(command) + + if (es < 0) or (json == nil) or (json == "") then + mp.osd_message("fetching formats failed...", 1) + msg.error("failed to get format list: " .. err) + return {}, 0 + end + + local json, err = utils.parse_json(json) + + if (json == nil) then + mp.osd_message("fetching formats failed...", 1) + msg.error("failed to parse JSON data: " .. err) + return {}, 0 + end + + res = {} + msg.verbose("youtube-dl succeeded!") + for i,v in ipairs(json.formats) do + if v.vcodec ~= "none" then + local fps = v.fps and v.fps.."fps" or "" + local resolution = string.format("%sx%s", v.width, v.height) + local l = string.format("%-9s %-5s (%-4s / %s)", resolution, fps, v.ext, v.vcodec) + local f = string.format("%s+bestaudio/best", v.format_id) + table.insert(res, {label=l, format=f, width=v.width }) + end + end + + table.sort(res, function(a, b) return a.width > b.width end) + + mp.osd_message("", 0) + format_cache[url] = res + return res, table_size(res) +end + + +-- register script message to show menu +mp.register_script_message("toggle-quality-menu", +function() + if destroyer ~= nil then + destroyer() + else + show_menu() + end +end) + +-- keybind to launch menu +mp.add_key_binding(opts.toggle_menu_binding, "quality-menu", show_menu) + +-- special thanks to reload.lua (https://github.com/4e6/mpv-reload/) +function reload_resume() + local playlist_pos = mp.get_property_number("playlist-pos") + local reload_duration = mp.get_property_native("duration") + local time_pos = mp.get_property("time-pos") + + mp.set_property_number("playlist-pos", playlist_pos) + + -- Tries to determine live stream vs. pre-recordered VOD. VOD has non-zero + -- duration property. When reloading VOD, to keep the current time position + -- we should provide offset from the start. Stream doesn't have fixed start. + -- Decent choice would be to reload stream from it's current 'live' positon. + -- That's the reason we don't pass the offset when reloading streams. + if reload_duration and reload_duration > 0 then + local function seeker() + mp.commandv("seek", time_pos, "absolute") + mp.unregister_event(seeker) + end + mp.register_event("file-loaded", seeker) + end +end