-- Source: https://github.com/dexeonify/mpv-config/blob/main/scripts/seek-to.lua -- with modifications local assdraw = require 'mp.assdraw' local utils = require 'mp.utils' local msg = require 'mp.msg' local active = false local cursor_position = 1 local time_scale = {60*60*10, 60*60, 60*10, 60, 10, 1, 0.1, 0.01, 0.001} local ass_begin = mp.get_property("osd-ass-cc/0") local ass_end = mp.get_property("osd-ass-cc/1") local history = { {} } for i = 1, 9 do history[1][i] = 0 end local history_position = 1 -- timer to redraw periodically the message -- to avoid leaving bindings when the seeker disappears for whatever reason -- pretty hacky tbh local timer = nil local timer_duration = 3 function show_seeker() local prepend_char = {'','',':','',':','','.','',''} local str = '' for i = 1, 9 do str = str .. prepend_char[i] if i == cursor_position then str = str .. '{\\b1}' .. history[history_position][i] .. '{\\r}' else str = str .. history[history_position][i] end end mp.osd_message("Seek to: " .. ass_begin .. str .. ass_end, timer_duration) end function copy_history_to_last() if history_position ~= #history then for i = 1, 9 do history[#history][i] = history[history_position][i] end history_position = #history end end function change_number(i) -- can't set above 60 minutes or seconds if (cursor_position == 3 or cursor_position == 5) and i >= 6 then return end if history[history_position][cursor_position] ~= i then copy_history_to_last() history[#history][cursor_position] = i end shift_cursor(false) end function shift_cursor(left) if left then cursor_position = math.max(1, cursor_position - 1) else cursor_position = math.min(cursor_position + 1, 9) end end function current_time_as_sec(time) local sec = 0 for i = 1, 9 do sec = sec + time_scale[i] * time[i] end return sec end function time_equal(lhs, rhs) for i = 1, 9 do if lhs[i] ~= rhs[i] then return false end end return true end function seek_to() copy_history_to_last() mp.commandv("osd-bar", "seek", current_time_as_sec(history[history_position]), "absolute") --deduplicate consecutive timestamps if #history == 1 or not time_equal(history[history_position], history[#history - 1]) then history[#history + 1] = {} history_position = #history end for i = 1, 9 do history[#history][i] = 0 end end function backspace() if history[history_position][cursor_position] ~= 0 then copy_history_to_last() history[#history][cursor_position] = 0 end shift_cursor(true) end function history_move(up) if up then history_position = math.max(1, history_position - 1) else history_position = math.min(history_position + 1, #history) end end local key_mappings = { LEFT = function() shift_cursor(true) show_seeker() end, RIGHT = function() shift_cursor(false) show_seeker() end, UP = function() history_move(true) show_seeker() end, DOWN = function() history_move(false) show_seeker() end, BS = function() backspace() show_seeker() end, ESC = function() set_inactive() end, ENTER = function() seek_to() set_inactive() end } for i = 0, 9 do local func = function() change_number(i) show_seeker() end key_mappings[string.format("KP%d", i)] = func key_mappings[string.format("%d", i)] = func end function set_active() if not mp.get_property("seekable") then return end -- find duration of the video and set cursor position accordingly local duration = mp.get_property_number("duration") if duration ~= nil then for i = 1, 9 do if duration > time_scale[i] then cursor_position = i break end end end for key, func in pairs(key_mappings) do mp.add_forced_key_binding(key, "seek-to-"..key, func) end show_seeker() timer = mp.add_periodic_timer(timer_duration, show_seeker) active = true end function set_inactive() mp.osd_message("") for key, _ in pairs(key_mappings) do mp.remove_key_binding("seek-to-"..key) end timer:kill() active = false end function paste_timestamp() -- get clipboard data local clipboard = utils.subprocess({ args = { "powershell", "-Command", "Get-Clipboard", "-Raw" }, playback_only = false, capture_stdout = true, capture_stderr = true }) -- error handling if not clipboard.error then timestamp = clipboard.stdout else msg.error("Error getting data from clipboard:") msg.error(" stderr: " .. clipboard.stderr) msg.error(" stdout: " .. clipboard.stdout) return end -- find timestamp from clipboard match = timestamp:match("%d?%d?:?%d%d:%d%d%.?%d*") -- paste and seek to timestamp if match ~= nil then mp.osd_message("Timestamp pasted: " .. match) mp.commandv("osd-bar", "seek", match, "absolute") else msg.warn("No pastable timestamp found!") end end -- keybindings are set in input.conf mp.add_key_binding(nil, "toggle-seeker", function() if active then set_inactive() else set_active() end end) mp.add_key_binding(nil, "paste-timestamp", paste_timestamp)