Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added update command to update uosc #700

Merged
merged 4 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<a href="https://user-images.githubusercontent.com/47283320/195073006-bfa72bcc-89d2-4dc7-b8dc-f3c13273910c.webm"><img src="https://user-images.githubusercontent.com/47283320/195072935-44d591d9-00bb-4a55-8795-9cf81f65d397.png" alt="Preview screenshot"></a>
</div>

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.
Expand All @@ -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.

Expand All @@ -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

Expand Down Expand Up @@ -290,6 +290,24 @@ Switch audio output device.

Open directory with `mpv.conf` in file explorer.

#### `update`

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. 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 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 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.

## Menu

**uosc** provides a way to build, display, and use your own menu. By default it displays a pre-configured menu with common actions.
Expand Down Expand Up @@ -401,6 +419,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
```

Expand Down
3 changes: 2 additions & 1 deletion script-opts/uosc.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 1 addition & 2 deletions scripts/uosc/elements/BufferingIndicator.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion scripts/uosc/elements/Curtain.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {}
Expand Down
12 changes: 6 additions & 6 deletions scripts/uosc/elements/Element.lua
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down
14 changes: 5 additions & 9 deletions scripts/uosc/elements/Elements.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions scripts/uosc/elements/Menu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -133,15 +133,15 @@ 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

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
Expand Down
2 changes: 1 addition & 1 deletion scripts/uosc/elements/PauseIndicator.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion scripts/uosc/elements/Timeline.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
170 changes: 170 additions & 0 deletions scripts/uosc/elements/Updater.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
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_down', button_rect, function() self:destroy() end)
end

return ass
end

return Updater
3 changes: 1 addition & 2 deletions scripts/uosc/elements/WindowBorder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions scripts/uosc/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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'},
Expand Down Expand Up @@ -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 ]]

Expand Down