[RELEASE] Canada On Demand (Successor to CTV Properties) - Printable Version +- Kodi Community Forum (https://forum.kodi.tv) +-- Forum: Support (https://forum.kodi.tv/forumdisplay.php?fid=33) +--- Forum: Add-on Support (https://forum.kodi.tv/forumdisplay.php?fid=27) +---- Forum: Video Add-ons (https://forum.kodi.tv/forumdisplay.php?fid=154) +---- Thread: [RELEASE] Canada On Demand (Successor to CTV Properties) (/showthread.php?tid=97262) |
RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - Skyhi - 2013-10-23 (2013-10-23, 04:11)DurhamDev Wrote:(2013-10-20, 22:50)Skyhi Wrote: Anyone else notice CityTV is working again with 0.7.8? 0.8.4.also works if you use the canwest.py from 0.7.8 I mentioned the wrong file above (I was trying to do something with Global TV). This is my custom 'brightcove.py' for CityTV based on 0.7.8 - I'm not saying this will work forever so YMMV: import time import cgi import datetime import simplejson from channel import BaseChannel, ChannelException,ChannelMetaClass, STATUS_BAD, STATUS_GOOD, STATUS_UGLY from utils import * import httplib import xbmcplugin import xbmc try: from pyamf import remoting has_pyamf = True except ImportError: has_pyamf = False class BrightcoveBaseChannel(BaseChannel): is_abstract = True def get_swf_url(self): conn = httplib.HTTPConnection('c.brightcove.com') qsdata = dict(width=640, height=480, flashID=self.flash_experience_id, bgcolor="#000000", playerID=self.player_id, publisherID=self.publisher_id, isSlim='true', wmode='opaque', optimizedContentLoad='true', autoStart='', debuggerID='') qsdata['@videoPlayer'] = self.video_id logging.debug("SWFURL: %s" % (urllib.urlencode(qsdata),)) conn.request("GET", "/services/viewer/federated_f9?&" + urllib.urlencode(qsdata)) resp = conn.getresponse() location = resp.getheader('location') base = location.split("?",1)[0] location = base.replace("BrightcoveBootloader.swf", "connection/ExternalConnection_2.swf") self.swf_url = location def get_clip_info(self, player_id, video_id): conn = httplib.HTTPConnection("c.brightcove.com") envelope = self.build_amf_request(player_id, video_id) conn.request("POST", "/services/amfgateway", str(remoting.encode(envelope).read()), {'content-type': 'application/x-amf'}) response = conn.getresponse().read() response = remoting.decode(response).bodies[0][1].body[0]['data']['videoDTO'] logging.debug(response) return response def choose_rendition(self, renditions): maxrate = int(self.plugin.get_setting("max_bitrate")) * 1024 rends = [r for r in renditions if r['encodingRate'] < maxrate] if not rends: rends = renditions rends.sort(key=lambda r: r['encodingRate']) return rends[-1] def build_amf_request_body(self, player_id, video_id): return [ player_id, { 'optimizeFeaturedContent': 1, 'featuredLineupFetchInfo': { 'fetchLevelEnum': 4, 'contentType': u'VideoLineup', 'childLimit': 100 }, 'lineupRefId': None, 'videoId': video_id, 'videoRefId': None, 'lineupId': None, 'fetchInfos': [ {'fetchLevelEnum': 1, 'contentType': u'VideoLineup', 'childLimit': 100}, {'grandchildLimit': 100, 'fetchLevelEnum': 3, 'contentType': u'VideoLineupList', 'childLimit': 100} ] } ] def build_amf_request(self, player_id, video_id): env = remoting.Envelope(amfVersion=0) env.bodies.append( ( "/2", remoting.Request( target="com.brightcove.templating.TemplatingFacade.getContentForTemplateInstance", body=self.build_amf_request_body(player_id, video_id), envelope=env ) ) ) return env def find_ids(self, url): soup = BeautifulSoup(self.plugin.fetch(url, max_age=self.cache_timeout)) self.flash_experience_id = soup.find("object")['id'] try: player_id = int(soup.find("object").find("param", {"name": "playerID"})['value']) except: player_id = None try: video_id = int(soup.find('object').find("param", {"name": "@videoPlayer"})['value']) except: video_id = None return player_id, video_id class CityTVBaseChannel(BrightcoveBaseChannel): status = STATUS_BAD default_action = "list_shows" base_url = "http://video.citytv.com" is_abstract = True def action_play_episode(self): url = "http://video.citytv.com" + self.args['remote_url'] player_id, video_id = self.find_ids(url) self.video_id = video_id self.player_id = player_id clipinfo = self.get_clip_info(player_id, video_id) logging.debug(clipinfo) self.publisher_id = clipinfo['publisherId'] self.video_length = clipinfo['length']/1000 self.get_swf_url() parser = URLParser(swf_url=self.swf_url, swf_verify=True) url = self.choose_rendition(clipinfo['renditions'])['defaultURL'] url = parser(url) logging.debug("STREAM_URL: %s" % (url,)) self.plugin.set_stream_url(url) def action_browse_show(self): html = self.plugin.fetch(self.base_url + self.args['remote_url'], max_age=self.cache_timeout) soup = BeautifulSoup(html) toplevel = self.args.get('toplevel', None) section = self.args.get('section', None) if section: return self.browse_section() elif toplevel: return self.browse_toplevel() else: tabdiv = soup.find("div", {'class': re.compile(r'tabs.*')}) toplevels = tabdiv.findAll("a") if len(toplevels) == 1: self.args['toplevel'] = toplevels[0].contents[0].strip() return self.browse_toplevel() else: for a in toplevels: data = {} data.update(self.args) data['Title'] = data['toplevel'] = a.contents[0].strip() self.plugin.add_list_item(data) self.plugin.end_list('seasons', [xbmcplugin.SORT_METHOD_LABEL]) def parse_episode_list(self, pages): monthnames = ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] for page in pages: page = self.plugin.fetch(self.base_url + page, max_age=self.cache_timeout) soup = BeautifulSoup(page) div = soup.find('div', {'id': 'episodes'}).find('div', {'class': 'episodes'}) for item in div.findAll('div', {'class': re.compile(r'item.*')}): data = {} data.update(self.args) data['action'] = 'play_episode' data['Plot'] = item.find('p').contents[0].strip() a = item.find('div', {'class': 'meta'}).h1.a try: date_s = item.find('h5').contents[0].strip().replace("Aired on ","").strip() m,d,y = date_s.split(" ") m = monthnames.index(m) d = d[:-1].strip() y = y.strip() data['Date'] = "%s.%s.%s" % (d,m,y) except: pass data['Title'] = a.contents[0].strip() data['remote_url'] = a['href'] data['Thumb'] = item.find('div', {'class': 'image'}).find('img')['src'] yield data def parse_clip_list(self, pages): monthnames = ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] for page in pages: page = self.plugin.fetch(self.base_url + page, max_age=self.cache_timeout) soup = BeautifulSoup(page) div = soup.find('div', {'id': 'clips'}).div.find('div', {'class': 'clips'}) for epdiv in div.findAll('div', {'class': 'item'}): data = {} data.update(self.args) data['Thumb'] = epdiv.find('div', {"class": 'image'})['style'][23:-3] data['Title'] = epdiv.find('h1').find('a').contents[0].strip() data['action'] = 'play_episode' data['remote_url'] = epdiv.find('h1').find('a')['href'] yield data def parse_show_list(self, pages): for page in pages: page = self.plugin.fetch(self.base_url + page, max_age=self.cache_timeout) soup = BeautifulSoup(page) div = soup.find("div", {'class': 'shows'}) for item in div.findAll('div', {'class': 'item'}): a = item.find("h1").a data = {} data.update(self.args) data['action'] = 'browse_show' data['remote_url'] = a['href'] data['Thumb'] = item.find("div", {'class': 'thumb'}).img['src'] data['Title'] = decode_htmlentities(a.contents[0].strip()) data['TVShowTitle'] = data['Title'] yield data def browse_section(self): page = self.plugin.fetch(self.base_url + self.args['remote_url'], max_age=self.cache_timeout) soup = BeautifulSoup(page) toplevel = self.args.get('toplevel') if toplevel == 'Full Episodes': div = soup.find("div", {'id': 'episodes'}) parser = self.parse_episode_list elif toplevel == 'Video Clips': div = soup.find("div", {'id': 'clips'}) parser = self.parse_clip_list paginator = div.find('ul', {'class': 'pagination'}) pageas = paginator.findAll('a') pages = [self.args['remote_url']] pages += [a['href'] for a in pageas] items = parser(pages) for item in items: self.plugin.add_list_item(item, is_folder=False) self.plugin.end_list('episodes', [xbmcplugin.SORT_METHOD_DATE, xbmcplugin.SORT_METHOD_LABEL]) def browse_toplevel(self): toplevel = self.args['toplevel'] page = self.plugin.fetch(self.base_url+self.args['remote_url'], max_age=self.cache_timeout) soup = BeautifulSoup(page) if toplevel == 'Full Episodes': div = soup.find("div", {'id': 'episodes'}) elif toplevel == 'Video Clips': div = soup.find("div", {'id': 'clips'}) try: section_div = div.find('div', {'class': 'widget'}).find('div', {'class': 'middle'}) sections = section_div.findAll('a') except: sections = [] if not sections: return self.browse_section() elif len(sections) == 1: self.args['section'] = decode_htmlentities(sections[0].contents[0].strip()) return self.browse_section() else: for section in sections: data = {} data.update(self.args) data['section'] = decode_htmlentities(section.contents[0].strip()) data['remote_url'] = section['href'] data['Title'] = data['section'] self.plugin.add_list_item(data) self.plugin.end_list('seasons', [xbmcplugin.SORT_METHOD_DATE, xbmcplugin.SORT_METHOD_LABEL]) def action_list_shows(self): page = self.plugin.fetch(self.base_url + self.root_url, max_age=self.cache_timeout) soup = BeautifulSoup(page) paginator = soup.find('ul', {'class': 'pagination'}) pageas = paginator.findAll('a') pages = [self.root_url] pages += [a['href'] for a in pageas] for item in self.parse_show_list(pages): self.plugin.add_list_item(item) self.plugin.end_list('tvshows', [xbmcplugin.SORT_METHOD_LABEL]) class CityTV(CityTVBaseChannel): short_name = 'citytv' long_name = "CityTV" root_url = "/video/navigation.htm?N=0&type=shows&sort=Display" class OLN(CityTVBaseChannel): short_name = 'oln' long_name = 'Outdoor Life Network' root_url = "/video/channel/oln/allmedia/4294965726/" class G4TV(CityTVBaseChannel): short_name = 'g4' long_name = "G4 Tech TV" root_url = "/video/channel/g4/allmedia/4294965638/" class Omni(CityTVBaseChannel): short_name = 'omni' long_name = 'OMNI TV' root_url = "/video/channel/omni/allmedia/4294965410/" class ShortsInTheCity(CityTVBaseChannel): short_name = 'shortsinthecity' long_name = 'Shorts in the City' root_url = '/video/channel/shortsinthecity/allmedia/4294965731/' class TVOKids(BrightcoveBaseChannel): short_name = 'tvokids' long_name = 'TVO Kids' default_action = 'root' base_url = 'http://www.tvokids.com' player_id = 48543011001 publisher_id = 15364602001 flash_experience_id="null" def get_swf_url(self): conn = httplib.HTTPConnection('c.brightcove.com') qsdata = dict(width=640, height=480, flashID=self.flash_experience_id, bgcolor="#000000", playerID=self.player_id, publisherID=self.publisher_id, isSlim='true', wmode='opaque', optimizedContentLoad='true', autoStart='', debuggerID='') qsdata['@videoPlayer'] = self.video_id logging.debug("SWFURL: %s" % (urllib.urlencode(qsdata),)) conn.request("GET", "/services/viewer/federated_f9?&" + urllib.urlencode(qsdata)) resp = conn.getresponse() location = resp.getheader('location') base = location.split("?",1)[0] location = base.replace("BrightcoveBootloader.swf", "federatedVideo/BrightcovePlayer.swf") self.swf_url = location def action_root(self): data = {} data.update(self.args) data['action'] = 'list_shows' data['age'] = 5 data['Title'] = "Ages 2-5" self.plugin.add_list_item(data) data['Title'] = "Ages 11 and under" data['age'] = 11 self.plugin.add_list_item(data) self.plugin.end_list() def action_play_video(self): info = self.get_clip_info(self.player_id, self.args['bc_id'], self.publisher_id) self.video_id = self.args.get('bc_id') self.get_swf_url() logging.debug(self.swf_url) parser = URLParser(swf_url=self.swf_url, swf_verify=True) url = self.choose_rendition(info['renditions'])['defaultURL'] app, playpath, wierdqs = url.split("&", 2) qs = "?videoId=%s&lineUpId=&pubId=%s&playerId=%s&affiliateId=" % (self.video_id, self.publisher_id, self.player_id) #playpath += "&" + wierdqs scheme,netloc = app.split("://") netloc, app = netloc.split("/",1) app = app.rstrip("/") + qs logging.debug("APP:%s" %(app,)) tcurl = "%s://%s:1935/%s" % (scheme, netloc, app) logging.debug("TCURL:%s" % (tcurl,)) #pageurl = 'http://www.tvokids.com/shows/worldofwonders' url = "%s tcUrl=%s app=%s playpath=%s%s swfUrl=%s conn=B:0 conn=S:%s&%s" % (tcurl,tcurl, app, playpath, qs, self.swf_url, playpath, wierdqs) logging.debug(url) self.plugin.set_stream_url(url) def action_browse_show(self): url = self.base_url + "/feeds/%s/all/videos_list.xml?random=%s" % (self.args['node_id'], int(time.time()), ) page = self.plugin.fetch(url, max_age=self.cache_timeout).read() soup = BeautifulStoneSoup(page) for node in soup.findAll('node'): data = {} logging.debug(node) data.update(self.args) data['action'] = 'play_video' data['Thumb'] = node.find('node_still').contents[0].strip() data['Title'] = decode_htmlentities(node.find('node_title').contents[0].strip()) data['Plot'] = decode_htmlentities(node.find("node_short_description").contents[0].strip()) data['bc_id'] = node.find("node_bc_id").contents[0].strip() data['bc_refid'] = node.find("node_bc_refid").contents[0].strip() self.plugin.add_list_item(data, is_folder=False) self.plugin.end_list('episodes') def action_list_shows(self): age = int(self.args.get('age')) if age == 11: url = '/feeds/all/98/shows' elif age == 5: url = '/feeds/all/97/shows' page = self.plugin.fetch(self.base_url + url, max_age=self.cache_timeout).read() soup = BeautifulStoneSoup(page) for node in soup.findAll('node'): data = {} data.update(self.args) data['Title'] = decode_htmlentities(node.find('node_title').contents[0].strip()) thumb = node.find('node_thumbnail').contents[0].strip() if not thumb.endswith(".swf"): data['Thumb'] = self.base_url + "/" + thumb data['node_id'] = node.find('node_id').contents[0].strip() data['action'] = 'browse_show' self.plugin.add_list_item(data) self.plugin.end_list() RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - icharania - 2013-10-23 @Skyhi: Nice! You should submit your fixes to https://github.com/marius-muja/plugin.video.canada.on.demand Once it's up there, I can update the repo so everyone can get a working 0.8.5 RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - Skyhi - 2013-10-23 @icharania: Ok I will do that from now on if I have anything to submit. I just want to clarify that I really did not do anything with the code other than patch in the '0.7.8' bit. I don't know why CityTV started working again - I just happen to have that version (ish) on my Xbox and saw that CityTV was working again so I patched it into my 0.8.4 version. I am trying to learn more so I have a better understanding of how this plugin really works. BTW - great work to everyone that has worked on this an kept it going - I have enjoyed it for many years already! I see both android and ios have working apps out for Global tv and I wish I knew how they are scraping that website now. Nice to see new channels being added too! But it is disappointing to hear CBC has now changed things as well... RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - maherc - 2013-10-23 Guys, What we supposed to do? update, reload, delete or What to get it works? RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - Dubhbairn - 2013-10-24 Canada On Demand: CBC, The National, etc, is getting a script error and the shows are not playing. CTV is also giving script errors and the shows are not playing. I can't get them to play and I am currently in the states. (No the shows I'm trying to play are not blocked here.) My wife is inside Canada and can't get them to play with XBMC either. (Another machine in another country and an independently verified source.) She called me after having problems in Canada and I can't get the videos to play either. Note: The videos DO play when you go DIRECTLY to CTV and CBC to play them, so the programs are there, but I assume the links are off. Thanks for working on this great program by the way. It enables my wife to maintain her Canadian habits of watching her shows when she travels. Cheers, Dubhbairn RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - rstone - 2013-10-25 I'm reporting the same problem. I updated Canada on Demand about a week ago and have not been able to play any CBC content. I get a script error. (2013-10-24, 18:46)Dubhbairn Wrote: Canada On Demand: CBC, The National, etc, is getting a script error and the shows are not playing. RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - Eye in the sky - 2013-10-25 (2013-10-24, 18:46)Dubhbairn Wrote: CTV is also giving script errors and the shows are not playing. CTV works fine here and I'm in Germany using a proxy to access Canada On Demand. RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - rm65453 - 2013-10-26 Same script error. hoping for a fix soon. (2013-10-25, 15:00)rstone Wrote: I'm reporting the same problem. I updated Canada on Demand about a week ago and have not been able to play any CBC content. I get a script error. RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - alibaba011 - 2013-10-27 CTV works fine here too. I checked csi conan, castle living in Canada. Please you need to share logs by uploading to xbmclogs.com if you having problems. RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - maherc - 2013-10-27 What i have noticed that COD in windows 7 works without errors BUT in Android nothing works? RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - Dubhbairn - 2013-10-27 (2013-10-23, 00:18)bash1979 Wrote: Anyone else having issues with CBC streaming? Other than that, the plugin works great! More info on CBC streaming problems: Someone noted that they've changed the manifest file and said that the rtmp stream link is now broken into separate pieces and the ad streams now start with "http" and contain "/pfadx/". Noted here: https://github.com/marius-muja/plugin.video.canada.on.demand/issues/27 Manifest/link example: http://link.theplatform.com/s/h9dtGB/lmdBmWNdqLgzvo7mEF7AKVHnNG13Jynh?site=cbc.dragonsden.ca&companions=true&show=dragons_den&shortClip=true&zone=dragons_den&mbr=true&audioonly=false&playertype=embed&format=SMIL&Tracking=true&Embedded=true RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - rm65453 - 2013-10-27 I have the same problem with raspberry pi and android, will post log soon. 08:12:07 T:2810147904 NOTICE: Previous line repeats 2 times. 08:12:07 T:2810147904 NOTICE: Thread Background Loader start, auto delete: false 08:12:08 T:2810147904 NOTICE: Thread Jobworker start, auto delete: true 08:12:11 T:2906649664 NOTICE: Previous line repeats 1 times. 08:12:11 T:2906649664 NOTICE: Thread XBPyThread start, auto delete: false 08:12:11 T:2906649664 NOTICE: -->Python Interpreter Initialized<-- 08:12:16 T:2931815488 NOTICE: Thread Background Loader start, auto delete: false 08:12:21 T:2906649664 NOTICE: Thread XBPyThread start, auto delete: false 08:12:21 T:2906649664 NOTICE: -->Python Interpreter Initialized<-- 08:12:25 T:2931815488 NOTICE: Thread Background Loader start, auto delete: false 08:12:27 T:2931815488 NOTICE: Thread XBPyThread start, auto delete: false 08:12:27 T:2931815488 NOTICE: -->Python Interpreter Initialized<-- 08:12:38 T:2931815488 ERROR: WARNING:root:RELURL: http://cbc.feeds.theplatform.com/ps/JSON/PortalService/2.2/getReleaseList?PID=_DyE_l_gC9yXF9BvDQ4XNfcCVLS4PQij&field=title&field=PID&field=ID&field=description&field=categoryIDs&field=thumbnailURL&field=URL&field=airdate&field=length&field=bitrate&sortField=airdate&sortDescending=true&startIndex=1&endIndex=100 08:12:38 T:2931815488 NOTICE: . 08:12:43 T:2931815488 NOTICE: Thread Background Loader start, auto delete: false 08:12:47 T:2931815488 NOTICE: Thread XBPyThread start, auto delete: false 08:12:48 T:2931815488 NOTICE: -->Python Interpreter Initialized<-- 08:12:52 T:2931815488 ERROR: EXCEPTION Thrown (PythonToCppException) : -->Python callback/script returned the following error<-- - NOTE: IGNORING THIS CAN LEAD TO MEMORY LEAKS! Error Type: <type 'exceptions.RuntimeError'> Error Contents: theplatform len(items) should be 1 Traceback (most recent call last): File "/home/pi/.xbmc/addons/plugin.video.canada.on.demand-master/default.py", line 494, in <module> plugin() File "/home/pi/.xbmc/addons/plugin.video.canada.on.demand-master/default.py", line 459, in __call__ return chan() File "/home/pi/.xbmc/addons/plugin.video.canada.on.demand-master/channel.py", line 122, in __call__ return action_method() File "/home/pi/.xbmc/addons/plugin.video.canada.on.demand-master/channels/theplatform.py", line 268, in action_play_episode if len(items) != 1: raise RuntimeError('theplatform len(items) should be 1') RuntimeError: theplatform len(items) should be 1 -->End of Python script error report<-- 08:12:52 T:3041112704 ERROR: Playlist Player: skipping unplayable item: 0, path [plugin://plugin.video.canada.on.demand/?Plot=Amanda+Lang+and+Kevin+O%27Leary+take+you+inside+the+business+world+with+their+trade+mark+thought-provoking+coverage.&Thumb=http%3A%2F%2Fthumbnails.cbc.ca%2Fmaven_legacy%2Fthumbnails%2FLangOLeary1.jpg&Title=Lang+%26+O%27Leary+Exchange&action=play_episode&entry_id=2414482954&bitrate=2539&remote_url=http%3A%2F%2Frelease.theplatform.com%2Fcontent.select%3Fpid%3DYNV1n5Mwm5MfyJdhQZpERKQdnLgHo7yq%26UserName%3DUnknown%26Embedded%3DTrue%26Portal%3DAll%2520Content%26Tracking%3DTrue&remote_PID=YNV1n5Mwm5MfyJdhQZpERKQdnLgHo7yq&medialen=2691308&Date=25.10.2013&channel=cbc&Icon=http%3A%2F%2Fthumbnails.cbc.ca%2Fmaven_legacy%2Fthumbnails%2FLangOLeary1.jpg] 08:12:52 T:2931815488 NOTICE: Thread Background Loader start, auto delete: false RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - htpcero - 2013-10-30 (2013-10-23, 00:18)bash1979 Wrote: Anyone else having issues with CBC streaming? Other than that, the plugin works great! +1 RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - oldgaf - 2013-10-30 Anything can be done for CBC? RE: [RELEASE] Canada On Demand (Successor to CTV Properties) - PatK - 2013-10-30 Yes CBC National nighties bring script errors now. Hoping for a fix. |