-
Notifications
You must be signed in to change notification settings - Fork 5
/
skiptosilence.lua
207 lines (177 loc) · 6.82 KB
/
skiptosilence.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
--[[
* skiptosilence.lua v.2024-08-22
*
* AUTHORS: detuur, microraptor, Eisa01, dyphire
* License: MIT
* link: https://github.com/detuur/mpv-scripts
*
* This script skips to the next silence in the file. The
* intended use for this is to skip until the end of an
* opening sequence, at which point there's often a short
* period of silence.
*
* The default keybind is F3. You can change this by adding
* the following line to your input.conf:
* KEY script-binding skip-to-silence
*
* In order to tweak the script parameters, you can place the
* text below, between the template markers, in a new file at
* script-opts/skiptosilence.conf in mpv's user folder. The
* parameters will be automatically loaded on start.
*
* Dev note about the used filters:
* - `silencedetect` is an audio filter that listens for silence and
* emits text output with details whenever silence is detected.
* Filter documentation: https://ffmpeg.org/ffmpeg-filters.html
****************** TEMPLATE FOR skiptosilence.conf ******************
#--(#number). Maximum amount of noise to trigger, in terms of dB. Lower is more sensitive.
silence_audio_level=-40
#--(#number). Duration of the silence that will be detected to trigger skipping.
silence_duration=0.7
#--(0/#number). The first detcted silence_duration will be ignored for the defined seconds in this option, and it will continue skipping until the next silence_duration.
# (0 for disabled, or specify seconds).
ignore_silence_duration=1
#--(0/#number). Minimum amount of seconds accepted to skip until the configured silence_duration.
# (0 for disabled, or specify seconds)
min_skip_duration=0
#--(0/#number). Maximum amount of seconds accepted to skip until the configured silence_duration.
# (0 for disabled, or specify seconds)
max_skip_duration=120
#--(yes/no). Default is muted, however if audio was enabled due to custom mpv settings, the fast-forwarded audio can sound jarring.
force_mute_on_skip=no
************************** END OF TEMPLATE **************************
--]]
local o = {
silence_audio_level = -40,
silence_duration = 0.7,
ignore_silence_duration=1,
min_skip_duration = 0,
max_skip_duration = 120,
force_mute_on_skip = false,
}
(require 'mp.options').read_options(o)
local mp = require 'mp'
local msg = require 'mp.msg'
speed_state = 1
pause_state = false
mute_state = false
sub_state = nil
secondary_sub_state = nil
vid_state = nil
geometry_state = nil
skip_flag = false
initial_skip_time = 0
local function restoreProp(pause)
if not pause then pause = pause_state end
local fullscreen = mp.get_property("fullscreen")
mp.set_property("vid", vid_state)
if not fullscreen then
mp.set_property("geometry", geometry_state)
end
mp.set_property_bool("mute", mute_state)
mp.set_property("speed", speed_state)
mp.unobserve_property(foundSilence)
mp.command("no-osd af remove @skiptosilence")
mp.set_property_bool("pause", pause)
mp.set_property("sub-visibility", sub_state)
mp.set_property("secondary-sub-visibility", secondary_sub_state)
timer:kill()
skip_flag = false
end
local function handleMinMaxDuration(timepos)
if not skip_flag then return end
if not timepos then timepos = mp.get_property_number("time-pos") end
skip_duration = timepos - initial_skip_time
if o.min_skip_duration > 0 and skip_duration <= o.min_skip_duration then
restoreProp()
mp.osd_message('Skipping Cancelled\nSilence is less than configured minimum')
msg.info('Skipping Cancelled\nSilence is less than configured minimum')
return true
end
if o.max_skip_duration > 0 and skip_duration >= o.max_skip_duration then
restoreProp()
mp.osd_message('Skipping Cancelled\nSilence is more than configured maximum')
msg.info('Skipping Cancelled\nSilence is more than configured maximum')
return true
end
return false
end
local function skippedMessage()
mp.osd_message("Skipped to silence at " .. mp.get_property_osd("time-pos"))
msg.info("Skipped to silence at " .. mp.get_property_osd("time-pos"))
end
function foundSilence(name, value)
if value == "{}" or value == nil then
return
end
timecode = tonumber(string.match(value, "%d+%.?%d+"))
if timecode == nil or timecode < initial_skip_time + o.ignore_silence_duration then
return
end
if handleMinMaxDuration(timecode) then return end
restoreProp()
mp.add_timeout(0.05, skippedMessage)
skip_flag = false
end
local function doSkip()
local audio = mp.get_property_number("aid") or 0
if audio == 0 then
mp.osd_message("No audio stream detected")
msg.info("No audio stream detected")
return
end
if skip_flag then return end
initial_skip_time = (mp.get_property_native("time-pos") or 0)
if math.floor(initial_skip_time) == math.floor(mp.get_property_native('duration') or 0) then return end
local width = mp.get_property_native("osd-width")
local height = mp.get_property_native("osd-height")
local fullscreen = mp.get_property_native("fullscreen")
geometry_state = mp.get_property("geometry")
if not fullscreen then
mp.set_property_native("geometry", ("%dx%d"):format(width, height))
end
mp.command(
"no-osd af add @skiptosilence:lavfi=[silencedetect=noise=" ..
o.silence_audio_level .. "dB:d=" .. o.silence_duration .. "]"
)
mp.observe_property("af-metadata/skiptosilence", "string", foundSilence)
sub_state = mp.get_property("sub-visibility")
mp.set_property("sub-visibility", "no")
secondary_sub_state = mp.get_property("secondary-sub-visibility")
mp.set_property("secondary-sub-visibility", "no")
vid_state = mp.get_property("vid")
mp.set_property("vid", "no")
mute_state = mp.get_property_native("mute")
if o.force_mute_on_skip then
mp.set_property_bool("mute", true)
end
pause_state = mp.get_property_native("pause")
mp.set_property_bool("pause", false)
speed_state = mp.get_property_native("speed")
mp.set_property("speed", 100)
skip_flag = true
timer = mp.add_periodic_timer(0.5, function()
local video_time = (mp.get_property_native("time-pos") or 0)
handleMinMaxDuration(video_time)
end)
end
mp.observe_property('pause', 'bool', function(_, value)
if value and skip_flag then
restoreProp(true)
end
end)
mp.observe_property('percent-pos', 'number', function(_, value)
if skip_flag and value and value > 99 then
local fullscreen = mp.get_property("fullscreen")
mp.set_property("vid", vid_state)
if not fullscreen then
mp.set_property("geometry", geometry_state)
end
end
end)
mp.add_hook('on_unload', 9, function()
if skip_flag then
restoreProp()
end
end)
mp.add_key_binding(nil, "skip-to-silence", doSkip)