Skip to content

Commit

Permalink
Add IPTV setting and display catchup data
Browse files Browse the repository at this point in the history
  • Loading branch information
f-lawe committed Jul 23, 2024
1 parent d42c813 commit 56542bb
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 69 deletions.
22 changes: 13 additions & 9 deletions resources/language/resource.language.en_gb/strings.po
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Kodi Media Center language file
# Addon Name: Orange TV France
# Addon id: plugin.video.orange.fr
# Addon Provider: BreizhReloaded
# Addon Provider: Flawe
msgid ""
msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: BreizhReloaded\n"
"Last-Translator: Flawe\n"
"Language-Team: English\n"
"Language: en\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
Expand All @@ -23,17 +23,25 @@ msgid "Install IPTV Manager…"
msgstr ""

msgctxt "#30102"
msgid "Help 30104"
msgstr "
msgid "Help 30102"
msgstr ""

msgctxt "#30103"
msgid "Go to IPTV Manager settings…"
msgid "Enable IPTV Manager integration"
msgstr ""

msgctxt "#30104"
msgid "Help 30104"
msgstr ""

msgctxt "#30105"
msgid "Go to IPTV Manager settings…"
msgstr ""

msgctxt "#30106"
msgid "Help 30106"
msgstr ""

# Provider settings (from 30200 to 30299)

msgctxt "#30200"
Expand Down Expand Up @@ -103,7 +111,3 @@ msgstr ""
msgctxt "#30901"
msgid "InputStream cannot be loaded."
msgstr ""

msgctxt "#30902"
msgid "Orange TV France must be used from the Kodi TV section."
msgstr ""
26 changes: 15 additions & 11 deletions resources/language/resource.language.fr_fr/strings.po
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Kodi Media Center language file
# Addon Name: Orange TV France
# Addon id: plugin.video.orange.fr
# Addon Provider: BreizhReloaded
# Addon Provider: Flawe
msgid ""
msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: BreizhReloaded\n"
"Last-Translator: Flawe\n"
"Language-Team: Français\n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
Expand All @@ -23,17 +23,25 @@ msgid "Install IPTV Manager…"
msgstr "Installer IPTV Manager…"

msgctxt "#30102"
msgid "Help 30104"
msgid "Help 30102"
msgstr ""

msgctxt "#30103"
msgid "Go to IPTV Manager settings…"
msgstr "Ouvrir les paramètres de IPTV Manager"
msgid "Enable IPTV Manager integration"
msgstr "Activer l'intégration avec IPTV Manager"

msgctxt "#30104"
msgid "Help 30104"
msgstr ""

msgctxt "#30105"
msgid "Go to IPTV Manager settings…"
msgstr "Ouvrir les paramètres de IPTV Manager…"

msgctxt "#30106"
msgid "Help 30106"
msgstr ""

# Provider settings (from 30200 to 30299)

msgctxt "#30200"
Expand Down Expand Up @@ -63,8 +71,8 @@ msgid "Proxy"
msgstr "Proxy"

msgctxt "#30301"
msgid "Activer"
msgstr ""
msgid "Enable"
msgstr "Activer"

msgctxt "#30302"
msgid "Help 30302"
Expand Down Expand Up @@ -103,7 +111,3 @@ msgstr "Cette chaîne ne fait pas partie de votre abonnement."
msgctxt "#30901"
msgid "InputStream cannot be loaded."
msgstr "InputStream n'a pas pu être chargé."

msgctxt "#30902"
msgid "Orange TV France must be used from the Kodi TV section."
msgstr "Orange TV France doit être utilisé depuis la section TV de Kodi."
7 changes: 0 additions & 7 deletions resources/lib/managers/catchup_manager.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
"""Catchup TV Manager."""

import xbmc
import xbmcplugin

from lib.providers import get_provider
from lib.router import router
from lib.utils.gui import create_directory_items
from lib.utils.kodi import build_addon_url


class CatchupManager:
Expand Down Expand Up @@ -47,8 +45,3 @@ def get_videos(self, catchup_channel_id: str, article_id: str) -> list:

succeeded = xbmcplugin.addDirectoryItems(router.handle, directory_items, len(directory_items))
xbmcplugin.endOfDirectory(router.handle, succeeded)

def play_video(self, video_id: str):
"""Play catchup video."""
player = xbmc.Player()
player.play(build_addon_url(f"/catchup-streams/{video_id}"))
42 changes: 20 additions & 22 deletions resources/lib/providers/abstract_orange_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from lib.providers.abstract_provider import AbstractProvider
from lib.utils.kodi import build_addon_url, get_drm, get_global_setting, log
from lib.utils.request import build_request, get_random_ua
from lib.utils.request import build_request, get_random_ua, open_request

_PROGRAMS_ENDPOINT = "https://rp-ott-mediation-tv.woopic.com/api-gw/live/v3/applications/STB4PC/programs?period={period}&epgIds=all&mco={mco}"
_CATCHUP_CHANNELS_ENDPOINT = "https://rp-ott-mediation-tv.woopic.com/api-gw/catchup/v4/applications/PC/channels"
Expand Down Expand Up @@ -43,9 +43,7 @@ def get_catchup_stream_info(self, stream_id: str) -> dict:
def get_streams(self) -> list:
"""Load stream data from Orange and convert it to JSON-STREAMS format."""
req = build_request(_CHANNELS_ENDPOINT)

with urlopen(req) as res:
channels = list(json.loads(res.read())["channels"])
channels = dict(open_request(req, {"channels": {}}))["channels"]

log(f"{len(channels)} channels found", xbmc.LOGINFO)
channels.sort(key=lambda channel: channel["displayOrder"])
Expand Down Expand Up @@ -121,9 +119,7 @@ def get_epg(self) -> dict:
def get_catchup_channels(self) -> list:
"""Load available catchup channels."""
req = build_request(_CATCHUP_CHANNELS_ENDPOINT)

with urlopen(req) as res:
channels = list(json.loads(res.read()))
channels = open_request(req, [])

log(f"{len(channels)} catchup channels found", xbmc.LOGINFO)

Expand All @@ -140,9 +136,7 @@ def get_catchup_channels(self) -> list:
def get_catchup_categories(self, catchup_channel_id: str) -> list:
"""Return a list of catchup categories for the specified channel id."""
req = build_request(_CATCHUP_CHANNELS_ENDPOINT + "/" + catchup_channel_id)

with urlopen(req) as res:
categories = list(json.loads(res.read())["categories"])
categories = dict(open_request(req, {"categories": {}}))["categories"]

return [
{
Expand All @@ -155,35 +149,39 @@ def get_catchup_categories(self, catchup_channel_id: str) -> list:

def get_catchup_articles(self, catchup_channel_id: str, category_id: str) -> list:
"""Return a list of catchup groups for the specified channel id and category id."""
req = build_request(
_CATCHUP_ARTICLES_ENDPOINT.format(catchup_channel_id=catchup_channel_id, category_id=category_id)
)
url = _CATCHUP_ARTICLES_ENDPOINT.format(catchup_channel_id=catchup_channel_id, category_id=category_id)
req = build_request(url)

with urlopen(req) as res:
articles = list(json.loads(res.read())["articles"])
articles = dict(open_request(req, {"articles": {}}))["articles"]

return [
{
"is_folder": True,
"label": article["title"],
"path": build_addon_url(f"/channels/{catchup_channel_id}/articles/{article['id']}/videos"),
"art": {"poster": article["covers"]["ref_16_9"]},
}
for article in articles
]

def get_catchup_videos(self, catchup_channel_id: str, article_id: str) -> list:
"""Return a list of catchup videos for the specified channel id and article id."""
req = build_request(_CATCHUP_VIDEOS_ENDPOINT.format(group_id=article_id))

with urlopen(req) as res:
videos = list(json.loads(res.read())["videos"])
videos = dict(open_request(req, {"videos": {}}))["videos"]

return [
{
"is_folder": False,
"label": video["title"],
"path": build_addon_url(f"/videos/{video['id']}"),
"art": {"thumb": video["covers"]["ref_4_3"]},
"path": build_addon_url(f"/catchup-streams/{video['id']}"),
"art": {"poster": video["covers"]["ref_16_9"]},
"info": {
"duration": int(video["duration"]) * 60,
"genres": video["genres"],
"plot": video["longSummary"],
"premiered": datetime.fromtimestamp(int(video["broadcastDate"]) / 1000).strftime("%Y-%m-%d"),
"year": int(video["productionDate"]),
},
}
for video in videos
]
Expand All @@ -197,7 +195,7 @@ def _get_stream_info(self, stream_type: str, version: str, item_type: str, strea
item_type=item_type,
stream_id=stream_id,
)
req, tv_token = self._build_request(url, auth_url=auth_url)
req, tv_token = self._build_auth_request(url, auth_url=auth_url)

try:
with urlopen(req) as res:
Expand Down Expand Up @@ -240,7 +238,7 @@ def _get_stream_info(self, stream_type: str, version: str, item_type: str, strea
log(stream_info, xbmc.LOGDEBUG)
return stream_info

def _build_request(self, url: str, additional_headers: dict = None, auth_url: str = None) -> (Request, str):
def _build_auth_request(self, url: str, additional_headers: dict = None, auth_url: str = None) -> (Request, str):
"""Build HTTP request."""
tv_token = None

Expand Down
2 changes: 1 addition & 1 deletion resources/lib/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@

def init_router():
"""Init addon router."""
log("Initializing addon router", xbmc.LOGDEBUG)
router.run()
log("Addon router initialized", xbmc.LOGDEBUG)
7 changes: 0 additions & 7 deletions resources/lib/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,6 @@ def channel_article_videos(catchup_channel_id: str, article_id: str):
CatchupManager().get_videos(catchup_channel_id, article_id)


@router.route("/videos/<video_id>")
def video(video_id: str):
"""Return catchup video listitem."""
log(f"Loading catchup video {video_id}", xbmc.LOGINFO)
CatchupManager().play_video(video_id)


@router.route("/live-streams/<stream_id>")
def live_stream(stream_id: str):
"""Load live stream for the required channel id."""
Expand Down
37 changes: 29 additions & 8 deletions resources/lib/utils/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,36 @@ def create_directory_items(data: list) -> list:
items = []

for d in data:
list_item = ListItem(label=d["label"], path=d["path"])

# list_item.setLabel2("LABEL2")
# list_item.setInfo("INFO", {"INFO1": "INFO1", "INFO2": "INFO2"})
is_folder = bool(d["is_folder"])

if "art" in d and "thumb" in d["art"]:
list_item.setArt({"thumb": d["art"]["thumb"]})

items.append((d["path"], list_item, bool(d["is_folder"])))
list_item = ListItem(label=d["label"], path=d["path"])
list_item.setIsFolder(is_folder)

if "art" in d:
list_item.setArt(
{
"poster": d["art"].get("poster", None),
"thumb": d["art"].get("thumb", None),
}
)

if not is_folder:
list_item.setProperties(
{
"inputstream": "inputstream.adaptive",
"IsPlayable": "true",
}
)

if "info" in d:
video_info_tag = list_item.getVideoInfoTag()
video_info_tag.setDuration(d["info"].get("duration", None))
video_info_tag.setGenres(d["info"].get("genres", None))
video_info_tag.setPlot(d["info"].get("plot", None))
video_info_tag.setYear(d["info"].get("year", None))
video_info_tag.setPremiered(d["info"].get("premiered", None))

items.append((d["path"], list_item, is_folder))

return items

Expand Down
30 changes: 28 additions & 2 deletions resources/lib/utils/request.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
"""Request utils."""

import gzip
import json
from random import randint
from urllib.error import HTTPError, URLError
from urllib.parse import urlparse
from urllib.request import Request
from urllib.request import Request, urlopen

import xbmc

# from socks import SOCKS5
# from sockshandler import SocksiPyHandler
from lib.utils.kodi import get_addon_setting
from lib.utils.kodi import get_addon_setting, log, ok_dialog

_USER_AGENTS = [
# Chrome
Expand Down Expand Up @@ -43,6 +48,27 @@ def build_request(url: str, additional_headers: dict = None) -> Request:
return Request(url, headers={"User-Agent": get_random_ua(), "Host": urlparse(url).netloc, **additional_headers})


def open_request(req: Request, value=None):
"""Open HTTP request and handle errors."""
try:
res = urlopen(req)
except HTTPError as e:
log(e.code, xbmc.LOGERROR)
ok_dialog("HTTPError")
return value
except URLError as e:
log(e.reason, xbmc.LOGERROR)
ok_dialog("URLError")
return value
else:
content = res.read()

if res.headers.get("Content-Encoding") == "gzip":
content = gzip.decompress(content)

return json.loads(content)


def install_proxy() -> None:
"""Install proxy server for the next requests."""
if get_addon_setting("proxy.enabled") != "true":
Expand Down
4 changes: 2 additions & 2 deletions resources/settings.xml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings>
<category label="30100">
<setting id="iptv.enabled" visible="false" default="true"/>
<setting id="iptv.channels_uri" visible="false" default="plugin://plugin.video.orange.fr/iptv/channels" />
<setting id="iptv.epg_uri" visible="false" default="plugin://plugin.video.orange.fr/iptv/epg" />
<setting visible="!System.HasAddon(service.iptv.manager)" label="30101" help="30102" type="action" action="InstallAddon(service.iptv.manager)" option="close" />
<setting visible="System.HasAddon(service.iptv.manager)" label="30103" help="30104" type="action" action="Addon.OpenSettings(service.iptv.manager)" option="close" subsetting="true" />
<setting id="iptv.enabled" visible="System.HasAddon(service.iptv.manager)" label="30103" help="30104" type="bool" default="true"/>
<setting visible="System.HasAddon(service.iptv.manager)" label="30105" help="30106" type="action" action="Addon.OpenSettings(service.iptv.manager)" option="close" subsetting="true" />
</category>

<category label="30200">
Expand Down

0 comments on commit 56542bb

Please sign in to comment.