2013-10-23, 15:14
(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
How did you get City working? I have 8.4.0 and it's not there?
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()