diff options
author | David Vazgenovich Shakaryan <dvshakaryan@gmail.com> | 2025-05-14 15:20:28 -0700 |
---|---|---|
committer | David Vazgenovich Shakaryan <dvshakaryan@gmail.com> | 2025-05-14 15:20:28 -0700 |
commit | cfddf60adc35dbf1bff0c3d29b7763a8b8b678a6 (patch) | |
tree | 5666f276d92ded0ff606b0b9a561934939bfebc2 | |
parent | d5b6d72a8a9375a32378033c4883a933355b63f5 (diff) | |
download | mpv-iptv-menu-cfddf60adc35dbf1bff0c3d29b7763a8b8b678a6.tar.gz mpv-iptv-menu-cfddf60adc35dbf1bff0c3d29b7763a8b8b678a6.tar.xz |
add EPG support
-rw-r--r-- | main.lua | 130 |
1 files changed, 123 insertions, 7 deletions
@@ -8,6 +8,7 @@ local colours = { bg='{\\alpha&H44&\\c&H&}', title='{\\c&999999&}', option='{\\c}', + info='{\\c&666666&}', group='{\\c&H99DDFF&}', group_empty='{\\c&H446677&}', selected='{\\c&HFF00&}', @@ -38,6 +39,7 @@ osd_bg.data = '{\\pos(0,0)}' .. colours.bg .. '{\\p1}m 0 0 l 7680 0 7680 720 0 720{\\p0}' local objects = {} +local epg = {} local favourites local playing_id @@ -166,6 +168,46 @@ local function load_section(section, name) end end +-- local (non-DST) offset from UTC +local tz_offset +do + local t = os.time() + tz_offset = os.time(os.date('*t', t)) - os.time(os.date('!*t', t)) +end +local function epg_parse_time(str) + local y, m, d, hh, mm, ss, zsign, zh, zm = str:match( + '(%d%d%d%d)(%d%d)(%d%d)(%d%d)(%d%d)(%d%d) ([+-])(%d%d)(%d%d)') + local dt = {year=y, month=m, day=d, hour=hh, min=mm, sec=ss, + isdst=false} + return os.time(dt) + tz_offset - + (tonumber(zsign..zh) * 3600 + tonumber(zsign..zm) * 60) +end + +local function load_epg() + local tmp = read_json_file('epg.json') + for _, v in ipairs(tmp) do + local ch = v.channel:lower() + local prog = { + start=epg_parse_time(v.start), + stop=epg_parse_time(v.stop), + title=v.title, + desc=v.desc, + } + + if epg[ch] then + epg[ch][#epg[ch]+1] = prog + else + epg[ch] = {prog} + end + end + + for _, progs in pairs(epg) do + table.sort(progs, function(a, b) + return a.start < b.start + end) + end +end + local function load_data() add_object({ type='group', @@ -182,6 +224,7 @@ local function load_data() load_section('live', 'Live TV') load_section('movie', 'Movies') load_section('series', 'Series') + load_epg() favourites = read_json_file('favourites.json') -- json loading/dumping breaks when the table is empty, so we need a @@ -192,7 +235,9 @@ local function load_data() end local function asscape(str) - return mp.command_native({'escape-ass', str}) + -- remove newlines before escaping, since we don't want output to + -- unexpectedly span multiple lines + return mp.command_native({'escape-ass', (str:gsub('\n', ' '))}) end local function osd_menu_lines() @@ -229,10 +274,10 @@ local function osd_option_icons(opt, info) if info.selected then str = colours.selected .. '› ' end - if opt.id == playing_id then + if opt.id and opt.id == playing_id then str = str .. colours.icon_playing .. '\226\143\186 ' end - if favourites[opt.id] then + if opt.id and favourites[opt.id] then str = str .. colours.icon_favourite .. '★ ' end return str @@ -271,6 +316,10 @@ local function osd_option_text(opt, info) str = col .. '[' .. str .. ']' end + if opt.info and #opt.info > 0 then + str = str .. colours.info .. ' (' .. asscape(opt.info) .. ')' + end + return str end @@ -399,8 +448,34 @@ local function push_menu(t) menus[depth] = menu end +local function epg_programme(channel, time) + local progs = epg[channel] + if not progs then + return + end + + for _, v in ipairs(progs) do + if time >= v.start and time < v.stop then + return v + end + end +end + +local function add_programme(opt, time) + if opt.epg_channel_id then + local prog = epg_programme(opt.epg_channel_id, time) + if prog then + opt = copy_table(opt) + opt.info = asscape(prog.title) + end + end + + return opt +end + local function favourites_group_menu_options(group) local options = {} + local time = os.time() for id in pairs(favourites) do local obj = objects[id] if obj then @@ -417,7 +492,7 @@ local function favourites_group_menu_options(group) obj.path = path end - options[#options+1] = obj + options[#options+1] = add_programme(obj, time) end end return options @@ -479,7 +554,12 @@ local function group_menu_options(group) return series_group_menu_options(group) end - return group.children + local options = {} + local time = os.time() + for i, v in ipairs(group.children) do + options[i] = add_programme(v, time) + end + return options end local function push_group_menu(group) @@ -511,7 +591,7 @@ end local function select_option() local menu = menus[depth] local opt = menu.options[menu.cursor] - if not opt then + if not opt or not opt.id then return end @@ -525,7 +605,7 @@ end local function favourite_option() local menu = menus[depth] local opt = menu.options[menu.cursor] - if not opt then + if not opt or not opt.id then return end @@ -561,6 +641,41 @@ local function goto_option() update_osd() end +local function open_option_epg() + local menu = menus[depth] + local opt = menu.options[menu.cursor] + if not opt or not opt.epg_channel_id then + return + end + + local ch = opt.epg_channel_id:lower() + if not epg[ch] then + return + end + + local options = {} + local curr = 0 + local time = os.time() + for i, v in ipairs(epg[ch]) do + options[i] = { + name=os.date('%a %d %b %H:%M', v.start) .. ' ' .. + os.date('%H:%M', v.stop) .. ' ' .. v.title, + info=v.desc, + } + + + if curr == 0 and time >= v.start and time < v.stop then + curr = i + end + end + + push_menu({ + title='EPG: ' .. opt.name .. ' (' .. ch .. ')', + options=options, + }) + set_cursor(curr, {centre=true}) +end + local function search_menu_options_build(options, t, path) local menu = menus[depth] local path = path or {} @@ -789,6 +904,7 @@ function bind_menu_keys() bind_key('ENTER', select_option) bind_key('Ctrl+f', favourite_option) bind_key('g', goto_option) + bind_key('?', open_option_epg) bind_key('UP', cursor_up, {repeatable=true}) bind_key('DOWN', cursor_down, {repeatable=true}) |