171 lines
5.6 KiB
Lua
171 lines
5.6 KiB
Lua
|
local Element = require('elements/Element')
|
||
|
local dots = {'.', '..', '...'}
|
||
|
|
||
|
local function cleanup_output(output)
|
||
|
return tostring(output):gsub('%c*\n%c*', '\n'):match('^[%s%c]*(.-)[%s%c]*$')
|
||
|
end
|
||
|
|
||
|
---@class Updater : Element
|
||
|
local Updater = class(Element)
|
||
|
|
||
|
function Updater:new() return Class.new(self) --[[@as Updater]] end
|
||
|
function Updater:init()
|
||
|
Element.init(self, 'updater', {render_order = 1000})
|
||
|
self.output = nil
|
||
|
self.message = t('Updating uosc')
|
||
|
self.state = 'pending' -- Matches icon name
|
||
|
local config_dir = mp.command_native({'expand-path', '~~/'})
|
||
|
|
||
|
Elements:maybe('curtain', 'register', self.id)
|
||
|
|
||
|
local function handle_result(success, result, error)
|
||
|
if success and result and result.status == 0 then
|
||
|
self.state = 'done'
|
||
|
self.message = t('uosc has been installed. Restart mpv for it to take effect.')
|
||
|
else
|
||
|
self.state = 'error'
|
||
|
self.message = t('An error has occurred.') .. ' ' .. t('See above for clues.')
|
||
|
end
|
||
|
|
||
|
local output = (result.stdout or '') .. '\n' .. (error or result.stderr or '')
|
||
|
if state.platform == 'darwin' then
|
||
|
output =
|
||
|
'Self-updater is known not to work on MacOS.\nIf you know about a solution, please make an issue and share it with us!.\n' ..
|
||
|
output
|
||
|
end
|
||
|
|
||
|
self.output = ass_escape(cleanup_output(output))
|
||
|
|
||
|
request_render()
|
||
|
end
|
||
|
|
||
|
local function update(args)
|
||
|
local env = utils.get_env_list()
|
||
|
env[#env + 1] = 'MPV_CONFIG_DIR=' .. config_dir
|
||
|
|
||
|
mp.command_native_async({
|
||
|
name = 'subprocess',
|
||
|
capture_stderr = true,
|
||
|
capture_stdout = true,
|
||
|
playback_only = false,
|
||
|
args = args,
|
||
|
env = env,
|
||
|
}, handle_result)
|
||
|
end
|
||
|
|
||
|
if state.platform == 'windows' then
|
||
|
local url = 'https://raw.githubusercontent.com/tomasklaen/uosc/HEAD/installers/windows.ps1'
|
||
|
update({'powershell', '-NoProfile', '-Command', 'irm ' .. url .. ' | iex'})
|
||
|
else
|
||
|
-- Detect missing dependencies. We can't just let the process run and
|
||
|
-- report an error, as on snap packages there's no error. Everything
|
||
|
-- either exits with 0, or no helpful output/error message.
|
||
|
local missing = {}
|
||
|
|
||
|
for _, name in ipairs({'curl', 'unzip'}) do
|
||
|
local result = mp.command_native({
|
||
|
name = 'subprocess',
|
||
|
capture_stdout = true,
|
||
|
playback_only = false,
|
||
|
args = {'which', name},
|
||
|
})
|
||
|
local path = cleanup_output(result and result.stdout or '')
|
||
|
if path == '' then
|
||
|
missing[#missing + 1] = name
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if #missing > 0 then
|
||
|
local stderr = 'Missing dependencies: ' .. table.concat(missing, ', ')
|
||
|
if config_dir:match('/snap/') then
|
||
|
stderr = stderr ..
|
||
|
'\nThis is a known error for mpv snap packages.\nYou can still update uosc by entering the Linux install command from uosc\'s readme into your terminal, it just can\'t be done this way.\nIf you know about a solution, please make an issue and share it with us!'
|
||
|
end
|
||
|
handle_result(false, {stderr = stderr})
|
||
|
else
|
||
|
local url = 'https://raw.githubusercontent.com/tomasklaen/uosc/HEAD/installers/unix.sh'
|
||
|
update({'/bin/bash', '-c', 'source <(curl -fsSL ' .. url .. ')'})
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function Updater:destroy()
|
||
|
Elements:maybe('curtain', 'unregister', self.id)
|
||
|
Element.destroy(self)
|
||
|
end
|
||
|
|
||
|
function Updater:render()
|
||
|
local ass = assdraw.ass_new()
|
||
|
|
||
|
local text_size = math.min(20 * state.scale, display.height / 20)
|
||
|
local icon_size = text_size * 2
|
||
|
local center_x = round(display.width / 2)
|
||
|
|
||
|
local color = fg
|
||
|
if self.state == 'done' then
|
||
|
color = config.color.success
|
||
|
elseif self.state == 'error' then
|
||
|
color = config.color.error
|
||
|
end
|
||
|
|
||
|
-- Divider
|
||
|
local divider_width = round(math.min(500 * state.scale, display.width * 0.8))
|
||
|
local divider_half, divider_border_half, divider_y = divider_width / 2, round(1 * state.scale), display.height * 0.65
|
||
|
local divider_ay, divider_by = round(divider_y - divider_border_half), round(divider_y + divider_border_half)
|
||
|
ass:rect(center_x - divider_half, divider_ay, center_x - icon_size, divider_by, {
|
||
|
color = color, border = options.text_border * state.scale, border_color = bg, opacity = 0.5,
|
||
|
})
|
||
|
ass:rect(center_x + icon_size, divider_ay, center_x + divider_half, divider_by, {
|
||
|
color = color, border = options.text_border * state.scale, border_color = bg, opacity = 0.5,
|
||
|
})
|
||
|
if self.state == 'pending' then
|
||
|
ass:spinner(center_x, divider_y, icon_size, {
|
||
|
color = fg, border = options.text_border * state.scale, border_color = bg,
|
||
|
})
|
||
|
else
|
||
|
ass:icon(center_x, divider_y, icon_size * 0.8, self.state, {
|
||
|
color = color, border = options.text_border * state.scale, border_color = bg,
|
||
|
})
|
||
|
end
|
||
|
|
||
|
-- Output
|
||
|
local output = self.output or dots[math.ceil((mp.get_time() % 1) * #dots)]
|
||
|
ass:txt(center_x, divider_y - icon_size, 2, output, {
|
||
|
size = text_size, color = fg, border = options.text_border * state.scale, border_color = bg,
|
||
|
})
|
||
|
|
||
|
-- Message
|
||
|
ass:txt(center_x, divider_y + icon_size, 5, self.message, {
|
||
|
size = text_size, bold = true, color = color, border = options.text_border * state.scale, border_color = bg,
|
||
|
})
|
||
|
|
||
|
-- Button
|
||
|
if self.state ~= 'pending' then
|
||
|
-- Background
|
||
|
local button_y = divider_y + icon_size * 1.75
|
||
|
local button_rect = {
|
||
|
ax = round(center_x - icon_size / 2),
|
||
|
ay = round(button_y),
|
||
|
bx = round(center_x + icon_size / 2),
|
||
|
by = round(button_y + icon_size),
|
||
|
}
|
||
|
local is_hovered = get_point_to_rectangle_proximity(cursor, button_rect) == 0
|
||
|
ass:rect(button_rect.ax, button_rect.ay, button_rect.bx, button_rect.by, {
|
||
|
color = fg,
|
||
|
radius = state.radius,
|
||
|
opacity = is_hovered and 1 or 0.5,
|
||
|
})
|
||
|
|
||
|
-- Icon
|
||
|
local x = round(button_rect.ax + (button_rect.bx - button_rect.ax) / 2)
|
||
|
local y = round(button_rect.ay + (button_rect.by - button_rect.ay) / 2)
|
||
|
ass:icon(x, y, icon_size * 0.8, 'close', {color = bg})
|
||
|
|
||
|
cursor:zone('primary_click', button_rect, function() self:destroy() end)
|
||
|
end
|
||
|
|
||
|
return ass
|
||
|
end
|
||
|
|
||
|
return Updater
|