-
Notifications
You must be signed in to change notification settings - Fork 5
/
mpv-animated.lua
231 lines (188 loc) · 7.59 KB
/
mpv-animated.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
-- Original by Scheliux, Dragoner7 which was ported from Ruin0x11
-- Adapted to webp by DonCanjas
-- Modify_: https://github.com/dyphire/mpv-scripts
-- Create animated webps or gifs with mpv
-- Requires ffmpeg.
-- Adapted from https://github.com/Scheliux/mpv-gif-generator
-- Usage: "w" to set start frame, "W" to set end frame, "Ctrl+w" to create.
-- Note:
-- Requires FFmpeg in PATH environment variable or edit ffmpeg_path in the script options,
-- Note:
-- A small circle at the top-right corner is a sign that creat is happenning now.
require 'mp.options'
local msg = require 'mp.msg'
local utils = require "mp.utils"
local options = {
type = "gif", -- gif or webp
ffmpeg_path = "ffmpeg",
dir = "~~desktop/",
rez = 600,
fps = 15,
lossless = 0,
quality = 90,
compression_level = 6,
loop = 0,
}
read_options(options)
local fps
local ext
local text
if options.type == "webp" then
ext = "webp"
text = "webP"
else
ext = "gif"
text = "GIF"
end
-- Check for invalid fps values
-- Can you believe Lua doesn't have a proper ternary operator in the year of our lord 2020?
if options.fps ~= nil and options.fps >= 1 and options.fps < 30 then
fps = options.fps
else
fps = 15
end
-- Set this to the filters to pass into ffmpeg's -vf option.
-- filters="fps=24,scale=320:-1:flags=spline"
filters=string.format("fps=%s,scale='trunc(ih*dar/2)*2:trunc(ih/2)*2',setsar=1/1,scale=%s:-1:flags=lanczos", fps, options.rez)
local is_windows = package.config:sub(1, 1) == "\\" -- detect path separator, windows uses backslashes
-- Setup output directory
local output_directory = mp.command_native({ "expand-path", options.dir })
--create output_directory if it doesn't exist
if output_directory ~= '' then
local meta, meta_error = utils.file_info(output_directory)
if not meta or not meta.is_dir then
local windows_args = { 'powershell', '-NoProfile', '-Command', 'mkdir', string.format("\"%s\"", output_directory) }
local unix_args = { 'mkdir', '-p', output_directory }
local args = is_windows and windows_args or unix_args
local res = mp.command_native({name = "subprocess", capture_stdout = true, playback_only = false, args = args})
if res.status ~= 0 then
msg.error("Failed to create animated_dir save directory "..output_directory..". Error: "..(res.error or "unknown"))
return
end
end
end
start_time = -1
end_time = -1
function make_animated_with_subtitles()
make_animated_internal(true)
end
function make_animated()
make_animated_internal(false)
end
function table_length(t)
local count = 0
for _ in pairs(t) do count = count + 1 end
return count
end
function make_animated_internal(burn_subtitles)
local start_time_l = start_time
local end_time_l = end_time
if start_time_l == -1 or end_time_l == -1 or start_time_l >= end_time_l then
mp.osd_message("Invalid start/end time.")
return
end
msg.info("Creating " .. text)
mp.osd_message("Creating " .. text)
-- shell escape
function esc_for_sub(s)
s = string.gsub(s, "\\", "/")
s = string.gsub(s, '"', '\\"')
s = string.gsub(s, ":", "\\:")
s = string.gsub(s, "'", "\\'")
s = string.gsub(s, "%[", "\\%[")
s = string.gsub(s, "%]", "\\%]")
return s
end
local pathname = mp.get_property("path", "")
local trim_filters = filters
local position = start_time_l
local duration = end_time_l - start_time_l
if burn_subtitles then
-- Determine currently active sub track
local i = 0
local tracks_count = mp.get_property_number("track-list/count")
local subs_array = {}
-- check for subtitle tracks
while i < tracks_count do
local type = mp.get_property(string.format("track-list/%d/type", i))
local selected = mp.get_property(string.format("track-list/%d/selected", i))
local external = mp.get_property(string.format("track-list/%d/external", i))
-- if it's a sub track, save it
if type == "sub" then
local length = table_length(subs_array)
if selected == "yes" and external == "yes" then
msg.info("Error: external subtitles have been selected")
mp.osd_message("Error: external subtitles have been selected", 2)
return
else
subs_array[length] = selected == "yes"
end
end
i = i + 1
end
if table_length(subs_array) > 0 then
local correct_track = 0
-- iterate through saved subtitle tracks until the correct one is found
for index, is_selected in pairs(subs_array) do
if (is_selected) then
correct_track = index
end
end
trim_filters = trim_filters .. string.format(",subtitles='%s':si=%s", esc_for_sub(pathname), correct_track)
end
end
-- make the animated
local filename = mp.get_property("filename/no-ext")
local file_path = utils.join_path(output_directory, filename)
-- increment filename
for i = 0, 999 do
local fn = string.format('%s_%03d.%s', file_path, i, ext)
if not file_exists(fn) then
animated_name = fn
break
end
end
if not animated_name then
mp.osd_message('No available filenames!')
return
end
local copyts = ""
if burn_subtitles then
copyts = "-copyts"
end
if options.type == "webp" then
arg = string.format("%s -y -hide_banner -loglevel error -ss %s %s -t %s -i '%s' -lavfi %s -lossless %s -q:v %s -compression_level %s -loop %s '%s'", options.ffmpeg_path, position, copyts, duration, pathname, trim_filters, options.lossless, options.quality, options.compression_level, options.loop, animated_name)
else
arg = string.format("%s -y -hide_banner -loglevel error -ss %s %s -t %s -i '%s' -lavfi %s,'split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse' -loop %s '%s'", options.ffmpeg_path, position, copyts, duration, pathname, trim_filters, options.loop, animated_name)
end
local windows_args = { 'powershell', '-NoProfile', '-Command', arg }
local unix_args = { '/bin/bash', '-c', arg }
local args = is_windows and windows_args or unix_args
local screenx, screeny, aspect = mp.get_osd_size()
mp.set_osd_ass(screenx, screeny, "{\\an9}● ")
local res = mp.command_native({name = "subprocess", capture_stdout = true, playback_only = false, args = args})
mp.set_osd_ass(screenx, screeny, "")
if res.status ~= 0 then
msg.info("Failed to creat " .. animated_name)
mp.osd_message("Error creating " .. text .. ", check console for more info.")
return
end
msg.info(animated_name .. " created.")
mp.osd_message(text .. " created.")
end
function set_animated_start()
start_time = mp.get_property_number("time-pos", -1)
mp.osd_message(text .. " Start: " .. start_time)
end
function set_animated_end()
end_time = mp.get_property_number("time-pos", -1)
mp.osd_message(text .. " End: " .. end_time)
end
function file_exists(name)
local f = io.open(name, "r")
if f ~= nil then io.close(f) return true else return false end
end
mp.add_key_binding("w", "set_animated_start", set_animated_start)
mp.add_key_binding("W", "set_animated_end", set_animated_end)
mp.add_key_binding("Ctrl+w", "make_animated", make_animated)
mp.add_key_binding("Ctrl+W", "make_animated_with_subtitles", make_animated_with_subtitles) --only works with srt for now