mpv/scripts/slicing.lua
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)