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.rtve]v1.0.0 #4591

Merged
merged 2 commits into from
Nov 26, 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
339 changes: 339 additions & 0 deletions plugin.video.rtve/LICENSE.txt

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions plugin.video.rtve/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# plugin.video.rtve
Kodi addon RTVE.
Entertainment, news, sports, documentaries, etc from spanish television https://www.rtve.es/play/

Complement per Kodi - XBMC.
Toda la programación de RTVE.



17 changes: 17 additions & 0 deletions plugin.video.rtve/addon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from logging import DEBUG

import xbmc
import sys
import urllib.parse
import xbmcplugin
from resources.lib.ui.UI import UI

addon_handle = int(sys.argv[1])
args = urllib.parse.parse_qs(sys.argv[2][1:])

xbmc.log("plugin.video.rtve - addon.py - args: " + str(args), xbmc.LOGDEBUG)

xbmcplugin.setContent(addon_handle, 'movies')

ui = UI(sys.argv[0], addon_handle, args)
ui.run(args.get('mode', None), args.get('url', ['']))
28 changes: 28 additions & 0 deletions plugin.video.rtve/addon.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon id="plugin.video.rtve" name="RTVE" version="1.0.0" provider-name="mcr222">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
<import addon="script.module.future" version="1.0.0"/>
<import addon="script.module.inputstreamhelper" version="0.4.2"/>
</requires>
<extension point="xbmc.python.pluginsource" library="addon.py">
<provides>video</provides>
</extension>
<extension point="xbmc.addon.metadata">
<platform>all</platform>
<summary lang="en_GB">Entertainment, news, documentaries, etc from spanish television https://www.rtve.es/play/</summary>
<description lang="en_GB">Entertainment, news, documentaries, etc from spanish television https://www.rtve.es/play/. </description>
<summary lang="ca_ES">Tots els programes de RTVE.</summary>
<description lang="ca_ES">Tota la programació de RTVE.</description>
<summary lang="es_ES">Toda la programación de RTVE</summary>
<description lang="es_ES">Toda la programación de RTVE</description>
<language>ca</language>
<license>GPL-2.0-or-later</license>
<source>https://github.com/mcr222/plugin.video.rtve</source>
<website>https://www.rtve.es/play/</website>
<assets>
<icon>resources/icon.png</icon>
<fanart>resources/fanart.jpg</fanart>
</assets>
</extension>
</addon>
2 changes: 2 additions & 0 deletions plugin.video.rtve/changelog.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
v1.0.0
- First version
Empty file.
Binary file added plugin.video.rtve/resources/fanart.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added plugin.video.rtve/resources/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
Empty file.
104 changes: 104 additions & 0 deletions plugin.video.rtve/resources/lib/rtve/rtve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from __future__ import division

import json
from builtins import object
from logging import DEBUG

from resources.lib.video.FolderVideo import FolderVideo
from resources.lib.utils.Utils import *
import xbmc

from resources.lib.video.Video import Video


class rtve(object):
def __init__(self, addon):
xbmc.log("plugin.video.rtve classe rtve - init() ", xbmc.LOGDEBUG)

# mode = None
def listHome(self):
xbmc.log("plugin.video.rtve classe rtve - listHome() ", xbmc.LOGDEBUG)
coleccions = FolderVideo('Television', "https://api.rtve.es/api/tematicas/823", "getProgrames", "",
"")
return [coleccions]

def listProgrames(self, urlApi):
xbmc.log("plugin.video.rtve - programas " + urlApi, xbmc.LOGDEBUG)
folders = []
videos = []

hijosJson ="hijos.json?page="
videosJson = "videos.json?page="
hijosUrl = ""
if hijosJson in urlApi:
hijosUrl = urlApi
elif not videosJson in urlApi:
hijosUrl = urlApi + "/" + hijosJson + "1"

if hijosUrl:
split = hijosUrl.split("=")
currentPage = int(split[1])
nextHijosUrl = split[0] + "=" + str(currentPage+1)
previousHijosUrl = ""
if currentPage>1:
previousHijosUrl = split[0] + "=" + str(currentPage - 1)

videosUrl = ""
if videosJson in urlApi:
videosUrl = urlApi
elif not hijosJson in urlApi:
videosUrl = urlApi + "/" + videosJson +"1"

if videosUrl:
split = videosUrl.split("=")
currentPage = int(split[1])
nextVideosUrl = split[0] + "=" + str(currentPage + 1)
previousVideosUrl = ""
if currentPage > 1:
previousVideosUrl = split[0] + "=" + str(currentPage - 1)

if hijosUrl:
hijosItems = getJsonData(hijosUrl)['page']['items']
if len(hijosItems)>0:

for item in hijosItems:
xbmc.log("plugin.video.rtve - element " + str(item), xbmc.LOGDEBUG)
itemid = item['id']
programa_url = "https://www.rtve.es/api/programas/{}".format(itemid)
img=""
try:
programaJson = getJsonData(programa_url, 1)['page']['items'][0]
img = programaJson.get('imgPoster', "")
if not img:
img = programaJson.get('imgCol', "")
if not img:
img = programaJson.get('thumbnail', "")
if not img:
img = programaJson.get('imgBackground', "")
except Exception as e:
img=""

foldVideo = FolderVideo(item['title'], "https://api.rtve.es/api/tematicas/" + itemid, 'getProgrames', img, img)
folders.append(foldVideo)

if previousHijosUrl:
folders.append(FolderVideo("Anterior Pag", previousHijosUrl, 'getProgrames'))

folders.append(FolderVideo("Siguiente Pag", nextHijosUrl, 'getProgrames'))
return (folders, videos)

if videosUrl:
videosItems = getJsonData(videosUrl)['page']['items']
if len(videosItems)>0:
for item in videosItems:
xbmc.log("plugin.video.rtve - element " + str(item), xbmc.LOGDEBUG)
img = item['thumbnail']
video = Video(item['title'], img, img, item['description'], item['id'], "")
videos.append(video)

if previousVideosUrl:
folders.append(FolderVideo("Anterior Pag", previousVideosUrl, 'getProgrames'))

folders.append(FolderVideo("Siguiente Pag", nextVideosUrl, 'getProgrames'))

return (folders, videos)
198 changes: 198 additions & 0 deletions plugin.video.rtve/resources/lib/ui/UI.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
from builtins import str
from builtins import object
from logging import DEBUG

from resources.lib.utils.Utils import buildUrl, getJsonData
from resources.lib.rtve.rtve import rtve
import xbmcaddon
import xbmcplugin
import xbmcgui
import xbmc
import xbmcvfs
import time

class UI(object):

def __init__(self, base_url, addon_handle, args):
xbmc.log("plugin.video.rtve classe UI - start init() ", xbmc.LOGDEBUG)
addon = xbmcaddon.Addon()
self.rtve = rtve(addon)
self.base_url = base_url
self.addon_handle = addon_handle
self.args = args
self.mode = args.get('mode', None)
self.url = args.get('url', [''])
xbmc.log("plugin.video.rtve classe UI - finish init()", xbmc.LOGDEBUG)


def run(self, mode, url):
xbmc.log("plugin.video.rtve classe UI - run() mode = " + str(mode) + ", url " + str(url), xbmc.LOGDEBUG)

if mode == None:
xbmc.log("plugin.video.rtve classe UI - mode = None", xbmc.LOGDEBUG)
lFolder = self.rtve.listHome()

if len(lFolder) > 0:
self.listFolder(lFolder)
else:
xbmc.log("plugin.video.rtve - UI.run() Home - No existeixen elements", xbmc.LOGDEBUG)

elif mode[0] == 'getProgrames':
xbmc.log("plugin.video.rtve - Programes", xbmc.LOGDEBUG)
(folders, videos) = self.rtve.listProgrames(url[0])
self.listFolder(folders, False)
self.listVideos(videos)

elif mode[0] == 'playVideo':
self.playVideo(url[0])

def listVideos(self, lVideos):
xbmc.log("plugin.video.rtve - UI - listVideos - Numero videos: " + str(len(lVideos)), xbmc.LOGDEBUG)

for video in lVideos:
# Create a list item with a text label
list_item = xbmcgui.ListItem(label=video.title)
# Set graphics (thumbnail, fanart, banner, poster, landscape etc.) for the list item.
# Here we use only poster for simplicity's sake.
# In a real-life plugin you may need to set multiple image types.
list_item.setArt({'poster': video.iconImage})
list_item.setProperty('IsPlayable', 'true')
# Set additional info for the list item via InfoTag.
# 'mediatype' is needed for skin to display info for this ListItem correctly.
info_tag = list_item.getVideoInfoTag()
info_tag.setMediaType('movie')
info_tag.setTitle(video.title)
info_tag.setPlot(video.information)
# Set 'IsPlayable' property to 'true'.

url = video.url
# Add the list item to a virtual Kodi folder.
# is_folder = False means that this item won't open any sub-list.
is_folder = False
# Add our item to the Kodi virtual folder listing.
xbmc.log("plugin.video.rtve - UI - directory item " + str(url), xbmc.LOGDEBUG)
urlPlugin = buildUrl({'mode': 'playVideo', 'url': url}, self.base_url)

xbmcplugin.addDirectoryItem(self.addon_handle, urlPlugin, list_item, is_folder)
# Add sort methods for the virtual folder items
xbmcplugin.addSortMethod(self.addon_handle, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE)
xbmcplugin.addSortMethod(self.addon_handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR)

# Finish creating a virtual folder.
xbmcplugin.endOfDirectory(self.addon_handle)

def listFolder(self, lFolderVideos, enddirectory=True):
xbmc.log("plugin.video.rtve classe UI - listFolder", xbmc.LOGDEBUG)
for folder in lFolderVideos:

mode = folder.mode
name = folder.name
url = folder.url
iconImage = folder.iconImage
thumbImage = folder.thumbnailImage

urlPlugin = buildUrl({'mode': mode, 'url': url}, self.base_url)
liz = xbmcgui.ListItem(name)
liz.setInfo(type="Video", infoLabels={"title": name})
liz.setArt({'thumb': thumbImage, 'icon' : iconImage})

xbmcplugin.addDirectoryItem(handle=self.addon_handle, url=urlPlugin, listitem=liz, isFolder=True)

if enddirectory:
xbmcplugin.endOfDirectory(self.addon_handle)

class DRMStreamPlayer(xbmc.Player):
def __init__(self):
super().__init__()
self.is_playing = False
self.playback_error = False

def onPlayBackStarted(self):
self.is_playing = True
xbmc.log('Playback started successfully', xbmc.LOGDEBUG)

def onPlayBackError(self):
self.playback_error = True
xbmc.log('Playback error occurred', xbmc.LOGERROR)

def onPlayBackStopped(self):
self.is_playing = False

def playVideo(self,videoId):
xbmc.log("plugin.video.rtve -UI - playVideo " + str(videoId), xbmc.LOGDEBUG)

stream_url = "https://ztnr.rtve.es/ztnr/{}.mpd".format(videoId)
xbmc.log("plugin.video.rtve - UI - playVideo apijson url" + str(stream_url), xbmc.LOGDEBUG)

license_url = ""
try:
tokenUrl = "https://api.rtve.es/api/token/{}".format(videoId)
tokenJson = getJsonData(tokenUrl)

xbmc.log("plugin.video.rtve - UI - playVideo token json" + str(tokenJson), xbmc.LOGDEBUG)

license_url = tokenJson['widevineURL']
xbmc.log("plugin.video.rtve - UI - playVideo widevine url" + str(license_url), xbmc.LOGDEBUG)
except Exception as e:
xbmc.log(f'Error playing DRM stream: {str(e)}', xbmc.LOGERROR)
mcr222 marked this conversation as resolved.
Show resolved Hide resolved


from inputstreamhelper import Helper # pylint: disable=import-outside-toplevel
from urllib.parse import quote

# Constants
PROTOCOL = 'mpd'
DRM = 'com.widevine.alpha'

# HTTP headers
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
'Referer': 'https://www.rtve.es/',
'Origin': 'https://www.rtve.es',
'Accept': '*/*'
}

# Convert headers to Kodi format
headers_string = '&'.join([f'{k}={quote(v)}' for k, v in headers.items()])

try:
# Initialize custom player
player = self.DRMStreamPlayer()

# Create and configure the ListItem
play_item = xbmcgui.ListItem(path=stream_url)

# Set required properties for DRM playback
play_item.setProperty('inputstream', 'inputstream.adaptive')
play_item.setProperty('inputstream.adaptive.manifest_type', PROTOCOL)
play_item.setProperty('inputstream.adaptive.manifest_headers', headers_string)
play_item.setProperty('inputstream.adaptive.stream_headers', headers_string)

# Configure license key with proper formatting and increased timeout
if license_url:
play_item.setProperty('inputstream.adaptive.license_type', DRM)
play_item.setProperty('inputstream.adaptive.license_key', license_url)

# Set additional properties
play_item.setMimeType('application/dash+xml')
play_item.setContentLookup(False)

# Add properties to help with buffering
play_item.setProperty('inputstream.adaptive.stream_selection_type', 'adaptive')

# Adjust these buffering settings
play_item.setProperty('inputstream.adaptive.stream_buffer_size', '524288') # Doubled buffer
play_item.setProperty('inputstream.adaptive.initial_buffer_duration', '15')
play_item.setProperty('inputstream.adaptive.persistent_storage', 'true')
play_item.setProperty('inputstream.adaptive.max_bandwidth', '20000000')
play_item.setProperty('inputstream.adaptive.min_bandwidth', '500000')

# Start playback
xbmcplugin.setResolvedUrl(handle=self.addon_handle, succeeded=True, listitem=play_item)

# Log success
xbmc.log('DRM Stream playback initiated successfully', xbmc.LOGDEBUG)

except Exception as e:
xbmc.log(f'Error playing DRM stream: {str(e)}', xbmc.LOGERROR)
xbmcgui.Dialog().notification('Error', 'Failed to play DRM stream', xbmcgui.NOTIFICATION_ERROR)
Empty file.
Loading
Loading