summaryrefslogtreecommitdiff
path: root/main.lua
diff options
context:
space:
mode:
Diffstat (limited to 'main.lua')
-rw-r--r--main.lua1092
1 files changed, 77 insertions, 1015 deletions
diff --git a/main.lua b/main.lua
index 31c58ab..ea11ebf 100644
--- a/main.lua
+++ b/main.lua
@@ -2,6 +2,7 @@
local cacher = require('cacher')
local config = require('config')
+local rt = require('rt')
local util = require('util')
local _catalogue = require('catalogue')
local _downloader = require('downloader')
@@ -45,6 +46,13 @@ xc = cacher.wrap(xc, {
local catalogue = _catalogue.new()
local epg = _epg.new()
+local ctx = {
+ binding_state = binding_state,
+ catalogue = catalogue,
+ epg = epg,
+ xc = xc,
+}
+
local osd
local function dl_img(url, path, cb)
downloader:schedule(url, path, function(success, _, path)
@@ -72,941 +80,7 @@ osd = _osd.new({
end
})
-local function cache_miss_status_msg(str)
- -- doesn't redraw after clearing message
- return {
- before_miss = function()
- osd:set_status(str)
- osd:redraw(state)
- end,
- after_miss = function()
- osd:set_status()
- end,
- }
-end
-
-local function set_key_mapping(m)
- binding_state.active = binding_state.mappings[m]
-end
-
-local function load_data(force)
- local arr = {
- {id = 'live', name = 'Live TV', type = 'live'},
- {id = 'movie', name = 'Movies', type = 'vod'},
- {id = 'series', name = 'Series', type = 'series'},
- }
-
- local base_str = 'Loading catalogue'
- local sect_str
- local disp_str
- local call_opts = {
- force = not not force,
- before_hit = function()
- if disp_str ~= base_str and disp_str ~= sect_str then
- osd:set_status(base_str .. '...')
- osd:redraw(state)
- disp_str = base_str
- end
- end,
- before_miss = function()
- if disp_str ~= sect_str then
- osd:set_status(sect_str .. '...')
- osd:redraw(state)
- disp_str = sect_str
- end
- end,
- }
- for _, v in ipairs(arr) do
- sect_str = base_str .. ' ยป ' .. v.name
- v.categories = xc:with_opts(
- 'get_' .. v.type .. '_categories', call_opts)
- v.elements = xc:with_opts(
- v.type == 'series' and 'get_series' or
- ('get_' .. v.type .. '_streams'),
- call_opts)
- catalogue:load_xc_section(v)
- end
-
- osd:set_status('Loading EPG...')
- osd:redraw(state)
- epg:load_xc_data(xc:with_opts('get_epg', {force = not not force}))
- osd:set_status()
-
- local t = util.read_json_file(config.favourites_file)
- state.favourites = t.favourites or {}
-end
-
-local function save_favourites()
- util.write_json_file(
- config.favourites_file, {favourites = state.favourites})
-end
-
-local function set_cursor(pos, opts)
- local moved = state:menu():set_cursor(pos, osd:menu_lines(state), opts)
- if moved then
- osd:dirty()
- end
-end
-
-local function cursor_up()
- set_cursor(state:menu().cursor - 1, {margin = config.scroll_margin})
-end
-
-local function cursor_down()
- set_cursor(state:menu().cursor + 1, {margin = config.scroll_margin})
-end
-
-local function cursor_start()
- set_cursor(1)
-end
-
-local function cursor_end()
- set_cursor(#state:menu().options)
-end
-
-local function cursor_page_up()
- set_cursor(
- state:menu().cursor - osd:menu_lines(state),
- {keep_offset = true, margin = config.scroll_margin})
-end
-
-local function cursor_page_down()
- set_cursor(
- state:menu().cursor + osd:menu_lines(state),
- {keep_offset = true, margin = config.scroll_margin})
-end
-
-local function cursor_wheel_up()
- set_cursor(state:menu().cursor - 1, {keep_offset = true})
-end
-
-local function cursor_wheel_down()
- set_cursor(state:menu().cursor + 1, {keep_offset = true})
-end
-
-local function cursor_wheel_page_up()
- set_cursor(
- state:menu().cursor - osd:menu_lines(state),
- {keep_offset = true})
-end
-
-local function cursor_wheel_page_down()
- set_cursor(
- state:menu().cursor + osd:menu_lines(state),
- {keep_offset = true})
-end
-
-local function cursor_to_object(id)
- for i, v in ipairs(state:menu().options) do
- if v.id == id then
- set_cursor(i, {centre = true})
- return
- end
- end
-end
-
-local function move_option(pos, opts)
- local menu = state:menu()
- if menu.group_id ~= 'favourites' or menu.sorted then
- return
- end
-
- local prev_cursor = menu.cursor
- local opts = opts or {}
- set_cursor(pos, opts)
- if menu.cursor == prev_cursor then
- return
- end
-
- local opt = table.remove(menu.options, prev_cursor)
- table.insert(menu.options, menu.cursor, opt)
-
- local ind = state:favourited(opt.id)
- if ind then
- state:remove_favourite_at(ind)
- state:insert_favourite_before_next_in_menu(opt.id)
- save_favourites()
- end
-
- osd:dirty()
-end
-
-local function move_option_up()
- move_option(state:menu().cursor - 1, {margin = config.scroll_margin})
-end
-
-local function move_option_down()
- move_option(state:menu().cursor + 1, {margin = config.scroll_margin})
-end
-
-local function move_option_start()
- move_option(1)
-end
-
-local function move_option_end()
- move_option(#state:menu().options)
-end
-
-local function move_option_page_up()
- move_option(
- state:menu().cursor - osd:menu_lines(state),
- {keep_offset = true, margin = config.scroll_margin})
-end
-
-local function move_option_page_down()
- move_option(
- state:menu().cursor + osd:menu_lines(state),
- {keep_offset = true, margin = config.scroll_margin})
-end
-
-local function move_option_wheel_up()
- move_option(state:menu().cursor - 1, {keep_offset = true})
-end
-
-local function move_option_wheel_down()
- move_option(state:menu().cursor + 1, {keep_offset = true})
-end
-
-local function move_option_wheel_page_up()
- move_option(
- state:menu().cursor - osd:menu_lines(state),
- {keep_offset = true})
-end
-
-local function move_option_wheel_page_down()
- move_option(
- state:menu().cursor + osd:menu_lines(state),
- {keep_offset = true})
-end
-
-local function sort_options(options)
- local scores = {}
- for _, v in ipairs(options) do
- local score = 0
- if v.missing then
- score = score - 4
- end
- if state:favourited(v.id) then
- score = score + 2
- end
- if v.type == 'group' and v.group_type ~= 'series' then
- score = score + 1
- end
- scores[v] = score
- end
-
- table.sort(options, function(a, b)
- local sa = scores[a]
- local sb = scores[b]
- if sa ~= sb then
- return sa > sb
- else
- return a.name < b.name
- end
- end)
-end
-
-local function add_programme(opt, time)
- if opt.epg_channel_id then
- local prog = epg:scheduled_programme(opt.epg_channel_id, time)
- if prog then
- opt.info = prog.title
- end
- end
-end
-
-local function group_count(group)
- if group.children and not group.lazy then
- local count = 0
- for _, v in ipairs(group.children) do
- if v.type == 'stream' or v.group_type == 'series' then
- count = count + 1
- elseif v.type == 'group' then
- local c = group_count(v)
- if c then
- count = count + c
- end
- end
- end
- return count
- elseif group.id == 'favourites' then
- -- not recursive
- return #state.favourites
- end
-end
-
-local function favourites_group_menu_options(group)
- local options = {}
- local time = os.time()
- for _, id in ipairs(state.favourites) do
- local obj = catalogue:get(id)
- if obj then
- obj = util.copy_table(obj)
- add_programme(obj, time)
- local c = group_count(obj)
- if c then
- obj.info = tostring(c)
- end
- local path = catalogue:path_from_root(obj)
- if path then
- obj.path = path
- end
- options[#options+1] = obj
- else
- -- display missing favourites so that they can be
- -- removed
- options[#options+1] = {
- id = id,
- name = id,
- missing = true,
- }
- end
- end
- return options
-end
-
-local function series_group_menu_options(series)
- local info = xc:with_opts('get_series_info', series.series_id,
- cache_miss_status_msg('Loading series info...'))
- if not info or not info.seasons then
- return {}
- end
-
- local seasons = {}
- for _, season in pairs(info.seasons) do
- local episodes = {}
- local season_num = tostring(season.season_number)
- if info.episodes and info.episodes[season_num] then
- for i, episode in pairs(info.episodes[season_num]) do
- local epinfo = episode.info or {}
- local t = {
- name = util.strip(episode.title),
- type = 'stream',
- stream_type = 'series',
- id = series.section .. ':stream:' ..
- episode.id,
- stream_id = episode.id,
- img_url = util.strip_ne(
- epinfo.movie_image),
- }
- t.info_data = {
- name = t.name,
- cover = t.img_url,
- description = epinfo.plot,
- releasedate = epinfo.releasedate,
- duration = epinfo.duration,
- video = epinfo.video,
- audio = epinfo.audio,
- }
- episodes[#episodes+1] = t
- end
- end
-
- local count = tostring(#episodes)
- local tmp = util.strip_ne(season.episode_count)
- if tmp then
- count = count .. '/' .. tmp
- end
- local t = {
- type = 'group',
- group_type = 'season',
- id = series.id .. ':season:' .. season.id,
- children = episodes,
- name = util.strip(season.name),
- info = count,
- img_url = util.strip_ne(season.cover_big) or
- util.strip_ne(season.cover),
- }
- t.info_data = {
- name = t.name,
- cover = t.img_url,
- description = season.overview,
- releasedate = season.air_date,
- num_episodes = count,
- }
- seasons[#seasons+1] = t
- end
-
- return seasons
-end
-
-local function group_menu_options(group)
- if group.id == 'favourites' then
- return favourites_group_menu_options(group)
- end
-
- if group.group_type == 'series' then
- return series_group_menu_options(group)
- end
-
- local options = {}
- local time = os.time()
- for i, v in ipairs(group.children) do
- v = util.copy_table(v)
- add_programme(v, time)
- local c = group_count(v)
- if c then
- v.info = tostring(c)
- end
- options[i] = v
- end
- return options
-end
-
-local function push_group_menu(group)
- state:push_menu({
- options = group_menu_options(group),
- title = group.name,
- type = 'group',
- group_id = group.id,
- })
-end
-
--- refresh options when navigating up the stack to a previous favourites menu.
--- existing menu options are never removed, even if unfavourited. the new order
--- is always respected, while still preserving the relative order of existing
--- options when possible.
-local function refresh_favourites_menu()
- local menu = state:menu()
- local opt = menu.options[menu.cursor]
- local sorted = menu.sorted
- if sorted then
- menu:set_sort(false)
- end
-
- local options = group_menu_options(catalogue:get(menu.group_id))
- local pos = {}
- for i, v in ipairs(options) do
- pos[v.id] = i
- end
-
- local res = {}
- local seen = {}
- local function append(v)
- if not seen[v.id] then
- res[#res+1] = v
- seen[v.id] = true
- end
- end
-
- local ind = 1
- for _, v in ipairs(menu.options) do
- if pos[v.id] then
- while ind <= pos[v.id] do
- append(options[ind])
- ind = ind + 1
- end
- end
- append(v)
- end
- for i = ind, #options do
- append(options[i])
- end
- menu.options = res
-
- if sorted then
- menu:set_sort(true, sort_options)
- end
- if opt then
- cursor_to_object(opt.id)
- end
-end
-
-local function prev_menu()
- state.depth = state.depth - 1
-
- if state.depth == 0 then
- -- reset main menu
- push_group_menu(catalogue:get(state.menus[1].group_id))
- else
- if state:menu().group_id == 'favourites' then
- refresh_favourites_menu()
- end
- end
-
- osd:dirty()
-end
-
-local function play_stream(stream)
- local url = stream.stream_url or
- xc:stream_url(stream.stream_type, stream.stream_id)
- if not url then
- return
- end
-
- -- add a per-file option containing the stream id, allowing it to be
- -- retrieved when a start-file event is received
- mp.commandv('loadfile', url, 'replace', -1,
- 'script-opt=iptv_menu.playing_id=' .. stream.id)
-end
-
-local function select_option()
- local menu = state:menu()
- local opt = menu.options[menu.cursor]
- if not opt or not opt.id then
- return
- end
-
- if opt.type == 'group' then
- push_group_menu(opt)
- osd:dirty()
- elseif opt.type == 'stream' then
- play_stream(opt)
- end
-end
-
-local function favourite_option()
- local menu = state:menu()
- local opt = menu.options[menu.cursor]
- if not opt or not opt.id then
- return
- end
-
- local ind = state:favourited(opt.id)
- if ind then
- state:remove_favourite_at(ind)
- elseif menu.group_id == 'favourites' then
- state:insert_favourite_before_next_in_menu(opt.id)
- else
- state:add_favourite(opt.id)
- end
-
- save_favourites()
- osd:dirty()
-end
-
-local function goto_option()
- local menu = state:menu()
- local opt = menu.options[menu.cursor]
- if not opt or not opt.path then
- return
- end
-
- if menu.group_id == 'favourites' then
- state.depth = 1
- elseif menu.type == 'search' then
- state.depth = state.depth - 1
- if state:menu().group_id == 'favourites' then
- refresh_favourites_menu()
- end
- end
-
- for i = 1, #opt.path do
- cursor_to_object(opt.path[i].id)
- push_group_menu(opt.path[i])
- end
- cursor_to_object(opt.id)
-
- osd:dirty()
-end
-
-local function goto_playing()
- local id = state.playing_id
- local obj = id and catalogue:get(id)
- local path = obj and catalogue:path_to_root(obj)
- if not path then
- return
- end
-
- state.depth = 1
- for i = #path, 1, -1 do
- cursor_to_object(path[i].id)
- push_group_menu(path[i])
- end
- cursor_to_object(id)
-
- osd:dirty()
-end
-
-local function open_epg_programme(prog, img_url)
- local options = {
- {name = 'Title: ' .. prog.title},
- {name = 'Start: ' .. os.date('%a %d %b %H:%M', prog.start)},
- {name = 'Stop: ' .. os.date('%a %d %b %H:%M', prog.stop)},
- }
-
- if prog.desc then
- options[#options+1] = {name = ''}
- for _, v in ipairs(util.wrap(prog.desc, 80)) do
- options[#options+1] = {name = v}
- end
- end
-
- local menu = state:push_menu({
- options = options,
- title = 'Programme: ' .. prog.title,
- type = 'epg',
- })
- if img_url then
- menu.img_url = img_url
- end
- osd:dirty()
-end
-
-local function open_option_epg(opt)
- local ch = opt.epg_channel_id:lower()
- local progs = epg:channel_programmes(ch)
- if not progs then
- return
- end
-
- local options = {}
- local curr = 0
- local time = os.time()
- for i, v in ipairs(progs) do
- prog = {
- name = os.date('%a %d %b %H:%M', v.start) .. ' ' ..
- os.date('%H:%M', v.stop) .. ' ' .. v.title,
- info = v.desc,
- programme = v,
- }
-
- if curr == 0 and time >= v.start and time < v.stop then
- curr = i
- prog.active = true
- end
-
- options[i] = prog
- end
-
- local menu = state:push_menu({
- options = options,
- title = 'EPG: ' .. opt.name .. ' (' .. ch .. ')',
- type = 'epg',
- })
- if opt.img_url then
- menu.img_url = opt.img_url
- end
- set_cursor(curr, {centre = true})
- osd:dirty()
-end
-
-local function add_info_field(dst, k, v, fmt)
- local str = util.strip_ne(v)
- if not str then
- return
- end
- if fmt then
- str = string.format(fmt, str)
- end
- if k then
- str = k .. ': ' .. str
- end
-
- -- continuation lines are 4 chars shorter and indented with 2 em spaces
- for i, v in ipairs(util.wrap(str, 80, 76)) do
- if i > 1 then
- v = '\xe2\x80\x83\xe2\x80\x83' .. v
- end
- dst[#dst+1] = {name = v}
- end
-end
-
-local function add_info_set(options, set)
- if #set > 0 then
- options[#options+1] = {name = ''}
- for _, v in ipairs(set) do
- options[#options+1] = v
- end
- end
-end
-
-local function open_option_title_info(title, info)
- if not info or not info.info then
- return
- end
- local info = info.info
-
- local options = {}
- add_info_field(options, nil, info.name)
- add_info_field(options, 'Directed by', info.director)
- add_info_field(options, 'Starring', info.cast)
-
- local desc = util.strip_ne(info.description) or
- util.strip_ne(info.plot)
- if desc then
- options[#options+1] = {name = ''}
- for _, v in ipairs(util.wrap(desc, 80)) do
- options[#options+1] = {name = v}
- end
- end
-
- local set = {}
- add_info_field(set, 'Genre', info.genre)
- local date = util.strip_ne(info.releasedate) or
- util.strip_ne(info.releaseDate)
- if date then
- local y, m, d = date:match('(%d+)-(%d+)-(%d+)')
- if y then
- local dt = {year = y, month = m, day = d}
- add_info_field(set, 'Release date',
- os.date('%d %B %Y', os.time(dt)))
- end
- end
- add_info_field(set, 'Episode count', info.num_episodes)
- add_info_field(set, 'Running time', info.duration)
- add_info_set(options, set)
-
- local set = {}
- if info.video then
- local w = util.strip_ne(info.video.width)
- local h = util.strip_ne(info.video.height)
- if w and h then
- local res = w .. 'x' .. h
- local ar = util.strip_ne(
- info.video.display_aspect_ratio)
- if ar then
- res = res .. ' (' .. ar .. ')'
- end
- add_info_field(set, 'Resolution', res)
- end
- add_info_field(set, 'Video codec', info.video.codec_long_name)
- end
- if info.audio then
- add_info_field(set, 'Audio codec', info.audio.codec_long_name)
- end
- if info.bitrate ~= 0 then
- add_info_field(set, 'Bitrate', info.bitrate, '%s Kbps')
- end
- add_info_set(options, set)
-
- local ytid = util.strip_ne(info.youtube_trailer)
- if ytid then
- local url = 'https://youtu.be/' .. ytid
- add_info_set(options, {{
- name = 'Trailer: ' .. url,
- type = 'stream',
- id = 'youtube:' .. ytid,
- stream_url = url,
- }})
- end
-
- local m = {
- options = options,
- title = title,
- type = 'info',
- }
- local img = util.strip_ne(info.cover_big) or util.strip_ne(info.cover)
- if img then
- m.img_url = img
- end
-
- state:push_menu(m)
- osd:dirty()
-end
-
-local function open_option_movie_info(opt)
- local info = xc:with_opts('get_vod_info', opt.stream_id,
- cache_miss_status_msg('Loading movie info...'))
- open_option_title_info('Movie Info: ' .. opt.name, info)
-end
-
-local function open_option_series_info(opt)
- local info = xc:with_opts('get_series_info', opt.series_id,
- cache_miss_status_msg('Loading series info...'))
- open_option_title_info('Series Info: ' .. opt.name, info)
-end
-
-local function open_option_season_info(opt)
- open_option_title_info(
- 'Season Info: ' .. opt.name,
- {info = opt.info_data})
-end
-
-local function open_option_episode_info(opt)
- open_option_title_info(
- 'Episode Info: ' .. opt.name,
- {info = opt.info_data})
-end
-
-local function open_option_info(opt)
- local menu = state:menu()
- local opt = opt or menu.options[menu.cursor]
- if not opt then
- return
- end
-
- if menu.type == 'epg' and opt.programme then
- open_epg_programme(opt.programme, menu.img_url)
- elseif opt.epg_channel_id then
- open_option_epg(opt)
- elseif opt.group_type == 'series' then
- open_option_series_info(opt)
- elseif opt.group_type == 'season' then
- open_option_season_info(opt)
- elseif opt.stream_type == 'series' then
- open_option_episode_info(opt)
- elseif opt.stream_type == 'movie' then
- open_option_movie_info(opt)
- end
-end
-
-local function search_menu_options_build(options, t, path)
- local menu = state:menu()
- local path = path or {}
-
- for _, v in ipairs(options) do
- local v = util.copy_table(v)
- v.path = path
- if v.type == 'group' and v.group_type ~= 'series' then
- t.categories[#t.categories+1] = v
- else
- t.elements[#t.elements+1] = v
- end
-
- -- contents of lazy-loaded groups should not be searchable
- if v.type == 'group' and not v.lazy then
- local path = util.copy_table(path)
- path[#path+1] = v
- search_menu_options_build(
- group_menu_options(v), t, path)
- end
- end
-end
-
-local function search_menu_options(options)
- local t = {categories = {}, elements = {}}
- search_menu_options_build(options, t)
-
- -- display categories first
- local ret = t.categories
- for _, v in ipairs(t.elements) do
- ret[#ret+1] = v
- end
- return ret
-end
-
-local function search_input_char(ev)
- if ev.event ~= 'down' and ev.event ~= 'repeat' then
- return
- end
-
- local menu = state:menu()
- menu:set_search_text(
- menu.search_text:sub(1, menu.search_cursor - 1) ..
- ev.key_text ..
- menu.search_text:sub(menu.search_cursor))
- menu:set_search_cursor(menu.search_cursor + #ev.key_text)
- osd:dirty()
-end
-
-local function search_input_bs()
- local menu = state:menu()
- if menu.search_cursor <= 1 then
- return
- end
-
- local pos = util.utf8_seek(menu.search_text, menu.search_cursor, -1)
- menu:set_search_text(
- menu.search_text:sub(1, pos - 1) ..
- menu.search_text:sub(menu.search_cursor))
- menu:set_search_cursor(pos)
- osd:dirty()
-end
-
-local function search_input_del()
- local menu = state:menu()
- if menu.search_cursor > #menu.search_text then
- return
- end
-
- menu:set_search_text(
- menu.search_text:sub(1, menu.search_cursor - 1) ..
- menu.search_text:sub(util.utf8_seek(
- menu.search_text, menu.search_cursor, 1)))
- osd:dirty()
-end
-
-local function set_search_cursor(pos)
- if state:menu():set_search_cursor(pos) then
- osd:dirty()
- end
-end
-
-local function search_cursor_left()
- local menu = state:menu()
- set_search_cursor(util.utf8_seek(
- menu.search_text, menu.search_cursor, -1))
-end
-
-local function search_cursor_right()
- local menu = state:menu()
- set_search_cursor(util.utf8_seek(
- menu.search_text, menu.search_cursor, 1))
-end
-
-local function search_cursor_start()
- set_search_cursor(1)
-end
-
-local function search_cursor_end()
- set_search_cursor(#state:menu().search_text + 1)
-end
-
-local function start_search()
- local menu = state:menu()
- local title = 'Searching: <text_with_cursor>' ..
- ' <colour.info>(<num_matches>/<num_total>)'
-
- if menu.type == 'search' then
- -- resuming search, save previous state
- menu.prev_search_text = menu.search_text
- menu.prev_cursor = menu.cursor
- menu.prev_view_top = menu.view_top
-
- menu.title = title
- menu.search_active = true
- menu:set_search_cursor(#menu.search_text + 1)
- menu:set_cursor(1)
- else
- state:push_menu({
- title = title,
- type = 'search',
- options = search_menu_options(menu.options),
- search_active = true,
- })
- end
-
- osd:dirty()
- set_key_mapping('SEARCH')
-end
-
-local function end_search()
- local menu = state:menu()
- menu.search_active = false
- menu.title = 'Search results: <text>' ..
- ' <colour.info>(<num_matches>/<num_total>)'
- osd:dirty()
- set_key_mapping('MENU')
-end
-
-local function cancel_search()
- local menu = state:menu()
-
- -- cancelling resumed search restores previous state
- if menu.prev_search_text then
- menu:set_search_text(menu.prev_search_text)
- menu.cursor = menu.prev_cursor
- menu.view_top = menu.prev_view_top
- end_search()
- return
- end
-
- menu.search_active = false
- state.depth = state.depth - 1
- osd:dirty()
- set_key_mapping('MENU')
-end
-
-local function toggle_menu_sort()
- local menu = state:menu()
- if menu.type ~= 'group' and menu.type ~= 'search' then
- return
- end
-
- menu:set_sort(not menu.sorted, sort_options)
- osd:dirty()
-end
+rt.init(state, osd, ctx)
local function mouse_has_drifted(x1, y1, x2, y2)
return math.abs(x1 - x2) > config.click_max_drift or
@@ -1091,7 +165,7 @@ local function mouse_click_left_menu(dbl, line)
end
if line == -1 then
- set_cursor(1)
+ rt.set_cursor(1)
else
state.depth = state.depth + line + 1
osd:dirty()
@@ -1106,13 +180,13 @@ local function mouse_click_left_menu(dbl, line)
return
end
- select_option()
+ rt.select_option()
else
if pos > #menu.options then
return
end
- set_cursor(pos)
+ rt.set_cursor(pos)
end
end
@@ -1122,7 +196,7 @@ local function mouse_click_left_scrollbar(dbl, ratio)
end
-- set_cursor handles out-of-bounds moves (when ratio == 1)
- set_cursor(
+ rt.set_cursor(
math.floor(ratio * #state:menu().options) + 1,
{centre = true})
end
@@ -1151,7 +225,7 @@ local function mouse_click_right_menu(dbl, line)
return
end
- open_option_info(menu.options[pos])
+ rt.open_option_info(menu.options[pos])
end
local function mouse_click_right(ev)
@@ -1165,82 +239,70 @@ local function mouse_click_right(ev)
end
end
-local function reload_data()
- if state.depth > 1 then
- osd:flash_error('Can only reload data from root menu')
- return
- end
-
- catalogue = _catalogue.new()
- load_data(true)
- state.depth = 0
- push_group_menu(catalogue:get('root'))
-end
-
binding_state.mappings.MENU = {
- ['BS'] = {prev_menu},
- ['/'] = {start_search},
- ['Ctrl+s'] = {toggle_menu_sort},
- ['Ctrl+R'] = {reload_data},
-
- ['ENTER'] = {select_option},
- ['Ctrl+f'] = {favourite_option},
- ['g'] = {goto_option},
- ['i'] = {open_option_info},
- ['?'] = {open_option_info},
- ['Ctrl+p'] = {goto_playing},
-
- ['MBTN_LEFT'] = {mouse_click_left, 'complex'},
- ['MBTN_RIGHT'] = {mouse_click_right, 'complex'},
-
- ['k'] = {cursor_up, 'repeat'},
- ['j'] = {cursor_down, 'repeat'},
- ['K'] = {cursor_page_up, 'repeat'},
- ['J'] = {cursor_page_down, 'repeat'},
- ['UP'] = {cursor_up, 'repeat'},
- ['DOWN'] = {cursor_down, 'repeat'},
- ['Shift+UP'] = {cursor_page_up, 'repeat'},
- ['Shift+DOWN'] = {cursor_page_down, 'repeat'},
- ['PGUP'] = {cursor_page_up, 'repeat'},
- ['PGDWN'] = {cursor_page_down, 'repeat'},
- ['HOME'] = {cursor_start},
- ['END'] = {cursor_end},
- ['WHEEL_UP'] = {cursor_wheel_up, 'repeat'},
- ['WHEEL_DOWN'] = {cursor_wheel_down, 'repeat'},
- ['Shift+WHEEL_UP'] = {cursor_wheel_page_up, 'repeat'},
- ['Shift+WHEEL_DOWN'] = {cursor_wheel_page_down, 'repeat'},
-
- ['Alt+k'] = {move_option_up, 'repeat'},
- ['Alt+j'] = {move_option_down, 'repeat'},
- ['Alt+K'] = {move_option_page_up, 'repeat'},
- ['Alt+J'] = {move_option_page_down, 'repeat'},
- ['Alt+UP'] = {move_option_up, 'repeat'},
- ['Alt+DOWN'] = {move_option_down, 'repeat'},
- ['Shift+Alt+UP'] = {move_option_page_up, 'repeat'},
- ['Shift+Alt+DOWN'] = {move_option_page_down, 'repeat'},
- ['Alt+PGUP'] = {move_option_page_up, 'repeat'},
- ['Alt+PGDWN'] = {move_option_page_down, 'repeat'},
- ['Alt+HOME'] = {move_option_start},
- ['Alt+END'] = {move_option_end},
- ['Alt+WHEEL_UP'] = {move_option_wheel_up, 'repeat'},
- ['Alt+WHEEL_DOWN'] = {move_option_wheel_down, 'repeat'},
- ['Shift+Alt+WHEEL_UP'] = {move_option_wheel_page_up, 'repeat'},
- ['Shift+Alt+WHEEL_DOWN'] = {move_option_wheel_page_down, 'repeat'},
+ ['BS'] = {rt.prev_menu},
+ ['/'] = {rt.start_search},
+ ['Ctrl+s'] = {rt.toggle_menu_sort},
+ ['Ctrl+R'] = {rt.reload_data},
+
+ ['ENTER'] = {rt.select_option},
+ ['Ctrl+f'] = {rt.favourite_option},
+ ['g'] = {rt.goto_option},
+ ['i'] = {rt.open_option_info},
+ ['?'] = {rt.open_option_info},
+ ['Ctrl+p'] = {rt.goto_playing},
+
+ ['MBTN_LEFT'] = {rt.mouse_click_left, 'complex'},
+ ['MBTN_RIGHT'] = {rt.mouse_click_right, 'complex'},
+
+ ['k'] = {rt.cursor_up, 'repeat'},
+ ['j'] = {rt.cursor_down, 'repeat'},
+ ['K'] = {rt.cursor_page_up, 'repeat'},
+ ['J'] = {rt.cursor_page_down, 'repeat'},
+ ['UP'] = {rt.cursor_up, 'repeat'},
+ ['DOWN'] = {rt.cursor_down, 'repeat'},
+ ['Shift+UP'] = {rt.cursor_page_up, 'repeat'},
+ ['Shift+DOWN'] = {rt.cursor_page_down, 'repeat'},
+ ['PGUP'] = {rt.cursor_page_up, 'repeat'},
+ ['PGDWN'] = {rt.cursor_page_down, 'repeat'},
+ ['HOME'] = {rt.cursor_start},
+ ['END'] = {rt.cursor_end},
+ ['WHEEL_UP'] = {rt.cursor_wheel_up, 'repeat'},
+ ['WHEEL_DOWN'] = {rt.cursor_wheel_down, 'repeat'},
+ ['Shift+WHEEL_UP'] = {rt.cursor_wheel_page_up, 'repeat'},
+ ['Shift+WHEEL_DOWN'] = {rt.cursor_wheel_page_down, 'repeat'},
+
+ ['Alt+k'] = {rt.move_option_up, 'repeat'},
+ ['Alt+j'] = {rt.move_option_down, 'repeat'},
+ ['Alt+K'] = {rt.move_option_page_up, 'repeat'},
+ ['Alt+J'] = {rt.move_option_page_down, 'repeat'},
+ ['Alt+UP'] = {rt.move_option_up, 'repeat'},
+ ['Alt+DOWN'] = {rt.move_option_down, 'repeat'},
+ ['Shift+Alt+UP'] = {rt.move_option_page_up, 'repeat'},
+ ['Shift+Alt+DOWN'] = {rt.move_option_page_down, 'repeat'},
+ ['Alt+PGUP'] = {rt.move_option_page_up, 'repeat'},
+ ['Alt+PGDWN'] = {rt.move_option_page_down, 'repeat'},
+ ['Alt+HOME'] = {rt.move_option_start},
+ ['Alt+END'] = {rt.move_option_end},
+ ['Alt+WHEEL_UP'] = {rt.move_option_wheel_up, 'repeat'},
+ ['Alt+WHEEL_DOWN'] = {rt.move_option_wheel_down, 'repeat'},
+ ['Shift+Alt+WHEEL_UP'] = {rt.move_option_wheel_page_up, 'repeat'},
+ ['Shift+Alt+WHEEL_DOWN'] = {rt.move_option_wheel_page_down, 'repeat'},
}
binding_state.mappings.SEARCH = {
- ['ANY_UNICODE'] = {search_input_char, 'complex'},
- ['BS'] = {search_input_bs, 'repeat'},
- ['DEL'] = {search_input_del, 'repeat'},
-
- ['ENTER'] = {end_search},
- ['ESC'] = {cancel_search},
- ['Ctrl+c'] = {cancel_search},
-
- ['LEFT'] = {search_cursor_left, 'repeat'},
- ['RIGHT'] = {search_cursor_right, 'repeat'},
- ['Ctrl+a'] = {search_cursor_start},
- ['Ctrl+e'] = {search_cursor_end},
+ ['ANY_UNICODE'] = {rt.search_input_char, 'complex'},
+ ['BS'] = {rt.search_input_bs, 'repeat'},
+ ['DEL'] = {rt.search_input_del, 'repeat'},
+
+ ['ENTER'] = {rt.end_search},
+ ['ESC'] = {rt.cancel_search},
+ ['Ctrl+c'] = {rt.cancel_search},
+
+ ['LEFT'] = {rt.search_cursor_left, 'repeat'},
+ ['RIGHT'] = {rt.search_cursor_right, 'repeat'},
+ ['Ctrl+a'] = {rt.search_cursor_start},
+ ['Ctrl+e'] = {rt.search_cursor_end},
}
-- mpv does not process key-binding changes requested by script functions until
@@ -1444,12 +506,12 @@ osc_visibility = mp.get_property_native('user-data/osc/visibility', 'auto')
set_osc_visibility()
mp.add_forced_key_binding('TAB', 'toggle-menu', toggle_menu)
-set_key_mapping('MENU')
+binding_state.active = binding_state.mappings['MENU']
set_key_bindings()
mp.add_timeout(0, function()
- load_data()
+ rt.load_data()
state.depth = 0
- push_group_menu(catalogue:get('root'))
+ rt.push_group_menu(catalogue:get('root'))
osd:redraw(state)
end)