mpv/scripts/slicing.lua (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
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 = "x264", acodec = "aac", prevf = "", -- vf = "format=yuv444p16$hqvf,scale=in_color_matrix=$matrix,format=bgr24", vf = "", hqvf = "", postvf = "", opts = "", ext = "mp4", 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") local logpath = utils.join_path(utils.getcwd(), "mpv_slice_cmd.log") f = io.open(logpath, "w") -- f:write(string.format("# %s\n%s\n", -- os.date("%Y-%m-%d %H:%M:%S"), -- str)) f:write(str) f:close() return logpath 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( utils.getcwd(), 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) local logpath = log(cmd) -- os.execute(cmd) os.execute(string.format("cat %s | xsel -ib",logpath)) 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) |