Skip to content

Commit ec69961

Browse files
committed
[plugin.video.nrk] 4.11.0
1 parent 8421984 commit ec69961

5 files changed

Lines changed: 826 additions & 2 deletions

File tree

plugin.video.nrk/addon.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# You should have received a copy of the GNU General Public License
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717

18+
from nrk import Base
1819
import time
1920
import xbmc
2021
import xbmcplugin
@@ -26,6 +27,8 @@
2627
from xbmcgui import ListItem
2728
import routing
2829
import nrktv
30+
import nrkradio
31+
import nrk
2932
import subs
3033
import inputstreamhelper
3134

@@ -34,9 +37,17 @@
3437

3538
@plugin.route('/')
3639
def root():
40+
items = [
41+
(plugin.url_for(tv), ListItem("TV"), True),
42+
(plugin.url_for(radio), ListItem("Radio"), True),
43+
]
44+
addDirectoryItems(plugin.handle, items)
45+
endOfDirectory(plugin.handle)
46+
47+
@plugin.route('/tv')
48+
def tv():
3749
items = [
3850
(plugin.url_for(live_tv), ListItem("Direkte TV"), True),
39-
(plugin.url_for(live_radio), ListItem("Direkte radio"), True),
4051
(plugin.url_for(recommended), ListItem("Anbefalt"), True),
4152
(plugin.url_for(popular), ListItem("Mest sett"), True),
4253
(plugin.url_for(mostrecent), ListItem("Sist sendt"), True),
@@ -221,6 +232,70 @@ def browse():
221232
urls = [plugin.url_for(category, item.id) for item in items]
222233
view(items, urls=urls)
223234

235+
@plugin.route('/radio')
236+
def radio():
237+
items = [
238+
(plugin.url_for(live_radio), ListItem("Direkte radio"), True),
239+
(plugin.url_for(radio_pages), ListItem("Kategorier"), True),
240+
]
241+
addDirectoryItems(plugin.handle, items)
242+
endOfDirectory(plugin.handle)
243+
244+
245+
@plugin.route('/radio/pages')
246+
def radio_pages():
247+
items: list[Base] = nrkradio.PagesOverview().children
248+
show_radio_list(items)
249+
250+
@plugin.route('/radio/<string:type>/<path:url>')
251+
def radio_navigate_by_url(type: str, url: str) -> None:
252+
item_class = nrkradio.map_type_to_class(type)
253+
item: Base = item_class.from_url(url)
254+
children: list[Base] = item.children
255+
if children:
256+
show_radio_list(children)
257+
else:
258+
media_url = item.media_url
259+
if media_url:
260+
li = ListItem(item.title, path=media_url)
261+
set_common_properties(item, li)
262+
playable = True
263+
#if playable:
264+
# set_stream_details(podcastEpisode, li)
265+
li.setProperty('isplayable', 'true')
266+
xbmcplugin.setResolvedUrl(plugin.handle, True, listitem=li)
267+
addDirectoryItem(plugin.handle, media_url, li, not playable)
268+
endOfDirectory(plugin.handle)
269+
270+
271+
def show_radio_list(items: list[nrk.Base]):
272+
total: int = len(items)
273+
for item in items:
274+
# if not getattr(item, 'available', True):
275+
# continue
276+
li = ListItem(item.title)
277+
# set_common_properties(item, li) TODO: set metadata
278+
playable = item.is_playable
279+
if playable:
280+
li.setProperty('isplayable', 'true')
281+
child_type = item.type
282+
manifest_url = item.manifest_url
283+
url = plugin.url_for(radio_navigate_by_url, child_type, manifest_url)
284+
art: dict[str, str] = {}
285+
if item.fanart:
286+
xbmc.log(f"setting fanart {item.title}: {item.fanart}", xbmc.LOGINFO)
287+
art["fanart"] = item.fanart
288+
if item.thumb:
289+
art["thumb"] = item.thumb
290+
if art:
291+
li.setArt(art)
292+
#tag = li.getMusicInfoTag()
293+
294+
#li.setInfo('video', {'count': i, 'title': item.title, 'mediatype': 'video'})
295+
addDirectoryItem(plugin.handle, url, li, not playable, total)
296+
endOfDirectory(plugin.handle)
297+
298+
224299

225300
@plugin.route('/search')
226301
def search():

plugin.video.nrk/addon.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
22
<addon id="plugin.video.nrk"
33
name="NRK Nett-TV"
4-
version="4.10.1"
4+
version="4.11.0"
55
provider-name="takoi+a99b">
66
<requires>
77
<import addon="xbmc.python" version="3.0.0"/>

plugin.video.nrk/changelog.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
[B]4.11.0[/B]
2+
- add on-demand radio categories
3+
14
[B]4.10.1[/B]
25
- add missing inputstreamhelper import to addon.py
36

plugin.video.nrk/nrk.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
from enum import Enum
2+
from typing import Any, override
3+
4+
5+
from requests.models import Response
6+
7+
8+
from requests import Session
9+
10+
session = Session()
11+
session.headers["User-Agent"] = "kodi.tv"
12+
13+
14+
class Base(object):
15+
def __init__(
16+
self,
17+
id: str | None,
18+
title: str | None,
19+
type: Enum,
20+
is_playable: bool,
21+
manifest_url: str | None,
22+
):
23+
self.__id: str | None = id
24+
self.__title: str | None = title
25+
self.__type: Enum = type
26+
self.__is_playable: bool = is_playable
27+
self.__manifest_url: str | None = manifest_url # must be an absolute path
28+
self.__children: list[Base] = []
29+
self.__media_url: str | None = None
30+
self.thumb: str | None = None
31+
self.fanart: str | None = None
32+
33+
def __str__(self):
34+
return f"{self.__class__.__name__}: { {str(self.__id)}, {str(self.__title)} }"
35+
36+
@property
37+
def id(self) -> str | None:
38+
return self.__id
39+
40+
@property
41+
def title(self) -> str:
42+
return self.__title or "Ingen tittel"
43+
44+
@property
45+
def manifest_url(self) -> str | None:
46+
return self.__manifest_url
47+
48+
def append_child(self, child: "Base") -> None:
49+
self.__children.append(child)
50+
51+
@property
52+
def children(self) -> list["Base"]:
53+
return self.__children
54+
55+
@property
56+
def is_playable(self) -> bool:
57+
return self.__is_playable
58+
59+
@property
60+
def type(self) -> str:
61+
return self.__type.value
62+
63+
@property
64+
def media_url(self) -> str | None:
65+
if self.__is_playable:
66+
return self.__media_url
67+
return None
68+
69+
@media_url.setter
70+
def media_url(self, value: str | None) -> None:
71+
self.__media_url = value
72+
73+
def add_images(self, images) -> None:
74+
self.thumb = deep_get_str(images, 0, "url")
75+
self.fanart = deep_get_str(images, -1, "url")
76+
77+
78+
class BasePage(Base):
79+
@override
80+
def add_images(self, images) -> None:
81+
self.thumb = deep_get_str(images, 0, "uri")
82+
self.fanart = deep_get_str(images, -1, "uri")
83+
84+
85+
def get(path: str, params: str = "") -> Any:
86+
api_key = "d1381d92278a47c09066460f2522a67d"
87+
if len(params) > 0 and params[0] != "&":
88+
raise ValueError("params must start with &")
89+
r: Response = session.get(
90+
"https://psapi.nrk.no{}?apiKey={}{}".format(path, api_key, params)
91+
)
92+
r.raise_for_status()
93+
return r.json()
94+
95+
96+
def deep_get(obj: Any, *keys: int | str) -> Any | None:
97+
if len(keys) == 0:
98+
return None
99+
100+
key: int | str = keys[0]
101+
102+
if isinstance(key, str) and isinstance(obj, dict):
103+
value: Any | None = obj.get(key)
104+
105+
elif isinstance(key, int) and isinstance(obj, list):
106+
if len(obj) <= key or key < -len(obj):
107+
return None
108+
value: Any | None = obj[key]
109+
else:
110+
return None
111+
112+
if len(keys) >= 2:
113+
return deep_get(value, *keys[1:])
114+
else:
115+
return value
116+
117+
118+
def deep_get_str(obj: Any, *keys: int | str) -> str | None:
119+
value: Any | None = deep_get(obj, *keys)
120+
if isinstance(value, str):
121+
return value
122+
else:
123+
return None
124+
125+
126+
def deep_get_dict(obj: Any, *keys: int | str) -> dict[Any, Any]:
127+
value: Any | None = deep_get(obj, *keys)
128+
if isinstance(value, dict):
129+
return value
130+
else:
131+
return {}
132+
133+
134+
def deep_get_list(obj: Any, *keys: int | str) -> list[Any]:
135+
value: Any | None = deep_get(obj, *keys)
136+
if isinstance(value, list):
137+
return value
138+
else:
139+
return []

0 commit comments

Comments
 (0)