forked from Ben-Kerman/mpv-sub-scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sub-skip.lua
230 lines (197 loc) · 7.11 KB
/
sub-skip.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
-- feel free to modify and/or redistribute as long as you give credit to the original creator; © 2022 Ben Kerman
local cfg = {
default_state = false,
seek_mode_default = false,
min_skip_interval = 3,
speed_skip_speed = 2.5,
lead_in = 0,
lead_out = 1,
speed_skip_speed_delta = 0.1,
min_skip_interval_delta = 0.25
}
require("mp.options").read_options(cfg, nil, function(changes)
if changes.default_state then toggle_script() end
if changes.seek_mode_default then switch_mode() end
if changes.min_skip_interval then set_min_interval(cfg.min_skip_interval) end
if changes.speed_skip_speed then set_speed_skip_speed(cfg.speed_skip_speed) end
end)
local active = cfg.default_state
local seek_skip = cfg.seek_mode_default
local skipping = false
local sped_up = false
local last_sub_end, next_sub_start
function calc_next_delay()
-- hide subtitles, otherwise sub could briefly flash on screen when stepping
local was_visible = mp.get_property_bool("sub-visibility")
if was_visible then
mp.set_property_bool("sub-visibility", false)
end
local initial_delay = mp.get_property_number("sub-delay")
-- get time to next line by adjusting subtitle delay
-- so that the next line starts at the current time
mp.commandv("sub-step", "1")
local new_delay = mp.get_property_number("sub-delay")
mp.set_property_number("sub-delay", initial_delay)
if was_visible then
mp.set_property_bool("sub-visibility", true)
end
-- if the delay didn't change, the next line hasn't been demuxed yet
-- (or there are no more lines)
-- else calculate difference between previous delay and shifted delay
if new_delay == initial_delay then return nil
else return -(new_delay - initial_delay) end
end
-- SEEK SKIP --
-- Seek skip generally works by directly skipping to the calculated start
-- of the next line. If it is not available, the script repeatedly seeks
-- to the end of the demuxer cache until a line is found.
function end_seek_skip(next_sub_begin)
mp.set_property_number("time-pos", next_sub_begin - cfg.lead_out)
end_skip()
end
-- create timer that determines if the next line has been found and reacts accordingly
local seek_skip_timer
seek_skip_timer = mp.add_periodic_timer(128, function()
if mp.get_property_bool("seeking") == false then
seek_skip_timer:stop()
local time_pos = mp.get_property_number("time-pos")
local next_delay = calc_next_delay()
if next_delay == nil then
-- if no line found, seek to end of demuxer cache (potentially unstable!)
local cache_duration = mp.get_property_number("demuxer-cache-duration")
local seek_time = cache_duration or 1
mp.set_property_number("time-pos", time_pos + seek_time)
seek_skip_timer:resume()
else
-- if line found, finish seek skip
seek_skip_timer:kill()
end_seek_skip(time_pos + next_delay)
mp.set_property_bool("pause", false)
end
end
end)
-- make sure that the timer never fires while the script is being loaded
seek_skip_timer:kill()
seek_skip_timer.timeout = 0.05
function start_seek_skip()
mp.unobserve_property(handle_tick)
local next_delay = calc_next_delay()
if next_delay ~= nil then
-- if next line is visible seek immediately
end_seek_skip(mp.get_property_number("time-pos") + next_delay)
else
mp.set_property_bool("pause", true)
seek_skip_timer:resume()
end
end
-- SPEED SKIP (mostly) --
-- Speed skip works by simply speeding up playback between the end of the last line
-- (+lead in) until the start of the next one (-lead out).
-- If the start of the next line is not known, playback is sped up and it's checked
-- on each tick (i.e. change of time-pos) if the next line has been loaded.
local initial_speed = mp.get_property_number("speed")
local initial_video_sync = mp.get_property("video-sync")
function handle_tick(_, time_pos)
-- time_pos might be nil after the file changes
if time_pos == nil then return end
if not sped_up and time_pos > last_sub_end + cfg.lead_in then
if seek_skip then start_seek_skip()
else
initial_speed = mp.get_property_number("speed")
-- Setting video-sync to desync and then audio prevents audio issues
-- after resetting speed back to its initial value.
initial_video_sync = mp.get_property("video-sync")
mp.set_property("video-sync", "desync")
mp.set_property_number("speed", cfg.speed_skip_speed)
sped_up = true
end
elseif sped_up and next_sub_start == nil then
-- next_sub_start == nil means that no next line has been
-- found during blind skip
local next_delay = calc_next_delay()
if next_delay ~= nil then
next_sub_start = time_pos + next_delay
end
elseif sped_up and time_pos > next_sub_start - cfg.lead_out then
end_skip()
end
end
-- INITIALIZATION/SHARED FUNCTIONALITY --
function start_skip()
skipping = true
mp.observe_property("time-pos", "number", handle_tick)
end
function end_skip()
mp.unobserve_property(handle_tick)
skipping = false
sped_up = false
mp.set_property_number("speed", initial_speed)
mp.set_property("video-sync", "audio")
mp.set_property("video-sync", initial_video_sync)
last_sub_end, next_sub_start = nil
end
function handle_sub_change(_, sub_end)
if not sub_end and not skipping then
local time_pos = mp.get_property_number("time-pos")
local next_delay = calc_next_delay()
-- if time-pos is nil the file probably just started playing
if not time_pos then
last_sub_end = -cfg.lead_in
else
last_sub_end = time_pos
end
if next_delay ~= nil then
if next_delay < cfg.min_skip_interval then return
else next_sub_start = time_pos + next_delay end
end
start_skip()
elseif skipping and sub_end then end_skip() end
end
function activate()
mp.observe_property("sub-end", "number", handle_sub_change)
active = true
end
function deactivate()
seek_skip_timer:kill()
end_skip()
mp.unobserve_property(handle_sub_change)
active = false
end
if active then activate() end
-- CONFIG --
function toggle_script()
if active then
deactivate()
mp.osd_message("Non-subtitle skip disabled")
else
activate()
mp.osd_message("Non-subtitle skip enabled")
end
end
mp.add_key_binding("Ctrl+n", "toggle", toggle_script)
function switch_mode()
seek_skip = not seek_skip
mp.osd_message("Seek skip " .. (seek_skip and "enabled" or "disabled"))
end
mp.add_key_binding("Ctrl+Alt+n", "switch-mode", switch_mode)
function set_speed_skip_speed(new_value)
cfg.speed_skip_speed = new_value
if skipping then mp.set_property_number("speed", new_value) end
mp.osd_message("Skip speed: " .. new_value)
end
mp.add_key_binding("Ctrl+Alt+[", "decrease-speed", function()
set_speed_skip_speed(cfg.speed_skip_speed - cfg.speed_skip_speed_delta)
end, {repeatable = true})
mp.add_key_binding("Ctrl+Alt+]", "increase-speed", function()
set_speed_skip_speed(cfg.speed_skip_speed + cfg.speed_skip_speed_delta)
end, {repeatable = true})
function set_min_interval(new_value)
cfg.min_skip_interval = new_value
mp.osd_message("Minimum interval: " .. new_value)
end
mp.add_key_binding("Ctrl+Alt+-", "decrease-interval", function()
set_min_interval(cfg.min_skip_interval - cfg.min_skip_interval_delta)
end, {repeatable = true})
mp.add_key_binding("Ctrl+Alt++", "increase-interval", function()
set_min_interval(cfg.min_skip_interval + cfg.min_skip_interval_delta)
end, {repeatable = true})