From f935979cfc25974051378d133ee5e2093cc6195e Mon Sep 17 00:00:00 2001 From: tomasklaen Date: Sat, 14 Oct 2023 17:11:09 +0200 Subject: [PATCH 1/4] feat: added `update` command to update uosc --- README.md | 5 + script-opts/uosc.conf | 3 +- scripts/uosc/elements/BufferingIndicator.lua | 3 +- scripts/uosc/elements/Curtain.lua | 2 +- scripts/uosc/elements/Element.lua | 12 +- scripts/uosc/elements/Elements.lua | 14 +-- scripts/uosc/elements/Menu.lua | 6 +- scripts/uosc/elements/PauseIndicator.lua | 2 +- scripts/uosc/elements/Timeline.lua | 2 +- scripts/uosc/elements/Updater.lua | 126 +++++++++++++++++++ scripts/uosc/elements/WindowBorder.lua | 3 +- scripts/uosc/main.lua | 6 + 12 files changed, 158 insertions(+), 26 deletions(-) create mode 100644 scripts/uosc/elements/Updater.lua diff --git a/README.md b/README.md index 8a1b278f..50f51058 100644 --- a/README.md +++ b/README.md @@ -290,6 +290,10 @@ Switch audio output device. Open directory with `mpv.conf` in file explorer. +#### `update` + +Updates uosc to the latest stable release. + ## Menu **uosc** provides a way to build, display, and use your own menu. By default it displays a pre-configured menu with common actions. @@ -401,6 +405,7 @@ ctrl+s async screenshot #! Utils > Screenshot alt+i script-binding uosc/inputs #! Utils > Inputs O script-binding uosc/show-in-directory #! Utils > Show in directory # script-binding uosc/open-config-directory #! Utils > Open config directory +# script-binding uosc/update #! Utils > Update uosc esc quit #! Quit ``` diff --git a/script-opts/uosc.conf b/script-opts/uosc.conf index 7abb11db..69d6bea0 100644 --- a/script-opts/uosc.conf +++ b/script-opts/uosc.conf @@ -150,7 +150,8 @@ text_border=1.2 # Border radius of buttons, menus, and all other rectangles border_radius=2 # A comma delimited list of color overrides in RGB HEX format. -# Defaults: foreground=ffffff,foreground_text=000000,background=000000,background_text=ffffff,curtain=111111 +# Defaults: foreground=ffffff,foreground_text=000000,background=000000,background_text=ffffff,curtain=111111, +# success=a5e075,error=ff616e color= # A comma delimited list of opacity overrides for various UI element backgrounds and shapes. # This does not affect any text, which is always rendered fully opaque. diff --git a/scripts/uosc/elements/BufferingIndicator.lua b/scripts/uosc/elements/BufferingIndicator.lua index 7f2a04fe..78fb5e19 100644 --- a/scripts/uosc/elements/BufferingIndicator.lua +++ b/scripts/uosc/elements/BufferingIndicator.lua @@ -5,8 +5,7 @@ local BufferingIndicator = class(Element) function BufferingIndicator:new() return Class.new(self) --[[@as BufferingIndicator]] end function BufferingIndicator:init() - Element.init(self, 'buffer_indicator', {render_order = 2}) - self.ignores_menu = true + Element.init(self, 'buffer_indicator', {ignores_curtain = true, render_order = 2}) self.enabled = false self:decide_enabled() end diff --git a/scripts/uosc/elements/Curtain.lua b/scripts/uosc/elements/Curtain.lua index dbce0336..ccebfb02 100644 --- a/scripts/uosc/elements/Curtain.lua +++ b/scripts/uosc/elements/Curtain.lua @@ -5,7 +5,7 @@ local Curtain = class(Element) function Curtain:new() return Class.new(self) --[[@as Curtain]] end function Curtain:init() - Element.init(self, 'curtain', {ignores_menu = true, render_order = 999}) + Element.init(self, 'curtain', {render_order = 999}) self.opacity = 0 ---@type string[] self.dependents = {} diff --git a/scripts/uosc/elements/Element.lua b/scripts/uosc/elements/Element.lua index 8aaa833f..3e3f4584 100644 --- a/scripts/uosc/elements/Element.lua +++ b/scripts/uosc/elements/Element.lua @@ -1,4 +1,4 @@ ----@alias ElementProps {enabled?: boolean; render_order?: number; ax?: number; ay?: number; bx?: number; by?: number; ignores_menu?: boolean; anchor_id?: string;} +---@alias ElementProps {enabled?: boolean; render_order?: number; ax?: number; ay?: number; bx?: number; by?: number; ignores_curtain?: boolean; anchor_id?: string;} -- Base class all elements inherit from. ---@class Element : Class @@ -21,8 +21,8 @@ function Element:init(id, props) self.min_visibility = 0 ---@type number `0-1` factor to force a visibility value. Used for flashing, fading out, and other animations self.forced_visibility = nil - ---@type boolean Render this element even when menu is open. - self.ignores_menu = false + ---@type boolean Show this element even when curtain is visible. + self.ignores_curtain = false ---@type nil|string ID of an element from which this one should inherit visibility. self.anchor_id = nil ---@type fun()[] Disposer functions called when element is destroyed. @@ -91,9 +91,9 @@ end -- Decide elements visibility based on proximity and various other factors function Element:get_visibility() - -- Hide when menu is open, unless this is a menu - ---@diagnostic disable-next-line: undefined-global - if not self.ignores_menu and Menu and Menu:is_open() then return 0 end + -- Hide when curtain is visible, unless this elements ignores it + local min_order = (Elements.curtain.opacity > 0 and not self.ignores_curtain) and Elements.curtain.render_order or 0 + if self.render_order < min_order then return 0 end -- Persistency if self:is_persistent() then return 1 end diff --git a/scripts/uosc/elements/Elements.lua b/scripts/uosc/elements/Elements.lua index e54a3198..e9e86fcc 100644 --- a/scripts/uosc/elements/Elements.lua +++ b/scripts/uosc/elements/Elements.lua @@ -32,22 +32,18 @@ function Elements:remove(idOrElement) end function Elements:update_proximities() - local menu_only = Elements.menu ~= nil + local curtain_render_order = Elements.curtain.opacity > 0 and Elements.curtain.render_order or 0 local mouse_leave_elements = {} local mouse_enter_elements = {} - -- Calculates proximities and opacities for defined elements + -- Calculates proximities for all elements for _, element in self:ipairs() do if element.enabled then local previous_proximity_raw = element.proximity_raw - -- If menu is open, all other elements have to be disabled - if menu_only then - if element.ignores_menu then - element:update_proximity() - else - element:reset_proximity() - end + -- If curtain is open, we disable all elements set to rendered below it + if not element.ignores_curtain and element.render_order < curtain_render_order then + element:reset_proximity() else element:update_proximity() end diff --git a/scripts/uosc/elements/Menu.lua b/scripts/uosc/elements/Menu.lua index d95ac6b1..06f31895 100644 --- a/scripts/uosc/elements/Menu.lua +++ b/scripts/uosc/elements/Menu.lua @@ -81,7 +81,7 @@ function Menu:new(data, callback, opts) return Class.new(self, data, callback, o ---@param callback MenuCallback ---@param opts? MenuOptions function Menu:init(data, callback, opts) - Element.init(self, 'menu', {ignores_menu = true, render_order = 1000}) + Element.init(self, 'menu', {render_order = 1001}) -----@type fun() self.callback = callback @@ -133,7 +133,7 @@ function Menu:init(data, callback, opts) self:tween_property('opacity', 0, 1) self:enable_key_bindings() - Elements:maybe('curtain', 'register', 'menu') + Elements:maybe('curtain', 'register', self.id) if self.opts.on_open then self.opts.on_open() end end @@ -141,7 +141,7 @@ function Menu:destroy() Element.destroy(self) self:disable_key_bindings() self.is_closed = true - if not self.is_being_replaced then Elements:maybe('curtain', 'unregister', 'menu') end + if not self.is_being_replaced then Elements:maybe('curtain', 'unregister', self.id) end if utils.shared_script_property_set then utils.shared_script_property_set('uosc-menu-type', nil) end diff --git a/scripts/uosc/elements/PauseIndicator.lua b/scripts/uosc/elements/PauseIndicator.lua index 45bd2f8e..d13c21e4 100644 --- a/scripts/uosc/elements/PauseIndicator.lua +++ b/scripts/uosc/elements/PauseIndicator.lua @@ -6,7 +6,7 @@ local PauseIndicator = class(Element) function PauseIndicator:new() return Class.new(self) --[[@as PauseIndicator]] end function PauseIndicator:init() Element.init(self, 'pause_indicator', {render_order = 3}) - self.ignores_menu = true + self.ignores_curtain = true self.paused = state.pause self.fadeout_requested = false self.opacity = 0 diff --git a/scripts/uosc/elements/Timeline.lua b/scripts/uosc/elements/Timeline.lua index cf8782d6..37d5f33e 100644 --- a/scripts/uosc/elements/Timeline.lua +++ b/scripts/uosc/elements/Timeline.lua @@ -26,7 +26,7 @@ function Timeline:init() end function Timeline:get_visibility() - return math.max(Elements:v('controls', 'proximity', 0), Element.get_visibility(self)) + return math.max(Elements:maybe('controls', 'get_visibility') or 0, Element.get_visibility(self)) end function Timeline:decide_enabled() diff --git a/scripts/uosc/elements/Updater.lua b/scripts/uosc/elements/Updater.lua new file mode 100644 index 00000000..fb39f253 --- /dev/null +++ b/scripts/uosc/elements/Updater.lua @@ -0,0 +1,126 @@ +local Element = require('elements/Element') +local dots = {'.', '..', '...'} + +---@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 + + Elements:maybe('curtain', 'register', self.id) + + local args = state.platform == 'windows' + and {'powershell', '-NoProfile', '-Command', + 'irm https://raw.githubusercontent.com/tomasklaen/uosc/HEAD/installers/windows.ps1 | iex'} + or {'/bin/bash', '-c', + 'source <(curl -fsSL https://raw.githubusercontent.com/tomasklaen/uosc/HEAD/installers/unix.sh)'} + local env = utils.get_env_list() + env[#env + 1] = 'MPV_CONFIG_DIR=' .. mp.command_native({'expand-path', '~~/'}) + + -- Update + mp.command_native_async({ + name = 'subprocess', + capture_stderr = true, + capture_stdout = true, + playback_only = false, + args = args, + env = env, + }, function(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('There has been an error. See above for clues.') + end + local output = ((result.stdout or '') .. '\n' .. (error or result.stderr or '')) + :gsub('%c*\n%c*', '\n') + :gsub('%c*$', '') + self.output = ass_escape(output) + request_render() + 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_down', button_rect, function() self:destroy() end) + end + + return ass +end + +return Updater diff --git a/scripts/uosc/elements/WindowBorder.lua b/scripts/uosc/elements/WindowBorder.lua index 4487611e..730c2caf 100644 --- a/scripts/uosc/elements/WindowBorder.lua +++ b/scripts/uosc/elements/WindowBorder.lua @@ -5,8 +5,7 @@ local WindowBorder = class(Element) function WindowBorder:new() return Class.new(self) --[[@as WindowBorder]] end function WindowBorder:init() - Element.init(self, 'window_border', {render_order = 1}) - self.ignores_menu = true + Element.init(self, 'window_border', {render_order = 9999}) self.size = 0 self:decide_enabled() end diff --git a/scripts/uosc/main.lua b/scripts/uosc/main.lua index 58a9d130..5b436a89 100644 --- a/scripts/uosc/main.lua +++ b/scripts/uosc/main.lua @@ -135,6 +135,8 @@ local config_defaults = { background = serialize_rgba('000000').color, background_text = serialize_rgba('ffffff').color, curtain = serialize_rgba('111111').color, + success = serialize_rgba('a5e075').color, + error = serialize_rgba('ff616e').color, }, opacity = { timeline = 0.9, @@ -293,6 +295,7 @@ function create_default_menu_items() {title = t('Inputs'), value = 'script-binding uosc/inputs'}, {title = t('Show in directory'), value = 'script-binding uosc/show-in-directory'}, {title = t('Open config folder'), value = 'script-binding uosc/open-config-directory'}, + {title = t('Update uosc'), value = 'script-binding uosc/update'}, }, }, {title = t('Quit'), value = 'quit'}, @@ -1141,6 +1144,9 @@ bind_command('open-config-directory', function() msg.error('Couldn\'t serialize config path "' .. config_path .. '".') end end) +bind_command('update', function() + if not Elements:has('updater') then require('elements/Updater'):new() end +end) --[[ MESSAGE HANDLERS ]] From e68952f1b2507dbb2f9aaf846e80d3ab1b668d1a Mon Sep 17 00:00:00 2001 From: tomasklaen Date: Wed, 18 Oct 2023 19:09:55 +0200 Subject: [PATCH 2/4] detect missing dependencies, warn for unsupported platforms --- scripts/uosc/elements/Updater.lua | 88 +++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/scripts/uosc/elements/Updater.lua b/scripts/uosc/elements/Updater.lua index fb39f253..07625a77 100644 --- a/scripts/uosc/elements/Updater.lua +++ b/scripts/uosc/elements/Updater.lua @@ -1,6 +1,10 @@ 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) @@ -10,26 +14,11 @@ function Updater:init() 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 args = state.platform == 'windows' - and {'powershell', '-NoProfile', '-Command', - 'irm https://raw.githubusercontent.com/tomasklaen/uosc/HEAD/installers/windows.ps1 | iex'} - or {'/bin/bash', '-c', - 'source <(curl -fsSL https://raw.githubusercontent.com/tomasklaen/uosc/HEAD/installers/unix.sh)'} - local env = utils.get_env_list() - env[#env + 1] = 'MPV_CONFIG_DIR=' .. mp.command_native({'expand-path', '~~/'}) - - -- Update - mp.command_native_async({ - name = 'subprocess', - capture_stderr = true, - capture_stdout = true, - playback_only = false, - args = args, - env = env, - }, function(success, result, error) + 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.') @@ -37,12 +26,67 @@ function Updater:init() self.state = 'error' self.message = t('There has been an error. See above for clues.') end - local output = ((result.stdout or '') .. '\n' .. (error or result.stderr or '')) - :gsub('%c*\n%c*', '\n') - :gsub('%c*$', '') - self.output = ass_escape(output) + + 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) + 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() From cdad03eddccada6027e394e4705f1f5dc1405ebb Mon Sep 17 00:00:00 2001 From: tomasklaen Date: Wed, 18 Oct 2023 19:29:31 +0200 Subject: [PATCH 3/4] readme --- README.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 50f51058..4c42a4f1 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Preview screenshot -Notable features: +Features: - UI elements hide and show based on their proximity to cursor instead of every time mouse moves. This provides 100% control over when you see the UI and when you don't. Click on the preview above to see it in action. - When timeline is unused, it can minimize itself into a small discrete progress bar. @@ -31,7 +31,7 @@ Notable features: [Changelog](https://github.com/tomasklaen/uosc/releases). -## Installation +## Install 1. These commands will install or update **uosc** and place a default `uosc.conf` file into `script-opts` if it doesn't exist already. @@ -49,7 +49,7 @@ Notable features: irm https://raw.githubusercontent.com/tomasklaen/uosc/HEAD/installers/windows.ps1 | iex ``` - **NOTE**: If this command is run in an mpv installation directory with `portable_config`, it'll install there instead of `AppData`. + _**NOTE**: If this command is run in an mpv installation directory with `portable_config`, it'll install there instead of `AppData`._ ### Linux & macOS @@ -292,7 +292,21 @@ Open directory with `mpv.conf` in file explorer. #### `update` -Updates uosc to the latest stable release. +Updates uosc to the latest stable release right from the UI. Available in default menu in the "Utils" section. + +Supported environments: + +| Env | Works | Note | +|:---|:---:|---| +| Windows | ✔️ | _Not tested on older PowerShell versions. And you might need to `Set-ExecutionPolicy` from the install instructions and install with the terminal command first._ | +| Linux (apt) | ✔️ | | +| Linux (flatpak) | ✔️ | | +| Linux (snap) | ❌ | We're not allowed to access commands like `curl` even if they're installed. (Or at least I think this is the issue.) | +| MacOS | ❌ | `(23) Failed writing body` error, whatever that means. | + +If you know about a solution to fix self-updater in one of the currently broken environments, please make an issue/PR and share it with us! + +**Note:** The terminal commands from install instructions still work fine everywhere, so you can use those to update instead. ## Menu From 9828415c292dcc691a26c411f4cae932f6f9e9a1 Mon Sep 17 00:00:00 2001 From: tomasklaen Date: Wed, 18 Oct 2023 22:51:10 +0200 Subject: [PATCH 4/4] words --- README.md | 8 ++++---- scripts/uosc/elements/Updater.lua | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4c42a4f1..f226b464 100644 --- a/README.md +++ b/README.md @@ -292,19 +292,19 @@ Open directory with `mpv.conf` in file explorer. #### `update` -Updates uosc to the latest stable release right from the UI. Available in default menu in the "Utils" section. +Updates uosc to the latest stable release right from the UI. Available in the "Utils" section of default menu . Supported environments: | Env | Works | Note | |:---|:---:|---| -| Windows | ✔️ | _Not tested on older PowerShell versions. And you might need to `Set-ExecutionPolicy` from the install instructions and install with the terminal command first._ | +| Windows | ✔️ | _Not tested on older PowerShell versions. You might need to `Set-ExecutionPolicy` from the install instructions and install with the terminal command first._ | | Linux (apt) | ✔️ | | | Linux (flatpak) | ✔️ | | -| Linux (snap) | ❌ | We're not allowed to access commands like `curl` even if they're installed. (Or at least I think this is the issue.) | +| Linux (snap) | ❌ | We're not allowed to access commands like `curl` even if they're installed. (Or at least this is what I think the issue is.) | | MacOS | ❌ | `(23) Failed writing body` error, whatever that means. | -If you know about a solution to fix self-updater in one of the currently broken environments, please make an issue/PR and share it with us! +If you know about a solution to fix self-updater for any of the currently broken environments, please make an issue/PR and share it with us! **Note:** The terminal commands from install instructions still work fine everywhere, so you can use those to update instead. diff --git a/scripts/uosc/elements/Updater.lua b/scripts/uosc/elements/Updater.lua index 07625a77..651c664d 100644 --- a/scripts/uosc/elements/Updater.lua +++ b/scripts/uosc/elements/Updater.lua @@ -24,7 +24,7 @@ function Updater:init() self.message = t('uosc has been installed. Restart mpv for it to take effect.') else self.state = 'error' - self.message = t('There has been an error. See above for clues.') + self.message = t('An error has occurred.') .. ' ' .. t('See above for clues.') end local output = (result.stdout or '') .. '\n' .. (error or result.stderr or '')