summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--downloader.lua4
-rw-r--r--main.lua22
-rw-r--r--osd.lua147
3 files changed, 127 insertions, 46 deletions
diff --git a/downloader.lua b/downloader.lua
index 7881129..c96ba7b 100644
--- a/downloader.lua
+++ b/downloader.lua
@@ -46,8 +46,8 @@ function mt:exec(url, file, cb)
self.running = false
self:exec_next()
- if success and cb then
- cb(url, file)
+ if cb then
+ cb(success, url, file)
end
end)
end
diff --git a/main.lua b/main.lua
index 7c572f5..64b2e0d 100644
--- a/main.lua
+++ b/main.lua
@@ -13,7 +13,6 @@ local _xc = require('xc')
local mp_utils = require('mp.utils')
local script_name = mp.get_script_name()
-local script_dir = mp.get_script_directory()
local state = _state.new()
local binding_state = {mappings = {}, active = {}}
@@ -45,19 +44,32 @@ xc = cacher.wrap(xc, {
local catalogue = _catalogue.new()
local epg = _epg.new()
-local osd = _osd.new({
+
+local osd
+local function dl_img(url, path, cb)
+ downloader:schedule(url, path, function(success, _, path)
+ if success then
+ cb(path)
+ else
+ osd:flash_error('Image download failed')
+ end
+ end)
+
+end
+osd = _osd.new({
img_path_func = function(url, cb)
local path = mp_utils.join_path(
config.img_dir, url:gsub('%W', '_'))
if not mp_utils.file_info(path) and cb then
- downloader:schedule(url, path, function(_, path)
- cb(path)
- end)
+ dl_img(url, path, cb)
end
return path
end,
+ img_fail_cb = function()
+ osd:flash_error('Image load failed')
+ end
})
local function cache_miss_status_msg(str)
diff --git a/osd.lua b/osd.lua
index e638ee8..bf5dfec 100644
--- a/osd.lua
+++ b/osd.lua
@@ -70,10 +70,36 @@ function mt:resize(w, h)
self.scale = h / 720
end
-function mt:set_status(msg, level)
+function mt:set_status(msg, level, no_dirty)
+ if self.status_timer then
+ self.status_timer:kill()
+ self.status_timer = nil
+ end
+
self.status_msg = msg
self.status_level = level
- self:dirty()
+
+ if not no_dirty then
+ self:dirty()
+ end
+end
+
+function mt:flash_status(msg, level, secs)
+ self:set_status(msg, level, true)
+ self:render()
+
+ self.status_timer = mp.add_timeout(secs or 3, function()
+ self.status_timer = nil
+
+ if self.status_msg == msg then
+ self:set_status(nil, nil, true)
+ self:render()
+ end
+ end)
+end
+
+function mt:flash_error(msg, secs)
+ self:flash_status(msg, 'error', secs)
end
function mt:status_line()
@@ -228,7 +254,14 @@ function mt:load_img()
self.magick_cmd = nil
self.magick_cmd_id = nil
- if not self.img or not res or res.status ~= 0 then
+ if not self.img then
+ return
+ end
+
+ if not res or res.status ~= 0 then
+ if self.img_fail_cb then
+ self.img_fail_cb()
+ end
return
end
@@ -370,9 +403,66 @@ function mt:draw_scrollbar(state)
'{\\pos(2,' .. top + pos .. ')}' .. draw_rect(0, 0, w, hh))
end
-function mt:redraw(state)
+-- this takes the data generated by redraw() and related functions, prepares it
+-- for rendering and pushes the result to the actual OSD. since it has no
+-- dependence on `state', it's simpler and more efficient to use this directly
+-- for things like displaying status messages.
+function mt:render()
local out = {}
+ for _, v in ipairs(self.out.titles) do
+ out[#out+1] = v
+ end
+
+ -- use spacer line between titles and options for status messages
+ out[#out+1] = self:status_line()
+
+ for _, v in ipairs(self.out.options) do
+ out[#out+1] = v
+ end
+
+ local buf = {}
+ -- each line uses a new ASS event with explicit positioning. otherwise,
+ -- lines with nonstandard height break things.
+ -- \q2 disables line wrapping
+ local pre = '{\\q2\\fs' .. config.font_size ..
+ '\\pos(' .. self.padding .. ',%s)}'
+ for i, v in ipairs(out) do
+ local offset = self.padding + ((i - 1) * config.font_size)
+ buf[#buf+1] = pre:format(offset) .. v
+ end
+ -- rendering `max' is needed to compute bounds only when there is an
+ -- image to display.
+ if self.out.img then
+ buf[#buf+1] = '{\\alpha&HFF&}' .. pre:format(0) ..
+ table.concat(self.out.max, '\\N')
+ end
+ self.fg.data = table.concat(buf, '\n')
+
+ self.fg.compute_bounds = not not self.out.img_path
+ local res = self.fg:update()
+ -- unset compute_bounds because it is relatively expensive and we don't
+ -- need it when update() is called to toggle visibility of the last
+ -- rendered data.
+ self.fg.compute_bounds = false
+ self.bg:update()
+ if self.out.img_path then
+ local upd, new = self:set_img(self.out.img_path, res)
+ if upd then
+ if new then
+ self:clear_img()
+ end
+ self:draw_img()
+ end
+ else
+ self:clear_img(true)
+ end
+end
+
+function mt:redraw(state)
+ local out_titles = {}
+ local out_options = {}
+
-- to determine the area left for images, we use the computed bounds of
-- the rendered menu, which shifts when the cursor is moved to/from the
-- longest line. to prevent this, this table stores the menu options
@@ -387,12 +477,9 @@ function mt:redraw(state)
local out_max = {}
for i = 1, state.depth do
- out[#out+1] = self:menu_title(state.menus[i])
+ out_titles[#out_titles+1] = self:menu_title(state.menus[i])
end
- -- use spacer line between titles and options for status messages
- out[#out+1] = self:status_line()
-
local menu = state:menu()
local img
@@ -422,7 +509,8 @@ function mt:redraw(state)
}
local str = self:option_text(opt, info) ..
self:option_path(opt, info)
- out[#out+1] = self:option_icons(opt, info) .. str
+ out_options[#out_options+1] = self:option_icons(opt, info) ..
+ str
if selected and opt.img_url then
img = opt.img_url
@@ -432,22 +520,14 @@ function mt:redraw(state)
out_max[#out_max+1] = self:option_icons(opt, info) .. str
end
- local buf = {}
- -- each line uses a new ASS event with explicit positioning. otherwise,
- -- lines with nonstandard height break things.
- -- \q2 disables line wrapping
- local pre = '{\\q2\\fs' .. config.font_size ..
- '\\pos(' .. self.padding .. ',%s)}'
- for i, v in ipairs(out) do
- local offset = self.padding + ((i - 1) * config.font_size)
- buf[#buf+1] = pre:format(offset) .. v
- end
- if img then
- buf[#buf+1] = '{\\alpha&HFF&}' .. pre:format(0) ..
- table.concat(out_max, '\\N')
- end
- self.fg.data = table.concat(buf, '\n')
+ self.out = {
+ titles = out_titles,
+ options = out_options,
+ max = out_max,
+ }
+ -- we can draw this directly from here since any changes handled by
+ -- render() will not modify this.
self.bg.data =
'{\\pos(0,0)\\alpha&H' .. config.bg_alpha .. '&\\c&H&}' ..
draw_rect(0, 0, 7680, 720)
@@ -456,28 +536,17 @@ function mt:redraw(state)
self.bg.data = self.bg.data .. '\n' .. sb
end
- self.fg.compute_bounds = not not img
- local res = self.fg:update()
- self.fg.compute_bounds = false
- self.bg:update()
if img and self.img_path_func then
- local path = self.img_path_func(img, function(path)
+ self.out.img_path = self.img_path_func(img, function(path)
+ -- these are set by render(), which we always call
+ -- before returning.
if self.img and path == self.img.path then
self:draw_img()
end
end)
-
- local upd, new = self:set_img(path, res)
- if upd then
- if new then
- self:clear_img()
- end
- self:draw_img()
- end
- else
- self:clear_img(true)
end
+ self:render()
self.is_dirty = false
end