From 7bafd9290195ce39adc83531d103a4a2a97d5b7b Mon Sep 17 00:00:00 2001 From: Christian Kölpin Date: Fri, 16 Dec 2016 19:22:24 +0100 Subject: [plugin.video.mediathek] 0.7.5 (#756) [plugin.video.mediathek] 0.7.5 --- plugin.video.mediathek/mediathek/__init__.py | 134 +++++++++++++ plugin.video.mediathek/mediathek/ard.py | 168 ++++++++++++++++ plugin.video.mediathek/mediathek/arte.py | 262 +++++++++++++++++++++++++ plugin.video.mediathek/mediathek/dreisat.py | 177 +++++++++++++++++ plugin.video.mediathek/mediathek/factory.py | 42 ++++ plugin.video.mediathek/mediathek/kika.py | 140 ++++++++++++++ plugin.video.mediathek/mediathek/ndr.py | 274 +++++++++++++++++++++++++++ plugin.video.mediathek/mediathek/orf.py | 162 ++++++++++++++++ plugin.video.mediathek/mediathek/wdr.py | 191 +++++++++++++++++++ plugin.video.mediathek/mediathek/zdf.py | 152 +++++++++++++++ 10 files changed, 1702 insertions(+) create mode 100644 plugin.video.mediathek/mediathek/__init__.py create mode 100644 plugin.video.mediathek/mediathek/ard.py create mode 100644 plugin.video.mediathek/mediathek/arte.py create mode 100644 plugin.video.mediathek/mediathek/dreisat.py create mode 100644 plugin.video.mediathek/mediathek/factory.py create mode 100644 plugin.video.mediathek/mediathek/kika.py create mode 100644 plugin.video.mediathek/mediathek/ndr.py create mode 100644 plugin.video.mediathek/mediathek/orf.py create mode 100644 plugin.video.mediathek/mediathek/wdr.py create mode 100644 plugin.video.mediathek/mediathek/zdf.py (limited to 'plugin.video.mediathek/mediathek') diff --git a/plugin.video.mediathek/mediathek/__init__.py b/plugin.video.mediathek/mediathek/__init__.py new file mode 100644 index 0000000..b3aedef --- /dev/null +++ b/plugin.video.mediathek/mediathek/__init__.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +#-------------LicenseHeader-------------- +# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters +# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import sys, urllib2,urllib, time; +import socket +socket.setdefaulttimeout(1); + +class SimpleLink(object): + def __init__(self, basePath, size): + self.basePath = basePath; + self.size = size; + +class ComplexLink(object): + def __init__(self, basePath, playPath, size): + self.basePath = basePath; + self.playPath = playPath; + self.size = size; + +class TreeNode(object): + def __init__(self,path,name,link,displayElements,childNodes = []): + self.name = name; + self.path = path; + self.link = link; + self.displayElements = displayElements; + self.childNodes = childNodes; + +class DisplayObject(object): + def __init__(self,title,subTitle,picture,description,link=[],isPlayable = True, date = None, duration = None): + self.title = title + self.subTitle = subTitle + self.link = link + self.picture = picture + self.isPlayable = isPlayable + self.description = description + self.date = date; + self.duration = duration; + +class Mediathek(object): + def loadPage(self,url, values = None, maxTimeout = None): + try: + safe_url = url.replace( " ", "%20" ).replace("&","&") + + if(values is not None): + data = urllib.urlencode(values) + req = urllib2.Request(safe_url, data) + else: + req = urllib2.Request(safe_url) + req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20100101 Firefox/15.0.1') + req.add_header('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8') + req.add_header('Accept-Language', 'de-de,de;q=0.8,en-us;q=0.5,en;q=0.3') + req.add_header('Accept-Charset', 'utf-8') + + if maxTimeout == None: + maxTimeout = 60; + + waittime = 0; + doc = False; + while not doc and waittime < maxTimeout: + try: + if waittime > 0: + time.sleep(waittime); + self.gui.log("download %s %d"%(safe_url,waittime)); + sock = urllib2.urlopen( req ) + doc = sock.read(); + sock.close() + except: + if(waittime == 0): + waittime = 1; + else: + waittime *= 2; + + if doc: + try: + return doc.encode('utf-8'); + except: + return doc; + else: + return '' + except: + return '' + + def buildMenu(self, path, treeNode = None): + if(isinstance(path, (str,unicode))): + path = path.split('.'); + if(len(path) > 0): + index = int(path.pop(0)); + + if(treeNode == None): + treeNode = self.menuTree[index]; + else: + treeNode = treeNode.childNodes[index]; + self.buildMenu(path,treeNode); + else: + if(treeNode == None): + treeNode = self.menuTree[0]; + self.gui.log(treeNode.name); + for childNode in treeNode.childNodes: + self.gui.buildMenuLink(childNode,self, len(treeNode.childNodes)); + if(treeNode.displayElements): + self.buildPageMenu(treeNode.link,len(treeNode.childNodes)); + + def displayCategories(self): + if(len(self.menuTree)>1 or not self.menuTree[0].displayElements): + for treeNode in self.menuTree: + self.gui.buildMenuLink(treeNode,self,len(self.menuTree)) + else: + self.buildPageMenu(self.menuTree[0].link, 0); + + def walkJson(self, path, jsonObject): + path = path.split('.'); + i = 0 + while(i < len(path)): + if(isinstance(jsonObject,list)): + index = int(path.pop(0)); + else: + index = path.pop(0); + jsonObject = jsonObject[index]; + + return jsonObject; + diff --git a/plugin.video.mediathek/mediathek/ard.py b/plugin.video.mediathek/mediathek/ard.py new file mode 100644 index 0000000..6c66ce2 --- /dev/null +++ b/plugin.video.mediathek/mediathek/ard.py @@ -0,0 +1,168 @@ +# -*- coding: utf-8 -*- +#-------------LicenseHeader-------------- +# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters +# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import re, time, datetime; +from mediathek import * + +class ARDMediathek(Mediathek): + def __init__(self, simpleXbmcGui): + self.gui = simpleXbmcGui; + self.rootLink = "http://www.ardmediathek.de" + self.menuTree = ( + TreeNode("0","Neuste Videos",self.rootLink+"/tv/Neueste-Videos/mehr?documentId=21282466",True), + TreeNode("1","Sendungen von A-Z","",False,( + TreeNode("1.0","0-9",self.rootLink+"/tv/sendungen-a-z?buchstabe=0-9",True), + TreeNode("1.1","A",self.rootLink+"/tv/sendungen-a-z?buchstabe=A",True), + TreeNode("1.2","B",self.rootLink+"/tv/sendungen-a-z?buchstabe=B",True), + TreeNode("1.3","C",self.rootLink+"/tv/sendungen-a-z?buchstabe=C",True), + TreeNode("1.4","D",self.rootLink+"/tv/sendungen-a-z?buchstabe=D",True), + TreeNode("1.5","E",self.rootLink+"/tv/sendungen-a-z?buchstabe=E",True), + TreeNode("1.6","F",self.rootLink+"/tv/sendungen-a-z?buchstabe=F",True), + TreeNode("1.7","G",self.rootLink+"/tv/sendungen-a-z?buchstabe=G",True), + TreeNode("1.8","H",self.rootLink+"/tv/sendungen-a-z?buchstabe=H",True), + TreeNode("1.9","I",self.rootLink+"/tv/sendungen-a-z?buchstabe=I",True), + TreeNode("1.10","J",self.rootLink+"/tv/sendungen-a-z?buchstabe=J",True), + TreeNode("1.11","K",self.rootLink+"/tv/sendungen-a-z?buchstabe=K",True), + TreeNode("1.12","L",self.rootLink+"/tv/sendungen-a-z?buchstabe=L",True), + TreeNode("1.13","M",self.rootLink+"/tv/sendungen-a-z?buchstabe=M",True), + TreeNode("1.14","N",self.rootLink+"/tv/sendungen-a-z?buchstabe=N",True), + TreeNode("1.15","O",self.rootLink+"/tv/sendungen-a-z?buchstabe=O",True), + TreeNode("1.16","P",self.rootLink+"/tv/sendungen-a-z?buchstabe=P",True), + TreeNode("1.17","Q",self.rootLink+"/tv/sendungen-a-z?buchstabe=Q",True), + TreeNode("1.18","R",self.rootLink+"/tv/sendungen-a-z?buchstabe=R",True), + TreeNode("1.19","S",self.rootLink+"/tv/sendungen-a-z?buchstabe=S",True), + TreeNode("1.20","T",self.rootLink+"/tv/sendungen-a-z?buchstabe=T",True), + TreeNode("1.21","U",self.rootLink+"/tv/sendungen-a-z?buchstabe=U",True), + TreeNode("1.22","V",self.rootLink+"/tv/sendungen-a-z?buchstabe=V",True), + TreeNode("1.23","W",self.rootLink+"/tv/sendungen-a-z?buchstabe=W",True), + TreeNode("1.24","X",self.rootLink+"/tv/sendungen-a-z?buchstabe=X",True), + TreeNode("1.25","Y",self.rootLink+"/tv/sendungen-a-z?buchstabe=Y",True), + TreeNode("1.26","Z",self.rootLink+"/tv/sendungen-a-z?buchstabe=Z",True), + )), + TreeNode("2","Ausgewählte Dokus".decode("utf-8"),self.rootLink+"/tv/Ausgew%C3%A4hlte-Dokus/mehr?documentId=33649086",True), + TreeNode("3","Ausgewählte Filme".decode("utf-8"),self.rootLink+"/tv/Ausgew%C3%A4hlte-Filme/mehr?documentId=33649088",True), + TreeNode("4","Alle Reportagen und Dokus",self.rootLink+"/tv/Alle-Dokus-Reportagen/mehr?documentId=29897596",True), + TreeNode("5","Alle Filme",self.rootLink+"/tv/Alle-Filme/mehr?documentId=33594630",True), + TreeNode("6","Alle Serien",self.rootLink+"/tv/Serien/mehr?documentId=26402940",True), + TreeNode("7","Themen",self.rootLink+"/tv/Themen/mehr?documentId=21301810",True), + TreeNode("8","Rubriken","",False,( + TreeNode("8.0","Kinder",self.rootLink+"/tv/Kinder/Tipps?documentId=21282542",True), + TreeNode("8.1","Unterhaltung & Comedy",self.rootLink+"/tv/Unterhaltung-Comedy/mehr?documentId=21282544",True), + TreeNode("8.2","Kultur",self.rootLink+"/tv/Kultur/mehr?documentId=21282546",True), + TreeNode("8.3","Wissen",self.rootLink+"/tv/Wissen/mehr?documentId=21282530",True), + TreeNode("8.4","Politik",self.rootLink+"/tv/Politik/mehr?documentId=29684598",True), + TreeNode("8.5","Ratgeber",self.rootLink+"/tv/Ratgeber/mehr?documentId=27112994",True), + TreeNode("8.6","Krimi",self.rootLink+"/tv/Krimi/mehr?documentId=27258656",True), + TreeNode("8.7","Reise",self.rootLink+"/tv/Reise/mehr?documentId=29769608",True), + )), + ) + self.configLink = self.rootLink+"/play/media/%s?devicetype=pc&feature=flash" + #.*Video\?bcastId=\d+&documentId=(\d+)\" class=\"textLink\">\s+?

(.*?)

\s+?

(.*?)

+ self.regex_VideoPageLink = re.compile("\s+?

(.*?)<\/p>\s+?

(.*?)<\/h4>\s+?

(?:(\d+.\d+.\d+) \| )?(\d*) Min.") + self.regex_CategoryPageLink = re.compile("(?:.|\n)+?

(.*?)<\/h4>") + self.pageSelectString = "&mcontent%s=page.%s" + self.regex_DetermineSelectedPage = re.compile("&mcontents{0,1}=page.(\d+)"); + + self.regex_videoLinks = re.compile("\"_quality\":(\d).*?\"_stream\":\[?\"(.*?)\""); + self.regex_pictureLink = re.compile("_previewImage\":\"(.*?)\""); + + + self.regex_Date = re.compile("\\d{2}\\.\\d{2}\\.\\d{2}"); + + + self.replace_html = re.compile("<.*?>"); + + @classmethod + def name(self): + return "ARD"; + def isSearchable(self): + return False; + + def buildPageMenu(self, link, initCount, subLink = False): + self.gui.log("Build Page Menu: %s SubLink: %d"%(link,subLink)); + mainPage = self.loadPage(link); + + elementCount = 0; + + elementCount = self.extractElements(mainPage); + + + self.generateNextPageElement(link, elementCount); + return elementCount; + def generateNextPageElement(self, link, elementCount): + marker = ""; + if("Sendung?documentId" in link): + marker = "s"; + + numberElement = self.regex_DetermineSelectedPage.search(link); + if(numberElement is not None): + oldNumber = int(numberElement.group(1)); + newNumber = oldNumber + 1; + link = link.replace(self.pageSelectString%(marker,oldNumber),self.pageSelectString%(marker,newNumber)); + + self.gui.buildVideoLink(DisplayObject("Weiter","","","",link,False),self,elementCount); + else: + link += self.pageSelectString%(marker,2) + + self.gui.buildVideoLink(DisplayObject("Weiter","","","",link,False),self,elementCount); + + def extractElements(self,mainPage): + videoElements = list(self.regex_VideoPageLink.finditer(mainPage)); + if len(videoElements) == 0: + linkElements = list(self.regex_CategoryPageLink.finditer(mainPage)); + else: + linkElements = [] + + counter = len(videoElements) + len(linkElements); + for element in linkElements: + link = self.rootLink+element.group(1); + title = element.group(2).decode('utf-8'); + # subTitle = element.group(3).decode('utf-8'); + subTitle = "" + self.gui.buildVideoLink(DisplayObject(title,subTitle,"","",link,False),self,counter); + for element in videoElements: + videoId = element.group(1); + title = element.group(2).decode('utf-8'); + subTitle = element.group(3).decode('utf-8'); + if element.group(4): + datestring = element.group(4).decode('utf-8'); + date = datetime.date(*[int(x) for x in datestring.split('.')[::-1]]).timetuple() + else: + date = None + durationstring = element.group(5).decode('utf-8'); + duration = int(durationstring) * 60; + self.decodeVideoInformation(videoId, title, subTitle, counter, date, duration); + return counter; + + def decodeVideoInformation(self, videoId, title, subTitle, nodeCount, date, duration): + link = self.configLink%videoId; + self.gui.log("VideoLink: "+link); + videoPage = self.loadPage(link); + videoLinks = {} + for match in self.regex_videoLinks.finditer(videoPage): + quality = int(match.group(1)); + link = SimpleLink(match.group(2),0); + + if(quality > 0): + quality -= 1 + videoLinks[quality] = link + match = self.regex_pictureLink.search(videoPage) + picture = None + if(match is not None): + picture = match.group(1); + if(len(videoLinks)>0): + self.gui.buildVideoLink(DisplayObject(title, subTitle,picture,"",videoLinks,True,date,duration),self,nodeCount); diff --git a/plugin.video.mediathek/mediathek/arte.py b/plugin.video.mediathek/mediathek/arte.py new file mode 100644 index 0000000..e0a158d --- /dev/null +++ b/plugin.video.mediathek/mediathek/arte.py @@ -0,0 +1,262 @@ +## -*- coding: utf-8 -*- +#-------------LicenseHeader-------------- +# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters +# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import re, traceback, urllib,json; +from mediathek import * +from xml.dom import minidom +from xml.dom import Node; +from bs4 import BeautifulSoup; + +regex_dateString = re.compile("\\d{1,2} ((\\w{3})|(\\d{2})) \\d{4}"); +month_replacements = { + "Jan":"01", + "Feb":"02", + "Mar":"03", + "Apr":"04", + "May":"05", + "Jun":"06", + "Jul":"07", + "Aug":"08", + "Sep":"09", + "Oct":"10", + "Nov":"11", + "Dec":"12", + }; + +class ARTEMediathek(Mediathek): + @classmethod + def name(self): + return "ARTE"; + @classmethod + def isSearchable(self): + return False; + + def __init__(self, simpleXbmcGui): + self.gui = simpleXbmcGui; + self.rootLink = "http://www.arte.tv"; + self.basePage = self.rootLink+"/guide/de/plus7"; + self.jsonLink = "https://api.arte.tv/api/player/v1/config/de/%s" + + self.menuTree = ( + TreeNode("0","Arte+7","mainPage",True), + TreeNode("1","Sendungen von A-Z","showCluster",True), + TreeNode("2","Kategorien","showCategories",True), + ); + + self.selector_videoPages = "li.video > a"; + + self.regex_VideoPageLinksHTML = re.compile("href=[\"'](http:\\\\/\\\\/www\\.arte\\.tv\\\\/guide\\\\/de\\\\/\d{6}-\d{3}/.+?)[\"']"); + self.regex_VideoPageLinksJSON = re.compile("\"url\":\"((http:\\\\/\\\\/www\\.arte\\.tv){0,1}\\\\/guide\\\\/de\\\\/\d{6}-\d{3}\\\\/.+?)\""); + self.regex_findVideoIds = re.compile("(\d{6}-\d{3})(-A)"); + self.regex_JSONPageLink = re.compile("http://arte.tv/papi/tvguide/videos/stream/player/D/\d{6}-\d{3}.+?/ALL/ALL.json"); + self.regex_JSON_VideoLink = re.compile("\"HTTP_MP4_.+?\":{.*?\"bitrate\":(\d+),.*?\"url\":\"(http://.*?.mp4)\".*?\"versionShortLibelle\":\"([a-zA-Z]{2})\".*?}"); + self.regex_JSON_ImageLink = re.compile("\"IUR\":\"(http://.*?\\.arte\\.tv/papi/tvguide/images/.*?\\..{3})\""); + self.regex_JSON_Detail = re.compile("\"VDE\":\"(.*?)\""); + self.regex_JSON_Titel = re.compile("\"VTI\":\"(.*?)\""); + + regexSourceString="%s=\"([\[{].*?[}\]])\""; + self.regex_cluster=re.compile(regexSourceString%"data-clusters"); + self.regex_categories = re.compile(regexSourceString%"data-categoriesVideos"); + self.regex_playlists = re.compile(regexSourceString%"data-highlightedPlaylists"); + + self.categories = { + "DailyMostViewed":re.compile(regexSourceString%"data-dailyMostViewedVideos"), + "Most Viewed": re.compile(regexSourceString%"data-mostViewedVideos"), + "ExpiringVideos":re.compile(regexSourceString%"data-nextExpiringVideos"), + } + + self.regex_extractVideoSources = ( + re.compile(regexSourceString%"data-highlightedVideos"), + re.compile(regexSourceString%"data-latestVideos"), + re.compile(regexSourceString%"data-categoryVideoSet"), + ); + + + + + def buildPageMenu(self, link, initCount): + if(link == "showCluster"): + self.showCluster(); + elif (link == "mainPage"): + self.showMainPage(); + elif (link == "showCategories"): + self.showCategories(); + else: + if(not link.startswith("http")): + link = self.rootLink+link; + pageContent = self.loadPage(link).decode('UTF-8'); + linkFound = self.extractVideoLinksFromHtml(pageContent) + if(not linkFound): + self.extractVideoLinks(pageContent,0); + + def buildJsonMenu(self, path,callhash, initCount): + jsonContent=self.gui.loadJsonFile(callhash); + if(path == "init"): + jsonObject = jsonContent; + else: + jsonObject=self.walkJson(path,jsonContent); + self.gui.log(json.dumps(jsonObject)); + if("videos" in jsonObject): + self.extractVideoLinksFromJson(jsonObject); + if(isinstance(jsonObject,list)): + for counter,jsonObject in enumerate(jsonObject): + if("day" in jsonObject): + self.gui.buildJsonLink(self,jsonObject["day"],"%d"%counter,callhash,0) + + def buildJsonLink(self,name,content): + content = BeautifulSoup(content); + jsonContent = json.loads(content.prettify(formatter=None)) + callhash = self.gui.storeJsonFile(jsonContent,name); + self.gui.buildJsonLink(self,name,"init",callhash,0) + + def showMainPage(self): + self.gui.log("buildPageMenu: "+self.basePage); + pageContent = self.loadPage(self.basePage).decode('UTF-8'); + + for name,regex in self.categories.iteritems(): + match = regex.search(pageContent); + if(match is not None): + self.buildJsonLink(name,match.group(1)); + + self.extractVideoLinksFromHtml(pageContent) + + def extractVideoLinksFromHtml(self, htmlPage): + someMatch = False; + for regex in self.regex_extractVideoSources: + match = regex.search(htmlPage); + if(match is not None): + someMatch = True; + content = BeautifulSoup(match.group(1)); + jsonContent = json.loads(content.prettify(formatter=None)) + self.extractVideoLinksFromJson(jsonContent) + return someMatch; + + + def extractVideoLinksFromJson(self,jsonContent): + for jsonObject in jsonContent["videos"]: + self.buildVideoEntry(jsonObject); + + def showCluster(self): + pageContent = self.loadPage(self.basePage).decode('UTF-8'); + content = BeautifulSoup(self.regex_cluster.search(pageContent).group(1)); + jsonContent = json.loads(content.prettify(formatter=None)) + for menuItem in jsonContent: + self.buildMenuEntry(menuItem); + + def showCategories(self): + pageContent = self.loadPage(self.basePage).decode('UTF-8'); + content = BeautifulSoup(self.regex_categories.search(pageContent).group(1)); + jsonContent = json.loads(content.prettify(formatter=None)) + for jsonObject in jsonContent: + jsonCategorie = jsonObject["category"] + title = unicode(jsonCategorie["title"]); + link=jsonCategorie["url"]; + self.gui.buildVideoLink(DisplayObject(title,"","","",link,False,None),self,0); + + def buildMenuEntry(self, menuItem): + title = menuItem["title"]; + subTitle = menuItem["subtitle"]; + link=menuItem["permalink"]; + self.gui.buildVideoLink(DisplayObject(title,subTitle,"","",link,False,None),self,0); + + def buildVideoEntry(self, jsonObject): + title = jsonObject["title"]; + subTitle = jsonObject["subtitle"]; + detail = jsonObject["teaser"]; + picture = None; + for pictureItem in jsonObject["thumbnails"]: + if(picture is None or picture["width"]. +import re,time +from mediathek import * +from xml.dom import minidom; + + +regex_dateString = re.compile("\\d{2} ((\\w{3})|(\\d{2})) \\d{4}"); +month_replacements = { + "Jan":"01", + "Feb":"02", + "Mar":"03", + "Apr":"04", + "May":"05", + "Jun":"06", + "Jul":"07", + "Aug":"08", + "Sep":"09", + "Oct":"10", + "Nov":"11", + "Dec":"12", + }; + +class DreiSatMediathek(Mediathek): + @classmethod + def name(self): + return "3Sat"; + def isSearchable(self): + return True; + def __init__(self, simpleXbmcGui): + self.gui = simpleXbmcGui; + if(self.gui.preferedStreamTyp == 0): + self.baseType = "video/x-ms-asf"; + elif (self.gui.preferedStreamTyp == 1): + self.baseType = "video/x-ms-asf" + elif (self.gui.preferedStreamTyp == 2): + self.baseType ="video/x-ms-asf"; + else: + self.baseType ="video/quicktime"; + self.webEmType = "video/webm"; + self.menuTree = ( + TreeNode("0","Bauerfeind","http://www.3sat.de/mediathek/rss/mediathek_bauerfeind.xml",True), + TreeNode("1","Bookmark","http://www.3sat.de/mediathek/rss/mediathek_bookmark.xml",True), + TreeNode("2",u"Börse","http://www.3sat.de/mediathek/rss/mediathek_boerse.xml",True), + TreeNode("3","Buchzeit","http://www.3sat.de/mediathek/rss/mediathek_buchzeit.xml",True), + TreeNode("4","daVinci","http://www.3sat.de/mediathek/rss/mediathek_davinci.xml",True), + TreeNode("5","delta","http://www.3sat.de/mediathek/rss/mediathek_delta.xml",True), + TreeNode("6","Film","http://www.3sat.de/mediathek/rss/mediathek_film.xml",True), + TreeNode("7","Gero von Boehm","http://www.3sat.de/mediathek/rss/mediathek_gero.xml",True), + TreeNode("8","hessenreporter","http://www.3sat.de/mediathek/rss/mediathek_hessenreporter.xml",True), + TreeNode("9","hitec","http://www.3sat.de/mediathek/rss/mediathek_hitec.xml",True), + TreeNode("10","Kabarett","http://www.3sat.de/mediathek/rss/mediathek_kabarett.xml",True), + TreeNode("11","Kinomagazin","http://www.3sat.de/mediathek/rss/mediathek_kinomag.xml",True), + TreeNode("12","Kulturzeit","http://www.3sat.de/mediathek/rss/mediathek_Kulturzeit.xml",True), + TreeNode("13","makro","http://www.3sat.de/mediathek/rss/mediathek_makro.xml",True), + TreeNode("14","Musik","http://www.3sat.de/mediathek/rss/mediathek_musik.xml",True), + TreeNode("15","nano","http://www.3sat.de/mediathek/rss/mediathek_nano.xml",True), + TreeNode("16","neues","http://www.3sat.de/mediathek/rss/mediathek_neues.xml",True), + TreeNode("17",u"Peter Voß fragt","http://www.3sat.de/mediathek/rss/mediathek_begegnungen.xml",True), + TreeNode("18","Recht brisant","http://www.3sat.de/mediathek/rss/mediathek_Recht%20brisant.xml",True), + TreeNode("19","scobel","http://www.3sat.de/mediathek/rss/mediathek_scobel.xml",True), + TreeNode("20","SCHWEIZWEIT","http://www.3sat.de/mediathek/rss/mediathek_schweizweit.xml",True), + TreeNode("21","Theater","http://www.3sat.de/mediathek/rss/mediathek_theater.xml",True), + TreeNode("22","vivo","http://www.3sat.de/mediathek/rss/mediathek_vivo.xml",True), + ); + + self.rootLink = "http://www.3sat.de" + self.searchLink = 'http://www.3sat.de/mediathek/mediathek'; + link = "/mediathek/mediathek.php\\?obj=\\d+"; + self.regex_searchResult = re.compile("href=\""+link+"\" class=\"media_result_thumb\""); + self.regex_searchResultLink = re.compile(link) + self.regex_searchLink = re.compile("http://(w|f)streaming.zdf.de/.*?(\\.asx|\\.smil)") + self.regex_searchTitle = re.compile("

.*

"); + self.regex_searchDetail = re.compile(".*"); + self.regex_searchDate = re.compile("\\d{2}.\\d{2}.\\d{4}"); + self.regex_searchImage = re.compile("(/dynamic/mediathek/stills/|/mediaplayer/stills/)\\d*_big\\.jpg"); + self.replace_html = re.compile("<.*?>"); + + def buildPageMenu(self, link, initCount): + self.gui.log("buildPageMenu: "+link); + rssFeed = self.loadConfigXml(link); + self.extractVideoObjects(rssFeed, initCount); + def searchVideo(self, searchText): + values ={'mode':'search', + 'query':searchText, + 'red': '', + 'query_time': '', + 'query_sort': '', + 'query_order':'' + } + mainPage = self.loadPage(self.searchLink,values); + results = self.regex_searchResult.findall(mainPage); + for result in results: + objectLink = self.regex_searchResultLink.search(result).group(); + infoLink = self.rootLink+objectLink + infoPage = self.loadPage(infoLink); + title = self.regex_searchTitle.search(infoPage).group(); + detail = self.regex_searchDetail.search(infoPage).group(); + image = self.regex_searchImage.search(infoPage).group(); + title = self.replace_html.sub("", title); + detail = self.replace_html.sub("", detail); + try: + dateString = self.regex_searchDate.search(infoPage).group(); + pubDate = time.strptime(dateString,"%d.%m.%Y"); + except: + pubDate = time.gmtime(); + videoLink = self.rootLink+objectLink+"&mode=play"; + videoPage = self.loadPage(videoLink); + video = self.regex_searchLink.search(videoPage).group(); + video = video.replace("fstreaming","wstreaming").replace(".smil",".asx"); + links = {} + links[2] = SimpleLink(video,0) + self.gui.buildVideoLink(DisplayObject(title,"",self.rootLink + image,detail,links,True, pubDate),self,len(results)); + def readText(self,node,textNode): + try: + node = node.getElementsByTagName(textNode)[0].firstChild; + return unicode(node.data); + except: + return ""; + def loadConfigXml(self, link): + self.gui.log("load:"+link) + xmlPage = self.loadPage(link); + return minidom.parseString(xmlPage); + def extractVideoObjects(self, rssFeed, initCount): + nodes = rssFeed.getElementsByTagName("item"); + nodeCount = initCount + len(nodes) + for itemNode in nodes: + try: + self.extractVideoInformation(itemNode,nodeCount); + except: + pass + + def parseDate(self,dateString): + dateString = regex_dateString.search(dateString).group(); + for month in month_replacements.keys(): + dateString = dateString.replace(month,month_replacements[month]); + return time.strptime(dateString,"%d %m %Y"); + + def extractVideoInformation(self, itemNode, nodeCount): + title = self.readText(itemNode,"title"); + self.gui.log(title) + dateString = self.readText(itemNode,"pubDate"); + pubDate = self.parseDate(dateString); + descriptionNode = itemNode.getElementsByTagName("description")[0].firstChild.data; + description = unicode(descriptionNode); + picture = ""; + pictureNodes = itemNode.getElementsByTagName("media:thumbnail"); + if(len(pictureNodes) > 0): + picture = pictureNodes[0].getAttribute("url"); + links = {}; + for contentNode in itemNode.getElementsByTagName("media:content"): + height = int(contentNode.getAttribute("height")); + url = contentNode.getAttribute("url"); + size = int(contentNode.getAttribute("fileSize")); + if(height < 300): + links[0] = SimpleLink(url, size); + elif (height < 480): + links[1] = SimpleLink(url, size); + else: + links[2] = SimpleLink(url, size); + if links: + self.gui.buildVideoLink(DisplayObject(title,"",picture,description,links,True, pubDate),self,nodeCount); diff --git a/plugin.video.mediathek/mediathek/factory.py b/plugin.video.mediathek/mediathek/factory.py new file mode 100644 index 0000000..7bcdb96 --- /dev/null +++ b/plugin.video.mediathek/mediathek/factory.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +#-------------LicenseHeader-------------- +# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters +# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +from mediathek.wdr import * +from mediathek.ard import * +from mediathek.zdf import * +from mediathek.arte import * +from mediathek.dreisat import * +from mediathek.orf import * +from mediathek.ndr import * +from mediathek.kika import * + +class MediathekFactory(object): + def __init__(self): + self.avaibleMediathekes = { + ARDMediathek.name():ARDMediathek, + ZDFMediathek.name():ZDFMediathek, + ARTEMediathek.name():ARTEMediathek, + DreiSatMediathek.name():DreiSatMediathek, + ORFMediathek.name():ORFMediathek, + NDRMediathek.name():NDRMediathek, + KIKA.name():KIKA + } + def getAvaibleMediathekTypes(self): + return sorted(self.avaibleMediathekes.keys()) + + def getMediathek(self,mediathekName, gui): + return self.avaibleMediathekes[mediathekName](gui); diff --git a/plugin.video.mediathek/mediathek/kika.py b/plugin.video.mediathek/mediathek/kika.py new file mode 100644 index 0000000..238ba9d --- /dev/null +++ b/plugin.video.mediathek/mediathek/kika.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +#-------------LicenseHeader-------------- +# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters +# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import re, time; +from bs4 import BeautifulSoup; +from mediathek import * + +class KIKA(Mediathek): + def __init__(self, simpleXbmcGui): + self.gui = simpleXbmcGui; + self.rootLink = "http://www.kika.de"; + self.menuTree = ( + TreeNode("0","Videos",self.rootLink+"/videos/index.html",True), + TreeNode("1","Sendungen von A-Z","",False, + ( + TreeNode("1.0","A",self.rootLink+"/sendungen/sendungenabisz100_page-A_zc-05fb1331.html",True), + TreeNode("1.1","B",self.rootLink+"/sendungen/sendungenabisz100_page-B_zc-1775e6d8.html",True), + TreeNode("1.2","C",self.rootLink+"/sendungen/sendungenabisz100_page-C_zc-6248eba0.html",True), + TreeNode("1.3","D",self.rootLink+"/sendungen/sendungenabisz100_page-D_zc-e090a8fb.html",True), + TreeNode("1.4","E",self.rootLink+"/sendungen/sendungenabisz100_page-E_zc-ec2376ed.html",True), + TreeNode("1.5","F",self.rootLink+"/sendungen/sendungenabisz100_page-F_zc-f76734a0.html",True), + TreeNode("1.6","G",self.rootLink+"/sendungen/sendungenabisz100_page-G_zc-34bda7c3.html",True), + TreeNode("1.7","H",self.rootLink+"/sendungen/sendungenabisz100_page-H_zc-7e25e70a.html",True), + TreeNode("1.8","I",self.rootLink+"/sendungen/sendungenabisz100_page-I_zc-b7f774f5.html",True), + TreeNode("1.9","J",self.rootLink+"/sendungen/sendungenabisz100_page-J_zc-3130680a.html",True), + TreeNode("1.10","K",self.rootLink+"/sendungen/sendungenabisz100_page-K_zc-c8f76ba1.html",True), + TreeNode("1.11","L",self.rootLink+"/sendungen/sendungenabisz100_page-L_zc-bbebc1a7.html",True), + TreeNode("1.12","M",self.rootLink+"/sendungen/sendungenabisz100_page-M_zc-00574a43.html",True), + TreeNode("1.13","N",self.rootLink+"/sendungen/sendungenabisz100_page-N_zc-b079366f.html",True), + TreeNode("1.14","O",self.rootLink+"/sendungen/sendungenabisz100_page-O_zc-febc55f5.html",True), + TreeNode("1.15","P",self.rootLink+"/sendungen/sendungenabisz100_page-P_zc-2c1a492f.html",True), + TreeNode("1.16","Q",self.rootLink+"/sendungen/sendungenabisz100_page-Q_zc-2cb019d6.html",True), + TreeNode("1.17","R",self.rootLink+"/sendungen/sendungenabisz100_page-R_zc-cab3e22b.html",True), + TreeNode("1.18","S",self.rootLink+"/sendungen/sendungenabisz100_page-S_zc-e7f420d0.html",True), + TreeNode("1.19","T",self.rootLink+"/sendungen/sendungenabisz100_page-T_zc-84a2709f.html",True), + TreeNode("1.20","U",self.rootLink+"/sendungen/sendungenabisz100_page-U_zc-a26c1157.html",True), + TreeNode("1.21","V",self.rootLink+"/sendungen/sendungenabisz100_page-V_zc-1fc26dc3.html",True), + TreeNode("1.22","W",self.rootLink+"/sendungen/sendungenabisz100_page-W_zc-25c5c777.html",True), + TreeNode("1.23","Y",self.rootLink+"/sendungen/sendungenabisz100_page-Y_zc-388beba7.html",True), + TreeNode("1.24","Z",self.rootLink+"/sendungen/sendungenabisz100_page-Z_zc-e744950d.html",True), + TreeNode("1.25","...",self.rootLink+"/sendungen/sendungenabisz100_page-1_zc-43c28d56.html",True) + ) + ) + ) + + self.regex_videoLinks=re.compile("
(.*?)"); + self.regex_xml_title=re.compile("(.*?)"); + self.regex_xml_image=re.compile("\\s*?(.*?)"); + self.regex_xml_videoLink=re.compile("\\s*?(.*?).*?(.*?)\\s*?",re.DOTALL) + self.regex_videoLink=re.compile("rtmp://.*?\.mp4"); + @classmethod + def name(self): + return "KI.KA"; + + def isSearchable(self): + return False; + + def searchVideo(self, searchText): + return; + + def buildVideoLink(self,pageLink): + xmlPage = self.loadPage(self.rootLink+pageLink); + channel = self.regex_xml_channel.search(xmlPage); + if(channel is not None): + channel = unicode(channel.group(1),"UTF-8"); + title = unicode(self.regex_xml_title.search(xmlPage).group(1),"UTF-8"); + image = self.regex_xml_image.search(xmlPage).group(1).replace("**aspectRatio**","tlarge169").replace("**width**","1472"); + + self.gui.log("%s %s"%(title,image)); + links = {}; + for match in self.regex_xml_videoLink.finditer(xmlPage): + profile = match.group(1); + directLink = match.group(2); + self.gui.log("%s %s"%(profile,directLink)); + if("MP4 Web S" in profile): + links[0] = SimpleLink(directLink, 0); + if("MP4 Web L" in profile): + links[1] = SimpleLink(directLink, 0); + if("MP4 Web L+" in profile): + links[2] = SimpleLink(directLink, 0); + if("MP4 Web XL" in profile): + links[3] = SimpleLink(directLink, 0); + + if(channel is not None): + return DisplayObject(channel,title,image,"",links,True, None); + else: + return DisplayObject(title,"",image,"",links,True, None); + + def buildPageMenu(self, link, initCount): + pageContent = self.loadPage(link); + htmlPage = BeautifulSoup(pageContent, 'html.parser') + htmlElements = htmlPage.select(self.selector_videoPages) + videoLinks = set() + + for item in htmlElements: + link = self.rootLink+item['href']; + videoPage = self.loadPage(link); + for match in self.regex_videoLinks.finditer(videoPage): + link=match.group(1)+"-avCustom.xml"; + if(link not in videoLinks): + videoLinks.add(link) + directLinks = list(self.regex_configLinks.finditer(pageContent)); + for match in directLinks: + link = match.group(1); + if(link not in videoLinks): + videoLinks.add(link) + self.gui.log("found %d video links"%len(videoLinks)) + count = initCount + len(videoLinks) + for link in videoLinks: + displayObject = self.buildVideoLink(link); + self.gui.buildVideoLink(displayObject,self, count); + if(len(videoLinks) > 0): + return; + htmlElements = htmlPage.select(self.selector_seriesPages); + count = count + len(htmlElements) + self.gui.log("found %d page links"%len(htmlElements)) + for item in htmlElements: + self.gui.log(item.prettify()); + link = self.rootLink+item['href']; + title = item['title']; + displayObject = DisplayObject(title,"",None,"",link,False, None); + self.gui.buildVideoLink(displayObject,self, count); diff --git a/plugin.video.mediathek/mediathek/ndr.py b/plugin.video.mediathek/mediathek/ndr.py new file mode 100644 index 0000000..cbe57df --- /dev/null +++ b/plugin.video.mediathek/mediathek/ndr.py @@ -0,0 +1,274 @@ +# -*- coding: utf-8 -*- +# -------------LicenseHeader-------------- +# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters +# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import re +import datetime +import time +import calendar +from mediathek import * +from xml.dom import minidom + + +class NDRMediathek(Mediathek): + @classmethod + def name(self): + return "NDR" + + def isSearchable(self): + return True + + def __init__(self, simpleXbmcGui): + self.gui = simpleXbmcGui + + self.rootLink = "http://www.ndr.de" + + self.searchLink = self.rootLink+"/suche10.html?search_mediathek=1&" + + # Hauptmenue + tmp_menu = [] + extractBroadcasts = re.compile("(.*?)") + htmlPage = self.loadPage("http://www.ndr.de/mediathek/sendungen_a-z/index.html").decode('utf-8') + + x = 0 + for menuNode in extractBroadcasts.finditer(htmlPage): + menuId = menuNode.group(1) + menuItem = menuNode.group(2) + menuLink = self.rootLink+"/mediatheksuche105_broadcast-"+menuId+"_format-video_page-1.html" + tmp_menu.append(TreeNode("0."+str(x), menuItem, menuLink, True)) + x = x+1 + + self.menuTree = [ + TreeNode("0", "Sendungen von A-Z", "", False, tmp_menu), + TreeNode("1", "Sendung verpasst?", "sendungverpasst", True), + TreeNode("2","Live","livestream",True),#Livestream ruckelt zu stark :-( + ] + + def buildPageMenuSendungVerpasst(self, action): + htmlPage = self.loadPage("http://www.ndr.de/mediathek/sendung_verpasst/epg1490_display-onlyvideo.html") + + regex_verpasstNow = re.compile( + '

\n.*?(\\d{2})\.(\\d{2})\.(\\d{4})' + ) + verpasstNow = ".".join(regex_verpasstNow.search(htmlPage).groups()) + try: + dateTimeTmp = datetime.datetime.strptime(verpasstNow, "%d.%m.%Y") + except TypeError: + dateTimeTmp = datetime.datetime(*(time.strptime(verpasstNow, "%d.%m.%Y")[0:6])) + + nodeCount = 0 + + if action == "": + verpasstHeute = dateTimeTmp.strftime("%Y-%m-%d") + dateTimeTmp = dateTimeTmp-datetime.timedelta(1) + verpasstGestern = dateTimeTmp.strftime("%Y-%m-%d") + dateTimeTmp = dateTimeTmp-datetime.timedelta(1) + verpasstVorGestern = dateTimeTmp.strftime("%Y-%m-%d") + + self.gui.buildVideoLink(DisplayObject("Heute", "", "", "description", self.rootLink + "/mediathek/sendung_verpasst/epg1490_date-" + verpasstHeute + "_display-onlyvideo.html", False), self, 1) + self.gui.buildVideoLink(DisplayObject("Gestern", "", "", "description", self.rootLink + "/mediathek/sendung_verpasst/epg1490_date-" + verpasstGestern + "_display-onlyvideo.html", False), self, 2) + self.gui.buildVideoLink(DisplayObject("Vorgestern", "", "", "description", self.rootLink + "/mediathek/sendung_verpasst/epg1490_date-" + verpasstVorGestern + "_display-onlyvideo.html", False), self, 3) + self.gui.buildVideoLink(DisplayObject("Datum waehlen", "", "", "description", "sendungverpasstselect", False), self, 4) + elif action == "select": + dateTimeTmp = dateTimeTmp-datetime.timedelta(3) + verpasstStartYear = int(dateTimeTmp.strftime("%Y")) + for verpasstStart in reversed(range(1, int(dateTimeTmp.strftime("%m")))): + menu_title = str(verpasstStart)+"."+str(verpasstStartYear) + menu_action = "sendungverpasstselectmonth" + str(verpasstStartYear) + str(verpasstStart) + self.gui.buildVideoLink(DisplayObject(menu_title, "", "", "description", menu_action, False), self, nodeCount) + verpasstStart = verpasstStart - 1 + nodeCount = nodeCount + 1 + + while verpasstStartYear > 2008: + verpasstStartYear = verpasstStartYear - 1 + menu_title = str(verpasstStartYear) + self.gui.buildVideoLink(DisplayObject(menu_title, "", "", "description", "sendungverpasstselectyear" + str(verpasstStartYear), False), self, nodeCount) + elif action[0:11] == "selectmonth": + action = action[11:] + action_year = action[0:4] + action_month = action[4:] + + try: + dateTimeTmp2 = datetime.datetime.strptime(action_year+action_month, "%Y%m") + except TypeError: + dateTimeTmp2 = datetime.datetime(*(time.strptime(action_year+action_month, "%Y%m")[0:6])) + + if dateTimeTmp.strftime("%Y%m") == dateTimeTmp2.strftime("%Y%m"): + startDay = int(dateTimeTmp2.strftime("%d")) + else: + startDay = calendar.monthrange(int(action_year), int(action_month))[1] + + try: + dateTimeTmp2 = datetime.datetime.strptime(action_year + action_month + str(startDay), "%Y%m%d") + except TypeError: + dateTimeTmp2 = datetime.datetime(*(time.strptime(action_year + action_month + str(startDay), "%Y%m%d")[0:6])) + + for i in reversed(range(1, startDay)): + verpasstDatum = dateTimeTmp2.strftime("%Y-%m-%d") + menu_title = dateTimeTmp2.strftime("%d.%m.%Y") + menu_action = self.rootLink + "/mediathek/sendung_verpasst/epg1490_date-" + verpasstDatum + "_display-onlyvideo.html" + self.gui.buildVideoLink(DisplayObject(menu_title, "", "", "description", menu_action, False), self, nodeCount) + nodeCount = nodeCount + 1 + dateTimeTmp2 = dateTimeTmp2-datetime.timedelta(1) + elif action[0:10] == "selectyear": + action = action[10:] + action_year = action[0:4] + for startMonth in reversed(range(1, 12)): + menu_title = str(startMonth) + "." + action_year + menu_action = "sendungverpasstselectmonth" + action_year + str(startMonth) + self.gui.buildVideoLink(DisplayObject(menu_title, "", "", "description", menu_action, False), self, nodeCount) + nodeCount = nodeCount + 1 + + def buildPageMenuLivestream(self): + nodeCount = 0 + + # Hamburg + nodeCount = nodeCount+1 + links = {} + links[0] = SimpleLink("http://ndr_fs-lh.akamaihd.net/i/ndrfs_hh@119223/master.m3u8", 0) + self.gui.buildVideoLink(DisplayObject("Hamburg", "", "", "", links, True), self, nodeCount) + + # Mecklenburg-Vorpommern + nodeCount = nodeCount+1 + links = {} + links[0] = SimpleLink("http://ndr_fs-lh.akamaihd.net/i/ndrfs_mv@119226/master.m3u8", 0) + self.gui.buildVideoLink(DisplayObject("Mecklenburg-Vorpommern", "", "", "", links, True), self, nodeCount) + + # Niedersachsen + nodeCount = nodeCount+1 + links = {} + links[0] = SimpleLink("http://ndr_fs-lh.akamaihd.net/i/ndrfs_nds@119224/master.m3u8", 0) + self.gui.buildVideoLink(DisplayObject("Niedersachsen", "", "", "", links, True), self, nodeCount) + + # Schleswig-Holstein + nodeCount = nodeCount+1 + links = {} + links[0] = SimpleLink("http://ndr_fs-lh.akamaihd.net/i/ndrfs_sh@119225/master.m3u8", 0) + self.gui.buildVideoLink(DisplayObject("Schleswig-Holstein", "", "", "", links, True), self, nodeCount) + + def buildPageMenuVideoListVerpasst(self, link, initCount): + self.gui.log("buildPageMenuVerpasst: " + link) + + htmlPage = self.loadPage(link) + + re_video_item = re.compile( + '|\n\n\n)", re.DOTALL) + regex_extractVideoItemHref = re.compile("", re.DOTALL) + regex_extractVideoItemDate = re.compile("
.*?(\\d{2}\.\\d{2}\.\\d{4} \\d{2}:\\d{2})
") + + videoItems = regex_extractVideoItems.findall(htmlPage) + nodeCount = initCount + len(videoItems) + + for videoItem in videoItems: + print "link: {0}".format(link) + print "videoItem: {0}".format(videoItem[0]) + if "
" not in videoItem[0]: + continue + videoLink = regex_extractVideoItemHref.search(videoItem[0]).group(1) + try: + dateString = regex_extractVideoItemDate.search(videoItem[0]).group(1) + dateTime = time.strptime(dateString, "%d.%m.%Y %H:%M") + except: + dateTime = None + # TODO: Some videos from Extra 3 are located on http://www.n-joy.de/ + # which cannot be parsed by this script, yet. + if not re.compile("http://www.n-joy.de/.*").search(videoLink): + self.extractVideoInformation(videoLink, dateTime, nodeCount) + + # Pagination (weiter) + regex_extractNextPage = re.compile( + "" + ) + for nextPageHref in regex_extractNextPage.finditer(htmlPage): + menuItemName = nextPageHref.group(2).decode("UTF-8") + link = self.rootLink+nextPageHref.group(1) + self.gui.buildVideoLink(DisplayObject(menuItemName, "", "", "description", link, False), self, nodeCount) + + def buildPageMenu(self, link, initCount): + + print link + if link[0:15] == "sendungverpasst": + self.buildPageMenuSendungVerpasst(link[15:]) + elif link == "livestream": + self.buildPageMenuLivestream() + elif "/sendung_verpasst/" in link: + self.buildPageMenuVideoListVerpasst(link, initCount) + else: + self.buildPageMenuVideoList(link, initCount) + + def searchVideo(self, searchText): + searchText = searchText.encode("UTF-8") + searchText = urllib.urlencode({"query": searchText}) + self.buildPageMenu(self.searchLink+searchText, 0) + + def extractVideoInformation(self, videoLink, pubDate, nodeCount): + + regexFindVideoLink = re.compile("http://.*(hq.mp4|hi.mp4|lo.flv)") + regexFindImageLink = re.compile("/.*v-ardgalerie.jpg") + regexFindMediaData = re.compile( + "
\\s*?
\\s*?" + "(.*?)" + "\\s*?.*?
.*?
\\s*?" + "(.*?)" + "

", re.DOTALL + ) + if not videoLink.startswith(self.rootLink): + videoLink = self.rootLink+videoLink + videoPage = self.loadPage(videoLink) + + self.gui.log("videolink: {0}".format(videoLink)) + videoLink = {} + videoLink[0] = SimpleLink(regexFindVideoLink.search(videoPage).group(0), 0) + + try: + pictureLink = self.rootLink+regexFindImageLink.search(videoPage).group(0) + except: + pictureLink = None + if '
' not in videoPage: + searchResult = regexFindMediaData.search(videoPage) + title = searchResult.group(1).decode('utf-8') + description = searchResult.group(2).decode('utf-8') + else: + title = re.search( + 'var trackTitle = "(.*?)"', + videoPage + ).group(1).decode('utf-8') + description = re.search( + '. +import re,time,urllib +from xml.dom import Node; +from xml.dom import minidom; +from mediathek import * + +class ORFMediathek(Mediathek): + def __init__(self, simpleXbmcGui): + + self.rootLink = "http://tvthek.orf.at" + self.gui = simpleXbmcGui; + self.menuTree = []; + self.menuTree.append(TreeNode("0","Startseite","http://tvthek.orf.at/",True)); + + menuPage = self.loadPage(self.rootLink+"/programs"); + findMenuLink = re.compile("
  • (.*?)
  • "); + findCategorie = re.compile("

    (.*?)

    \\s*?
      ((\\s*?%s\\s*?)+)
    "%findMenuLink.pattern) + categories = []; + + for categorieMatch in findCategorie.finditer(menuPage): + title = categorieMatch.group(1); + items = []; + for menuMatch in findMenuLink.finditer(categorieMatch.group(2)): + items.append(TreeNode("1.%d.%d"%(len(categories),len(items)), menuMatch.group(2),"%s%s"%(self.rootLink,menuMatch.group(1)),True)); + categories.append(TreeNode("1.%d"%len(categories), title,"",False,items)); + + self.menuTree.append(TreeNode("1","Sendungen","",False,categories)); + + videoLinkPage = "/programs/.*" + imageLink = "http://tvthek.orf.at/assets/.*?.jpeg" + + + self.regex_extractVideoPageLink = re.compile(videoLinkPage+"?\""); + self.regex_extractImageLink = re.compile(imageLink); + self.regex_extractTitle = re.compile(".*\\s*\\s*\".*\"\\s*\\s*.*.*\\s*.*\\s*\\s*"); + + self.regex_extractSearchObject = re.compile("
  • \\s*\".*\".*\\s*

    .*

    \\s*

    .*

    \\s*

    \\s*
  • "); + + self.regex_extractProgrammLink = re.compile("/programs/.*?\""); + self.regex_extractProgrammTitle = re.compile("title=\".*?\""); + self.regex_extractProgrammPicture = re.compile("/binaries/asset/segments/\\d*/image1"); + + self.regex_extractFlashVars = re.compile("ORF.flashXML = '.*?'"); + self.regex_extractHiddenDate = re.compile("\d{4}-\d{2}-\d{2}"); + self.regex_extractXML = re.compile("%3C.*%3E"); + self.regex_extractReferingSites = re.compile("
  • "); + self.searchLink = "http://tvthek.orf.at/search?q=" + @classmethod + def name(self): + return "ORF"; + + def isSearchable(self): + return True; + + def createVideoLink(self,title,image,videoPageLink,elementCount): + videoPage = self.loadPage(self.rootLink+videoPageLink); + + videoLink = self.regex_extractVideoLink.search(videoPage); + if(videoLink == None): + return; + + simpleLink = SimpleLink(self.rootLink+videoLink.group(), 0); + videoLink = {0:simpleLink}; + counter = 0 + playlist = self.loadPage(simpleLink.basePath); + for line in playlist: + counter+=1; + + if(counter == 1): + self.gui.buildVideoLink(DisplayObject(title,"",image,"",videoLink, True, time.gmtime()),self,elementCount); + else: + self.gui.buildVideoLink(DisplayObject(title,"",image,"",videoLink, "PlayList", time.gmtime()),self,elementCount); + + def searchVideo(self, searchText): + link = self.searchLink = "http://tvthek.orf.at/search?q="+searchText; + mainPage = self.loadPage(link); + result = self.regex_extractSearchObject.findall(mainPage); + for searchObject in result: + videoLink = self.regex_extractProgrammLink.search(searchObject).group().replace("\"",""); + title = self.regex_extractProgrammTitle.search(searchObject).group().replace("title=\"","").replace("\"",""); + title = title.decode("UTF-8"); + pictureLink = self.regex_extractProgrammPicture.search(searchObject).group(); + + print videoLink; + + self.createVideoLink(title,pictureLink,videoLink, len(result)); + + def extractLinksFromFlashXml(self, flashXml, date, elementCount): + print flashXml.toprettyxml().encode('UTF-8'); + playlistNode = flashXml.getElementsByTagName("Playlist")[0]; + linkNode=flashXml.getElementsByTagName("AsxUrl")[0]; + link=linkNode.firstChild.data; + asxLink = SimpleLink(self.rootLink+link,0); + videoLink = {0:asxLink}; + for videoItem in playlistNode.getElementsByTagName("Items")[0].childNodes: + if(videoItem.nodeType == Node.ELEMENT_NODE): + titleNode=videoItem.getElementsByTagName("Title")[0]; + + descriptionNode=videoItem.getElementsByTagName("Description")[0]; + title=titleNode.firstChild.data; + + stringArray = link.split("mp4:"); + + try: + description=descriptionNode.firstChild.data; + except: + description=""; + self.gui.buildVideoLink(DisplayObject(title,"","",description,videoLink, True, date),self,elementCount); + def extractFlashLinks(self, flashVars,videoPageLinks,elementCount): + for flashVar in flashVars: + encodedXML = self.regex_extractXML.search(flashVar).group(); + dateString = self.regex_extractHiddenDate.search(flashVar).group(); + date = time.strptime(dateString,"%Y-%m-%d"); + parsedXML = minidom.parseString(urllib.unquote(encodedXML)); + self.extractLinksFromFlashXml(parsedXML, date,elementCount); + for videoPageLink in videoPageLinks: + videoPageLink = self.rootLink+videoPageLink.replace("
  • . +import re, time; +from mediathek import * +from xml.dom import minidom; +regex_dateString = re.compile("\\d{4}-\\d{2}-\\d{2}"); +class WDRMediathek(Mediathek): + def __init__(self, simpleXbmcGui): + self.gui = simpleXbmcGui; + self.pageSize = 20; #max 49; + self.rootLink = "http://www.wdr.de" + self.menuTree = ( + TreeNode("0","Neuste Videos",self.rootLink+"/mediathek/rdf/regional/index.xml",True), + TreeNode("1","Sendungen von A-Z","",False, + ( + TreeNode("1.0",u"A40","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=A40",True), + TreeNode("1.1",u"Aktuelle Stunde","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Aktuelle+Stunde",True), + TreeNode("1.2",u"Am Sonntag","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Der+Sonntag",True), + TreeNode("1.3",u"Cosmo","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Funkhaus+Europa+-+Cosmo",True), + TreeNode("1.4",u"daheim & unterwegs","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=daheim+%26+unterwegs",True), + TreeNode("1.5",u"die story","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=die+story",True), + TreeNode("1.6",u"Dittsche","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Dittsche",True), + TreeNode("1.7",u"eins zu eins","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=eins+zu+eins",True), + TreeNode("1.8",u"frauTV","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=frauTV",True), + TreeNode("1.9",u"hier und heute","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Hier+und+Heute",True), + TreeNode("1.10",u"Kabarett","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Kabarett",True), + TreeNode("1.11",u"Lokalzeit aus Aachen","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+Aachen",True), + TreeNode("1.12",u"Lokalzeit aus Düsseldorf","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+D%FCsseldorf",True), + TreeNode("1.13",u"Lokalzeit OWL","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+OWL+aktuell",True), + TreeNode("1.14",u"Lokalzeit aus Bonn","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+Bonn",True), + TreeNode("1.15",u"Lokalzeit aus Köln","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+K%F6ln",True), + TreeNode("1.16",u"Lokalzeit Ruhr","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+Ruhr",True), + TreeNode("1.17",u"Lokalzeit aus Dortmund","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+Dortmund",True), + TreeNode("1.18",u"Lokalzeit Bergisches Land","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+Bergisches+Land",True), + TreeNode("1.19",u"Lokalzeit Südwestfalen","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+S%FCdwestfalen",True), + TreeNode("1.20",u"Lokalzeit aus Duisburg","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+Duisburg",True), + TreeNode("1.21",u"Lokalzeit Münsterland","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+M%FCnsterland",True), + TreeNode("1.22",u"markt","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=markt",True), + TreeNode("1.23",u"Menschen hautnah","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Menschen+hautnah",True), + TreeNode("1.24",u"Mittagsecho","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Mittagsecho",True), + TreeNode("1.25",u"Mittagsmagazin","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Mittagsmagazin",True), + TreeNode("1.26",u"mittwochs live","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=mittwochs+live",True), + TreeNode("1.27",u"Morgenecho","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Morgenecho",True), + TreeNode("1.28",u"Morgenmagazin","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Morgenmagazin",True), + TreeNode("1.29",u"Mosaik","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+3+-+Mosaik",True), + TreeNode("1.30",u"Piazza","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Funkhaus+Europa+-+Piazza",True), + TreeNode("1.31",u"Platz der Republik","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Platz+der+Republik",True), + TreeNode("1.32",u"Quarks & Co","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Quarks+%26+Co",True), + TreeNode("1.33",u"Resonanzen","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+3+-+Resonanzen",True), + TreeNode("1.34",u"Scala","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Scala",True), + TreeNode("1.35",u"schön hier","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=sch%F6n+hier",True), + TreeNode("1.36",u"Servicezeit","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Servicezeit",True), + TreeNode("1.37",u"sport inside","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=sport+inside",True), + TreeNode("1.38",u"Stichtag","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Stichtag",True), + TreeNode("1.39",u"Thema NRW","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Thema+NRW",True), + TreeNode("1.40",u"WDR aktuell","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+aktuell",True), + TreeNode("1.41",u"WDR sport aktuell","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+sport+aktuell",True), + TreeNode("1.42",u"west.art","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=West.art",True), + TreeNode("1.43",u"Westblick","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Westblick",True), + TreeNode("1.44",u"WESTPOL","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WESTPOL",True), + TreeNode("1.45",u"Westzeit","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Westzeit",True), + TreeNode("1.46",u"Zeiglers wunderbare Welt des Fußballs","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Zeiglers+wunderbare+Welt+des+Fu%DFballs",True), + TreeNode("1.47",u"ZeitZeichen","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+3+%2F+WDR+5+-+ZeitZeichen",True), + TreeNode("1.48",u"Zimmer Frei!","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Zimmer+Frei%21",True), + TreeNode("1.49",u"Zwischen Rhein und Weser","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Zwischen+Rhein+und+Weser",True), + ) + ), + TreeNode("2","Themen","",False, + ( + TreeNode("2.0","Politik","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Politik",True), + TreeNode("2.1","Wirtschaft","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Wirtschaft",True), + TreeNode("2.2","Kultur","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Kultur",True), + TreeNode("2.3","Panorama","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Panorama",True), + TreeNode("2.4","Service","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Service",True), + TreeNode("2.5","Freizeit","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Freizeit",True), + TreeNode("2.6","Sport","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Sport",True), + ) + ), + ) + self._regex_extractTitle = re.compile("

    .*?"); + self._regex_extractDescription = re.compile(""); + self._regex_extractPicture = re.compile(""); + self._regex_extractDate = re.compile(""); + self._regex_extractDuration = re.compile("\\((.*)\\)"); + + self._regex_extractVideoPage = re.compile(""); + self._regex_extractLink = re.compile("/mediathek/html/.*?\\.xml"); + self._regex_extractAudioLink = re.compile(self.rootLink+"/mediathek/.*?\\.mp3"); + self._regex_extractVideoLink = re.compile("(dsl|isdn)Src=rtmp://.*?\\.(mp4|flv)"); + self.replace_html = re.compile("<.*?>"); + self.replace_tag = re.compile("()"); + self.searchLink = "http://www.wdr.de/mediathek/html/regional/suche/index.xml?wsSucheAusgabe=liste&wsSucheSuchart=volltext&wsSucheMedium=av&suche_submit=Suche+starten&wsSucheBegriff=" + + @classmethod + def name(self): + return "WDR"; + def isSearchable(self): + return True; + + def searchVideo(self, searchText): + link = self.searchLink+searchText; + self.buildPageMenu(link, 0, False) + def buildPageMenu(self, link, initCount, subLink = False): + link = link+"&rankingcount="+str(self.pageSize); + self.gui.log("MenuLink: %s"%link); + mainPage = self.loadPage(link); + if(mainPage.startswith("-1): + linkString = linkString.split("mediartmp://") + return SimpleLink("rtmp://%s"%linkString[1], 0); + elif(linkString.find("mediahttp://")>-1): + linkString = linkString.split("mediahttp://") + return SimpleLink("http://%s"%linkString[1], 0); + else: + return SimpleLink(linkString, 0); diff --git a/plugin.video.mediathek/mediathek/zdf.py b/plugin.video.mediathek/mediathek/zdf.py new file mode 100644 index 0000000..3ece7a9 --- /dev/null +++ b/plugin.video.mediathek/mediathek/zdf.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +#-------------LicenseHeader-------------- +# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters +# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de] +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import re,math,traceback,time +from mediathek import * +from datetime import datetime,timedelta +import json + +class ZDFMediathek(Mediathek): + def __init__(self, simpleXbmcGui): + self.gui = simpleXbmcGui; + + today = datetime.today(); + + self.menuTree = ( + TreeNode("0","Startseite","https://zdf-cdn.live.cellular.de/mediathekV2/start-page",True), + TreeNode("1","Kategorien","https://zdf-cdn.live.cellular.de/mediathekV2/categories",True), + TreeNode("2","Sendungen von A-Z","https://zdf-cdn.live.cellular.de/mediathekV2/brands-alphabetical",True), + TreeNode("3","Sendung verpasst?","",False,( + TreeNode("3.0","Heute","https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%(today.strftime("%Y-%m-%d")),True), + TreeNode("3.1","Gestern","https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=1)).strftime("%Y-%m-%d")),True), + TreeNode("3.2","Vorgestern","https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=2)).strftime("%Y-%m-%d")),True), + TreeNode("3.3",(today-timedelta(days=3)).strftime("%A"),"https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=3)).strftime("%Y-%m-%d")),True), + TreeNode("3.4",(today-timedelta(days=4)).strftime("%A"),"https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=4)).strftime("%Y-%m-%d")),True), + TreeNode("3.5",(today-timedelta(days=5)).strftime("%A"),"https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=5)).strftime("%Y-%m-%d")),True), + TreeNode("3.6",(today-timedelta(days=6)).strftime("%A"),"https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=6)).strftime("%Y-%m-%d")),True), + TreeNode("3.7",(today-timedelta(days=7)).strftime("%A"),"https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=7)).strftime("%Y-%m-%d")),True), + )), + TreeNode("4","Live TV","https://zdf-cdn.live.cellular.de/mediathekV2/live-tv/%s"%(today.strftime("%Y-%m-%d")),True) + ); + @classmethod + def name(self): + return "ZDF"; + + def isSearchable(self): + return False; + + def searchVideo(self, searchText): + return; + + def buildPageMenu(self, link, initCount): + self.gui.log("buildPageMenu: "+link); + jsonObject = json.loads(self.loadPage(link)); + callhash = self.gui.storeJsonFile(jsonObject); + + if("stage" in jsonObject): + for stageObject in jsonObject["stage"]: + if(stageObject["type"]=="video"): + self.buildVideoLink(stageObject,initCount); + + if("cluster" in jsonObject): + for counter, clusterObject in enumerate(jsonObject["cluster"]): + if "teaser" in clusterObject and "name" in clusterObject: + path = "cluster.%d.teaser"%(counter) + self.gui.buildJsonLink(self,clusterObject["name"],path,callhash,initCount) + if("broadcastCluster" in jsonObject): + for counter, clusterObject in enumerate(jsonObject["broadcastCluster"]): + if clusterObject["type"].startswith("teaser") and "name" in clusterObject: + path = "broadcastCluster.%d.teaser"%(counter) + self.gui.buildJsonLink(self,clusterObject["name"],path,callhash,initCount) + if("epgCluster" in jsonObject): + for epgObject in jsonObject["epgCluster"]: + if("liveStream" in epgObject and len(epgObject["liveStream"])>0): + self.buildVideoLink(epgObject["liveStream"], initCount); + + def buildJsonMenu(self, path,callhash, initCount): + jsonObject=self.gui.loadJsonFile(callhash); + jsonObject=self.walkJson(path,jsonObject); + categoriePages=[]; + videoObjects=[]; + + for entry in jsonObject: + if entry["type"] == "brand": + categoriePages.append(entry); + if entry["type"] == "video": + videoObjects.append(entry); + self.gui.log("CategoriePages: %d"%len(categoriePages)); + self.gui.log("VideoPages: %d"%len(videoObjects)); + for categoriePage in categoriePages: + title=categoriePage["titel"]; + subTitle=categoriePage["beschreibung"]; + imageLink=""; + for width,imageObject in categoriePage["teaserBild"].iteritems(): + if int(width)<=840: + imageLink=imageObject["url"]; + url = categoriePage["url"]; + self.gui.buildVideoLink(DisplayObject(title,subTitle,imageLink,"",url,False),self,initCount); + + for videoObject in videoObjects: + self.buildVideoLink(videoObject,initCount); + + def buildVideoLink(self,videoObject,counter): + title=videoObject["headline"]; + subTitle=videoObject["titel"]; + + if(len(title)==0): + title = subTitle; + subTitle = ""; + if("beschreibung" in videoObject): + description=videoObject["beschreibung"]; + imageLink=""; + if("teaserBild" in videoObject): + for width,imageObject in videoObject["teaserBild"].iteritems(): + if int(width)<=840: + imageLink=imageObject["url"]; + if("formitaeten" in videoObject): + links = self.extractLinks(videoObject); + self.gui.buildVideoLink(DisplayObject(title,subTitle,imageLink,description,links,True,None,videoObject.get('length')),self,counter); + else: + link = videoObject["url"]; + self.gui.buildVideoLink(DisplayObject(title,subTitle,imageLink,description,link,"JsonLink",None,videoObject.get('length')),self,counter); + + def playVideoFromJsonLink(self,link): + jsonObject = json.loads(self.loadPage(link)); + links = self.extractLinks(jsonObject["document"]); + self.gui.play(links); + def extractLinks(self,jsonObject): + links={}; + for formitaete in jsonObject["formitaeten"]: + url = formitaete["url"]; + quality = formitaete["quality"]; + hd = formitaete["hd"]; + self.gui.log("quality:%s hd:%s url:%s"%(quality,hd,url)); + if hd == True: + links[4] = SimpleLink(url, -1); + else: + if quality == "low": + links[0] = SimpleLink(url, -1); + if quality == "med": + links[1] = SimpleLink(url, -1); + if quality == "high": + links[2] = SimpleLink(url, -1); + if quality == "veryhigh": + links[3] = SimpleLink(url, -1); + if quality == "auto": + links[3] = SimpleLink(url, -1); + return links; + -- cgit v1.2.3