summaryrefslogtreecommitdiff
path: root/main.lua
diff options
context:
space:
mode:
authorDavid Vazgenovich Shakaryan <dvshakaryan@gmail.com>2025-05-14 15:20:28 -0700
committerDavid Vazgenovich Shakaryan <dvshakaryan@gmail.com>2025-05-14 15:20:28 -0700
commitcfddf60adc35dbf1bff0c3d29b7763a8b8b678a6 (patch)
tree5666f276d92ded0ff606b0b9a561934939bfebc2 /main.lua
parentd5b6d72a8a9375a32378033c4883a933355b63f5 (diff)
downloadmpv-iptv-menu-cfddf60adc35dbf1bff0c3d29b7763a8b8b678a6.tar.gz
mpv-iptv-menu-cfddf60adc35dbf1bff0c3d29b7763a8b8b678a6.tar.xz
add EPG support
Diffstat (limited to 'main.lua')
-rw-r--r--main.lua130
1 files changed, 123 insertions, 7 deletions
diff --git a/main.lua b/main.lua
index 086dd4b..e9b48d2 100644
--- a/main.lua
+++ b/main.lua
@@ -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})