-
Notifications
You must be signed in to change notification settings - Fork 8
skiptosilence: disable video/subtitle while skipping #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
74883bc
f76f31a
ed1f46f
7f2cfea
60202ae
665e889
9abf1cd
165b507
68d8e36
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| --[[ | ||
| * skiptosilence.lua v.2022-02-27 | ||
| * skiptosilence.lua v.2023-08-27 | ||
| * | ||
| * AUTHORS: detuur, microraptor | ||
| * AUTHORS: detuur, microraptor, Eisa01 | ||
| * License: MIT | ||
| * link: https://github.com/detuur/mpv-scripts | ||
| * | ||
|
|
@@ -19,110 +19,173 @@ | |
| * 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 ****************** | ||
| # Maximum amount of noise to trigger, in terms of dB. | ||
| # The default is -30 (yes, negative). -60 is very sensitive, | ||
| # -10 is more tolerant to noise. | ||
| quietness = -30 | ||
| #--(#number). Maximum amount of noise to trigger, in terms of dB. Lower is more sensitive. | ||
| silence_audio_level=-40 | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid changing variable names unless they're outright wrong or conflict with new variables. Changing them results in needless noise in the git diff/history. |
||
| # Minimum duration of silence to trigger. | ||
| duration = 0.1 | ||
| #--(#number). Duration of the silence that will be detected to trigger skipping. | ||
| silence_duration=0.7 | ||
| # The fast-forwarded audio can sound jarring. Set to 'yes' | ||
| # to mute it while skipping. | ||
| mutewhileskipping = no | ||
| #--(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 | ||
| #--(yes/no). Display osd messages when actions occur. | ||
| osd_msg=yes | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm on the fence about this option. I don't like it from the perspective of hiding/showing debug messages (like "silence was less than minimum" on line 102). These messages don't belong in the OSD; users can find them in the terminal. On the other hand, this option is valuable for people who want to hide "skipped to silence at XXX" (line 116). Additionally, on line 108 I've found another use for this option. Weigh in with your opinion. |
||
| ************************** END OF TEMPLATE ************************** | ||
| --]] | ||
|
|
||
| local opts = { | ||
| quietness = -30, | ||
| duration = 0.1, | ||
| mutewhileskipping = false | ||
| local o = { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't use single-character variable names. This variable is impossible to search for in the code. This is also an instance of "don't change variable names". |
||
| 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, | ||
| osd_msg = true, | ||
| } | ||
|
|
||
| (require 'mp.options').read_options(o) | ||
| local mp = require 'mp' | ||
| local msg = require 'mp.msg' | ||
| local options = require 'mp.options' | ||
|
|
||
| old_speed = 1 | ||
| was_paused = false | ||
| was_muted = false | ||
| speed_state = 1 | ||
| pause_state = false | ||
| mute_state = false | ||
| sub_state = nil | ||
| secondary_sub_state = nil | ||
| vid_state = nil | ||
| window_state = nil | ||
| skip_flag = false | ||
| initial_skip_time = 0 | ||
|
|
||
| function restoreProp(timepos,pause) | ||
| if not timepos then timepos = mp.get_property_number("time-pos") end | ||
| if not pause then pause = pause_state end | ||
|
|
||
| mp.set_property("vid", vid_state) | ||
| mp.set_property("force-window", window_state) | ||
| 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_number("time-pos", timepos) | ||
| mp.set_property("sub-visibility", sub_state) | ||
| mp.set_property("secondary-sub-visibility", secondary_sub_state) | ||
| timer:kill() | ||
| skip_flag = false | ||
| end | ||
|
|
||
| --[[ | ||
| 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. | ||
| - `nullsink` interrupts the video stream requests to the decoder, | ||
| which stops it from bogging down the fast-forward. | ||
| - `color` generates a blank image, which renders very quickly and is | ||
| good for fast-forwarding. | ||
| - Filter documentation: https://ffmpeg.org/ffmpeg-filters.html | ||
| --]] | ||
| 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(initial_skip_time) | ||
| if o.osd_msg then mp.osd_message('Skipping Cancelled\nSilence is less than configured minimum') end | ||
| 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(initial_skip_time) | ||
| if o.osd_msg then mp.osd_message('Skipping Cancelled\nSilence is more than configured maximum') end | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could be changed to:
This message in particular will be seen by many users who won't have bothered to read the header. In this case we could even change it to something like this?
What do you think? I want to move away from |
||
| msg.info('Skipping Cancelled\nSilence is more than configured maximum') | ||
| return true | ||
| end | ||
| return false | ||
| end | ||
|
|
||
| function skippedMessage() | ||
| if o.osd_msg then mp.osd_message("Skipped to silence at " .. mp.get_property_osd("time-pos")) end | ||
| msg.info("Skipped to silence at " .. mp.get_property_osd("time-pos")) | ||
| end | ||
|
|
||
| function doSkip() | ||
| -- Get video dimensions | ||
| local width = mp.get_property_native("width"); | ||
| local height = mp.get_property_native("height") | ||
|
|
||
| -- Create audio and video filters | ||
| mp.command( | ||
| "no-osd af add @skiptosilence:lavfi=[silencedetect=noise=" .. | ||
| opts.quietness .. "dB:d=" .. opts.duration .. "]" | ||
| ) | ||
| mp.command( | ||
| "no-osd vf add @skiptosilence-blackout:lavfi=" .. | ||
| "[nullsink,color=c=black:s=" .. width .. "x" .. height .. "]" | ||
| ) | ||
|
|
||
| -- Triggers whenever the `silencedetect` filter emits output | ||
| mp.observe_property("af-metadata/skiptosilence", "string", foundSilence) | ||
|
|
||
| was_muted = mp.get_property_native("mute") | ||
| if opts.mutewhileskipping then | ||
| 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") | ||
| mp.set_property_native("geometry", ("%dx%d"):format(width, height)) | ||
|
|
||
| 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") | ||
| window_state = mp.get_property("force-window") | ||
| mp.set_property("force-window", "yes") | ||
| 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 | ||
|
|
||
| was_paused = mp.get_property_native("pause") | ||
| mp.set_property_bool("pause", false) | ||
| old_speed = mp.get_property_native("speed") | ||
| mp.set_property("speed", 100) | ||
| 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 | ||
|
|
||
| function foundSilence(name, value) | ||
| if value == "{}" or value == nil then | ||
| return -- For some reason these are sometimes emitted. Ignore. | ||
| end | ||
|
|
||
| timecode = tonumber(string.match(value, "%d+%.?%d+")) | ||
| time_pos = mp.get_property_native("time-pos") | ||
| if timecode == nil or timecode < time_pos + 1 then | ||
| return -- Ignore anything less than a second ahead. | ||
| end | ||
|
|
||
| mp.set_property_bool("mute", was_muted) | ||
| mp.set_property_bool("pause", was_paused) | ||
| mp.set_property("speed", old_speed) | ||
| mp.unobserve_property(foundSilence) | ||
|
|
||
| -- Remove used audio and video filters | ||
| mp.command("no-osd af remove @skiptosilence") | ||
| mp.command("no-osd vf remove @skiptosilence-blackout") | ||
|
|
||
| -- Seeking to the exact moment even though we've already | ||
| -- fast forwarded here allows the video decoder to skip | ||
| -- the missed video. This prevents massive A-V lag. | ||
| mp.set_property_number("time-pos", timecode) | ||
|
|
||
| -- If we don't wait at least 50ms before messaging the user, we | ||
| -- end up displaying an old value for time-pos. | ||
| mp.add_timeout(0.05, skippedMessage) | ||
| if value == "{}" or value == nil then | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't change the code style. By changing spaces to tabs for the entire file, this line shows up in the diff while nothing actually changed. In general, code style changes are pretty disruptive to git history and should be avoided unless technically required. |
||
| return | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a similar vein to the previous note, avoid removing (or even spell-checking!) comments unless something about them is wrong. This is another source of noise in git diff/history and reviews. |
||
| 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(timecode) | ||
|
|
||
| mp.add_timeout(0.05, skippedMessage) | ||
| skip_flag = false | ||
| end | ||
|
|
||
| function skippedMessage() | ||
| msg.info("Skipped to silence at " .. mp.get_property_osd("time-pos")) | ||
| mp.osd_message("Skipped to silence at " .. mp.get_property_osd("time-pos")) | ||
| end | ||
| mp.observe_property('pause', 'bool', function(name, value) | ||
| if value and skip_flag then | ||
| restoreProp(initial_skip_time, true) | ||
| end | ||
| end) | ||
|
|
||
|
|
||
| options.read_options(opts) | ||
| mp.add_hook('on_unload', 9, function() | ||
| if skip_flag then | ||
| restoreProp() | ||
| end | ||
| end) | ||
|
|
||
| mp.add_key_binding("F3", "skip-to-silence", doSkip) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nitpick: the dev note should stay in the code. For userscripts, the header is aimed at users, not devs.