Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle n levels within catchup directories #68

Merged
merged 2 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# 2.x

## [2.3.0](https://github.com/f-lawe/plugin.video.orange.fr/releases/tag/v2.3.1) - 2024-10-11
## [2.3.1](https://github.com/f-lawe/plugin.video.orange.fr/releases/tag/v2.3.1) - 2024-10-12

### Changed
- Routing and provider interface now allow various depths in catchup menus ([#67](https://github.com/f-lawe/plugin.video.orange.fr/issues/67))

## [2.3.0](https://github.com/f-lawe/plugin.video.orange.fr/releases/tag/v2.3.0) - 2024-10-11

### Changed
- Update to InputstreamAdaptive for Kodi 21 ([#63](https://github.com/f-lawe/plugin.video.orange.fr/issues/63))
Expand Down
2 changes: 1 addition & 1 deletion addon.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.orange.fr" name="Orange TV France" version="2.3.0" provider-name="Flawe">
<addon id="plugin.video.orange.fr" name="Orange TV France" version="2.3.1" provider-name="Flawe">
<requires>
<import addon="xbmc.python" version="3.0.1"/>
<import addon="script.module.requests" version="2.31.0"/>
Expand Down
41 changes: 8 additions & 33 deletions resources/lib/managers/catchup_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,13 @@ def __init__(self):
"""Initialize Catchup Manager object."""
self.provider = get_provider()

def get_channels(self) -> list:
"""Return channels available for catchup TV."""
channels = self.provider.get_catchup_channels()

for channel in channels:
xbmcplugin.addDirectoryItem(router.handle, channel["path"], create_list_item(channel, True), True)

xbmcplugin.endOfDirectory(router.handle)

def get_categories(self, catchup_channel_id: str) -> list:
"""Return content categories for the required channel."""
categories = self.provider.get_catchup_categories(catchup_channel_id)

for category in categories:
xbmcplugin.addDirectoryItem(router.handle, category["path"], create_list_item(category, True), True)

xbmcplugin.endOfDirectory(router.handle)

def get_articles(self, catchup_channel_id: str, category_id: str) -> list:
"""Return content (TV show, movie, etc) for the required channel and category."""
articles = self.provider.get_catchup_articles(catchup_channel_id, category_id)

for article in articles:
xbmcplugin.addDirectoryItem(router.handle, article["path"], create_list_item(article, True), True)

xbmcplugin.endOfDirectory(router.handle)

def get_videos(self, catchup_channel_id: str, article_id: str) -> list:
"""Return the video list for the required show."""
videos = self.provider.get_catchup_videos(catchup_channel_id, article_id)

for video in videos:
xbmcplugin.addDirectoryItem(router.handle, video["path"], create_list_item(video))
def build_directory(self, levels: str = None) -> None:
"""Build catchup TV directory."""
levels = levels.split("/") if levels else []
items = self.provider.get_catchup_items(levels)

for item in items:
is_folder = item.get("is_folder")
xbmcplugin.addDirectoryItem(router.handle, item["path"], create_list_item(item, is_folder), is_folder)

xbmcplugin.endOfDirectory(router.handle)
43 changes: 31 additions & 12 deletions resources/lib/providers/abstract_orange_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import re
from abc import ABC
from datetime import date, datetime, timedelta
from typing import List
from urllib.parse import urlencode

import xbmc
Expand All @@ -18,7 +19,7 @@

_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"
_CATCHUP_ARTICLES_ENDPOINT = "https://rp-ott-mediation-tv.woopic.com/api-gw/catchup/v4/applications/PC/channels/{catchup_channel_id}/categories/{category_id}"
_CATCHUP_ARTICLES_ENDPOINT = "https://rp-ott-mediation-tv.woopic.com/api-gw/catchup/v4/applications/PC/channels/{channel_id}/categories/{category_id}"
_CATCHUP_VIDEOS_ENDPOINT = "https://rp-ott-mediation-tv.woopic.com/api-gw/catchup/v4/applications/PC/groups/{group_id}"
_CHANNELS_ENDPOINT = "https://rp-ott-mediation-tv.woopic.com/api-gw/pds/v1/live/ew?everywherePopulation=OTT_Metro"

Expand All @@ -43,6 +44,7 @@ def get_live_stream_info(self, stream_id: str) -> dict:
return self._get_stream_info(auth_url, _LIVE_STREAM_ENDPOINT, stream_id)

def get_catchup_stream_info(self, stream_id: str) -> dict:
"""Get catchup stream info."""
auth_url = _CATCHUP_VIDEO_URL.format(stream_id=stream_id)
return self._get_stream_info(auth_url, _CATCHUP_STREAM_ENDPOINT, stream_id)

Expand All @@ -59,7 +61,7 @@ def get_streams(self) -> list:
"name": channel["name"],
"preset": str(channel["displayOrder"]),
"logo": self._extract_logo(channel["logos"]),
"stream": build_addon_url(f"/live-streams/{channel['idEPG']}"),
"stream": build_addon_url(f"/stream/live/{channel['idEPG']}"),
"group": [group_name for group_name in self.groups if int(channel["idEPG"]) in self.groups[group_name]],
}
for channel in channels
Expand Down Expand Up @@ -124,55 +126,72 @@ def get_epg(self) -> dict:

return epg

def get_catchup_channels(self) -> list:
def get_catchup_items(self, levels: List[str]) -> list:
"""Return a list of directory items for the specified levels."""
depth = len(levels)

if depth == 0:
return self._get_catchup_channels()
elif depth == 1:
return self._get_catchup_categories(levels[0])
elif depth == 2:
return self._get_catchup_articles(levels[0], levels[1])
elif depth == 3:
return self._get_catchup_videos(levels[0], levels[1], levels[2])

def _get_catchup_channels(self) -> list:
"""Load available catchup channels."""
channels = request_json(_CATCHUP_CHANNELS_ENDPOINT, default=[])

return [
{
"is_folder": True,
"label": str(channel["name"]).upper(),
"path": build_addon_url(f"/channels/{channel['id']}/categories"),
"path": build_addon_url(f"/catchup/{channel['id']}"),
"art": {"thumb": channel["logos"]["ref_millenials_partner_white_logo"]},
}
for channel in channels
]

def get_catchup_categories(self, catchup_channel_id: str) -> list:
def _get_catchup_categories(self, channel_id: str) -> list:
"""Return a list of catchup categories for the specified channel id."""
url = _CATCHUP_CHANNELS_ENDPOINT + "/" + catchup_channel_id
url = _CATCHUP_CHANNELS_ENDPOINT + "/" + channel_id
categories = request_json(url, default={"categories": {}})["categories"]

return [
{
"is_folder": True,
"label": category["name"][0].upper() + category["name"][1:],
"path": build_addon_url(f"/channels/{catchup_channel_id}/categories/{category['id']}/articles"),
"path": build_addon_url(f"/catchup/{channel_id}/{category['id']}"),
}
for category in categories
]

def get_catchup_articles(self, catchup_channel_id: str, category_id: str) -> list:
def _get_catchup_articles(self, channel_id: str, category_id: str) -> list:
"""Return a list of catchup groups for the specified channel id and category id."""
url = _CATCHUP_ARTICLES_ENDPOINT.format(catchup_channel_id=catchup_channel_id, category_id=category_id)
url = _CATCHUP_ARTICLES_ENDPOINT.format(channel_id=channel_id, category_id=category_id)
articles = request_json(url, default={"articles": {}})["articles"]

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

def get_catchup_videos(self, catchup_channel_id: str, article_id: str) -> list:
def _get_catchup_videos(self, channel_id: str, category_id: str, article_id: str) -> list:
"""Return a list of catchup videos for the specified channel id and article id."""
url = _CATCHUP_VIDEOS_ENDPOINT.format(group_id=article_id)
videos = request_json(url, default={"videos": {}})["videos"]

return [
{
"is_folder": False,
"label": video["title"],
"path": build_addon_url(f"/catchup-streams/{video['id']}"),
"path": build_addon_url(f"/stream/catchup/{video['id']}"),
"art": {"poster": video["covers"]["ref_16_9"]},
"info": {
"duration": int(video["duration"]) * 60,
Expand Down
20 changes: 3 additions & 17 deletions resources/lib/providers/abstract_provider.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Abstract TV Provider."""

from abc import ABC, abstractmethod
from typing import List


class AbstractProvider(ABC):
Expand All @@ -27,21 +28,6 @@ def get_epg(self) -> dict:
pass

@abstractmethod
def get_catchup_channels(self) -> list:
"""Return a list of available catchup channels."""
pass

@abstractmethod
def get_catchup_categories(self, catchup_channel_id: str) -> list:
"""Return a list of catchup categories for the specified channel id."""
pass

@abstractmethod
def get_catchup_articles(self, catchup_channel_id: str, category_id: str) -> list:
"""Return a list of catchup articles for the specified channel id and category id."""
pass

@abstractmethod
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."""
def get_catchup_items(self, levels: List[str]) -> list:
"""Return a list of directory items for the specified levels."""
pass
1 change: 0 additions & 1 deletion resources/lib/providers/fr/orange_france.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# ruff: noqa: D102
"""Orange France."""

from lib.providers.abstract_orange_provider import AbstractOrangeProvider
Expand Down
38 changes: 12 additions & 26 deletions resources/lib/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,27 @@

@router.route("/")
def index():
"""Display catchup TV channels."""
log("Display index", xbmc.LOGINFO)
CatchupManager().get_channels()
"""Display catchup service index."""
log("Display catchup index", xbmc.LOGINFO)
CatchupManager().build_directory()


@router.route("/channels/<catchup_channel_id>/categories")
def channel_categories(catchup_channel_id: str):
"""Return catchup category listitems for the required channel id."""
log(f"Loading catchup categories for channel {catchup_channel_id}", xbmc.LOGINFO)
CatchupManager().get_categories(catchup_channel_id)
@router.route("/catchup/<path:levels>")
def catchup_(levels: str):
"""Display catchup service directory."""
log(f"Display catchup directory {levels}", xbmc.LOGINFO)
CatchupManager().build_directory(levels)


@router.route("/channels/<catchup_channel_id>/categories/<category_id>/articles")
def channel_category_articles(catchup_channel_id: str, category_id: str):
"""Return catchup category article listitems."""
log(f"Loading catchup articles for category {category_id}", xbmc.LOGINFO)
CatchupManager().get_articles(catchup_channel_id, category_id)


@router.route("/channels/<catchup_channel_id>/articles/<article_id>/videos")
def channel_article_videos(catchup_channel_id: str, article_id: str):
"""Return catchup article video listitems."""
log(f"Loading catchup videos for article {article_id}", xbmc.LOGINFO)
CatchupManager().get_videos(catchup_channel_id, article_id)


@router.route("/live-streams/<stream_id>")
def live_stream(stream_id: str):
@router.route("/stream/live/<stream_id>")
def stream_live(stream_id: str):
"""Load live stream for the required channel id."""
log(f"Loading live stream {stream_id}", xbmc.LOGINFO)
StreamManager().load_live_stream(stream_id)


@router.route("/catchup-streams/<stream_id>")
def catchup_stream(stream_id: str):
@router.route("/stream/catchup/<stream_id>")
def stream_catchup(stream_id: str):
"""Load live stream for the required video id."""
log(f"Loading catchup stream {stream_id}", xbmc.LOGINFO)
StreamManager().load_chatchup_stream(stream_id)
Expand Down