summaryrefslogtreecommitdiff
path: root/catalogue.lua
blob: cf9d897ceabebb90809182374a65b50347757544 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
-- Copyright 2025 David Vazgenovich Shakaryan

local util = require('util')

local catalogue = {}
local mt = {}
mt.__index = mt

function catalogue.new()
	local t = setmetatable({data = {}}, mt)

	t:add({
		type = 'group',
		id = 'root',
		name = '/',
	})
	t:add({
		type = 'group',
		id = 'favourites',
		parent_id = 'root',
		name = 'Favourites',
		lazy = true, -- prevent infinite recursion on search
	})

	return t
end

function mt:get(id)
	return self.data[id]
end

function mt:_ensure_catchall(section_id)
	local id = section_id .. ':category:catchall'
	local entry = self.data[id]
	if entry then
		return entry
	end

	return self:add({
		section = section_id,
		type = 'group',
		group_type = 'category',
		id = id,
		parent_id = section_id .. ':category:0',
		-- non-ascii symbol to sort near end
		name = '∗CATCHALL∗',
	})
end

function mt:add(entry)
	self.data[entry.id] = entry

	if entry.type == 'group' then
		entry.children = {}
	end

	if not entry.parent_id then
		return entry
	end

	local parent = self.data[entry.parent_id]

	-- dump any entries referencing nonexistent categories into a single
	-- catchall category
	if not parent then
		parent = self:_ensure_catchall(entry.section)
		entry.parent_id = parent.id
	end

	if parent.id == entry.id then
		entry.parent_id = nil
	else
		parent.children[#parent.children+1] = entry
	end

	return entry
end

function mt:load_xc_section(section)
	self:add({
		section = section.id,
		type = 'group',
		group_type = 'category',
		id = section.id .. ':category:0',
		parent_id = 'root',
		name = section.name,
	})

	-- currently, this will not correctly handle subcategories which come
	-- before their parent category
	for _, v in ipairs(section.categories) do
		self:add({
			section = section.id,
			type = 'group',
			group_type = 'category',
			id = section.id .. ':category:' .. v.category_id,
			parent_id = section.id .. ':category:' .. v.parent_id,
			name = util.strip(v.category_name),
		})
	end

	for _, v in ipairs(section.elements) do
		local vv = {
			section = section.id,
			parent_id = section.id .. ':category:' ..
				v.category_id,
			name = util.strip(v.name),
		}

		if section.type == 'series' then
			vv.type = 'group'
			vv.group_type = 'series'
			vv.id = section.id .. ':series:' .. v.series_id
			vv.series_id = v.series_id
			vv.img_url = util.strip_ne(v.cover)
			vv.lazy = true -- avoid API calls on search
		else
			vv.type = 'stream'
			vv.id = section.id .. ':stream:' .. v.stream_id
			vv.stream_type = v.stream_type
			vv.stream_id = v.stream_id
			vv.img_url = util.strip_ne(v.stream_icon)
			vv.epg_channel_id = util.strip_ne(v.epg_channel_id)
		end

		self:add(vv)
	end
end

function mt:path_to_root(entry)
	local path = {}

	local curr = entry
	while curr.parent_id and curr.parent_id ~= 'root' do
		curr = self:get(curr.parent_id)
		if not curr then
			return
		end
		path[#path+1] = curr
	end
	if curr.parent_id ~= 'root' then
		return
	end

	return path
end

function mt:path_from_root(entry)
	local path = self:path_to_root(entry)
	if path then
		util.reverse(path)
	end
	return path
end

return catalogue