update speed-transition

* use sub-end instead of sub-text. Disadvantage is that ignoring subtitles is broken right now. Advantage: image based subtitles are working.
For dvd subtitles (that's what I tested) it can happen that some subtitle lines, get 'eaten'. They don't show even though they should.
* implement sub-timeout: when subtitles are displayed without change for some time (5s) -> speedup. (Useful for autogenerated subtitles)
* improvements to skip mode:
    - better behaviour if next subtitle is not in cache
    - option for non-exact skips. This also implements seek-back since a skip is not guaranteed to end at the next line. It might skip over it.
* users can now supply a script configuration file (speed_transition.conf)
* remove key binding for sub visibility. Pressing v seems to handle this nicely.
* change key binding for skip mode to alt + j
* added bunch of debugging code to better debug issues
This commit is contained in:
zenyd 2024-02-12 03:28:35 +01:00
parent 7100d19d18
commit 9bdce00501
2 changed files with 559 additions and 283 deletions

View File

@ -1,47 +1,69 @@
## speed-transition
This is a lua script for the mpv media player. The purpose of this script is to speed up the video if no subtitles are visible for a certain amount of (user configurable) time. It is inspired by the [speed-between-subs](https://gist.github.com/bitingsock/47c5ba6466c63c68bcf991dd376f1d18) script.
### How it works
The script looks for the next subtitle and if it is ahead by 5 (default) seconds the video gets sped up and resumes normal playback just before the subtitle becomes visible. This is done to prevent audio glitches when speech starts.
### Usage
For the script to work it is necessary to have an appropriate 'text' subtitle selected and visible.
The script works best in `video-sync=audio` mode (the default in mpv), because it will then be able to minimize frame drops on speed transition from high->normal. Stutter-free playback is the result.
For the script to work it is necessary to have an appropriate subtitle selected. It's possible to hide the subtitles with `v` and still have the benefit of the script. To enable it press `ctrl + j` and to switch modes press `alt + j`.
It works best in `video-sync=audio` mode (the default in mpv), because it will then be able to minimize frame drops on speed transition from high->normal. Stutter-free playback is the result.
Sensible defaults have been set, but if you want to change the `lookahead` value take care to not set it larger than what the buffers can provide. This applies to embedded subtitles and not external.
**skipmode:**
There is an alternative speedup mode called 'skipmode'. In this mode the video doesn't speed up, but it gets skipped till the next subtitle. It can be configured to skip directly to the next subtitle or in steps. To enable skipmode set `skipmode = true` and to enable direct skip set `directskip = true`.
#### operation modes
The script supports two operation modes *speed mode* (default) and *skip mode*. In *skip mode* the video doesn't speed up, but it gets skipped (with delay) till the next subtitle. It can be configured to skip directly to the next subtitle or in steps. To switch between them press `alt + j`.
*skip mode* can be further configured to allow non-exact but fast skips. Disadvantage: it is likely that the the next subtitle gets skipped over. Therefore seek-back is implemented so the video gets seeked back just before the skip to aleviate this problem. In general leaving exact skips on (default) is recommended.
#### Key binds
Key Bind|Effect
--------|------
--------|--------
`ctrl + j`|Toggle script on/off
`ctrl + alt+ j`|Toggle skip mode
`alt + j`|Toggle sub visibility on/off (non-styled subs)
`alt + j`|switch modes (speed up/skip) - also enables script if disabled
`alt + '+'`|Increase speedup
`alt + '-'`|Decrease speedup
`alt + '0'`|Increase leadin (time before the next subtitle to return to normal speed)
`alt + '9'`|Decrease leadin
`alt + '8'`|Increase Look ahead (if the next subtitle is closer than this, don't speed up)
`alt + '7'`|Decrease Look ahead
`alt + '8'`|Increase lookahead (if the next subtitle is closer than this, don't speed up)
`alt + '7'`|Decrease lookahead
#### Options
Option|Description
--------|--------
lookahead|if the next subtitle appears after this threshold then speedup/skip
speedup|the video speed set during speedup
maxSkip|max. seconds to skip
directskip|directly skip to next subtitle
exact_skip|use slow but accurate skips (default)
These and more options can be set in a `speed_transition.conf` file in the `script-opts` folder.
## subselect
A lua script for downloading subtitles using a GUI and automatically loading them in mpv. It lets you input the name of the video but mainly tries to guess it based on the video title. Uses subliminal for subtitle download and Python tkinter for GUI. Works both on Windows and Linux (possibly macOS too?).
Right now it only lists subtitles from OpenSubtitles, but has the ability to search for the best subtitle which searches all subtitle providers.
### Prerequisits
1. Install Python 3
2. Make sure Python is in your PATH
3. Linux: Depending on the used distribution installation of `pip` and `tk` may be necessary
3. Install subliminal: `python -m pip install subliminal` should do the trick
### Installation
Copy the `subselect` folder containing `main.lua` and `subselect.py` into your script folder. If you have previous installations of subselect where it was not inside the subselect sub folder, remove them.
### Configuration
Changing the configuration is optional. Options:
* *down_dir*: set the download path for the subtitles
* *sub_language*: set language for subtitles [default english]; value is a 3-letter ISO-639-3 language code
@ -56,10 +78,12 @@ sub_language=deu
```
### Usage
First invoke the script using `alt + u`, input a movie name, or use the one provided by the script, search and download subtitles. If you want to change the language for the subtitles append `;[3-letter ISO-639-3 code]`. So if you want to search for e.g. german subtitles append `;deu`.
## delete-file
As the name suggests this is a small script for deleting files played through mpv. You can mark a file to be deleted and can unmark it if desired. Once quitting mpv, the script starts the deletion process. This is platform-agnostic so should work everywhere.
Key Bind|Effect
@ -74,10 +98,12 @@ MoveToFolder=yes
```
## copy-paste-URL
Like its name suggests - copy and paste links into mpv with `ctrl + v` to start playback of a video. This needs an open mpv window like `mpv --idle --force-window` or a window already playing a video. Also the script utilizes powershell, so that needs to be installed as well.
## censor
Skip over parts of videos you don't want (others) to view.
##download and installation
@ -85,7 +111,7 @@ Skip over parts of videos you don't want (others) to view.
download only "censor" folder and its contents.
put the "censor" folder(along with files in it i.e "names" folder and "main.lua") in the "scripts" folder inside mpv configuration folder.
**Usage:**
**Usage:**
imagine you want to skip the intro which begins on timestamp 0:2:30 and lasts until timestamp 0:4:00
your anime is called Attack on Titan - 03.mkv.
So you create a text file named Attack on Titan - 03.txt inside the names folder.

View File

@ -1,286 +1,536 @@
lookahead = 5 --if the next subtitle appears after this threshold then speedup
speedup = 2 --the value that "speed" is set to during speedup
leadin = 1 --seconds to stop short of the next subtitle
skipmode = false --instead of speeding up playback seek to the next known subtitle
maxSkip = 5 --max seek distance (seconds) when skipmode is enabled
minSkip = leadin --this is also configurable but setting it too low can actually make your watch time longer
skipdelay = 1 --in skip mode, this setting delays each skip by x seconds (must be >=0)
directskip = false --seek to next known subtitle (must be in cache) no matter how far away
dropOnAVdesync = true
--Because mpv syncs subtitles to audio it is possible that if audio processing lags behind--
--video processing then normal playback may not resume in sync with the video. If "avsync" > leadin--
--then this disables the audio so that we can ensure normal playback resumes on time.
ignorePattern = false --if true, subtitles are matched against "subPattern". A successful match will be treated as if there was no subtitle
subPattern = "^[#♯♩♪♬♫🎵🎶%[%(]+.*[#♯♩♪♬♫🎵🎶%]%)]+$"
---------------User options above this line--
local opt = require 'mp.options'
local msg = require 'mp.msg'
readahead_secs = mp.get_property_native("demuxer-readahead-secs")
normalspeed=mp.get_property_native("speed")
cfg = {
lookahead = 5, --if the next subtitle appears after this threshold then speedup
speedup = 2, --the value that 'speed' is set to during speedup
leadin = 1, --seconds to stop short of the next subtitle
sub_timeout = 5, --if a subtitle is visible for longer than this value, speedup begins; set to 0 to disable
skipmode = false, --instead of speeding up playback seek to the next known subtitle
maxSkip = 2.5, --max skip distance (seconds) when skipmode is enabled
minSkip = 1, --this is also configurable but setting it too low can actually make your watch time longer
skipdelay = 0.8, --in skip mode, this setting delays each skip by x seconds (must be >=0)
directskip = false, --seek to next known subtitle (must be in cache) no matter how far away
exact_skip = true, --use accurate but slow skips
--Because mpv syncs subtitles to audio it is possible that if audio processing lags behind--
--video processing then normal playback may not resume in sync with the video. If 'avsync' > leadin--
--then this disables the audio so that we can ensure normal playback resumes on time.
dropOnAVdesync = true,
ignorePattern = false, --if true, subtitles are matched against 'subPattern'. A successful match will be treated as if there was no subtitle
subPattern = '^[#♯♩♪♬♫🎵🎶%[%(]+.*[#♯♩♪♬♫🎵🎶%]%)]+$'
}
function shouldIgnore(subtext)
if ignorePattern and subtext and subtext~="" then
local st = subtext:match("^%s*(.-)%s*$") -- trim whitespace
if st:find(subPattern) then
return true
end
else
return false
end
end
opt.read_options(cfg)
function set_timeout()
local time_out
if mp.get_property_native("cache-size") ~= nil then
time_out = mp.get_property_native("cache-secs")
else
time_out = mp.get_property_native("demuxer-readahead-secs")
end
return time_out
end
local aid
function restore_normalspeed()
mp.set_property("speed", normalspeed)
if mp.get_property_native("video-sync") == "desync" then
mp.set_property("video-sync", "audio")
end
if (aid~=nil and aid~=mp.get_property("aid")) then mp.set_property("aid", aid) end
end
function check_should_speedup()
local subdelay = mp.get_property_native("sub-delay")
mp.command("no-osd set sub-visibility no")
mp.command("no-osd sub-step 1")
local mark = mp.get_property_native("time-pos")
local nextsubdelay = mp.get_property_native("sub-delay")
local nextsub = subdelay - nextsubdelay
if ignorePattern and nextsub > 0 then
local lookNext = true
local ignore = shouldIgnore(mp.get_property("sub-text"))
while ignore and lookNext do
local delay1 = mp.get_property_native("sub-delay")
mp.command("no-osd sub-step 1")
local delay2 = mp.get_property_native("sub-delay")
ignore = shouldIgnore(mp.get_property("sub-text"))
if delay1 == delay2 then
lookNext = false
nextsub = 0
else
nextsub = subdelay - delay2
end
end
end
mp.set_property("sub-delay", subdelay)
mp.command("no-osd set sub-visibility yes")
return nextsub, nextsub >= lookahead or nextsub == 0, mark
end
function check_audio(_,ds)
if state==0 then
return
elseif ds and tonumber(ds)>leadin and mp.get_property("aid")~="no" then
aid = mp.get_property("aid")
mp.set_property("aid", "no")
print("avsync greater than leadin, dropping audio")
end
end
function check_position(_, position)
if position then
if nextsub ~= 0 and position >= (mark+nextsub-leadin) then
restore_normalspeed()
mp.unobserve_property(check_position)
mp.unobserve_property(check_audio)
elseif nextsub == 0 and position >= (mark+set_timeout()-leadin) then
nextsub, _ , mark = check_should_speedup()
end
end
end
function skipval()
local skipval = mp.get_property_native("demuxer-cache-duration", 0)
if nextsub > 0 then
if directskip then
skipval = nextsub - leadin
elseif nextsub - skipval - leadin <= 0 then
skipval = clamp(nextsub - leadin, 0, maxSkip)
else
skipval = clamp(skipval, 0, maxSkip)
end
elseif directskip then
skipval = clamp(skipval - leadin, 1, nil)
else
skipval = clamp(skipval - leadin, 1, maxSkip)
end
return skipval
end
firstskip = true --make the first skip in skip mode not have to wait for skipdelay
delayEnd = true
function speed_transition(_, sub)
if sub~=nil and shouldIgnore(sub) then
sub = ""
end;
if state == 0 then
local subcodec = mp.get_property("current-tracks/sub/codec")
      if sub == "" and subcodec and (subcodec=="subrip" or subcodec=="ass" or subcodec=="webvtt") then
last_speedup_zone_begin = speedup_zone_begin
nextsub, shouldspeedup, speedup_zone_begin = check_should_speedup()
mark = speedup_zone_begin
speedup_zone_end = mark + nextsub
if shouldspeedup or (skipmode and not firstskip) then
local temp_disable_skipmode = false
if last_speedup_zone_begin and mark < last_speedup_zone_begin then
temp_disable_skipmode = true
end
if skipmode and not temp_disable_skipmode and mp.get_property("pause") == "no" then
if firstskip or skipdelay == 0 then
mp.commandv("no-osd", "seek", skipval(), "relative", "exact")
firstskip = false
elseif delayEnd then
delayEnd = false
mp.add_timeout(skipdelay, function()
delayEnd = true
if mp.get_property("pause") == "no" then
nextsub, shouldskip = check_should_speedup()
if shouldskip or nextsub > leadin then
local tSkip = skipval()
currentSub = mp.get_property("sub-text")
if tSkip > minSkip and (currentSub == "" or shouldIgnore(currentSub)) then
mp.commandv("no-osd", "seek", tSkip, "relative", "exact")
else
firstskip = true
end
end
end
end)
end
else
normalspeed = mp.get_property("speed")
if mp.get_property_native("video-sync") == "audio" then
mp.set_property("video-sync", "desync")
end
mp.set_property("speed", speedup)
mp.observe_property("time-pos", "native", check_position)
state = 1
if dropOnAVdesync then
aid = mp.get_property("aid")
mp.observe_property("avsync", "native", check_audio)
end
end
else
firstskip = true
end
end
elseif state == 1 then
if (sub ~= "" and sub ~= nil) or not mp.get_property_native("sid") then
mp.unobserve_property(check_position)
mp.unobserve_property(check_audio)
restore_normalspeed()
state = 0
else
local pos = mp.get_property_native("time-pos", 0)
if pos < speedup_zone_begin or pos > speedup_zone_end then
nextsub, _ , mark = check_should_speedup()
end
end
end
end
toggle2 = false
function toggle_sub_visibility()
if not toggle2 then
sub_color = mp.get_property("sub-color", "1/1/1/1")
sub_color2 = mp.get_property("sub-border-color", "0/0/0/1")
sub_color3 = mp.get_property("sub-shadow-color", "0/0/0/1")
mp.set_property("sub-color", "0/0/0/0")
mp.set_property("sub-border-color", "0/0/0/0")
mp.set_property("sub-shadow-color", "0/0/0/0")
else
mp.set_property("sub-color", sub_color)
mp.set_property("sub-border-color", sub_color2)
mp.set_property("sub-shadow-color", sub_color3)
end
mp.osd_message("subtitle visibility: "..tostring(toggle2))
toggle2 = not toggle2
end
function toggle_skipmode()
skipmode = not skipmode
if enable then
mp.unobserve_property(speed_transition)
mp.unobserve_property(check_position)
mp.observe_property("sub-text", "native", speed_transition)
state = 0
end
if skipmode then
mp.osd_message("skip mode")
else
mp.osd_message("speed mode")
end
end
function clamp(v,l,u)
if l and v < l then
v = l
elseif u and v > u then
v = u
end
return v
end
function change_speedup(v)
speedup = speedup + v
mp.osd_message("speedup: "..speedup)
end
function change_leadin(v)
--leadin = clamp(leadin + v, 0, 2)
leadin = clamp(leadin + v, 0, nil)
mp.osd_message("leadin: "..leadin)
end
function change_lookAhead(v)
lookahead = clamp(lookahead + v , 0, nil)
mp.osd_message("lookahead: "..lookahead)
end
readahead_secs = mp.get_property_native('demuxer-readahead-secs')
normalspeed = mp.get_property_native('speed')
enable = false
state = 0
firstskip = true --make the first skip in skip mode not have to wait for skipdelay
aid = nil
function shouldIgnore(subtext)
if cfg.ignorePattern and subtext and subtext ~= '' then
local st = subtext:match('^%s*(.-)%s*$') -- trim whitespace
if st:find(cfg.subPattern) then
return true
end
else
return false
end
end
function clamp(v, l, u)
if l and v < l then
v = l
elseif u and v > u then
v = u
end
return v
end
function formatTime(s)
if not s then
return nil
end
s = math.abs(s)
local _s = s % 60
s = s / 60
local m = math.floor(s % 60)
s = s / 60
local h = math.floor(s)
return string.format('%02d:%02d:%02f', h, m, _s)
end
function sleep(s)
local ntime = os.clock() + s
repeat until os.clock() > ntime
end
function reset_state()
nextsub, shouldspeedup, speedup_zone_begin, speedup_zone_end = nil, false, nil, nil
last_speedup_zone_begin = nil
last_skip_position = nil
last_nextsub_check = nil
firstskip = true
state = 0
end
function restore_normalspeed()
if not cfg.skipmode then
mp.set_property('speed', normalspeed)
if video_sync then
mp.set_property('video-sync', video_sync)
end
end
if aid and aid ~= mp.get_property('aid') then
mp.set_property('aid', aid)
end
end
function speed_up()
normalspeed = mp.get_property('speed')
video_sync = mp.get_property('video-sync')
mp.set_property('video-sync', 'desync')
mp.set_property('speed', cfg.speedup)
if cfg.dropOnAVdesync then
aid = mp.get_property('aid')
mp.observe_property('avsync', 'native', check_audio)
end
end
function skip(skipval)
if skipval < cfg.minSkip then
msg.warn('skip(): tskip < minSkip; abort!')
return
end
if cfg.exact_skip then
mp.commandv('seek', skipval, 'relative', 'exact')
else
mp.commandv('seek', skipval, 'relative')
end
end
function delayskip(position, skipdelay)
if not (firstskip or skipdelay == 0) then
sleep(skipdelay)
local tposition = mp.get_property_number('time-pos')
if not tposition then
position = position + skipdelay
else
position = tposition
end
end
firstskip = false
return position
end
function skipval(nextsub)
local demuxer_cache_duration = mp.get_property_number('demuxer-cache-duration', 0)
msg.trace('skipval()')
msg.trace(' demuxer_cache_duration:', demuxer_cache_duration)
msg.trace(' nextsub:', nextsub)
local skipval = demuxer_cache_duration * 0.8
if skipval == 0 or nextsub == 0 then
skipval = cfg.maxSkip
end
if nextsub > 0 then
if cfg.directskip then
skipval = clamp(nextsub - cfg.leadin, 0, nil)
elseif nextsub - cfg.leadin <= skipval then
skipval = clamp(nextsub - cfg.leadin, 0, skipval)
else
skipval = clamp(skipval, 0, clamp(skipval, 0, clamp(nextsub - cfg.leadin, 0, cfg.maxSkip)))
end
end
if skipval < cfg.minSkip then
skipval = 0
elseif skipval > cfg.maxSkip and not cfg.directskip then
skipval = cfg.maxSkip
end
msg.trace(' skipval:', skipval)
return skipval
end
function wait_finish_seeking()
repeat
local seeking = mp.get_property_bool('seeking')
until not seeking
end
function check_audio(_, ds)
if not ds or cfg.skipmode or state == 0 or cfg.leadin == 0 then
return
elseif (state == 1 or state == 3) and tonumber(ds) > cfg.leadin and mp.get_property('aid') ~= 'no' then
aid = mp.get_property('aid')
mp.set_property('aid', 'no')
msg.warn('avsync greater than leadin, dropping audio')
end
end
function check_should_speedup(subend)
local subspeed = mp.get_property_number('sub-speed', 1)
local subdelay = mp.get_property_number('sub-delay')
local substart = mp.get_property_number('sub-start')
subend = subend * subspeed + subdelay
if substart then
substart = substart * subspeed + subdelay
end
if cfg.sub_timeout > 0 and substart and substart < subend then
if subend - substart >= cfg.sub_timeout then
subend = substart + cfg.sub_timeout
end
end
local sub_visibility = mp.get_property_bool('sub-visibility')
if sub_visibility then
mp.set_property_bool('sub-visibility', false)
end
mp.commandv('sub-step', 1)
local nextsubstart = mp.get_property_number('sub-start')
if nextsubstart then
nextsubstart = nextsubstart * subspeed + subdelay
end
if cfg.ignorePattern and nextsubstart and subend < nextsubstart then
repeat
local ignore = shouldIgnore(mp.get_property('sub-text'))
if ignore then
local t_nextsubstart = mp.get_property_number('sub-end')
if t_nextsubstart then
t_nextsubstart = t_nextsubstart * subspeed + subdelay
end
if t_nextsubstart and t_nextsubstart > nextsubstart then
nextsubstart = t_nextsubstart
mp.commandv('sub-step', 1)
else
break
end
end
until not ignore
end
mp.set_property_number('sub-delay', subdelay)
if sub_visibility then
mp.set_property_bool('sub-visibility', true)
end
msg.trace('s-start,s-end,ns-start:', formatTime(substart), formatTime(subend), formatTime(nextsubstart))
local nextsub
if nextsubstart then
if subend < nextsubstart then
nextsub = nextsubstart - subend
end
end
if cfg.leadin > cfg.lookahead then
cfg.leadin = 0
end
local shouldspeedup = nextsub and nextsub >= cfg.lookahead - cfg.leadin
local speedup_begin = subend
if shouldspeedup then
msg.debug('check_should_speedup()')
msg.debug(' shouldspeedup:', tostring(shouldspeedup))
msg.debug(' speedup_begin:', formatTime(speedup_begin) or '')
msg.debug(' nextsub:', nextsub or '')
end
return nextsub, shouldspeedup, speedup_begin
end
function check_position(_, position)
if position then
if state == 0 and speedup_zone_begin and position >= speedup_zone_begin and shouldspeedup then
if cfg.skipmode then
msg.debug('check_position[0] -> [2]')
msg.debug(' position:', formatTime(position))
firstskip = true
state = 2
else
msg.debug('check_position[0] -> [1]')
msg.debug(' position:', formatTime(position))
speed_up()
state = 1
end
msg.debug(' speedup_zone_begin:', formatTime(speedup_zone_begin))
msg.debug(' speedup_zone_end:', formatTime(speedup_zone_end))
elseif state == 0 and not nextsub and last_speedup_zone_begin and position - last_speedup_zone_begin > 2 then
msg.debug('check_position[0] -> [3]')
msg.debug(' position:', formatTime(position))
if not cfg.skipmode then
speed_up()
end
last_speedup_zone_begin = nil
last_nextsub_check = position
speedup_zone_begin = position
speedup_zone_end = nil
firstskip = true
state = 3
elseif state == 1 and speedup_zone_end and position >= speedup_zone_end then
restore_normalspeed()
reset_state()
msg.debug('check_position[1] -> [0]')
msg.debug(' position:', formatTime(position))
elseif state == 2 then
-- msg.debug('check_position[2]')
-- msg.debug(' position:', formatTime(position))
if speedup_zone_end and position >= speedup_zone_end then
msg.debug('check_position[2] -> [0] pos >= end')
msg.debug(' position:', formatTime(position))
msg.debug(' speedup_zone_end:', formatTime(speedup_zone_end))
if not cfg.exact_skip and last_skip_position and position > speedup_zone_end then
msg.debug(' ->seek back to:', formatTime(last_skip_position))
wait_finish_seeking()
mp.set_property_number('time-pos', last_skip_position)
end
reset_state()
elseif speedup_zone_begin <= position and position < speedup_zone_end then
if mp.get_property('pause') == 'no' then
local position_after_skipdelay = position
wait_finish_seeking()
if position + cfg.skipdelay < speedup_zone_end then
position_after_skipdelay = delayskip(position, cfg.skipdelay)
end
local nextsub = speedup_zone_end - position_after_skipdelay
local tSkip = 0
if nextsub > 0 then
tSkip = skipval(nextsub)
if position_after_skipdelay + tSkip >= speedup_zone_end then
if speedup_zone_end - position_after_skipdelay >= cfg.minSkip then
wait_finish_seeking()
mp.set_property_number('time-pos', speedup_zone_end)
msg.debug('check_position[2]')
msg.debug(' position:', formatTime(position_after_skipdelay))
msg.debug(' nextsub:', nextsub)
msg.debug(' direct skip to:', formatTime(speedup_zone_end))
reset_state()
end
elseif tSkip >= cfg.minSkip then
local seeking = mp.get_property_bool('seeking')
if not seeking then
last_skip_position = position_after_skipdelay
skip(tSkip)
msg.debug('check_position[2]')
msg.debug(' position:', formatTime(position_after_skipdelay))
msg.debug(' nextsub:', nextsub)
msg.debug(' skipval:', tSkip)
end
end
elseif nextsub < 0 and not cfg.exact_skip then
local cursubend = mp.get_property_number('sub-end')
local margin = 0.5
if cursubend and cursubend > speedup_zone_end + cfg.leadin then
margin = clamp((cursubend - (speedup_zone_end + cfg.leadin)) * 0.35, 0, 1)
end
if position_after_skipdelay > speedup_zone_end + cfg.leadin + margin then
wait_finish_seeking()
mp.set_property_number('time-pos', speedup_zone_end)
msg.debug('check_position[2]')
msg.debug(' position:', formatTime(position_after_skipdelay))
msg.debug(' nextsub:', nextsub)
msg.debug(' skipval:', tSkip)
msg.debug(' margin:', margin)
msg.debug(' ->seek back to: ' .. formatTime(speedup_zone_end))
end
reset_state()
else
reset_state()
end
end
end
elseif state == 3 then
if position - last_nextsub_check > 0.5 then
local t_nextsub, t_shouldspeedup, t_speedup_zone_begin = check_should_speedup(position)
if t_nextsub then
msg.debug('check_position[3]')
msg.debug(' position:', formatTime(position))
msg.debug(' ->found next sub')
if not t_shouldspeedup then
msg.debug(' ->stop speedup')
msg.debug(' [3] -> [0]')
restore_normalspeed()
reset_state()
return
else
nextsub, shouldspeedup = t_nextsub, t_shouldspeedup
speedup_zone_end = t_speedup_zone_begin + nextsub - cfg.leadin
if cfg.skipmode then
msg.debug('check_position[3] -> [2]')
state = 2
return
else
msg.debug('check_position[3] -> [1]')
state = 1
last_nextsub_check = position
return
end
end
end
last_nextsub_check = position
end
if cfg.skipmode then
local seeking = mp.get_property_bool('seeking')
if mp.get_property('pause') == 'no' and not seeking then
local tlast_skip_position = position
position = delayskip(position, cfg.skipdelay)
local tSkip = skipval(0)
if tSkip >= cfg.minSkip then
last_skip_position = tlast_skip_position
skip(tSkip)
msg.debug('check_position[3]')
msg.debug(' position:', formatTime(position))
msg.debug(' nextsub: ---')
msg.debug(' skipval:', tSkip)
end
end
end
else
end
end
end
function speed_transition(_, subend)
if not subend then
return
end
msg.debug('speed_transition()')
if state == 3 or (state == 2 and not cfg.exact_skip) then
msg.debug(' state >= 2: check seek back / reset')
local position = mp.get_property_number('time-pos')
if cfg.skipmode and last_skip_position then
msg.debug(' position:', formatTime(position))
msg.debug(' ->seek back to:', formatTime(last_skip_position))
wait_finish_seeking()
mp.set_property_number('time-pos', last_skip_position)
reset_state()
return
end
restore_normalspeed()
reset_state()
end
local t_nextsub, t_shouldspeedup, t_speedup_zone_begin = check_should_speedup(subend)
if t_shouldspeedup then
if state ~= 0 then
msg.debug(' ->reset: state > 0')
restore_normalspeed()
reset_state()
end
nextsub, shouldspeedup, speedup_zone_begin = t_nextsub, t_shouldspeedup, t_speedup_zone_begin
speedup_zone_end = speedup_zone_begin + nextsub - cfg.leadin
msg.debug(' speedup_zone_end:', formatTime(speedup_zone_end) or '')
else
if state ~= 0 then
msg.debug(' ->reset: state > 0')
restore_normalspeed()
end
reset_state()
end
last_speedup_zone_begin = t_speedup_zone_begin
end
function toggle()
if not enable then
normalspeed = mp.get_property("speed")
mp.set_property("demuxer-readahead-secs",lookahead+leadin)
mp.observe_property("sub-text", "native", speed_transition)
mp.osd_message("speed-transition enabled")
else
restore_normalspeed()
mp.set_property("demuxer-readahead-secs",readahead_secs)
mp.unobserve_property(speed_transition)
mp.unobserve_property(check_position)
mp.osd_message("speed-transition disabled")
end
state = 0
enable = not enable
if not enable then
normalspeed = mp.get_property('speed')
local calculated_readaheadsecs = math.max(5, readahead_secs, cfg.maxSkip + cfg.leadin,
cfg.lookahead + cfg.leadin)
if readahead_secs < calculated_readaheadsecs then
mp.set_property('demuxer-readahead-secs', calculated_readaheadsecs)
end
last_speedup_zone_begin = mp.get_property_number('time-pos')
mp.observe_property('sub-end', 'number', speed_transition)
mp.observe_property('time-pos', 'number', check_position)
mp.osd_message('speed-transition enabled')
msg.info('enabled')
else
restore_normalspeed()
reset_state()
mp.set_property('demuxer-readahead-secs', readahead_secs)
mp.unobserve_property(speed_transition)
mp.unobserve_property(check_position)
mp.unobserve_property(check_audio)
mp.osd_message('speed-transition disabled')
msg.info('disabled')
end
state = 0
enable = not enable
end
function switch_mode()
cfg.skipmode = not cfg.skipmode
if not enable then
toggle()
end
if cfg.skipmode then
if state == 1 or state == 3 then
if state == 1 then
state = 2
end
mp.set_property('speed', normalspeed)
end
mp.osd_message('skip mode')
msg.info('skip mode')
else
if state == 2 or state == 3 then
if state == 2 then
state = 1
end
speed_up()
end
mp.osd_message('speed mode')
msg.info('speed mode')
end
end
function reset_on_file_load()
if state == 1 then
mp.unobserve_property(check_position)
restore_normalspeed()
state = 0
end
restore_normalspeed()
reset_state()
end
mp.add_key_binding("ctrl+j", "toggle_speedtrans", toggle)
mp.add_key_binding("alt+j", "toggle_sub_visibility", toggle_sub_visibility)
mp.add_key_binding("ctrl+alt+j", "toggle_skipmode", toggle_skipmode)
mp.add_key_binding("alt++", "increase_speedup", function() change_speedup(0.1) end, {repeatable=true})
mp.add_key_binding("alt+-", "decrease_speedup", function() change_speedup(-0.1) end, {repeatable=true})
mp.add_key_binding("alt+0", "increase_leadin", function() change_leadin(0.25) end)
mp.add_key_binding("alt+9", "decrease_leadin", function() change_leadin(-0.25) end)
mp.add_key_binding("alt+8", "increase_lookahead", function() change_lookAhead(0.25) end)
mp.add_key_binding("alt+7", "decrease_lookahead", function() change_lookAhead(-0.25) end)
mp.register_event("file-loaded", reset_on_file_load)
function change_speedup(v)
cfg.speedup = cfg.speedup + v
mp.osd_message('speedup: ' .. cfg.speedup)
msg.info('speedup:', cfg.speedup)
end
function change_leadin(v)
cfg.leadin = clamp(cfg.leadin + v, 0, 2)
mp.osd_message('leadin: ' .. cfg.leadin)
msg.info('leadin:', cfg.leadin)
end
function change_lookAhead(v)
cfg.lookahead = clamp(cfg.lookahead + v, 0, nil)
mp.osd_message('lookahead: ' .. cfg.lookahead)
msg.info('lookahead:', cfg.lookahead)
end
mp.add_key_binding('ctrl+j', 'toggle_speedtrans', toggle)
mp.add_key_binding('alt+j', 'switch_mode', switch_mode)
mp.add_key_binding('alt++', 'increase_speedup', function() change_speedup(0.1) end, { repeatable = true })
mp.add_key_binding('alt+-', 'decrease_speedup', function() change_speedup(-0.1) end, { repeatable = true })
mp.add_key_binding('alt+0', 'increase_leadin', function() change_leadin(0.25) end)
mp.add_key_binding('alt+9', 'decrease_leadin', function() change_leadin(-0.25) end)
mp.add_key_binding('alt+8', 'increase_lookahead', function() change_lookAhead(0.25) end)
mp.add_key_binding('alt+7', 'decrease_lookahead', function() change_lookAhead(-0.25) end)
mp.register_event('file-loaded', reset_on_file_load)