1026 lines
35 KiB
Lua
1026 lines
35 KiB
Lua
|
local msg = require('mp.msg')
|
||
|
local log = {
|
||
|
debug = function(format, ...)
|
||
|
return msg.debug(format:format(...))
|
||
|
end,
|
||
|
info = function(format, ...)
|
||
|
return msg.info(format:format(...))
|
||
|
end,
|
||
|
warn = function(format, ...)
|
||
|
return msg.warn(format:format(...))
|
||
|
end,
|
||
|
dump = function(item, ignore)
|
||
|
local level = 2
|
||
|
if "table" ~= type(item) then
|
||
|
msg.info(tostring(item))
|
||
|
return
|
||
|
end
|
||
|
local count = 1
|
||
|
local tablecount = 1
|
||
|
local result = {
|
||
|
"{ @" .. tostring(tablecount)
|
||
|
}
|
||
|
local seen = {
|
||
|
[item] = tablecount
|
||
|
}
|
||
|
local recurse
|
||
|
recurse = function(item, space)
|
||
|
for key, value in pairs(item) do
|
||
|
if not (key == ignore) then
|
||
|
if "table" == type(value) then
|
||
|
if not (seen[value]) then
|
||
|
tablecount = tablecount + 1
|
||
|
seen[value] = tablecount
|
||
|
count = count + 1
|
||
|
result[count] = space .. tostring(key) .. ": { @" .. tostring(tablecount)
|
||
|
recurse(value, space .. " ")
|
||
|
count = count + 1
|
||
|
result[count] = space .. "}"
|
||
|
else
|
||
|
count = count + 1
|
||
|
result[count] = space .. tostring(key) .. ": @" .. tostring(seen[value])
|
||
|
end
|
||
|
else
|
||
|
if "string" == type(value) then
|
||
|
value = ("%q"):format(value)
|
||
|
end
|
||
|
count = count + 1
|
||
|
result[count] = space .. tostring(key) .. ": " .. tostring(value)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
recurse(item, " ")
|
||
|
count = count + 1
|
||
|
result[count] = "}"
|
||
|
return msg.info(table.concat(result, "\n"))
|
||
|
end
|
||
|
}
|
||
|
local options = require('mp.options')
|
||
|
local utils = require('mp.utils')
|
||
|
local script_name = 'torque-progressbar'
|
||
|
mp.get_osd_size = mp.get_osd_size or mp.get_screen_size
|
||
|
local settings = {
|
||
|
__defaults = { }
|
||
|
}
|
||
|
local settingsMeta = {
|
||
|
__reload = function(self)
|
||
|
for key, value in pairs(self.__defaults) do
|
||
|
settings[key] = value
|
||
|
end
|
||
|
options.read_options(self, script_name .. '/main')
|
||
|
if self['bar-height-inactive'] <= 0 then
|
||
|
self['bar-hide-inactive'] = true
|
||
|
self['bar-height-inactive'] = 1
|
||
|
end
|
||
|
end,
|
||
|
__migrate = function(self)
|
||
|
local oldConfig = mp.find_config_file(('lua-settings/%s.conf'):format(script_name))
|
||
|
local newConfigFile = ('lua-settings/%s/main.conf'):format(script_name)
|
||
|
local newConfig = mp.find_config_file(newConfigFile)
|
||
|
if oldConfig and not newConfig then
|
||
|
local folder, _ = utils.split_path(oldConfig)
|
||
|
local configDir = utils.join_path(folder, script_name)
|
||
|
newConfig = utils.join_path(configDir, 'main.conf')
|
||
|
log.info(('Old configuration detected. Attempting to migrate %q -> %q'):format(oldConfig, newConfig))
|
||
|
local dirExists = mp.find_config_file(configDir)
|
||
|
if dirExists and not utils.readdir(configDir) then
|
||
|
log.warn(('Configuration migration failed. %q exists and does not appear to be a folder'):format(configDir))
|
||
|
return
|
||
|
else
|
||
|
if not dirExists then
|
||
|
local res = utils.subprocess({
|
||
|
args = {
|
||
|
'mkdir',
|
||
|
configDir
|
||
|
}
|
||
|
})
|
||
|
if res.error or res.status ~= 0 then
|
||
|
log.warn(('Making directory %q failed.'):format(configDir))
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
local res = utils.subprocess({
|
||
|
args = {
|
||
|
'mv',
|
||
|
oldConfig,
|
||
|
newConfig
|
||
|
}
|
||
|
})
|
||
|
if res.error or res.status ~= 0 then
|
||
|
log.warn(('Moving file %q -> %q failed.'):format(oldConfig, newConfig))
|
||
|
return
|
||
|
end
|
||
|
if mp.find_config_file(newConfigFile) then
|
||
|
return log.info('Configuration successfully migrated.')
|
||
|
end
|
||
|
end
|
||
|
end,
|
||
|
__newindex = function(self, key, value)
|
||
|
self.__defaults[key] = value
|
||
|
return rawset(self, key, value)
|
||
|
end
|
||
|
}
|
||
|
settingsMeta.__index = settingsMeta
|
||
|
setmetatable(settings, settingsMeta)
|
||
|
settings:__migrate()
|
||
|
local helpText = { }
|
||
|
settings['hover-zone-height'] = 40
|
||
|
helpText['hover-zone-height'] = [[Sets the height of the rectangular area at the bottom of the screen that expands
|
||
|
the progress bar and shows playback time information when the mouse is hovered
|
||
|
over it.
|
||
|
]]
|
||
|
settings['top-hover-zone-height'] = 40
|
||
|
helpText['top-hover-zone-height'] = [[Sets the height of the rectangular area at the top of the screen that shows the
|
||
|
file name and system time when the mouse is hovered over it.
|
||
|
]]
|
||
|
settings['display-scale-factor'] = 1
|
||
|
helpText['display-scale-factor'] = [[Acts as a multiplier to increase the size of every UI element. Useful for high-
|
||
|
DPI displays that cause the UI to be rendered too small (happens at least on
|
||
|
macOS).
|
||
|
]]
|
||
|
settings['default-style'] = [[\fnSource Sans Pro\b1\bord2\shad0\fs30\c&HFC799E&\3c&H2D2D2D&]]
|
||
|
helpText['default-style'] = [[Default style that is applied to all UI elements. A string of ASS override tags.
|
||
|
Individual elements have their own style settings which override the tags here.
|
||
|
Changing the font will likely require changing the hover-time margin settings
|
||
|
and the offscreen-pos settings.
|
||
|
|
||
|
Here are some useful ASS override tags (omit square brackets):
|
||
|
\fn[Font Name]: sets the font to the named font.
|
||
|
\fs[number]: sets the font size to the given number.
|
||
|
\b[1/0]: sets the text bold or not (\b1 is bold, \b0 is regular weight).
|
||
|
\i[1/0]: sets the text italic or not (same semantics as bold).
|
||
|
\bord[number]: sets the outline width to the given number (in pixels).
|
||
|
\shad[number]: sets the shadow size to the given number (pixels).
|
||
|
\c&H[BBGGRR]&: sets the fill color for the text to the given color (hex pairs in
|
||
|
the order, blue, green, red).
|
||
|
\3c&H[BBGGRR]&: sets the outline color of the text to the given color.
|
||
|
\4c&H[BBGGRR]&: sets the shadow color of the text to the given color.
|
||
|
\alpha&H[AA]&: sets the line's transparency as a hex pair. 00 is fully opaque
|
||
|
and FF is fully transparent. Some UI elements are composed of
|
||
|
multiple layered lines, so adding transparency may not look good.
|
||
|
For further granularity, \1a&H[AA]& controls the fill opacity,
|
||
|
\3a&H[AA]& controls the outline opacity, and \4a&H[AA]& controls
|
||
|
the shadow opacity.
|
||
|
]]
|
||
|
settings['enable-bar'] = true
|
||
|
helpText['enable-bar'] = [[Controls whether or not the progress bar is drawn at all. If this is disabled,
|
||
|
it also (naturally) disables the click-to-seek functionality.
|
||
|
]]
|
||
|
settings['bar-hide-inactive'] = false
|
||
|
helpText['bar-hide-inactive'] = [[Causes the bar to not be drawn unless the mouse is hovering over it or a
|
||
|
request-display call is active. This is somewhat redundant with setting bar-
|
||
|
height-inactive=0, except that it can allow for very rudimentary context-
|
||
|
sensitive behavior because it can be toggled at runtime. For example, by using
|
||
|
the binding `f cycle pause; script-binding progressbar/toggle-inactive-bar`, it
|
||
|
is possible to have the bar be persistently present only in windowed or
|
||
|
fullscreen contexts, depending on the default setting.
|
||
|
]]
|
||
|
settings['bar-height-inactive'] = 2
|
||
|
helpText['bar-height-inactive'] = [[Sets the height of the bar display when the mouse is not in the active zone and
|
||
|
there is no request-display active. A value of 0 or less will cause bar-hide-
|
||
|
inactive to be set to true and the bar height to be set to 1. This should result
|
||
|
in the desired behavior while avoiding annoying debug logging in mpv (libass
|
||
|
does not like zero-height objects).
|
||
|
]]
|
||
|
settings['bar-height-active'] = 8
|
||
|
helpText['bar-height-active'] = [[Sets the height of the bar display when the mouse is in the active zone or
|
||
|
request-display is active. There is no logic attached to this, so 0 or negative
|
||
|
values may have unexpected results.
|
||
|
]]
|
||
|
settings['progress-bar-width'] = 0
|
||
|
helpText['progress-bar-width'] = [[If greater than zero, changes the progress bar style to be a small segment
|
||
|
rather than a continuous bar and sets its width.
|
||
|
]]
|
||
|
settings['seek-precision'] = 'exact'
|
||
|
helpText['seek-precision'] = [[Affects precision of seeks due to clicks on the progress bar. Should be 'exact' or
|
||
|
'keyframes'. Exact is slightly slower, but won't jump around between two
|
||
|
different times when clicking in the same place.
|
||
|
|
||
|
Actually, this gets passed directly into the `seek` command, so the value can be
|
||
|
any of the arguments supported by mpv, though the ones above are the only ones
|
||
|
that really make sense.
|
||
|
]]
|
||
|
settings['bar-default-style'] = [[\bord0\shad0]]
|
||
|
helpText['bar-default-style'] = [[A string of ASS override tags that get applied to all three layers of the bar:
|
||
|
progress, cache, and background. You probably don't want to remove \bord0 unless
|
||
|
your default-style includes it.
|
||
|
]]
|
||
|
settings['bar-foreground-style'] = ''
|
||
|
helpText['bar-foreground-style'] = [[A string of ASS override tags that get applied only to the progress layer of the
|
||
|
bar.
|
||
|
]]
|
||
|
settings['bar-cache-style'] = [[\c&H515151&]]
|
||
|
helpText['bar-cache-style'] = [[A string of ASS override tags that get applied only to the cache layer of the
|
||
|
bar. The default sets only the color.
|
||
|
]]
|
||
|
settings['bar-background-style'] = [[\c&H2D2D2D&]]
|
||
|
helpText['bar-background-style'] = [[A string of ASS override tags that get applied only to the background layer of
|
||
|
the bar. The default sets only the color.
|
||
|
]]
|
||
|
settings['enable-elapsed-time'] = true
|
||
|
helpText['enable-elapsed-time'] = [[Sets whether or not the elapsed time is displayed at all.
|
||
|
]]
|
||
|
settings['elapsed-style'] = ''
|
||
|
helpText['elapsed-style'] = [[A string of ASS override tags that get applied only to the elapsed time display.
|
||
|
]]
|
||
|
settings['elapsed-left-margin'] = 4
|
||
|
helpText['elapsed-left-margin'] = [[Controls how far from the left edge of the window the elapsed time display is
|
||
|
positioned.
|
||
|
]]
|
||
|
settings['elapsed-bottom-margin'] = 0
|
||
|
helpText['elapsed-bottom-margin'] = [[Controls how far above the expanded progress bar the elapsed time display is
|
||
|
positioned.
|
||
|
]]
|
||
|
settings['enable-remaining-time'] = true
|
||
|
helpText['enable-remaining-time'] = [[Sets whether or not the remaining time is displayed at all.
|
||
|
]]
|
||
|
settings['remaining-style'] = ''
|
||
|
helpText['remaining-style'] = [[A string of ASS override tags that get applied only to the remaining time
|
||
|
display.
|
||
|
]]
|
||
|
settings['remaining-right-margin'] = 4
|
||
|
helpText['remaining-right-margin'] = [[Controls how far from the right edge of the window the remaining time display is
|
||
|
positioned.
|
||
|
]]
|
||
|
settings['remaining-bottom-margin'] = 0
|
||
|
helpText['remaining-bottom-margin'] = [[Controls how far above the expanded progress bar the remaining time display is
|
||
|
positioned.
|
||
|
]]
|
||
|
settings['enable-hover-time'] = true
|
||
|
helpText['enable-hover-time'] = [[Sets whether or not the calculated time corresponding to the mouse position
|
||
|
is displayed when the mouse hovers over the progress bar.
|
||
|
]]
|
||
|
settings['hover-time-style'] = [[\fs26]]
|
||
|
helpText['hover-time-style'] = [[A string of ASS override tags that get applied only to the hover time display.
|
||
|
Unfortunately, due to the way the hover time display is animated, alpha values
|
||
|
set here will be overridden. This is subject to change in future versions.
|
||
|
]]
|
||
|
settings['hover-time-left-margin'] = 120
|
||
|
helpText['hover-time-left-margin'] = [[Controls how close to the left edge of the window the hover time display can
|
||
|
get. If this value is too small, it will end up overlapping the elapsed time
|
||
|
display.
|
||
|
]]
|
||
|
settings['hover-time-right-margin'] = 130
|
||
|
helpText['hover-time-right-margin'] = [[Controls how close to the right edge of the window the hover time display can
|
||
|
get. If this value is too small, it will end up overlapping the remaining time
|
||
|
display.
|
||
|
]]
|
||
|
settings['hover-time-bottom-margin'] = 0
|
||
|
helpText['hover-time-bottom-margin'] = [[Controls how far above the expanded progress bar the remaining time display is
|
||
|
positioned.
|
||
|
]]
|
||
|
settings['enable-title'] = true
|
||
|
helpText['enable-title'] = [[Sets whether or not the video title is displayed at all.
|
||
|
]]
|
||
|
settings['title-style'] = ''
|
||
|
helpText['title-style'] = [[A string of ASS override tags that get applied only to the video title display.
|
||
|
]]
|
||
|
settings['title-left-margin'] = 4
|
||
|
helpText['title-left-margin'] = [[Controls how far from the left edge of the window the video title display is
|
||
|
positioned.
|
||
|
]]
|
||
|
settings['title-top-margin'] = 0
|
||
|
helpText['title-top-margin'] = [[Controls how far from the top edge of the window the video title display is
|
||
|
positioned.
|
||
|
]]
|
||
|
settings['title-print-to-cli'] = true
|
||
|
helpText['title-print-to-cli'] = [[Controls whether or not the script logs the video title and playlist position
|
||
|
to the console every time a new video starts.
|
||
|
]]
|
||
|
settings['enable-system-time'] = true
|
||
|
helpText['enable-system-time'] = [[Sets whether or not the system time is displayed at all.
|
||
|
]]
|
||
|
settings['system-time-style'] = ''
|
||
|
helpText['system-time-style'] = [[A string of ASS override tags that get applied only to the system time display.
|
||
|
]]
|
||
|
settings['system-time-format'] = '%H:%M'
|
||
|
helpText['system-time-format'] = [[Sets the format used for the system time display. This must be a strftime-
|
||
|
compatible format string.
|
||
|
]]
|
||
|
settings['system-time-right-margin'] = 4
|
||
|
helpText['system-time-right-margin'] = [[Controls how far from the right edge of the window the system time display is
|
||
|
positioned.
|
||
|
]]
|
||
|
settings['system-time-top-margin'] = 0
|
||
|
helpText['system-time-top-margin'] = [[Controls how far from the top edge of the window the system time display is
|
||
|
positioned.
|
||
|
]]
|
||
|
settings['pause-indicator'] = true
|
||
|
helpText['pause-indicator'] = [[Sets whether or not the pause indicator is displayed. The pause indicator is a
|
||
|
momentary icon that flashes in the middle of the screen, similar to youtube.
|
||
|
]]
|
||
|
settings['pause-indicator-foreground-style'] = [[\c&HFC799E&]]
|
||
|
helpText['pause-indicator-foreground-style'] = [[A string of ASS override tags that get applied only to the foreground of the
|
||
|
pause indicator.
|
||
|
]]
|
||
|
settings['pause-indicator-background-style'] = [[\c&H2D2D2D&]]
|
||
|
helpText['pause-indicator-background-style'] = [[A string of ASS override tags that get applied only to the background of the
|
||
|
pause indicator.
|
||
|
]]
|
||
|
settings['enable-chapter-markers'] = true
|
||
|
helpText['enable-chapter-markers'] = [[Sets whether or not the progress bar is decorated with chapter markers. Due to
|
||
|
the way the chapter markers are currently implemented, videos with a large
|
||
|
number of chapters may slow down the script somewhat, but I have yet to run
|
||
|
into this being a problem.
|
||
|
]]
|
||
|
settings['chapter-marker-width'] = 2
|
||
|
helpText['chapter-marker-width'] = [[Controls the width of each chapter marker when the progress bar is inactive.
|
||
|
]]
|
||
|
settings['chapter-marker-width-active'] = 4
|
||
|
helpText['chapter-marker-width-active'] = [[Controls the width of each chapter marker when the progress bar is active.
|
||
|
]]
|
||
|
settings['chapter-marker-active-height-fraction'] = 1
|
||
|
helpText['chapter-marker-active-height-fraction'] = [[Modifies the height of the chapter markers when the progress bar is active. Acts
|
||
|
as a multiplier on the height of the active progress bar. A value greater than 1
|
||
|
will cause the markers to be taller than the expanded progress bar, whereas a
|
||
|
value less than 1 will cause them to be shorter.
|
||
|
]]
|
||
|
settings['chapter-marker-before-style'] = [[\c&HFC799E&]]
|
||
|
helpText['chapter-marker-before-style'] = [[A string of ASS override tags that get applied only to chapter markers that have
|
||
|
not yet been passed.
|
||
|
]]
|
||
|
settings['chapter-marker-after-style'] = [[\c&H2D2D2D&]]
|
||
|
helpText['chapter-marker-after-style'] = [[A string of ASS override tags that get applied only to chapter markers that have
|
||
|
already been passed.
|
||
|
]]
|
||
|
settings['request-display-duration'] = 1
|
||
|
helpText['request-display-duration'] = [[Sets the amount of time in seconds that the UI stays on the screen after it
|
||
|
receives a request-display signal. A value of 0 will keep the display on screen
|
||
|
only as long as the key bound to it is held down.
|
||
|
]]
|
||
|
settings['redraw-period'] = 0.03
|
||
|
helpText['redraw-period'] = [[Controls how often the display is redrawn, in seconds. This does not seem to
|
||
|
significantly affect the smoothness of animations, and it is subject to the
|
||
|
accuracy limits imposed by the scheduler mpv uses. Probably not worth changing
|
||
|
unless you have major performance problems.
|
||
|
]]
|
||
|
settings['animation-duration'] = 0.25
|
||
|
helpText['animation-duration'] = [[Controls how long the UI animations take. A value of 0 disables all animations
|
||
|
(which breaks the pause indicator).
|
||
|
]]
|
||
|
settings['elapsed-offscreen-pos'] = -100
|
||
|
helpText['elapsed-offscreen-pos'] = [[Controls how far off the left side of the window the elapsed time display tries
|
||
|
to move when it is inactive. If you use a non-default font, this value may need
|
||
|
to be tweaked. If this value is not far enough off-screen, the elapsed display
|
||
|
will disappear without animating all the way off-screen. Positive values will
|
||
|
cause the display to animate the wrong direction.
|
||
|
]]
|
||
|
settings['remaining-offscreen-pos'] = -100
|
||
|
helpText['remaining-offscreen-pos'] = [[Controls how far off the left side of the window the remaining time display
|
||
|
tries to move when it is inactive. If you use a non-default font, this value may
|
||
|
need to be tweaked. If this value is not far enough off-screen, the elapsed
|
||
|
display will disappear without animating all the way off-screen. Positive values
|
||
|
will cause the display to animate the wrong direction.
|
||
|
]]
|
||
|
settings['hover-time-offscreen-pos'] = -50
|
||
|
helpText['hover-time-offscreen-pos'] = [[Controls how far off the bottom of the window the mouse hover time display tries
|
||
|
to move when it is inactive. If you use a non-default font, this value may need
|
||
|
to be tweaked. If this value is not far enough off-screen, the elapsed
|
||
|
display will disappear without animating all the way off-screen. Positive values
|
||
|
will cause the display to animate the wrong direction.
|
||
|
]]
|
||
|
settings['system-time-offscreen-pos'] = -100
|
||
|
helpText['system-time-offscreen-pos'] = [[Controls how far off the left side of the window the system time display tries
|
||
|
to move when it is inactive. If you use a non-default font, this value may need
|
||
|
to be tweaked. If this value is not far enough off-screen, the elapsed display
|
||
|
will disappear without animating all the way off-screen. Positive values will
|
||
|
cause the display to animate the wrong direction.
|
||
|
]]
|
||
|
settings['title-offscreen-pos'] = -40
|
||
|
helpText['title-offscreen-pos'] = [[Controls how far off the left side of the window the video title display tries
|
||
|
to move when it is inactive. If you use a non-default font, this value may need
|
||
|
to be tweaked. If this value is not far enough off-screen, the elapsed display
|
||
|
will disappear without animating all the way off-screen. Positive values will
|
||
|
cause the display to animate the wrong direction.
|
||
|
]]
|
||
|
settings:__reload()
|
||
|
local Stack
|
||
|
do
|
||
|
local _class_0
|
||
|
local removeElementMetadata, reindex
|
||
|
local _base_0 = {
|
||
|
insert = function(self, element, index)
|
||
|
if index then
|
||
|
table.insert(self, index, element)
|
||
|
element[self] = index
|
||
|
else
|
||
|
table.insert(self, element)
|
||
|
element[self] = #self
|
||
|
end
|
||
|
if self.containmentKey then
|
||
|
element[self.containmentKey] = true
|
||
|
end
|
||
|
end,
|
||
|
remove = function(self, element)
|
||
|
if element[self] == nil then
|
||
|
error("Trying to remove an element that doesn't exist in this stack.")
|
||
|
end
|
||
|
table.remove(self, element[self])
|
||
|
reindex(self, element[self])
|
||
|
return removeElementMetadata(self, element)
|
||
|
end,
|
||
|
clear = function(self)
|
||
|
local element = table.remove(self)
|
||
|
while element do
|
||
|
removeElementMetadata(self, element)
|
||
|
element = table.remove(self)
|
||
|
end
|
||
|
end,
|
||
|
removeSortedList = function(self, elementList)
|
||
|
if #elementList < 1 then
|
||
|
return
|
||
|
end
|
||
|
for i = 1, #elementList - 1 do
|
||
|
local element = table.remove(elementList)
|
||
|
table.remove(self, element[self])
|
||
|
removeElementMetadata(self, element)
|
||
|
end
|
||
|
local lastElement = table.remove(elementList)
|
||
|
table.remove(self, lastElement[self])
|
||
|
reindex(self, lastElement[self])
|
||
|
return removeElementMetadata(self, lastElement)
|
||
|
end,
|
||
|
removeList = function(self, elementList)
|
||
|
table.sort(elementList, function(a, b)
|
||
|
return a[self] < b[self]
|
||
|
end)
|
||
|
return self:removeSortedList(elementList)
|
||
|
end
|
||
|
}
|
||
|
_base_0.__index = _base_0
|
||
|
_class_0 = setmetatable({
|
||
|
__init = function(self, containmentKey)
|
||
|
self.containmentKey = containmentKey
|
||
|
end,
|
||
|
__base = _base_0,
|
||
|
__name = "Stack"
|
||
|
}, {
|
||
|
__index = _base_0,
|
||
|
__call = function(cls, ...)
|
||
|
local _self_0 = setmetatable({}, _base_0)
|
||
|
cls.__init(_self_0, ...)
|
||
|
return _self_0
|
||
|
end
|
||
|
})
|
||
|
_base_0.__class = _class_0
|
||
|
local self = _class_0
|
||
|
removeElementMetadata = function(self, element)
|
||
|
element[self] = nil
|
||
|
if self.containmentKey then
|
||
|
element[self.containmentKey] = false
|
||
|
end
|
||
|
end
|
||
|
reindex = function(self, start)
|
||
|
if start == nil then
|
||
|
start = 1
|
||
|
end
|
||
|
for i = start, #self do
|
||
|
(self[i])[self] = i
|
||
|
end
|
||
|
end
|
||
|
Stack = _class_0
|
||
|
end
|
||
|
local Window
|
||
|
do
|
||
|
local _class_0
|
||
|
local osdScale
|
||
|
local _base_0 = { }
|
||
|
_base_0.__index = _base_0
|
||
|
_class_0 = setmetatable({
|
||
|
__init = function() end,
|
||
|
__base = _base_0,
|
||
|
__name = "Window"
|
||
|
}, {
|
||
|
__index = _base_0,
|
||
|
__call = function(cls, ...)
|
||
|
local _self_0 = setmetatable({}, _base_0)
|
||
|
cls.__init(_self_0, ...)
|
||
|
return _self_0
|
||
|
end
|
||
|
})
|
||
|
_base_0.__class = _class_0
|
||
|
local self = _class_0
|
||
|
osdScale = settings['display-scale-factor']
|
||
|
self.__class.w, self.__class.h = 0, 0
|
||
|
self.update = function(self)
|
||
|
local w, h = mp.get_osd_size()
|
||
|
w, h = math.floor(w / osdScale), math.floor(h / osdScale)
|
||
|
if w ~= self.w or h ~= self.h then
|
||
|
self.w, self.h = w, h
|
||
|
return true
|
||
|
else
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
Window = _class_0
|
||
|
end
|
||
|
local Rect
|
||
|
do
|
||
|
local _class_0
|
||
|
local _base_0 = {
|
||
|
cacheMaxBounds = function(self)
|
||
|
self.xMax = self.x + self.w
|
||
|
self.yMax = self.y + self.h
|
||
|
end,
|
||
|
setPosition = function(self, x, y)
|
||
|
self.x = x or self.x
|
||
|
self.y = y or self.y
|
||
|
return self:cacheMaxBounds()
|
||
|
end,
|
||
|
setSize = function(self, w, h)
|
||
|
self.w = w or self.w
|
||
|
self.h = h or self.h
|
||
|
return self:cacheMaxBounds()
|
||
|
end,
|
||
|
reset = function(self, x, y, w, h)
|
||
|
self.x = x or self.x
|
||
|
self.y = y or self.y
|
||
|
self.w = w or self.w
|
||
|
self.h = h or self.h
|
||
|
return self:cacheMaxBounds()
|
||
|
end,
|
||
|
move = function(self, x, y)
|
||
|
self.x = self.x + (x or self.x)
|
||
|
self.y = self.y + (y or self.y)
|
||
|
return self:cacheMaxBounds()
|
||
|
end,
|
||
|
stretch = function(self, w, h)
|
||
|
self.w = self.w + (w or self.w)
|
||
|
self.h = self.h + (h or self.h)
|
||
|
return self:cacheMaxBounds()
|
||
|
end,
|
||
|
containsPoint = function(self, x, y)
|
||
|
return (x >= self.x) and (x < self.xMax) and (y >= self.y) and (y < self.yMax)
|
||
|
end
|
||
|
}
|
||
|
_base_0.__index = _base_0
|
||
|
_class_0 = setmetatable({
|
||
|
__init = function(self, x, y, w, h)
|
||
|
if x == nil then
|
||
|
x = -1
|
||
|
end
|
||
|
if y == nil then
|
||
|
y = -1
|
||
|
end
|
||
|
if w == nil then
|
||
|
w = -1
|
||
|
end
|
||
|
if h == nil then
|
||
|
h = -1
|
||
|
end
|
||
|
self.x, self.y, self.w, self.h = x, y, w, h
|
||
|
return self:cacheMaxBounds()
|
||
|
end,
|
||
|
__base = _base_0,
|
||
|
__name = "Rect"
|
||
|
}, {
|
||
|
__index = _base_0,
|
||
|
__call = function(cls, ...)
|
||
|
local _self_0 = setmetatable({}, _base_0)
|
||
|
cls.__init(_self_0, ...)
|
||
|
return _self_0
|
||
|
end
|
||
|
})
|
||
|
_base_0.__class = _class_0
|
||
|
Rect = _class_0
|
||
|
end
|
||
|
local AnimationQueue
|
||
|
do
|
||
|
local _class_0
|
||
|
local animationList, deletionQueue
|
||
|
local _base_0 = { }
|
||
|
_base_0.__index = _base_0
|
||
|
_class_0 = setmetatable({
|
||
|
__init = function() end,
|
||
|
__base = _base_0,
|
||
|
__name = "AnimationQueue"
|
||
|
}, {
|
||
|
__index = _base_0,
|
||
|
__call = function(cls, ...)
|
||
|
local _self_0 = setmetatable({}, _base_0)
|
||
|
cls.__init(_self_0, ...)
|
||
|
return _self_0
|
||
|
end
|
||
|
})
|
||
|
_base_0.__class = _class_0
|
||
|
local self = _class_0
|
||
|
animationList = Stack('active')
|
||
|
deletionQueue = { }
|
||
|
self.addAnimation = function(animation)
|
||
|
if not (animation.active) then
|
||
|
return animationList:insert(animation)
|
||
|
end
|
||
|
end
|
||
|
self.removeAnimation = function(animation)
|
||
|
if animation.active then
|
||
|
return animationList:remove(animation)
|
||
|
end
|
||
|
end
|
||
|
self.destroyAnimationStack = function()
|
||
|
return animationList:clear()
|
||
|
end
|
||
|
self.animate = function()
|
||
|
if #animationList == 0 then
|
||
|
return
|
||
|
end
|
||
|
local currentTime = mp.get_time()
|
||
|
for _, animation in ipairs(animationList) do
|
||
|
if animation:update(currentTime) then
|
||
|
table.insert(deletionQueue, animation)
|
||
|
end
|
||
|
end
|
||
|
if #deletionQueue > 0 then
|
||
|
return animationList:removeSortedList(deletionQueue)
|
||
|
end
|
||
|
end
|
||
|
self.active = function()
|
||
|
return #animationList > 0
|
||
|
end
|
||
|
AnimationQueue = _class_0
|
||
|
end
|
||
|
local EventLoop
|
||
|
do
|
||
|
local _class_0
|
||
|
local _base_0 = {
|
||
|
reconfigure = function(self)
|
||
|
settings:__reload()
|
||
|
AnimationQueue.destroyAnimationStack()
|
||
|
for _, zone in ipairs(self.activityZones) do
|
||
|
zone:reconfigure()
|
||
|
end
|
||
|
for _, element in ipairs(self.uiElements) do
|
||
|
element:reconfigure()
|
||
|
end
|
||
|
end,
|
||
|
addZone = function(self, zone)
|
||
|
if zone == nil then
|
||
|
return
|
||
|
end
|
||
|
return self.activityZones:insert(zone)
|
||
|
end,
|
||
|
removeZone = function(self, zone)
|
||
|
if zone == nil then
|
||
|
return
|
||
|
end
|
||
|
return self.activityZones:remove(zone)
|
||
|
end,
|
||
|
generateUIFromZones = function(self)
|
||
|
local seenUIElements = { }
|
||
|
self.script = { }
|
||
|
self.uiElements:clear()
|
||
|
AnimationQueue.destroyAnimationStack()
|
||
|
for _, zone in ipairs(self.activityZones) do
|
||
|
for _, uiElement in ipairs(zone.elements) do
|
||
|
if not (seenUIElements[uiElement]) then
|
||
|
self:addUIElement(uiElement)
|
||
|
seenUIElements[uiElement] = true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return self.updateTimer:resume()
|
||
|
end,
|
||
|
addUIElement = function(self, uiElement)
|
||
|
if uiElement == nil then
|
||
|
error('nil UIElement added.')
|
||
|
end
|
||
|
self.uiElements:insert(uiElement)
|
||
|
return table.insert(self.script, '')
|
||
|
end,
|
||
|
removeUIElement = function(self, uiElement)
|
||
|
if uiElement == nil then
|
||
|
error('nil UIElement removed.')
|
||
|
end
|
||
|
table.remove(self.script, uiElement[self.uiElements])
|
||
|
return self.uiElements:remove(uiElement)
|
||
|
end,
|
||
|
resize = function(self)
|
||
|
for _, zone in ipairs(self.activityZones) do
|
||
|
zone:resize()
|
||
|
end
|
||
|
for _, uiElement in ipairs(self.uiElements) do
|
||
|
uiElement:resize()
|
||
|
end
|
||
|
end,
|
||
|
redraw = function(self, forceRedraw)
|
||
|
if Window:update() then
|
||
|
self:resize()
|
||
|
end
|
||
|
for index, zone in ipairs(self.activityZones) do
|
||
|
zone:update(self.displayRequested, false)
|
||
|
end
|
||
|
AnimationQueue.animate()
|
||
|
for index, uiElement in ipairs(self.uiElements) do
|
||
|
if uiElement:redraw() then
|
||
|
self.script[index] = uiElement:stringify()
|
||
|
end
|
||
|
end
|
||
|
return mp.set_osd_ass(Window.w, Window.h, table.concat(self.script, '\n'))
|
||
|
end
|
||
|
}
|
||
|
_base_0.__index = _base_0
|
||
|
_class_0 = setmetatable({
|
||
|
__init = function(self)
|
||
|
self.script = { }
|
||
|
self.uiElements = Stack()
|
||
|
self.activityZones = Stack()
|
||
|
self.displayRequested = false
|
||
|
self.updateTimer = mp.add_periodic_timer(settings['redraw-period'], (function()
|
||
|
local _base_1 = self
|
||
|
local _fn_0 = _base_1.redraw
|
||
|
return function(...)
|
||
|
return _fn_0(_base_1, ...)
|
||
|
end
|
||
|
end)())
|
||
|
self.updateTimer:stop()
|
||
|
mp.register_event('shutdown', function()
|
||
|
return self.updateTimer:kill()
|
||
|
end)
|
||
|
local displayRequestTimer
|
||
|
local displayDuration = settings['request-display-duration']
|
||
|
mp.add_key_binding("tab", "request-display", function(event)
|
||
|
if event.event == "repeat" then
|
||
|
return
|
||
|
end
|
||
|
if event.event == "down" or event.event == "press" then
|
||
|
if displayRequestTimer then
|
||
|
displayRequestTimer:kill()
|
||
|
end
|
||
|
self.displayRequested = true
|
||
|
end
|
||
|
if event.event == "up" or event.event == "press" then
|
||
|
if displayDuration == 0 then
|
||
|
self.displayRequested = false
|
||
|
else
|
||
|
displayRequestTimer = mp.add_timeout(displayDuration, function()
|
||
|
self.displayRequested = false
|
||
|
end)
|
||
|
end
|
||
|
end
|
||
|
end, {
|
||
|
complex = true
|
||
|
})
|
||
|
return mp.add_key_binding('ctrl+r', 'reconfigure', (function()
|
||
|
local _base_1 = self
|
||
|
local _fn_0 = _base_1.reconfigure
|
||
|
return function(...)
|
||
|
return _fn_0(_base_1, ...)
|
||
|
end
|
||
|
end)(), {
|
||
|
repeatable = false
|
||
|
})
|
||
|
end,
|
||
|
__base = _base_0,
|
||
|
__name = "EventLoop"
|
||
|
}, {
|
||
|
__index = _base_0,
|
||
|
__call = function(cls, ...)
|
||
|
local _self_0 = setmetatable({}, _base_0)
|
||
|
cls.__init(_self_0, ...)
|
||
|
return _self_0
|
||
|
end
|
||
|
})
|
||
|
_base_0.__class = _class_0
|
||
|
EventLoop = _class_0
|
||
|
end
|
||
|
local Animation
|
||
|
do
|
||
|
local _class_0
|
||
|
local _base_0 = {
|
||
|
update = function(self, now)
|
||
|
if self.isReversed then
|
||
|
self.linearProgress = math.max(0, math.min(1, self.linearProgress + (self.lastUpdate - now) * self.durationR))
|
||
|
if self.linearProgress == 0 then
|
||
|
self.isFinished = true
|
||
|
end
|
||
|
else
|
||
|
self.linearProgress = math.max(0, math.min(1, self.linearProgress + (now - self.lastUpdate) * self.durationR))
|
||
|
if self.linearProgress == 1 then
|
||
|
self.isFinished = true
|
||
|
end
|
||
|
end
|
||
|
self.lastUpdate = now
|
||
|
local progress = math.pow(self.linearProgress, self.accel)
|
||
|
self.value = (1 - progress) * self.initialValue + progress * self.endValue
|
||
|
self.updateCb(self.value)
|
||
|
if self.isFinished and self.finishedCb then
|
||
|
self:finishedCb()
|
||
|
end
|
||
|
return self.isFinished
|
||
|
end,
|
||
|
interrupt = function(self, reverse)
|
||
|
self.finishedCb = nil
|
||
|
self.lastUpdate = mp.get_time()
|
||
|
self.isReversed = reverse
|
||
|
if not (self.active) then
|
||
|
self.isFinished = false
|
||
|
return AnimationQueue.addAnimation(self)
|
||
|
end
|
||
|
end
|
||
|
}
|
||
|
_base_0.__index = _base_0
|
||
|
_class_0 = setmetatable({
|
||
|
__init = function(self, initialValue, endValue, duration, updateCb, finishedCb, accel)
|
||
|
if accel == nil then
|
||
|
accel = 1
|
||
|
end
|
||
|
self.initialValue, self.endValue, self.duration, self.updateCb, self.finishedCb, self.accel = initialValue, endValue, duration, updateCb, finishedCb, accel
|
||
|
self.value = self.initialValue
|
||
|
self.linearProgress = 0
|
||
|
self.lastUpdate = mp.get_time()
|
||
|
self.durationR = 1 / self.duration
|
||
|
self.isFinished = (self.duration <= 0)
|
||
|
self.active = false
|
||
|
self.isReversed = false
|
||
|
end,
|
||
|
__base = _base_0,
|
||
|
__name = "Animation"
|
||
|
}, {
|
||
|
__index = _base_0,
|
||
|
__call = function(cls, ...)
|
||
|
local _self_0 = setmetatable({}, _base_0)
|
||
|
cls.__init(_self_0, ...)
|
||
|
return _self_0
|
||
|
end
|
||
|
})
|
||
|
_base_0.__class = _class_0
|
||
|
Animation = _class_0
|
||
|
end
|
||
|
local UIElement
|
||
|
do
|
||
|
local _class_0
|
||
|
local _base_0 = {
|
||
|
stringify = function(self)
|
||
|
self.needsUpdate = false
|
||
|
if not self.active then
|
||
|
return ''
|
||
|
else
|
||
|
return table.concat(self.line)
|
||
|
end
|
||
|
end,
|
||
|
activate = function(self, activate)
|
||
|
if activate == true then
|
||
|
self.animation:interrupt(false)
|
||
|
self.active = true
|
||
|
else
|
||
|
self.animation:interrupt(true)
|
||
|
self.animation.finishedCb = function()
|
||
|
self.active = false
|
||
|
end
|
||
|
end
|
||
|
end,
|
||
|
reconfigure = function(self)
|
||
|
self.needsUpdate = true
|
||
|
self.animationDuration = settings['animation-duration']
|
||
|
end,
|
||
|
resize = function(self)
|
||
|
return error('UIElement updateSize called')
|
||
|
end,
|
||
|
redraw = function(self)
|
||
|
return self.needsUpdate
|
||
|
end
|
||
|
}
|
||
|
_base_0.__index = _base_0
|
||
|
_class_0 = setmetatable({
|
||
|
__init = function(self)
|
||
|
self.needsUpdate = false
|
||
|
self.active = false
|
||
|
self.animationDuration = settings['animation-duration']
|
||
|
end,
|
||
|
__base = _base_0,
|
||
|
__name = "UIElement"
|
||
|
}, {
|
||
|
__index = _base_0,
|
||
|
__call = function(cls, ...)
|
||
|
local _self_0 = setmetatable({}, _base_0)
|
||
|
cls.__init(_self_0, ...)
|
||
|
return _self_0
|
||
|
end
|
||
|
})
|
||
|
_base_0.__class = _class_0
|
||
|
UIElement = _class_0
|
||
|
end
|
||
|
local PauseIndicator
|
||
|
do
|
||
|
local _class_0
|
||
|
local _base_0 = {
|
||
|
stringify = function(self)
|
||
|
return table.concat(self.line)
|
||
|
end,
|
||
|
resize = function(self)
|
||
|
local w, h = 0.5 * Window.w, 0.5 * Window.h
|
||
|
self.line[5] = ([[%g,%g]]):format(w, h)
|
||
|
self.line[12] = ([[%g,%g]]):format(w, h)
|
||
|
end,
|
||
|
redraw = function()
|
||
|
return true
|
||
|
end,
|
||
|
animate = function(self, value)
|
||
|
local scale = value * 50 + 100
|
||
|
local scaleStr = ([[{\fscx%g\fscy%g]]):format(scale, scale)
|
||
|
local alphaStr = ('%02X'):format(value * value * 255)
|
||
|
self.line[1] = scaleStr
|
||
|
self.line[8] = scaleStr
|
||
|
self.line[3] = alphaStr
|
||
|
self.line[10] = alphaStr
|
||
|
end,
|
||
|
destroy = function(self, animation)
|
||
|
return self.eventLoop:removeUIElement(self)
|
||
|
end
|
||
|
}
|
||
|
_base_0.__index = _base_0
|
||
|
_class_0 = setmetatable({
|
||
|
__init = function(self, eventLoop, paused)
|
||
|
self.eventLoop = eventLoop
|
||
|
local w, h = 0.5 * Window.w, 0.5 * Window.h
|
||
|
self.line = {
|
||
|
[[{\fscx0\fscy0]],
|
||
|
[[\alpha&H]],
|
||
|
0,
|
||
|
[[&\pos(]],
|
||
|
([[%g,%g]]):format(w, h),
|
||
|
([[)\an5\bord0%s\p1}]]):format(settings['pause-indicator-background-style']),
|
||
|
0,
|
||
|
[[{\fscx0\fscy0]],
|
||
|
[[\alpha&H]],
|
||
|
0,
|
||
|
[[&\pos(]],
|
||
|
([[%g,%g]]):format(w, h),
|
||
|
([[)\an5\bord0%s\p1}]]):format(settings['pause-indicator-foreground-style']),
|
||
|
0
|
||
|
}
|
||
|
if paused then
|
||
|
self.line[7] = 'm 75 37.5 b 75 58.21 58.21 75 37.5 75 16.79 75 0 58.21 0 37.5 0 16.79 16.79 0 37.5 0 58.21 0 75 16.79 75 37.5 m 23 20 l 23 55 33 55 33 20 m 42 20 l 42 55 52 55 52 20\n'
|
||
|
self.line[14] = 'm 0 0 m 75 75 m 23 20 l 23 55 33 55 33 20 m 42 20 l 42 55 52 55 52 20'
|
||
|
else
|
||
|
self.line[7] = 'm 75 37.5 b 75 58.21 58.21 75 37.5 75 16.79 75 0 58.21 0 37.5 0 16.79 16.79 0 37.5 0 58.21 0 75 16.79 75 37.5 m 25.8333 17.18 l 25.8333 57.6 60.8333 37.39\n'
|
||
|
self.line[14] = 'm 0 0 m 75 75 m 25.8333 17.18 l 25.8333 57.6 60.8333 37.39'
|
||
|
end
|
||
|
AnimationQueue.addAnimation(Animation(0, 1, settings['animation-duration'], (function()
|
||
|
local _base_1 = self
|
||
|
local _fn_0 = _base_1.animate
|
||
|
return function(...)
|
||
|
return _fn_0(_base_1, ...)
|
||
|
end
|
||
|
end)(), (function()
|
||
|
local _base_1 = self
|
||
|
local _fn_0 = _base_1.destroy
|
||
|
return function(...)
|
||
|
return _fn_0(_base_1, ...)
|
||
|
end
|
||
|
end)()))
|
||
|
return self.eventLoop:addUIElement(self)
|
||
|
end,
|
||
|
__base = _base_0,
|
||
|
__name = "PauseIndicator"
|
||
|
}, {
|
||
|
__index = _base_0,
|
||
|
__call = function(cls, ...)
|
||
|
local _self_0 = setmetatable({}, _base_0)
|
||
|
cls.__init(_self_0, ...)
|
||
|
return _self_0
|
||
|
end
|
||
|
})
|
||
|
_base_0.__class = _class_0
|
||
|
PauseIndicator = _class_0
|
||
|
end
|
||
|
local eventLoop = EventLoop()
|
||
|
local notFrameStepping = false
|
||
|
if settings['pause-indicator'] then
|
||
|
local PauseIndicatorWrapper
|
||
|
PauseIndicatorWrapper = function(event, paused)
|
||
|
if notFrameStepping then
|
||
|
return PauseIndicator(eventLoop, paused)
|
||
|
elseif paused then
|
||
|
notFrameStepping = true
|
||
|
end
|
||
|
end
|
||
|
mp.add_key_binding('.', 'step-forward', function()
|
||
|
notFrameStepping = false
|
||
|
return mp.commandv('frame_step')
|
||
|
end, {
|
||
|
repeatable = true
|
||
|
})
|
||
|
mp.add_key_binding(',', 'step-backward', function()
|
||
|
notFrameStepping = false
|
||
|
return mp.commandv('frame_back_step')
|
||
|
end, {
|
||
|
repeatable = true
|
||
|
})
|
||
|
mp.observe_property('pause', 'bool', PauseIndicatorWrapper)
|
||
|
end
|
||
|
local initDraw
|
||
|
initDraw = function()
|
||
|
mp.unregister_event(initDraw)
|
||
|
notFrameStepping = true
|
||
|
eventLoop:resize()
|
||
|
eventLoop:redraw()
|
||
|
return eventLoop.updateTimer:resume()
|
||
|
end
|
||
|
local fileLoaded
|
||
|
fileLoaded = function()
|
||
|
return mp.register_event('playback-restart', initDraw)
|
||
|
end
|
||
|
return mp.register_event('file-loaded', fileLoaded)
|