From bcb9d82a1f4d5b168d11270d7e3fdfd1b9591831 Mon Sep 17 00:00:00 2001 From: David Vazgenovich Shakaryan Date: Tue, 20 May 2025 18:52:15 -0700 Subject: reimplement downloader using async subprocesses --- downloader.lua | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ iptv-menu-dl.lua | 48 ------------------------------------------------ main.lua | 27 +++++++++++++-------------- 3 files changed, 68 insertions(+), 62 deletions(-) create mode 100644 downloader.lua delete mode 100644 iptv-menu-dl.lua diff --git a/downloader.lua b/downloader.lua new file mode 100644 index 0000000..67fb675 --- /dev/null +++ b/downloader.lua @@ -0,0 +1,55 @@ +-- Copyright 2025 David Vazgenovich Shakaryan + +local utils = require('mp.utils') + +local downloader = {} +local mt = {} +mt.__index = mt + +function downloader.new() + return setmetatable({ + pending = {}, + running = false, + }, mt) +end + +function mt:exec(url, file, cb) + if utils.file_info(file) then + self:exec_next() + return + end + + local cmd = {'curl', '-sSfLo', file, url} + print('exec: ' .. utils.to_string(cmd)) + + self.running = true + mp.command_native_async({ + name = 'subprocess', + args = cmd, + playback_only = false, + }, function(success, res) + self.running = false + self:exec_next() + if cb and success and res.status == 0 then + cb(url, file) + end + end) +end + +function mt:exec_next() + if #self.pending > 0 then + self:exec(unpack(table.remove(self.pending))) + end +end + +-- more recently requested downloads are executed first, as they are more +-- likely to be used for the current display state +function mt:schedule(...) + if self.running then + self.pending[#self.pending+1] = {...} + else + self:exec(...) + end +end + +return downloader diff --git a/iptv-menu-dl.lua b/iptv-menu-dl.lua deleted file mode 100644 index a207c91..0000000 --- a/iptv-menu-dl.lua +++ /dev/null @@ -1,48 +0,0 @@ --- Copyright 2025 David Vazgenovich Shakaryan - -local utils = require('mp.utils') - -local queue = {} - -function download(target, name, url, path) - if utils.file_info(path) then - return - end - - local cmd = 'curl -sSfLo \'' .. path .. '\'' .. ' \'' .. url .. '\'' - print('exec: ' .. cmd) - local ret = os.execute(cmd) - - if ret == 0 then - mp.commandv('script-message-to', target, name, url, path) - end -end - -function process_next() - download(unpack(table.remove(queue))) - - if #queue > 0 then - mp.add_timeout(0, process_next) - end -end - -mp.register_script_message('download-file', function(...) - queue[#queue+1] = {...} - - -- we want the ability for a later request to be downloaded before - -- earlier ones, e.g., to prioritise the image for the current menu. - -- - -- using a small timeout between receiving a message and processing it - -- allows for multiple pending messages to be received before any are - -- processed. however, immediately scheduling a timer for each request - -- does not work, as all elapsed timeouts are executed before any new - -- messages are received. - -- - -- the workaround is to schedule a timeout only when adding to an empty - -- queue and have each callback schedule an additional timeout if the - -- queue is not empty after completion. this effectively results in new - -- messages being received after each download. - if #queue == 1 then - mp.add_timeout(0, process_next) - end -end) diff --git a/main.lua b/main.lua index 1d2d252..88e15bc 100644 --- a/main.lua +++ b/main.lua @@ -1,6 +1,7 @@ -- Copyright 2025 David Vazgenovich Shakaryan -local xclib = require('xc') +local _downloader = require('downloader') +local _xc = require('xc') local utils = require('mp.utils') @@ -26,7 +27,8 @@ local colours = { local script_name = mp.get_script_name() local script_dir = mp.get_script_directory() -local xc = xclib.new({ +local downloader = _downloader.new() +local xc = _xc.new({ server = mp.get_opt('iptv_menu.xc_server'), user = mp.get_opt('iptv_menu.xc_user'), pass = mp.get_opt('iptv_menu.xc_pass'), @@ -133,6 +135,8 @@ local function write_json_file(fn, data) f:close() end +local update_osd + local function get_image_path(url, dl) local path = 'img/' .. url:gsub('%W', '_') @@ -142,10 +146,12 @@ local function get_image_path(url, dl) end if dl then - mp.commandv('script-message-to', - 'iptv_menu_dl', 'download-file', - script_name, 'downloaded-image', -- callback - url, path) + downloader:schedule(url, path, function(_, file) + if osd_img and file == osd_img.path then + update_osd() + end + end) + return path end end @@ -480,7 +486,7 @@ local function remove_osd_image() osd_img = nil end -local function update_osd() +function update_osd() local out = {} if depth > 1 then @@ -1366,12 +1372,6 @@ local function toggle_menu() end end -mp.register_script_message('downloaded-image', function(url, path) - if osd_img and path == osd_img.path then - update_osd() - end -end) - mp.observe_property('osd-dimensions', 'native', function(_, val) osd_width = val.w osd_height = val.h @@ -1394,4 +1394,3 @@ mp.add_forced_key_binding('TAB', 'toggle-menu', toggle_menu) bind_menu_keys() load_data() push_group_menu(objects['root']) -mp.commandv('load-script', script_dir .. '/iptv-menu-dl.lua') -- cgit v1.2.3-70-g09d2