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

[plugin.video.retrospect] v5.7.15 #4548

Merged
merged 1 commit into from
Aug 7, 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
16 changes: 7 additions & 9 deletions plugin.video.retrospect/addon.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.retrospect"
version="5.7.14"
version="5.7.15"
name="Retrospect"
provider-name="Bas Rieter">

Expand Down Expand Up @@ -123,22 +123,20 @@
<platform>all</platform>
<license>GPL-3.0-or-later</license>
<language>en nl de sv no lt lv fi</language>
<news>[B]Retrospect v5.7.14 - Changelog - 2024-07-21[/B]
<news>[B]Retrospect v5.7.15 - Changelog - 2024-08-07[/B]

This version introduces enhanced searching and keeps track of previous searches, so you can reuse them. It also includes a number of fixes for channels, such as GoPlay and UR Play. And finally some artwork was updated.
This version includes some minor fixes in the &quot;search&quot; feature and fixes the GoPlay and Videoland channels.

[B]Framework related[/B]
* Added: New search functionality with search history options (Fixes #1805).
* Fixed: Highlighter of searchresults were shown in the favourites (Fixes #1818).
* Fixed: In some cases a new searched items would be cleaned up immediately.

[B]GUI/Settings/Language related[/B]
_None_

[B]Channel related[/B]
* Updated: UR Play artwork and fixed categories.
* Updated: SVT poster and icon.
* Fixed: SVT HTML based videos (Fixes #1808).
* Fixed: GoPlay (Vier, Vijf, Zes, Zeven) channels broken (Fixes #1809).
* Fixed: UR Play listings (Fixes #1812).
* Fixed: Vier.be season and video parsing (See #1820).
* Fixed: Videoland streams and live TV (Fixes #1813 and Fixes #1804)

</news>
<assets>
Expand Down
70 changes: 25 additions & 45 deletions plugin.video.retrospect/channels/channel.be/vier/chn_vier.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ def create_program_typed_item(self, result_set: dict) -> MediaItemResult:
url = f"{self.baseUrl}{path}"

if item_sub_type == "movie":
url = f"{self.baseUrl}/video{path}"
item = MediaItem(title, url, media_type=mediatype.MOVIE)
# item.metaData["retrospect:parser"] = "movie"
else:
Expand Down Expand Up @@ -333,7 +334,10 @@ def create_season_item(self, result_set):
item.set_date(date_stamp.year, date_stamp.month, date_stamp.day, date_stamp.hour,
date_stamp.minute, date_stamp.second)

self.__extract_stream_collection(item, video_info)
duration = (video_info.get("streamCollection", {}) or {}).get("duration", 0)
if duration:
item.set_info_label(MediaItem.LabelDuration, duration)

videos.append(item)
if season_item:
season_item.items.append(item)
Expand Down Expand Up @@ -362,7 +366,11 @@ def create_search_result(self, result_set: dict) -> MediaItemResult:
if item_type == "video":
item = MediaItem(title, url, media_type=mediatype.VIDEO)
elif item_type == "program":
item = FolderItem(title, url, content_type=contenttype.EPISODES)
if (data["tracking"] and data["tracking"]["item_category"] == "Film") or data["program"] == "":
url = url.replace(self.baseUrl, f"{self.baseUrl}/video")
item = MediaItem(title, url, media_type=mediatype.MOVIE)
else:
item = FolderItem(title, url, content_type=contenttype.EPISODES)
else:
Logger.warning("Unknown search result type.")
return None
Expand Down Expand Up @@ -510,14 +518,10 @@ def update_video_item_from_nextjs(self, item: MediaItem) -> MediaItem:
data = UriHandler.open(item.url, additional_headers=self.httpHeaders)
json_data = Regexer.do_regex(r"({\"video\":{.+?})\]}\],", data)[0]
nextjs_json = JsonHelper(json_data)

# See if the NextJS data has stream info.
self.__extract_stream_collection(item, nextjs_json.get_value("video"))

# if not streams were included, perhaps this is a drm protected sstream.
if not item.has_streams():
return self.update_video_item_with_id(item)
return item
video_id = nextjs_json.get_value("videoId")
item.metaData["whatsonId"] = nextjs_json.get_value("video", "tracking", "whatsonId")
item.url = f"https://api.goplay.be/web/v1/videos/long-form/{video_id}"
return self.update_video_item_with_id(item)

def update_video_item_with_id(self, item: MediaItem) -> MediaItem:
""" Updates an existing MediaItem with more data.
Expand Down Expand Up @@ -570,6 +574,12 @@ def update_video_item_with_id(self, item: MediaItem) -> MediaItem:
if error_message == "Locked":
# set it for the error statistics
item.isGeoLocked = True

whatson_id = item.metaData.get("whatsonId", None)
if whatson_id:
# 2615619 = GoPlay contentSourceID.
return self.__get_ssai_streams_for_content_source(item, 2615619, whatson_id, None)

Logger.info("No stream manifest found: {}".format(error_message))
item.complete = False
return item
Expand All @@ -582,40 +592,6 @@ def update_video_item_with_id(self, item: MediaItem) -> MediaItem:
item.complete = M3u8.update_part_with_m3u8_streams(
item, m3u8_url, channel=self, encrypted=False)

def __extract_stream_collection(self, item, video_info):
stream_collection = video_info.get("streamCollection", {})
prefer_widevine = True # video_info.get("videoType") != "longForm"

if stream_collection:
drm_key = stream_collection["drmKey"]
video_id = video_info["uuid"]
if drm_key:
Logger.info(f"Found DRM enabled item: {item.name}")
item.url = f"https://api.goplay.be/web/v1/videos/long-form/{video_id}"
item.isGeoLocked = True
item.isDrmProtected = True
item.metaData["drm"] = drm_key
return

streams = stream_collection["streams"]
duration = stream_collection["duration"]
if duration:
item.set_info_label(MediaItem.LabelDuration, duration)
for stream in streams:
proto = stream["protocol"]
stream_url = stream["url"]
if proto == "dash":
stream = item.add_stream(stream_url, 1550 if prefer_widevine else 1450)
Mpd.set_input_stream_addon_input(stream)
elif proto == "hls":
stream = item.add_stream(stream_url, 1500)
M3u8.set_input_stream_addon_input(stream)

if "/geo" in stream_url:
item.isGeoLocked = True

item.complete = item.has_streams()

def __extract_artwork(self, item: MediaItem, images: dict, set_fanart: bool = True):
if not images:
return
Expand All @@ -636,7 +612,11 @@ def __get_ssai_streams(self, item, json_data):
content_source_id = json_data.get_value("ssai", "contentSourceID")
video_id = json_data.get_value("ssai", "videoID")
drm_header = json_data.get_value("drmXml", fallback=None)

return self.__get_ssai_streams_for_content_source(item, content_source_id, video_id, drm_header)

def __get_ssai_streams_for_content_source(self, item, content_source_id, video_id, drm_header):
Logger.info("No stream data found, trying SSAI data")

streams_url = 'https://dai.google.com/ondemand/dash/content/{}/vid/{}/streams'.format(
content_source_id, video_id)
# streams_url = "https://pubads.g.doubleclick.net/ondemand/dash/content/{}/vid/{}/streams".format(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,8 @@ def update_video_item(self, item):

if AddonSettings.use_adaptive_stream_add_on():
stream = item.add_stream(url, 0)
M3u8.set_input_stream_addon_input(stream, item.HttpHeaders)
M3u8.set_input_stream_addon_input(
stream, stream_headers=item.HttpHeaders, manifest_headers=item.HttpHeaders)
item.complete = True
else:
for s, b in M3u8.get_streams_from_m3u8(url, append_query_string=True):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,9 @@ def update_graphql_item(self, item):
key_value=encryption_json,
key_headers={"Content-Type": "application/json", "authorization": "Basic {}".format(token)}
)
Mpd.set_input_stream_addon_input(stream, license_key=encryption_key, headers={"user-agent": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13 (.NET CLR 3.5.30729)"})
Mpd.set_input_stream_addon_input(
stream, license_key=encryption_key,
stream_headers={"user-agent": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13 (.NET CLR 3.5.30729)"})
item.complete = True

elif stream_type == "m3u8" and not drm:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
import datetime
import re
from typing import Union, List, Optional, Tuple, Dict
from typing import Union, List, Optional, Tuple

import pytz

Expand Down Expand Up @@ -66,8 +66,10 @@ def __init__(self, channel_info):
preprocessor=self.extract_program_id,
parser=["blocks"], creator=self.create_program_item)

self._add_data_parser(
"https://layout.videoland.bedrock.tech/front/v1/rtlnl/m6group_web/main/token-web-4/video/",
self._add_data_parsers([
"https://layout.videoland.bedrock.tech/front/v1/rtlnl/m6group_web/main/token-web-4/video/",
"https://layout.videoland.bedrock.tech/front/v1/rtlnl/m6group_web/main/token-web-4/live/"
],
requires_logon=True,
name="Video updater", json=True, updater=self.update_video_item)

Expand Down Expand Up @@ -96,20 +98,20 @@ def add_others_and_check_correct_url(self, data: str) -> Tuple[JsonHelper, List[
self.search_url, content_type=contenttype.TVSHOWS)
items.append(search)

extras: Dict[int, Tuple[str, str]] = {
LanguageHelper.Recent: (
"page_6599c70de291a2.86703456--6918fda7-49db-49d5-b7f3-1e4a5b6bfab3",
contenttype.TVSHOWS),
# Already a standard item
# LanguageHelper.Popular: ("page_6599c70de291a2.86703456--47a90b2c-669e-453f-8e3d-07eebbe4d4d0_167", contenttype.TVSHOWS)
}

for lang_id, extra in extras.items():
page_id, content_type = extra
title = LanguageHelper.get_localized_string(lang_id)
url = f"https://layout.videoland.bedrock.tech/front/v1/rtlnl/m6group_web/main/token-web-4/service/videoland_root/block/{page_id}?nbPages={self.__pages}"
item = FolderItem(title, url, content_type=content_type)
items.append(item)
# extras: Dict[int, Tuple[str, str]] = {
# LanguageHelper.Recent: (
# "page_6599c70de291a2.86703456--6918fda7-49db-49d5-b7f3-1e4a5b6bfab3",
# contenttype.TVSHOWS),
# # Already a standard item
# # LanguageHelper.Popular: ("page_6599c70de291a2.86703456--47a90b2c-669e-453f-8e3d-07eebbe4d4d0_167", contenttype.TVSHOWS)
# }
#
# for lang_id, extra in extras.items():
# page_id, content_type = extra
# title = LanguageHelper.get_localized_string(lang_id)
# url = f"https://layout.videoland.bedrock.tech/front/v1/rtlnl/m6group_web/main/token-web-4/service/videoland_root/block/{page_id}?nbPages={self.__pages}"
# item = FolderItem(title, url, content_type=content_type)
# items.append(item)

# Now apparently the root of Videoland returns a different response every now and then. We
# need to check that and retry otherwise.
Expand Down Expand Up @@ -180,8 +182,7 @@ def create_mainlist_item(self, result_set: Union[str, dict]) -> Union[MediaItem,
item.poster = poster_url
return item

def create_content_item(self, result_set: Union[str, dict]) -> Union[
MediaItem, List[MediaItem], None]:
def create_content_item(self, result_set: Union[str, dict]) -> Union[MediaItem, List[MediaItem], None]:
result_set: dict = result_set["itemContent"]

title = result_set["title"]
Expand All @@ -200,6 +201,15 @@ def create_content_item(self, result_set: Union[str, dict]) -> Union[
item = FolderItem(title, url, content_type=contenttype.TVSHOWS)
elif "collectie" in action:
return None
elif "live" in action:
item_id = action_info["target"]["value_layout"]["seo"]
url = f"https://layout.videoland.bedrock.tech/front/v1/rtlnl/m6group_web/main/token-web-4/live/{item_id}/layout?nbPages={self.__pages}"
if "rtl" in item_id:
title = f"{item_id.upper()}: {title}"
else:
title = f"{item_id.title()}: {title}"
item = MediaItem(title, url, media_type=mediatype.EPISODE)
item.isLive = True
else:
url = f"https://layout.videoland.bedrock.tech/front/v1/rtlnl/m6group_web/main/token-web-4/video/{item_id}/layout?nbPages=2"
item = MediaItem(title, url, media_type=mediatype.EPISODE)
Expand Down Expand Up @@ -273,11 +283,11 @@ def postprocess_episodes(self, data, items: List[MediaItem]) -> List[MediaItem]:
# This is probably not the real broadcast date, but it at least fixes the order.
if (len(items) > 1
and data.json.get("featureId") == "videos_by_season_by_program"
and all(item.media_type == mediatype.EPISODE and not re.match("[Aa]flevering \d+", item.name) for item in items)):
and all(item.media_type == mediatype.EPISODE and not re.match(r"[Aa]flevering \d+", item.name) for item in items)):
date = ""
for index, item in enumerate(items, start=1):
item.name = f"{index:02} {item.name}"
if (not item.has_date() and date):
if not item.has_date() and date:
previous_episode_date = DateHelper.get_datetime_from_string(date, "%Y-%m-%d")
item.set_date(previous_episode_date.year, previous_episode_date.month, previous_episode_date.day)
item.name = f"{item.name} [date unknown]"
Expand All @@ -287,7 +297,12 @@ def postprocess_episodes(self, data, items: List[MediaItem]) -> List[MediaItem]:
return items

def create_program_item(self, result_set: dict) -> Union[MediaItem, List[MediaItem], None]:
if not result_set["title"]:
if not result_set["title"] and result_set.get("blockTemplateId") == "Solo":
# Most likely a single video or movie
if result_set.get("content", {}).get("items"):
result_set["content"]["items"][0]["itemContent"]["title"] = self.parentItem.name
return self.create_content_item(result_set["content"]["items"][0])

return None

title = result_set["title"].get("long", result_set["title"].get("short"))
Expand Down Expand Up @@ -365,33 +380,41 @@ def filter_premium(self) -> Optional[bool]:
def update_video_item(self, item: MediaItem) -> MediaItem:
data = JsonHelper(UriHandler.open(item.url, additional_headers=self.httpHeaders))
video_info = data.get_value("blocks", 0, "content", "items", 0, "itemContent", "video")
video_id = video_info["id"]

# Find the first Dash item for DRM info (assuming they are all equally DRM-ed).
dash_assets = [v for v in video_info["assets"] if v["format"] == "dashcenc"]
dash_asset = dash_assets[0]
drm_config = dash_asset["drm"]["config"]
preferred_service = drm_config["serviceCode"]
content_type = drm_config["contentType"]
video_id = drm_config["contentId"]

# Construct license info
license_token_url = f"https://drm.videoland.bedrock.tech/v1/customers/rtlnl/platforms/m6group_web/services/videoland_catchup/users/{self.__uid}/videos/{video_id}/upfront-token"
license_token = JsonHelper(
UriHandler.open(license_token_url, additional_headers=self.httpHeaders)).get_value(
"token")
license_key = Mpd.get_license_key("https://lic.drmtoday.com/license-proxy-widevine/cenc/",
key_headers={
"x-dt-auth-token": license_token,
"content-type": "application/octstream"
}, json_filter="JBlicense")
license_token_url = f"https://drm.videoland.bedrock.tech/v1/customers/rtlnl/platforms/m6group_web/services/{preferred_service}/users/{self.__uid}/{content_type}/{video_id}/upfront-token"
license_token = JsonHelper(UriHandler.open(license_token_url, additional_headers=self.httpHeaders)).get_value("token")
license_key = Mpd.get_license_key(
"https://lic.drmtoday.com/license-proxy-widevine/cenc/",
key_headers={
"x-dt-auth-token": license_token,
"content-type": "application/octstream"
}, json_filter="JBlicense")

for asset in video_info["assets"]:
quality = asset["video_quality"]
url = asset["path"]
video_type = asset["video_container"]
# video_container = asset["container"]
video_format = asset["format"]
can_playback_drm = asset["drm"]["type"].lower() == "software"

if quality == "hd":
if not can_playback_drm:
continue

if video_type == "mpd" or video_format == "dashcenc" or video_format == "dash":
stream = item.add_stream(url, 2000 if quality == "hd" else 1200)
Mpd.set_input_stream_addon_input(stream, license_key=license_key)
item.complete = True

# elif video_type == "m3u8":
# # Not working in Kodi
# stream = item.add_stream(url, 2000 if quality == "hd" else 1200)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,10 @@ msgctxt "#30371"
msgid "Trending"
msgstr ""

msgctxt "#30372"
msgid "New Search"
msgstr ""

# empty strings from id 30365 to 30400
msgctxt "#30401"
msgid "Dutch"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,10 @@ msgctxt "#30371"
msgid "Trending"
msgstr ""

msgctxt "#30372"
msgid "New Search"
msgstr ""

# empty strings from id 30365 to 30400
msgctxt "#30401"
msgid "Dutch"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,10 @@ msgctxt "#30371"
msgid "Trending"
msgstr ""

msgctxt "#30372"
msgid "New Search"
msgstr ""

# empty strings from id 30365 to 30400
msgctxt "#30401"
msgid "Dutch"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,10 @@ msgctxt "#30371"
msgid "Trending"
msgstr ""

msgctxt "#30372"
msgid "New Search"
msgstr ""

# empty strings from id 30365 to 30400
msgctxt "#30401"
msgid "Dutch"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,10 @@ msgctxt "#30371"
msgid "Trending"
msgstr ""

msgctxt "#30372"
msgid "New Search"
msgstr ""

# empty strings from id 30365 to 30400
msgctxt "#30401"
msgid "Dutch"
Expand Down
Loading
Loading