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 1 commit
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.



15 changes: 15 additions & 0 deletions plugin.video.rtve/addon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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))

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.jpg</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.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
12 changes: 12 additions & 0 deletions plugin.video.rtve/resources/lib/rtve/TestHtml.py
mcr222 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import json

from resources.lib.rtve.rtve import rtve
import urllib.request, urllib.parse, urllib.error
#un munt de testos de les diferents funcions usades

rtve = rtve("", "")
(temporades, videos) = rtve.listProgrames("https://api.rtve.es/api/tematicas/823")
type(temporades[0])
print(len(temporades))
for temp in temporades:
print(temp.url)
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 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_path, addon):
xbmc.log("plugin.video.rtve classe rtve - init() ")
self.addon_path = addon_path
mcr222 marked this conversation as resolved.
Show resolved Hide resolved

# mode = None
def listHome(self):
xbmc.log("plugin.video.rtve classe rtve - listHome() ")
mcr222 marked this conversation as resolved.
Show resolved Hide resolved
coleccions = FolderVideo('Television', "https://api.rtve.es/api/tematicas/823", "getProgrames", "",
"")
return [coleccions]

def listProgrames(self, urlApi):
xbmc.log("plugin.video.rtve - programas " + urlApi)
mcr222 marked this conversation as resolved.
Show resolved Hide resolved
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))
mcr222 marked this conversation as resolved.
Show resolved Hide resolved
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))
mcr222 marked this conversation as resolved.
Show resolved Hide resolved
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 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() ")
addon = xbmcaddon.Addon()
addon_path = xbmcvfs.translatePath(addon.getAddonInfo('path'))
mcr222 marked this conversation as resolved.
Show resolved Hide resolved
self.rtve = rtve(addon_path, 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()")
mcr222 marked this conversation as resolved.
Show resolved Hide resolved


def run(self, mode, url):
xbmc.log("plugin.video.rtve classe UI - run() mode = " + str(mode) + ", url " + str(url))
mcr222 marked this conversation as resolved.
Show resolved Hide resolved

if mode == None:
xbmc.log("plugin.video.rtve classe UI - mode = None")
mcr222 marked this conversation as resolved.
Show resolved Hide resolved
lFolder = self.rtve.listHome()

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

elif mode[0] == 'getProgrames':
xbmc.log("plugin.video.rtve - Programes")
(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)))
mcr222 marked this conversation as resolved.
Show resolved Hide resolved

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))
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")
mcr222 marked this conversation as resolved.
Show resolved Hide resolved
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.LOGINFO)
mcr222 marked this conversation as resolved.
Show resolved Hide resolved

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))
mcr222 marked this conversation as resolved.
Show resolved Hide resolved

stream_url = "https://ztnr.rtve.es/ztnr/{}.mpd".format(videoId)
xbmc.log("plugin.video.rtve - UI - playVideo apijson url" + str(stream_url))
mcr222 marked this conversation as resolved.
Show resolved Hide resolved

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))

license_url = tokenJson['widevineURL']
xbmc.log("plugin.video.rtve - UI - playVideo widevine url" + str(license_url))
mcr222 marked this conversation as resolved.
Show resolved Hide resolved
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.LOGINFO)

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