## -*- 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,json; from mediathek import *; 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 True; def __init__(self, simpleXbmcGui): self.gui = simpleXbmcGui; self.rootLink = "http://www.arte.tv"; self.basePage = self.rootLink+"/de/"; self.jsonLink = "https://api.arte.tv/api/player/v1/config/de/%s?lifeCycle=1" self.serachLink = self.rootLink+"/de/search/?q=%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_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("\"kind\":\"MAGAZINE\",\"programId\":\"RC-\d+\",\"language\":\"\w{2}\",\"url\":\"(http.*?)\",\"title\":\"(.*?)\",\"subtitle\":(\"(.*?)\"|null),\"images\""); self.regex_categories = re.compile("\"link\":{\"page\":\"\w{3}\",\"title\":\"(.*?)\",\"url\":\"(http.*?)\"}"); self.regex_playlists = re.compile(regexSourceString%"data-highlightedPlaylists"); self.searchContent = re.compile(regexSourceString%"data-results"); self.regex_ExtractJson = re.compile("__INITIAL_STATE__ = ({.*});"); def searchVideo(self, searchText): link = self.serachLink%searchText; pageContent = self.loadPage(link).decode('UTF-8'); self.extractVideoLinks(pageContent,0); 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; self.parsePage(link); def extractJsonFromPage(self,link): pageContent = self.loadPage(link).decode('UTF-8'); pageContent = BeautifulSoup(self.regex_ExtractJson.search(pageContent).group(1),"html.parser"); return json.loads(pageContent.prettify(formatter=None)) def parsePage(self, link): jsonContent = self.extractJsonFromPage(link); for video in jsonContent["collection"]["videos"]: self.buildVideoEntry(video); def showMainPage(self): self.gui.log("buildPageMenu: "+self.basePage); jsonContent = self.extractJsonFromPage(self.basePage); for zone in jsonContent["page"]["zones"]: if(zone["type"] in ("highlight","playlist") ): for teaser in zone["teasers"]: self.buildVideoEntry(teaser); def buildJsonMenu(self, path,callhash, initCount): jsonContent=self.gui.loadJsonFile(callhash); for teaser in jsonContent["teasers"]: self.buildVideoEntry(teaser); def buildJsonLink(self,name,jsonContent): callhash = self.gui.storeJsonFile(jsonContent,name); self.gui.buildJsonLink(self,name,"init",callhash,0) 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),"html.parser"); 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 showCategories(self): jsonContent = self.extractJsonFromPage(self.basePage); for zone in jsonContent["page"]["zones"]: if(zone["type"] == "category" ): self.buildJsonLink(zone["title"],zone); def showCluster(self): jsonContent = self.extractJsonFromPage(self.basePage); for zone in jsonContent["page"]["zones"]: if(zone["type"] == "magazine" ): for teaser in zone["teasers"]: self.buildVideoEntry(teaser); 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 = unicode(jsonObject["title"]); if(jsonObject["subtitle"] is not None): subTitle = unicode(jsonObject["subtitle"]); else: subTitle = None; if("teaser" in jsonObject): detail = unicode(jsonObject["teaser"]); else: detail = ""; pictures = None; pictureUrl = None; if("thumbnails" in jsonObject): pictures = jsonObject["thumbnails"]; if("images" in jsonObject): pictures = jsonObject["images"]; if(pictures is not None): picture = None; for pictureItem in pictures: if(picture is None or picture["width"]