diff --git a/ChangeLog b/ChangeLog index 95d3376..9fa95e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +Version 0.6.3 - 2018-02-17 + +* Use HLS video instead of no longer supported RTMP [aphipps] + +Version 0.6.2 - 2017-07-19 + +* Tweak attribute grabbed for video to match current ITV behaviour [aphipps] + +Version 0.6.1 - 2017-04-22 + +* Minor tweaks to reflect site requiring https and beta not working [aphipps] + +Version 0.6.0 - 2015-11-22 + +* Basic functionality for new ITV player hub website [aphipps] + Version 0.5.2 - 2014-02-06 * RMTP Related changes [sander1] diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index c732d14..546a15e 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -2,9 +2,9 @@ # # # ITV Player Plex Plugin # # # -# Version: 0.5.2 # +# Version: 0.6.0 # # Created: 08th Sept 2010 # -# Last Updated: 06th Feb 2014 # +# Last Updated: 22nd Nov 2015 # # # ################################################# @@ -29,34 +29,14 @@ ICON = 'icon-2013.png' -ITV_URL = "http://www.itv.com" -ITV_POPULAR_URL = "https://www.itv.com/itvplayer/categories/browse/popular/catch-up" -ITV_GENRE_POPULAR_URL = "https://www.itv.com/itvplayer/categories/%s/popular/catch-up" -ITV_AZ_URL = "https://www.itv.com/itvplayer/a-z/%s" -ITV_DATE_URL = "https://www.itv.com/itvplayer/by-day/%s" -ITV_SEARCH_URL = "https://www.itv.com/itvplayer/search/term/%s/catch-up" +ITV_URL = "https://www.itv.com" +ITV_ALL_URL = "https://www.itv.com/hub/shows" -ITV_PROGRAMME_XPATH = "//div[@id='categories-content']/div[@class='item-list']/ul/li" -ITV_EPISODE_XPATH = "//div[@class='view-content']/div[@class='views-row']" -ITV_SEARCH_XPATH = "//div[@class='search-results-wrapper']/div[@class='search-wrapper']" +ITV_PROGRAMME_XPATH = "//ul[@id='az-list']/li/ul/li" VIDEO_TITLE = L('VideoTitle') -POPULAR_TITLE = "Most Popular Programmes" -MOST_WATCHED_TITLE = "Most Watched Shows" -TV_CHANNELS_TITLE = "TV Channels" -TV_GENRES_TITLE = "Genres" -TV_DATE_TITLE = "Browse by Date" -A_Z_TITLE = "A to Z" -SEARCH_TITLE = "Search Programmes" -SEARCH_SUBTITLE = "Search for your Programme" -SEARCH_SUMMARY = "This lets you search for ITV Programmes" - -ITV1_LOGO = "http://thumbs.tvgenius.net/512x512/bds-itv.jpg" -ITV2_LOGO = "http://thumbs.tvgenius.net/512x512/bds-itv2.jpg" -ITV3_LOGO = "http://thumbs.tvgenius.net/512x512/bds-itv3.jpg" -ITV4_LOGO = "http://thumbs.tvgenius.net/512x512/bds-itv4.jpg" -CITV_LOGO = "http://thumbs.tvgenius.net/512x512/bds-citv.jpg" +ALL_TITLE = "All Programmes" ################################################# @@ -73,10 +53,9 @@ def Start(): ObjectContainer.art = R(ART) HTTP.CacheTime = CACHE_1HOUR - HTTP.Headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:17.0) Gecko/20100101 Firefox/17.0' + HTTP.Headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0' ################################################# - @handler('/video/itv', NAME, thumb=ICON, art=ART) def VideoMainMenu(): @@ -84,141 +63,15 @@ def VideoMainMenu(): oc.add( DirectoryObject( - key=Callback(RenderProgramList, url=ITV_POPULAR_URL, parent_name=oc.title1, section_name=POPULAR_TITLE), - title=POPULAR_TITLE, - summary="Browse the most popular programmes available on catchup across all the ITV channels.", - thumb=R(ICON), - ) - ) - - oc.add( - DirectoryObject( - key=Callback(AddGenres, parent_name=oc.title1), - title=TV_GENRES_TITLE, - summary="Browse for programmes available on catchup by genre.", - thumb=R(ICON), - ) - ) - - - oc.add( - DirectoryObject( - key=Callback(AddTVChannels, parent_name=oc.title1), - title=TV_CHANNELS_TITLE, - summary="Browse for programmes available on catchup by channel.", - thumb=R(ICON), - ) - ) - - oc.add( - DirectoryObject( - key=Callback(AddDateList, parent_name=oc.title1), - title=TV_DATE_TITLE, - summary="Browse the last 30 days of programmes.", - thumb=R(ICON), - ) - ) - - oc.add( - InputDirectoryObject( - key=Callback(RenderSearchResults, parent_name=oc.title1), - title=SEARCH_TITLE, - summary="Search for TV programmes", + key=Callback(RenderProgramList, url=ITV_ALL_URL, parent_name=oc.title1, section_name=ALL_TITLE), + title=ALL_TITLE, + summary="Browse all programmes.", thumb=R(ICON), - prompt="Enter search term" - ) - ) - - return oc - - -################################################# -def AddGenres(parent_name=None): - - oc = ObjectContainer(title1=parent_name, title2=TV_GENRES_TITLE, view_group="List") - - genres = [ - ['Children','children'], - ['Comedy','comedy'], - ['Drama & Soaps','drama-soaps'], - ['Entertainment','entertainment'], - ['Factual','factual'], - ['Films','films'], - ['Lifestyle','lifestyle'], - ['Music','music'], - ['News & Weather','news-weather'], - ['Sport','sport'], - ] - - for genre in genres: - - oc.add( - DirectoryObject( - key=Callback(RenderProgramList, url=ITV_GENRE_POPULAR_URL % genre[1], sort_by_name=True, parent_name=oc.title2, section_name=genre[0]), - title=genre[0], - ) - ) - - return oc - -################################################# -def AddTVChannels(parent_name=None): - - oc = ObjectContainer(title1=parent_name, title2=TV_CHANNELS_TITLE, view_group="List") - - channels = [ - ['ITV','itv',ITV1_LOGO], - ['ITV 2','itv2',ITV2_LOGO], - ['ITV 3','itv3',ITV3_LOGO], - ['ITV 4','itv4',ITV4_LOGO], - ['CITV','citv',CITV_LOGO], - ] - - for channel in channels: - - oc.add( - DirectoryObject( - key=Callback(RenderProgramList, url=ITV_GENRE_POPULAR_URL % channel[1], sort_by_name=True, parent_name=oc.title2, section_name=channel[0]), - title=channel[0], - thumb=channel[2], - ) - ) - - return oc - -################################################# -def AddDateList(parent_name=None): - - oc = ObjectContainer(no_cache=True, view_group="List", title1=parent_name, title2=TV_DATE_TITLE) - - date = Datetime.Now() - oc.add( - DirectoryObject( - key=Callback(RenderEpisodesForDay, url=ITV_DATE_URL % date.strftime("%d-%b-%Y"), parent_name=oc.title2, section_name="Today"), - title="Today", - ) - ) - - date += Datetime.Delta(days=-1) - oc.add( - DirectoryObject( - key=Callback(RenderEpisodesForDay, url=ITV_DATE_URL % date.strftime("%d-%b-%Y"), parent_name=oc.title2, section_name="Yesterday"), - title="Yesterday", ) ) - for number in range (2, 31): - date += Datetime.Delta(days=-1) - oc.add( - DirectoryObject( - key=Callback(RenderEpisodesForDay, url=ITV_DATE_URL % date.strftime("%d-%b-%Y"), parent_name=oc.title2, section_name=date.strftime("%a, %d %b %Y")), - title=date.strftime("%a, %d %b %Y"), - ) - ) - return oc - ################################################# def RenderProgramList(url=None, sort_by_name=False, parent_name=None, section_name=None): @@ -247,110 +100,16 @@ def RenderProgramList(url=None, sort_by_name=False, parent_name=None, section_na if (prog.additionalInfo.episodeCount is not None): title += ' [' + str(prog.additionalInfo.episodeCount) + ']' - - oc.add( - DirectoryObject( - key = Callback(RenderEpisodeList, url=prog.pageUri, parent_name=oc.title2, section_name=title), - title = title, - thumb = prog.imageUri, - ) - ) - - if len(oc) <= 0: - return MessageContainer( - "No Programmes Found", - "No Programmes have been found matching your search\nPlease try again." - ) - - return oc -################################################# - -def RenderEpisodesForDay(url, parent_name=None, section_name=None): - - oc = ObjectContainer(no_cache=True, title1=parent_name, title2=section_name, view_group="InfoList") - eps = [] - - content = HTML.ElementFromURL(url, errors='ignore', cacheTime=1800) - - for channel in content.xpath("//div[contains(@class,'view-display-id-by_day')]"): - - channelName = re.search("global-(\\w*)-large", channel.xpath(".//span[contains(@class,'chan-button')]/@class")[0]).groups()[0] - episodes = channel.xpath(".//li[contains(@class,'min-container')]") - - for episode in episodes: - ep = ConstructEpisode(pageUri=url, element=episode) - ep.channel = channelName - eps.append(ep) - - for ep in sorted(eps, key=lambda ep: ep.lastBroadcast, reverse=True): - - oc.add( - VideoClipObject( - url = ep.id, - title = ep.lastBroadcast.strftime("%H:%M") + " - " + ep.channel + " - " + ep.title, - thumb = ep.posterFrameUri, - summary = ep.summary(), - duration = ep.durationMilliseconds() - ) - ) - - return oc - - -################################################# - -def RenderSearchResults(query=None, parent_name=None): - - oc = ObjectContainer(no_cache=True, title1=parent_name, view_group="InfoList") - - content = HTML.ElementFromURL(ITV_SEARCH_URL % String.Quote(query), errors='ignore', cacheTime=1800) - - programmes = content.xpath(ITV_SEARCH_XPATH) - - if len(programmes) < 1: - return MessageContainer( - "No Programmes Found", - "No Programmes have been found matching your search\nPlease try again." - ) - - for programme in programmes: - - # See if the search result is for a programme or an episode. - matchType = programme.xpath(".//div[@data-matchtype]/@data-matchtype")[0] - - prog = ConstructProgrammeSearch(programme) - - if (matchType == 'programme'): - - title = prog.title - if (prog.additionalInfo.episodeCount is not None): - title += ' [' + str(prog.additionalInfo.episodeCount) + ' matched]' - + if("/hub/" in prog.pageUri): oc.add( DirectoryObject( - key = Callback(RenderEpisodeList, url = prog.pageUri), + key = Callback(RenderEpisodeList, url=prog.pageUri, parent_name=oc.title2, section_name=title), title = title, thumb = prog.imageUri, - summary = prog.shortSynopsis, ) ) - else: - - for episode in programme.xpath(".//div[contains(@class,'episodenid-')]"): - - ep = ConstructEpisodeSearch(episode) - - oc.add( - VideoClipObject( - url = ep.id, - title = prog.title + " - " + ep.title, - summary = ep.summary(), - thumb = prog.imageUri, - ) - ) - if len(oc) <= 0: return MessageContainer( "No Programmes Found", @@ -358,43 +117,36 @@ def RenderSearchResults(query=None, parent_name=None): ) return oc - ################################################# def RenderEpisodeList(url=None, parent_name=None, section_name=None): oc = ObjectContainer(no_cache=True, title1=parent_name, title2=section_name, view_group="InfoList") + content = HTML.ElementFromURL(url, errors='ignore', cacheTime=1800) - episodes = content.xpath(ITV_EPISODE_XPATH) + episodes = content.xpath('//a[@data-content-type="episode"]') + + for episode in episodes: + epUrl = episode.xpath("@href")[0] + epContent = HTML.ElementFromURL(epUrl, errors='ignore', cacheTime=1800) + eptitle = epContent.xpath('//meta[@property="og:title"]/@content')[0] + epsummary = epContent.xpath('//meta[@property="og:description"]/@content')[0] + epimage = epContent.xpath('//meta[@property="og:image"]/@content')[0] + oc.add( + VideoClipObject( + url = epUrl, + title = eptitle, + summary = epsummary, + thumb = epimage + ) + ) - if len(episodes) <= 1: - - episodes = content.xpath("//div[@class='hero']") - - if len(episodes) < 1: - return MessageContainer( - "No Episodes Found", - "No Episodes have been found matching your search\nPlease try again." - ) - - for episode in episodes: - - ep = ConstructEpisode(pageUri=url, element=episode) - - oc.add( - VideoClipObject( - url = ep.id, - title = ep.titleDisplay(), - thumb = ep.posterFrameUri, - summary = ep.summary(), - duration = ep.durationMilliseconds() - ) - ) - return oc + + ################################################# def ConstructProgramme(element): @@ -404,131 +156,16 @@ def ConstructProgramme(element): imageUri = imageUri.replace('player_image_thumb_standard','posterframe') additionInfoEpisodeCount = None - if len(element.xpath(".//span[@class='episode-free']")) > 0: - additionInfoEpisodeCount = element.xpath(".//span[@class='episode-free']")[0].text +# if len(element.xpath(".//p")) > 0: +# additionInfoEpisodeCount = element.xpath(".//p")[0].text # TODO: strip ' episodes' from end of string return MediaItems.Programme( - title = element.xpath(".//div[@class='programme-title cell-title']/a")[0].text, - pageUri = ITV_URL + element.xpath(".//div[@class='programme-title cell-title']/a/@href")[0], + title = element.xpath(".//h3")[0].text.strip(), + pageUri = element.xpath(".//a/@href")[0], imageUri = imageUri, - #genres = element.xpath('./d:Genres',namespaces={'d' : ITV_XML_NS})[0].text, shortSynopsis = '', - #longSynopsis = element.xpath('./d:LongSynopsis',namespaces={'d' : ITV_XML_NS})[0].text, - #additionInfoText = element.xpath('./d:AdditionalInfo/d:Text',namespaces={'d' : ITV_XML_NS})[0].text, - #additionInfoUri = element.xpath('./d:AdditionalInfo/d:Uri',namespaces={'d' : ITV_XML_NS})[0].text, - additionInfoEpisodeCount = additionInfoEpisodeCount, - #additionHeaderText = element.xpath('./d:AdditionalInfo/d:AdditionalHeaderText',namespaces={'d' : ITV_XML_NS})[0].text, - #additionalSynopsisText = element.xpath('./d:AdditionalInfo/d:AdditionalSynopsisText',namespaces={'d' : ITV_XML_NS})[0].text, - #channel = element.xpath('./d:AdditionalInfo/d:Channel',namespaces={'d' : ITV_XML_NS})[0].text, - #latestEpisodeId = element.xpath('./d:LatestEpisode/d:Id',namespaces={'d' : ITV_XML_NS})[0].text, - latestEpisodeDate = '', - latestEpisodeTime = '', - ) - -################################################# -def ConstructProgrammeSearch(element): - - imageUri = '' - if len(element.xpath(".//div[@class='search-result-image']//img/@src")) > 0: - imageUri = element.xpath(".//div[@class='search-result-image']//img/@src")[0] - imageUri = imageUri.replace('player_image_thumb_standard','posterframe') - - additionInfoEpisodeCount = None - if len(element.xpath(".//span[@class='search-max']")) > 0: - additionInfoEpisodeCount = element.xpath(".//span[@class='search-max']")[0].text - - return MediaItems.Programme( - title = element.xpath(".//h4[@class='programme-title']/a")[0].text, - pageUri = element.xpath(".//h4[@class='programme-title']/a/@href")[0], - imageUri = imageUri, - shortSynopsis = " ".join(element.xpath(".//div[@class='programme-description']//text()")).strip(), additionInfoEpisodeCount = additionInfoEpisodeCount, latestEpisodeDate = '', latestEpisodeTime = '', ) -################################################# -def ConstructEpisode(element, pageUri): - - id = pageUri - if len(element.xpath(".//div[contains(@class, 'node-episode')]/a[1]/@href")) > 0: - id = ITV_URL + element.xpath(".//div[contains(@class, 'node-episode')]/a[1]/@href")[0] - elif len(element.xpath("./a")) > 0: - id = ITV_URL + element.xpath("./a[1]/@href")[0] - - title = 'Unknown' - if len(element.xpath("//h2[@class='title episode-title']")) > 0: - title = element.xpath("//h2[@class='title episode-title']")[0].text - elif len(element.xpath(".//div[@class='programme-title']")) > 0: - title = " ".join(element.xpath(".//div[@class='programme-title']//text()")).strip() - - seasonNumber = '' - if len(element.xpath(".//div[contains(@class, 'field-name-field-season-number')]//text()")) > 0: - seasonNumber = element.xpath(".//div[contains(@class, 'field-name-field-season-number')]//text()")[0] - - episodeNumber = '' - if len(element.xpath(".//div[contains(@class, 'field-name-field-episode-number')]//text()")) > 0: - episodeNumber = element.xpath(".//div[contains(@class, 'field-name-field-episode-number')]//text()")[0] - - daysRemaining = None - if len(element.xpath(".//div[@class='offer-duration']")) > 0: - daysRemaining = re.search("(\\d*)",element.xpath(".//div[@class='offer-duration']")[0].text.strip()).groups()[0] - #elif len() > 0: - # re.search("(\\d*)",element.xpath(".//div[@class='offer-duration']")[0].text.strip()).groups()[0] - - shortSynopsis = '' - if len(element.xpath(".//div[contains(@class,'field-name-field-short-synopsis')]//text()")) > 0: - shortSynopsis = element.xpath(".//div[contains(@class,'field-name-field-short-synopsis')]//text()")[0] - - duration = None - if len(element.xpath(".//div[contains(@class, 'field-name-field-duration')]//text()")) > 0: - res = re.search( - "(?:(?:(\\d+)(?:\s*hours?\s*))|(?:(\\d+)(?:\s*minutes?))){1,2}", - " ".join(element.xpath(".//div[contains(@class, 'field-name-field-duration')]//text()")).strip() - ) - if res: - duration = (int(res.groups()[0]) if res.groups()[0] else 0) * 60 - duration += (int(res.groups()[1]) if res.groups()[1] else 0) - - lastBroadcast = datetime.datetime.strptime( - element.xpath(".//div[contains(@class, 'field-name-field-broadcastdate')]//span/@content")[0][:-6], - '%Y-%m-%dT%H:%M:%S' - ) - - posterFrameUri = None - if len(element.xpath(".//div[contains(@class,'field-name-field-image')]//img/@src")) > 0: - posterFrameUri = element.xpath(".//div[contains(@class,'field-name-field-image')]//img/@src")[0] - elif len(element.xpath(".//param[@name='poster']")) > 0: - posterFrameUri = element.xpath(".//param[@name='poster']/@value")[0] - - if posterFrameUri: - posterFrameUri = posterFrameUri.replace('player_image_thumb_standard','posterframe') - - return MediaItems.Episode( - id = id, - title = title, - seasonNumber = seasonNumber, - episodeNumber = episodeNumber, - duration = duration, - lastBroadcast = lastBroadcast, - daysRemaining = daysRemaining, - shortSynopsis = shortSynopsis, - posterFrameUri = posterFrameUri, - #channel = element.xpath('./d:AdditionalInfo/d:Channel',namespaces={'d' : ITV_XML_NS})[0].text, - #channelLogoUrl = element.xpath('./d:AdditionalInfo/d:ChannelLogoUrl',namespaces={'d' : ITV_XML_NS})[0].text, - ) - -def ConstructEpisodeSearch(element): - - daysRemaining = None - res = re.search("(\\d+)", " ".join(element.xpath(".//div[@class='remaining-time']//text()")).strip()) - if res: - daysRemaining = res.groups()[0] - - return MediaItems.Episode( - id = ITV_URL + element.xpath(".//a/@href")[0], - title = " ".join(element.xpath(".//div[@class='episode-title']//text()")).strip(), - shortSynopsis = " ".join(element.xpath(".//div[@class='description']//text()")).strip(), - daysRemaining = daysRemaining - ) - diff --git a/Contents/Services/ServiceInfo.plist b/Contents/Services/ServiceInfo.plist index 0ef6133..49b01e4 100644 --- a/Contents/Services/ServiceInfo.plist +++ b/Contents/Services/ServiceInfo.plist @@ -8,7 +8,7 @@ URLPatterns - https?://(www\.)?itv\.com/itvplayer + http(s)?://(www\.)?itv\.com/hub diff --git a/Contents/Services/URL/ITV/ServiceCode.pys b/Contents/Services/URL/ITV/ServiceCode.pys index 658714e..7122cc5 100644 --- a/Contents/Services/URL/ITV/ServiceCode.pys +++ b/Contents/Services/URL/ITV/ServiceCode.pys @@ -1,207 +1,93 @@ import re import datetime +import json def NormalizeURL(url): - return url def MetadataObjectForURL(url): - content = HTML.ElementFromURL(url, errors='ignore', cacheTime=1800) - element = content.xpath("//div[@class='hero']")[0] - - id = url - if len(element.xpath(".//div[contains(@class, 'node-episode')]/a[1]/@href")) > 0: - id = ITV_URL + element.xpath(".//div[contains(@class, 'node-episode')]/a[1]/@href")[0] - elif len(element.xpath("./a")) > 0: - id = ITV_URL + element.xpath("./a[1]/@href")[0] - - title = 'Unknown' - if len(element.xpath("//*[@property='dc:title']")) > 0: - title = element.xpath("//*[@property='dc:title']")[0].text.strip() - - seasonNumber = '' - if len(element.xpath(".//div[contains(@class, 'field-name-field-season-number')]//text()")) > 0: - seasonNumber = element.xpath(".//div[contains(@class, 'field-name-field-season-number')]//text()")[0] - title += "(Season: " + seasonNumber - - episodeNumber = '' - if len(element.xpath(".//div[contains(@class, 'field-name-field-episode-number')]//text()")) > 0: - episodeNumber = element.xpath(".//div[contains(@class, 'field-name-field-episode-number')]//text()")[0] - title += ", Ep.: " + episodeNumber - - title += ")" - - daysRemaining = None - if len(element.xpath(".//div[@class='offer-duration']")) > 0: - daysRemaining = re.search("(\\d*)",element.xpath(".//div[@class='offer-duration']")[0].text.strip()).groups()[0] - - shortSynopsis = '' - if len(element.xpath(".//div[contains(@class,'field-name-field-short-synopsis')]//text()")) > 0: - shortSynopsis = element.xpath(".//div[contains(@class,'field-name-field-short-synopsis')]//text()")[0] - - duration = None - if len(element.xpath(".//div[contains(@class, 'field-name-field-duration')]//text()")) > 0: - res = re.search( - "(?:(?:(\\d+)(?:\s*hours?\s*))|(?:(\\d+)(?:\s*minutes?))){1,2}", - " ".join(element.xpath(".//div[contains(@class, 'field-name-field-duration')]//text()")).strip() - ) - if res: - duration = (int(res.groups()[0]) if res.groups()[0] else 0) * 60 - duration += (int(res.groups()[1]) if res.groups()[1] else 0) - - lastBroadcast = datetime.datetime.strptime( - element.xpath(".//div[contains(@class, 'field-name-field-broadcastdate')]//span/@content")[0][:-6], - '%Y-%m-%dT%H:%M:%S' - ) - - posterFrameUri = None - if len(element.xpath(".//div[contains(@class,'field-name-field-image')]//img/@src")) > 0: - posterFrameUri = element.xpath(".//div[contains(@class,'field-name-field-image')]//img/@src")[0] - elif len(element.xpath(".//param[@name='poster']")) > 0: - posterFrameUri = element.xpath(".//param[@name='poster']/@value")[0] - - if posterFrameUri: - posterFrameUri = posterFrameUri.replace('player_image_thumb_standard','posterframe') + mytitle = content.xpath('//meta[@property="og:title"]/@content')[0] + mysummary = content.xpath('//meta[@property="og:description"]/@content')[0] + myimage = content.xpath('//meta[@property="og:image"]/@content')[0] return VideoClipObject( - title = title, - summary = shortSynopsis, - thumb = posterFrameUri, - originally_available_at = lastBroadcast, - duration = duration * 60 * 1000, + title = mytitle, + summary = mysummary, source_title = "ITV Player", + thumb = myimage ) +@deferred def MediaObjectsForURL(url): - ret = [] - ret.append( - MediaObject( - protocol = 'rtmp', - audio_channels = 2, - bitrate = 1200, - parts = [PartObject(key=Callback(PlayVideo, url=url))] - ) - ) - - return ret - -@indirect -def PlayVideo(url): - - Log('=============================================================== ' + url) - - # Borrowed from XBMC ITV Plugin and reworked for Plex - html = HTTP.Request(url).content - html = re.sub(''','\'',html) - - #Log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") - - if re.search('"title":"This episode is no longer available."',html): - raise Exception("Sorry This episode is no longer available") - - match1 = re.findall(ur'"productionId":"(.*?)",', html, flags=re.DOTALL) - if match1: - productionID = match1[0] - productionID=re.sub('\\\\','',productionID) - else: - Log("NO PRODUCTION ID ") - - #Log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") - #Log("Production ID is %s" % productionID) - - SM_TEMPLATE = """ - - - - - - %s - FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF - - - itv.com - - - - Itv - - - - ITVPLAYER.12.18.4 - - - - - - None - ITV - Any - ITVPLAYER.VIDEO - - DotCom - ItvCom - - - Big - - - 2 - - - - - """ - - SoapMessage = SM_TEMPLATE%(productionID) - + content = HTML.ElementFromURL(url, errors='ignore', cacheTime=1800) + productionID = content.xpath('//div[@id="video"]/@data-video-autoplay-id')[0] + iosPlaylistUrl = content.xpath('//div[@id="video"]/@data-video-id')[0] + hmac = content.xpath('//div[@id="video"]/@data-video-hmac')[0] + headers = { - "Host":"mercury.itv.com", - "Referer":"http://www.itv.com/mercury/Mercury_VideoPlayer.swf?v=1.6.479/[[DYNAMIC]]/2", - "Content-type":"text/xml; charset=utf-8", - "Content-length":"%d" % len(SoapMessage), - "SOAPAction":"http://tempuri.org/PlaylistService/GetPlaylist" + "Accept": "application/vnd.itv.vod.playlist.v2+json", + "Content-type": "application/json; charset=utf-8", + "hmac": hmac.upper() } - res = HTTP.Request("http://mercury.itv.com/PlaylistService.svc", headers=headers, data=SoapMessage).content - #Log(res) - - title1= res.split("") - #Log(title1) - - title2= title1[1].split("") - - match2 = re.findall(ur'.*?<\!\[CDATA\[(.*?)\].*?', res, flags=re.DOTALL) - if match2: - thumbfile = match2[0] - else: - thumbfile = None - - res = re.search('.+?', res, re.DOTALL).group(0) - rendition_offset= res.split("rendition-offset=") - offset_seconds = rendition_offset[1].split(":") - offset = int(offset_seconds[2]) - - mediafile = res.split("