Guest User

mlb.py

a guest
Jun 27th, 2011
435
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 61.84 KB | None | 0 0
  1. import mc
  2. import tracker
  3. import urllib
  4. import random
  5. import base64
  6. import datetime as dt
  7. import time
  8. import simplejson as json
  9. import re
  10. import monthdelta as md
  11. from xml.dom.minidom import parse, parseString
  12. import jobmanager
  13. import md5
  14. import threading
  15. class MLB(object):
  16. __module__ = __name__
  17. __doc__ = '\n Default values\n '
  18.  
  19. def __init__(self):
  20. self.status_error = -2
  21. self.status_valid = 1
  22. self.status_invalid = 0
  23. self.status_missing = -1
  24. self.send_to_server = True
  25. self.send_to_history = True
  26. self.pitches = {'PO': ['Pitchout',
  27. 'Pitchout'],
  28. 'AB': ['Automatic Ball',
  29. 'Automatic Ball'],
  30. 'AS': ['Automatic Strike',
  31. 'Automatic Strike'],
  32. 'CH': ['Changeup',
  33. 'Changeup'],
  34. 'CU': ['Curveball',
  35. 'Curveball'],
  36. 'FA': ['Fastball',
  37. 'Fastball'],
  38. 'FT': ['Two-seam FB',
  39. 'Fastball (2-seam)'],
  40. 'FF': ['Four-seam FB',
  41. 'Fastball (4-seam)'],
  42. 'FC': ['Cutter',
  43. 'Fastball (Cut)'],
  44. 'FS': ['Splitter',
  45. 'Fastball (Split-finger)'],
  46. 'FO': ['Forkball',
  47. 'Forkball'],
  48. 'GY': ['Gyroball',
  49. 'Gyroball'],
  50. 'IN': ['Intentional Ball',
  51. 'Intentional Ball'],
  52. 'KC': ['Knuckle Curve',
  53. 'Knuckle Curve'],
  54. 'KN': ['Knuckleball',
  55. 'Knuckleball'],
  56. 'NP': ['No Pitch',
  57. 'No Pitch'],
  58. 'SC': ['Screwball',
  59. 'Screwball'],
  60. 'SI': ['Sinker',
  61. 'Sinker'],
  62. 'SL': ['Slider',
  63. 'Slider'],
  64. 'UN': ['Unknown',
  65. 'Unknown']}
  66. self.event_filter = ['Hit By Pitch',
  67. 'Double',
  68. 'Home Run',
  69. 'Single',
  70. 'Triple',
  71. 'Batter Interference',
  72. 'Catcher Interference',
  73. 'Error',
  74. 'Fan interference',
  75. 'Field Error',
  76. 'Fielder Interference',
  77. 'Runner Interference',
  78. 'Double Play',
  79. 'Grounded Into DP',
  80. 'Sac Bunt',
  81. 'Sac Fly',
  82. 'Sac Fly DP',
  83. 'Triple Play',
  84. 'Ejection',
  85. 'Player Injured',
  86. 'Pickoff 1B',
  87. 'Pickoff 2B ',
  88. 'Pickoff 3B',
  89. 'Pickoff Error 1B',
  90. 'Pickoff Error 2B',
  91. 'Pickoff Error 3B',
  92. 'Run',
  93. 'Balk',
  94. 'Caught Stealing 2B',
  95. 'Caught Stealing 3B',
  96. 'Caught Stealing Home',
  97. 'Caught Stealing DP',
  98. 'Passed Ball',
  99. 'Picked off stealing 2B',
  100. 'Picked off stealing 3B',
  101. 'Picked off stealing home',
  102. 'Stolen Base 2B',
  103. 'Stolen Base 3B',
  104. 'Stolen Base Home',
  105. 'Wild Pitch',
  106. 'Strikeout - DP',
  107. 'Strikeout - TP',
  108. 'Sacrifice Bunt DP',
  109. 'In play, run(s)']
  110. self.mr_url = 'https://mlb-ws.mlb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3?%s'
  111. self.boxee_server = 'http://dir.boxee.tv/apps'
  112. self.url_base = (self.boxee_server + '/mlb/mlb.php?func=%s&%s')
  113. self.data_uri = 'http://gdx.mlb.com/components/game/mlb'
  114. self.realtime_uri = 'http://lwsa.mlb.com/tfs/tfs?file=components/game/mlb'
  115. self.message_uri = 'http://dir.boxee.tv/apps/mlb/messages.xml'
  116. self.time_uri = 'http://dir.boxee.tv/apps/mlb/mlbTime.php'
  117. self.myTracker = tracker.Tracker()
  118. try:
  119. self.device_id = mc.GetDeviceId()
  120. except:
  121. self.device_id = None
  122.  
  123.  
  124.  
  125. def request(self, data):
  126. try:
  127. self.info('request', 'processing post request to boxee.tv')
  128. if ((not data) or (str(type(data)) != "<type 'dict'>")):
  129. return self.raiseError(log='request', error='data passed is not usable. contact support@boxee.tv')
  130. try:
  131. params = urllib.urlencode(data)
  132. except:
  133. return self.raiseError(log='request', error='data passed is not usable. contact support@boxee.tv')
  134. http = mc.Http()
  135. result = http.Post('http://dir.boxee.tv/apps/mlb/mlb.php', params)
  136. code = http.GetHttpResponseCode()
  137. http.Reset()
  138. if ((code != 200) and self.debug('request', ('post returned response code ' + str(code)))):
  139. pass
  140. if (result or self.debug('request', 'post return zero bytes')):
  141. pass
  142. if ((code == 200) and result):
  143. self.info('request', 'post was successful')
  144. response = {'data': result,
  145. 'code': code}
  146. return response
  147. except Exception, e:
  148. return self.raiseError(log='request', error=e)
  149.  
  150.  
  151.  
  152. def callService(self, func, values = {}, authenticated = True, content = False):
  153. try:
  154. self.info('callservice', ('calling the boxee_mlb service w/%s, authentication/%s' % (func,
  155. str(authenticated).lower())))
  156. params = {}
  157. http_service = mc.Http()
  158. if authenticated:
  159. app = mc.GetApp()
  160. cf = app.GetLocalConfig()
  161. params['nsfp'] = cf.GetValue('fprt')
  162. params['nsid'] = cf.GetValue('ipid')
  163. if values:
  164. for i in values:
  165. params[i] = values[i]
  166.  
  167. url_base = self.underscore((self.url_base % (func,
  168. urllib.urlencode(params))))
  169. query_result = http_service.Get(url_base)
  170. if (content == 'json'):
  171. query_result = re.sub('//.*?\n|/\\*.*?\\*/', '', query_result, re.S)
  172. query_result = json.loads(query_result)
  173. return query_result
  174. except Exception, e:
  175. return self.raiseError(log='callservice', error=e)
  176.  
  177.  
  178.  
  179. def underscore(self, string):
  180. return string.replace('_', '%5F')
  181.  
  182.  
  183.  
  184. def ordinal(self, n):
  185. if 10 < n < 14:
  186. return (u'%sth' % n)
  187. if ((n % 10) == 1):
  188. return (u'%sst' % n)
  189. if ((n % 10) == 2):
  190. return (u'%snd' % n)
  191. if ((n % 10) == 3):
  192. return (u'%srd' % n)
  193. return ('%sth' % n)
  194.  
  195.  
  196.  
  197. def gameIdToEventURI(self, game_id, realtime = False):
  198. game_id = game_id.replace('-', '_')
  199. underscored = game_id.replace('/', '_')
  200. split = game_id.split('/')
  201. if realtime:
  202. return (self.realtime_uri + ('/year_%s/month_%s/day_%s/gid_%s/' % (split[0],
  203. split[1],
  204. split[2],
  205. underscored)))
  206. return (self.data_uri + ('/year_%s/month_%s/day_%s/gid_%s/' % (split[0],
  207. split[1],
  208. split[2],
  209. underscored)))
  210.  
  211.  
  212.  
  213. def info(self, func, msg):
  214. mc.LogInfo(((('@mlb.tv (' + func) + ') ') + str(msg)))
  215.  
  216.  
  217.  
  218. def debug(self, func, msg):
  219. mc.LogDebug(((('@mlb.tv (' + func) + ') ') + str(msg)))
  220.  
  221.  
  222.  
  223. def error(self, func, msg):
  224. mc.LogError(((('@mlb.tv (' + func) + ') ') + str(msg)))
  225.  
  226.  
  227.  
  228. def raiseError(self, message = False, log = False, error = False):
  229. mc.HideDialogWait()
  230. if (log and error):
  231. mc.LogError(((('@mlb.tv (' + log) + ') ') + str(error)))
  232. if message:
  233. response = message
  234. else:
  235. response = 'An error has occurred. Details have been saved in your log. Please notify Boxee support.'
  236. mc.ShowDialogOk('MLB.TV', response)
  237. return False
  238.  
  239.  
  240.  
  241. def getJson(self, url = False, data = False):
  242. try:
  243. if url:
  244. data = mc.Http().Get(url)
  245. data = re.sub('//.*?\n|/\\*.*?\\*/', '', data, re.S)
  246. data = json.loads(data)
  247. return data
  248. except Exception, e:
  249. return self.raiseError(log='getjson', error=e)
  250.  
  251.  
  252.  
  253. def digitsToTimecode(self, digits):
  254. return ((((digits[0:2] + ':') + digits[2:4]) + ':') + digits[4:6])
  255.  
  256.  
  257.  
  258. def getCurrentMLBTime(self):
  259. self.debug('time', 'Getting MLB time...')
  260. xml = mc.Http().Get(self.time_uri)
  261. if xml:
  262. dom = parseString(xml)
  263. if dom.getElementsByTagName('time'):
  264. value = dom.getElementsByTagName('time')[0].firstChild.data
  265. return time.strptime(value, '%d %b %Y %H:%M:%S')
  266. else:
  267. self.debug('time', 'Could not retrieve MLB time.')
  268. return False
  269.  
  270.  
  271.  
  272. def authenticate(self):
  273. try:
  274. content = mc.Http().Get('http://app.boxee.tv/api/get_application_data?id=mlb')
  275. if content:
  276. return self.status_error
  277. else:
  278. auth_dom = parseString(content)
  279. email = auth_dom.getElementsByTagName('email')[0].firstChild.data
  280. account = auth_dom.getElementsByTagName('rand_account')[0].firstChild.data
  281. post_data = self.request({'func': '_login',
  282. 'email': email,
  283. 'pass': account})
  284. if ((not post_data) or (post_data['data'] == '0')):
  285. Exception('post request return false')
  286. cf = mc.GetApp().GetLocalConfig()
  287. response = self.getJson(data=post_data['data'])
  288. response = response.get('identity')
  289. code = str(response.get('code'))
  290. if ((code != '1') and self.info('authenticate.code', code)):
  291. cf.Reset('fprt')
  292. cf.Reset('ipid')
  293. cf.Reset('username')
  294. cf.Reset('password')
  295. self.info('login', 'stored/entered credentials invalid')
  296. return self.status_invalid
  297. mc.HideDialogWait()
  298. except Exception, e:
  299. self.updateArchiveSpoiler()
  300. return self.status_invalid
  301.  
  302.  
  303.  
  304. def isLoggedIn(self):
  305. cf = mc.GetApp().GetLocalConfig()
  306. if (cf.GetValue('fprt') and cf.GetValue('ipid')):
  307. return True
  308.  
  309.  
  310.  
  311. def getCredentials(self):
  312. try:
  313. cf = mc.GetApp().GetLocalConfig()
  314. if (cf.GetValue('username') and cf.GetValue('password')):
  315. return {'user': cf.GetValue('username'),
  316. 'pass': cf.GetValue('password')}
  317. except Exception, e:
  318. return self.raiseError(log='getCredentials', error=e)
  319.  
  320.  
  321.  
  322. def launchWithItem(self, args):
  323. try:
  324. item = mc.ListItem(mc.ListItem.MEDIA_VIDEO_OTHER)
  325. item.SetLabel(args['title'][0])
  326. item.SetDescription(args['description'][0])
  327. item.SetThumbnail(args['thumbnail'][0])
  328. item.SetProperty('alt-label', args['alt-label'][0])
  329. item.SetProperty('event-id', args['event-id'][0])
  330. item.SetProperty('content-id', args['content-id'][0])
  331. item.SetProperty('bx-ourl', args['bx-ourl'][0])
  332. item.SetProperty('audio-stream', args['audio-stream'][0])
  333. item.SetProperty('media-state', args['media-state'][0])
  334. self.playItem(mlbList=0, playFromListItem=item)
  335. except Exception, e:
  336. return self.raiseError('Unable to play requested stream. If this continues please contact support@boxee.tv', 'launchWithItem', e)
  337.  
  338.  
  339.  
  340. def init(self, args = False):
  341. try:
  342. self.info('init', 'mlb launched, checking authentication')
  343. auth = self.authenticate()
  344. if (auth == self.status_valid):
  345. self.authenticated = True
  346. if (args and self.launchWithItem(args)):
  347. pass
  348. else:
  349. if ((auth == self.status_missing) or (auth == self.status_invalid)):
  350. self.authenticated = False
  351. mc.ShowDialogOk('MLB.TV', 'Got MLB.tv? Go to boxee.tv/services to link your account. [CR]No MLB.tv? Sign up at mlb.com/boxee and get a free game a day through April!')
  352. return False
  353. except Exception, e:
  354. return self.raiseError(False, True)
  355.  
  356.  
  357.  
  358. def populateTodayScreen(self):
  359. try:
  360. w = mc.GetWindow(14000)
  361. w.GetList(120).SetFocusedItem(0)
  362. w.GetControl(120).SetFocus()
  363. w.GetList(120).SetFocusedItem(0)
  364. dt = self.getMonth(0)
  365. w.GetLabel(101).SetLabel(dt.strftime('%B %d, %Y'))
  366. games = self.getGames()
  367. if (games and w.GetList(120).SetItems(games)):
  368. pass
  369. except Exception, e:
  370. if (('AppException' in str(e)) and self.info('populateTodayScreen', e)):
  371. return False
  372.  
  373.  
  374.  
  375. def setUpCalendar(self):
  376. try:
  377. mc.GetWindow(14001).GetList(121).SetFocus()
  378. self.setMonth(0, False)
  379. except Exception, e:
  380. return self.raiseError(log='setUpCalendar', error=e)
  381.  
  382.  
  383.  
  384. def standings(self, league):
  385. try:
  386. mc.ShowDialogWait()
  387. if (league == 'national'):
  388. league = 0
  389. elif (league == 'american'):
  390. league = 1
  391. data = self.getJson('http://mlb.mlb.com/lookup/json/named.standings_all_league_repeater.bam?sit_code=%27h0%27&league_id=104&league_id=103&season=2010')
  392. data = data.get('standings_all_league_repeater').get('standings_all')[league]
  393. stand = data.get('queryResults').get('row')
  394. east = mc.ListItems()
  395. west = mc.ListItems()
  396. central = mc.ListItems()
  397. for team in stand:
  398. item = mc.ListItem(mc.ListItem.MEDIA_UNKNOWN)
  399. item.SetLabel(str(team.get('team_short')))
  400. item.SetThumbnail(('http://mlb.mlb.com/images/logos/200x200/200x200_%s.png' % str(team.get('team_abbrev'))))
  401. item.SetProperty('games-back', str(team.get('gb')))
  402. item.SetProperty('wild-card', str(team.get('wild_card')))
  403. item.SetProperty('elim-wildcard', str(team.get('elim_wildcard')))
  404. details = (((((((((((((((('Steak (' + team.get('streak')) + '), Home (') + team.get('home')) + '), Away (') + team.get('away')) + '), Vs Division (') + team.get('vs_division')) + '), Last Ten (') + team.get('last_ten')) + ')[CR]Winning Percentage (') + team.get('pct')) + '%), Wildcard (') + team.get('wild_card')) + '), Elimination Wildcard (') + team.get('elim_wildcard')) + ')')
  405. item.SetDescription(str(details))
  406. division = str(team.get('division'))
  407. if (('East' in division) and east.append(item)):
  408. pass
  409.  
  410. mc.GetActiveWindow().GetList(3002).SetItems(west)
  411. mc.GetActiveWindow().GetList(3003).SetItems(central)
  412. mc.GetActiveWindow().GetList(3004).SetItems(east)
  413. mc.HideDialogWait()
  414. except Exception, e:
  415. return self.raiseError(message='There was a problem accessing standings. Please try again later.', log='league', error=e)
  416.  
  417.  
  418.  
  419. def getGames(self, year = False, month = False, day = False):
  420. try:
  421. mc.ShowDialogWait()
  422. if self.isLoggedIn():
  423. params = {}
  424. if (year and (month and day)):
  425. params['year'] = year
  426. params['month'] = month
  427. params['day'] = day
  428. try:
  429. games = self.callService('today', params, content='json').get('games')
  430. except:
  431. try:
  432. games = self.callService('today', params, content='json').get('games')
  433. except:
  434. return self.raiseError('Unable to fetch games list. Please try again later.', 'getgames', 'unable to fetch game list.')
  435. list = mc.ListItems()
  436. for game in games:
  437. item = mc.ListItem(mc.ListItem.MEDIA_VIDEO_OTHER)
  438. item.SetLabel(str(game.get('title')))
  439. item.SetThumbnail(str(game.get('thumbnail')))
  440. item.SetDescription(str(game.get('description')))
  441. custom = game.get('custom:items')
  442. for value in custom:
  443. item.SetProperty(str(value), str(custom.get(str(value))))
  444.  
  445. images = game.get('image:items')
  446. for (key, value,) in enumerate(images):
  447. item.SetImage(key, str(value))
  448.  
  449. media_string = ''
  450. audio_string = ''
  451. media = game.get('media:items')
  452. if bool(media.get('has_video')):
  453. video = media.get('video')
  454. for stream in video:
  455. if media_string:
  456. media_string = (media_string + '||')
  457. media_string = (((((media_string + str(stream.get('title'))) + '|') + str(stream.get('contentid'))) + '|') + str(stream.get('eventid')))
  458.  
  459. if (media_string and item.SetProperty('media-string', media_string)):
  460. pass
  461. if bool(media.get('has_audio')):
  462. audio = media.get('audio')
  463. for stream in audio:
  464. if audio_string:
  465. audio_string = (audio_string + '||')
  466. audio_string = (((((audio_string + str(stream.get('title'))) + '|') + str(stream.get('contentid'))) + '|') + str(stream.get('eventid')))
  467.  
  468. if (audio_string and item.SetProperty('audio-string', audio_string)):
  469. pass
  470. list.append(item)
  471.  
  472. mc.HideDialogWait()
  473. return list
  474. mc.HideDialogWait()
  475. except Exception, e:
  476. return self.raiseError('Unable to fetch game list! Please wait and try again. If problem persists contact support@boxee.tv!', 'getgames', e)
  477.  
  478.  
  479.  
  480. def getMonth(self, month = 0, formatted = False):
  481. try:
  482. date = (dt.date.today() + md.monthdelta(month))
  483. if formatted:
  484. return date
  485. else:
  486. return date.strftime('%B %Y')
  487. except Exception, e:
  488. self.raiseError(log='getmonth', error=e)
  489. return dt.date.today()
  490.  
  491.  
  492.  
  493. def setMonth(self, active, setList = True):
  494. try:
  495. window = mc.GetActiveWindow()
  496. cf = mc.GetApp().GetLocalConfig()
  497. cf.SetValue('calendar', str(active))
  498. if setList:
  499. month = self.getMonth(active, False)
  500. if (active == 0):
  501. url = month.strftime('rss://dir.boxee.tv/apps/mlb/feed/%Y/%m')
  502. else:
  503. url = 'rss://dir.boxee.tv/apps/mlb/feed/calendar'
  504. mc.GetActiveWindow().GetList(121).SetContentURL(url)
  505. window.GetLabel(102).SetLabel((('[UPPERCASE]' + self.getMonth(active, True)) + '[/UPPERCASE]'))
  506. window.GetLabel(103).SetLabel((('[UPPERCASE]' + self.getMonth((active + 1), True)) + '[/UPPERCASE]'))
  507. window.GetLabel(101).SetLabel((('[UPPERCASE]' + self.getMonth((active - 1), True)) + '[/UPPERCASE]'))
  508. except Exception, e:
  509. return self.raiseError(log='nextmonth', error=e)
  510.  
  511.  
  512.  
  513. def nextMonth(self):
  514. try:
  515. cf = mc.GetApp().GetLocalConfig()
  516. if cf.GetValue('calendar'):
  517. active = 0
  518. else:
  519. active = int(cf.GetValue('calendar'))
  520. self.setMonth((active + 1))
  521. except Exception, e:
  522. return self.raiseError(log='nextmonth', error=e)
  523.  
  524.  
  525.  
  526. def prevMonth(self):
  527. try:
  528. cf = mc.GetApp().GetLocalConfig()
  529. if cf.GetValue('calendar'):
  530. active = 0
  531. else:
  532. active = int(cf.GetValue('calendar'))
  533. self.setMonth((active - 1))
  534. except Exception, e:
  535. return self.raiseError(log='prevmonth', error=e)
  536.  
  537.  
  538.  
  539. def mediaServiceResponseCodes(self, id = -9999):
  540. if (id == -1000):
  541. return 'The requested media is not currently available.'
  542. elif (id == -2000):
  543. return 'invalid app account/partner'
  544. elif (id == -2500):
  545. return 'system error determining blackouts'
  546. elif (id == -3500):
  547. return 'too many active sessions/devices, this account is temporarily locked.'
  548. elif (id == -4000):
  549. return 'general system error'
  550. elif (id == -9999):
  551. return 'an unknown error'
  552. elif (id != -3000):
  553. return 'an unknown error'
  554. elif (id == -3000):
  555. return 'authentication key expired. please log in again to refresh.'
  556.  
  557.  
  558.  
  559. def queryMediaService(self, media_request, isAudio = False):
  560. try:
  561. http = mc.Http()
  562. cf = mc.GetApp().GetLocalConfig()
  563. rand_number = str(random.randint(10000, 100000000))
  564. if http.Get((('http://mlbglobal08.112.2o7.net/b/ss/mlbglobal08/1/H.19--NS/' + rand_number) + '?ch=Media&pageName=BOXEE%20Request&c1=BOXEE')):
  565. http.Reset()
  566. media_service = http.Get(media_request)
  567. try:
  568. media_service_dom = parseString(media_service)
  569. except Exception, e:
  570. self.raiseError(log='querymediaservice', error=e)
  571. status_code = ''
  572. if (media_service and ('status-code' in media_service)):
  573. status_code = int(media_service_dom.getElementsByTagName('status-code')[0].firstChild.data)
  574. if ('notAuthorizedStatus' in media_service):
  575. return {'playlist_url': -2,
  576. 'media_state': ''}
  577. if (('<url>' in media_service) and media_service_dom.getElementsByTagName('url')[0].firstChild):
  578. base_encoded_string = media_service_dom.getElementsByTagName('url')[0].firstChild.data
  579. media_state = ''
  580. if ('<state>' in media_service):
  581. media_state = media_service_dom.getElementsByTagName('state')[0].firstChild.data
  582. content_id = ''
  583. if ('<content-id>' in media_service):
  584. content_id = media_service_dom.getElementsByTagName('content-id')[0].firstChild.data
  585. event_id = ''
  586. if ('<event-id>' in media_service):
  587. event_id = media_service_dom.getElementsByTagName('event-id')[0].firstChild.data
  588. if ('domain-attribute' in media_service):
  589. domain_attributes = {}
  590. for element in media_service_dom.getElementsByTagName('domain-attribute'):
  591. domain_attributes[element.attributes['name'].value] = element.firstChild.data
  592.  
  593. game_id = domain_attributes['game_id']
  594. startDate = '0'
  595. innings_index = ''
  596. if (('innings-index' in media_service) and media_service_dom.getElementsByTagName('innings-index')[0].firstChild):
  597. innings_index = media_service_dom.getElementsByTagName('innings-index')[0].firstChild.data
  598. self.info('innings_index', innings_index)
  599. if innings_index.startswith('http'):
  600. innings_xml = mc.Http().Get(str(innings_index))
  601. if innings_xml:
  602. startDate = '0'
  603. else:
  604. innings_dom = parseString(innings_xml)
  605. if ('start_timecode' in innings_xml):
  606. startDate = innings_dom.getElementsByTagName('game')[0].attributes['start_timecode'].value
  607. else:
  608. startDate = '0'
  609. if ('<session-key>' in media_service):
  610. old_session = cf.GetValue('sessionid')
  611. for element in media_service_dom.getElementsByTagName('session-key'):
  612. session_key = element.firstChild.data
  613.  
  614. cf.SetValue('sessionid', str(session_key))
  615. if ((not isAudio) and ((not base_encoded_string.startswith('http://')) and (not base_encoded_string.startswith('rtmp://')))):
  616. request_params = base64.b64decode(base_encoded_string).split('|')
  617. (stream_url, stream_fingerprint, stream_params,) = request_params
  618. rand_number = str(random.randint(10000, 100000000))
  619. bx_ourl = ('http://mlb.mlb.com/media/player/entry.jsp?calendar_event_id=%s&source=boxeeRef' % event_id)
  620. tracking_url = (((('http://mlbglobal08.112.2o7.net/b/ss/mlbglobal08/1/G.5--NS/' + rand_number) + '?ch=Media&pageName=BOXEE%20Media%20Return&c25=') + content_id) + '%7CHTTP%5FCLOUD%5FWIRED&c27=Media%20Player&c43=BOXEE')
  621. params = {'stream-fingerprint': stream_fingerprint,
  622. 'tracking-url': tracking_url,
  623. 'startDate': startDate,
  624. 'stream-params': stream_params}
  625. playlist_url = ('playlist://%s?%s' % (urllib.quote_plus(stream_url),
  626. urllib.urlencode(params)))
  627. data = {'playlist_url': playlist_url,
  628. 'media_state': media_state,
  629. 'game_id': str(game_id),
  630. 'innings_index': str(innings_index)}
  631. return data
  632. except Exception, e:
  633. self.raiseError(log='querymediaservice', error=e)
  634. self.log.exception('Error from querymedia service:')
  635. return {'playlist_url': False,
  636. 'media_state': ''}
  637.  
  638.  
  639.  
  640. def parseAudioStreams(self, item):
  641. try:
  642. play_audio = False
  643. audio_string = item.GetProperty('audio-string')
  644. audio_items = audio_string.split('||')
  645. if (len(audio_items) == 0):
  646. return self.raiseError('Unable to located proper audio items. This may be an error, please contact support@boxee.tv. We apologize for the inconvenience.', 'playitem', 'problem locating audio streams, audio-string property is empty or malformed')
  647. elif (len(audio_items) == 1):
  648. stream_1 = audio_items[0]
  649. stream_1 = stream_1.split('|')
  650. play_audio = stream_1
  651. elif (len(audio_items) > 1):
  652. stream_1 = audio_items[0]
  653. stream_2 = audio_items[1]
  654. stream_1 = stream_1.split('|')
  655. stream_2 = stream_2.split('|')
  656. confirm = mc.ShowDialogConfirm('MLB.TV', 'Please select the audio stream you wish to listen to...', stream_1[0], stream_2[0])
  657. if confirm:
  658. play_audio = stream_1
  659. else:
  660. play_audio = stream_2
  661. if (play_audio or self.raiseError()):
  662. return False
  663. return play_audio
  664. except Exception, e:
  665. return self.raiseError(log='parseaudiostreams', error=e)
  666.  
  667.  
  668.  
  669. def promptQuality(self):
  670. try:
  671. cf = mc.GetApp().GetLocalConfig()
  672. q_ask = bool(cf.GetValue('ask_quality'))
  673. q_high = bool(cf.GetValue('high_quality'))
  674. q_default = bool(cf.GetValue('default_quality'))
  675. if ((not q_ask) and ((not q_high) and (not q_default))):
  676. q_ask = True
  677. cf.SetValue('ask_quality', '1')
  678. if q_ask:
  679. q_message = 'Please select your video quality (manage this and other options in the settings tab):'
  680. quality = mc.ShowDialogConfirm('MLB.TV', q_message, 'Normal', 'High')
  681. quality = int(quality)
  682. elif q_high:
  683. quality = 1
  684. else:
  685. quality = 0
  686. return str(quality)
  687. except Exception, e:
  688. return raiseError(log='promptquality', error=e)
  689.  
  690.  
  691.  
  692. def playItem(self, mlbList, forceAudioCheck = False, playFromListItem = False):
  693. if mc.ShowDialogWait():
  694. play_audio = False
  695. if playFromListItem:
  696. window = mc.GetActiveWindow()
  697. list = window.GetList(mlbList)
  698. index = list.GetFocusedItem()
  699. item = list.GetItem(index)
  700. else:
  701. item = playFromListItem
  702. session_id = 'null'
  703. cf = mc.GetApp().GetLocalConfig()
  704. if cf.GetValue('sessionid'):
  705. session_id = cf.GetValue('sessionid')
  706. if self.isLoggedIn():
  707. return self.raiseError('You must first log in before you can watch this game.')
  708. video_request_type = 'HTTP_CLOUD_WIRED'
  709. audio_request_type = 'AUDIO_SHOUTCAST_32K'
  710. audio_set_shout_protocol = False
  711. simulate_blackout = False
  712. simulate_not_authorized = False
  713. params = {'subject': 'LIVE_EVENT_COVERAGE',
  714. 'playbackScenario': video_request_type,
  715. 'eventId': item.GetProperty('event-id'),
  716. 'contentId': item.GetProperty('content-id'),
  717. 'sessionKey': session_id,
  718. 'fingerprint': cf.GetValue('fprt'),
  719. 'identityPointId': cf.GetValue('ipid'),
  720. 'platform': 'BOXEE'}
  721. web_url = ('http://mlb.mlb.com/media/player/entry.jsp?calendar_event_id=%s&source=boxeeRef' % item.GetProperty('event-id'))
  722. media_request = self.underscore((self.mr_url % urllib.urlencode(params)))
  723. if simulate_blackout:
  724. playlist_url = -1
  725. elif simulate_not_authorized:
  726. playlist_url = -2
  727. else:
  728. media_data = self.queryMediaService(media_request)
  729. playlist_url = media_data['playlist_url']
  730. update_media_state = media_data['media_state']
  731. if (bool(update_media_state) and (str(update_media_state).lower() != item.GetProperty('media-state').lower())):
  732. self.info('playitem', ('updating media_state (%s)' % update_media_state.lower()))
  733. item.SetProperty('media-state', update_media_state.lower())
  734. if (playlist_url == -3000):
  735. check_auth = self.authenticate()
  736. if (check_auth == self.status_valid):
  737. media_data = self.queryMediaService(media_request)
  738. playlist_url = media_data['playlist_url']
  739. update_media_state = media_data['media_state']
  740. if (bool(update_media_state) and (str(update_media_state).lower() != item.GetProperty('media-state').lower())):
  741. self.info('playitem', ('updating media_state (%s)' % update_media_state.lower()))
  742. item.SetProperty('media-state', update_media_state.lower())
  743. else:
  744. self.raiseError('Unable to validate your account. Please make sure your mlb.tv account is linked with Boxee! See boxee.tv/services.', 'playitem', 'lost users login credentials')
  745. mc.HideDialogWait()
  746. return False
  747. if ((playlist_url == -1) and ((not item.GetProperty('audio-string')) and (item.GetProperty('media-state') != 'media_on'))):
  748. return self.raiseError('No available audio streams found for this game. We apologize for the inconvenience.')
  749. confirm = mc.ShowDialogConfirm('MLB.TV', 'Video is not currently available for this game. Would you like to listen to the live audio broadcast?', 'No', 'Yes')
  750. if confirm:
  751. play_audio = self.parseAudioStreams(item)
  752. if play_audio:
  753. return False
  754. params = {'subject': 'LIVE_EVENT_COVERAGE',
  755. 'playbackScenario': audio_request_type,
  756. 'eventId': item.GetProperty('event-id'),
  757. 'contentId': play_audio[1],
  758. 'sessionKey': session_id,
  759. 'fingerprint': cf.GetValue('fprt'),
  760. 'identityPointId': cf.GetValue('ipid'),
  761. 'platform': 'BOXEE'}
  762. del params['platform']
  763. media_request = self.underscore((self.mr_url % urllib.urlencode(params)))
  764. media_data = self.queryMediaService(media_request)
  765. playlist_url = media_data['playlist_url']
  766. update_media_state = media_data['media_state']
  767. if (bool(update_media_state) and (str(update_media_state).lower() != item.GetProperty('media-state').lower())):
  768. self.info('playitem', ('updating media_state (%s)' % update_media_state.lower()))
  769. item.SetProperty('media-state', update_media_state.lower())
  770. else:
  771. mc.HideDialogWait()
  772. return False
  773. if ((playlist_url == -2) and mc.GetActiveWindow().ClearStateStack()):
  774. return self.raiseError('You must own MLB.TV to watch live baseball. Please go to mlb.com/boxee to sign up.')
  775. if play_audio:
  776. content_type = 'audio/mpeg'
  777. stream_type = mc.ListItem.MEDIA_AUDIO_OTHER
  778. playlist_url = playlist_url.replace('http://', 'shout://')
  779. live = 0
  780. else:
  781. live = 0
  782. playlist_url = (playlist_url + ('&quality=%s' % self.promptQuality()))
  783. if (item.GetProperty('media-state') == 'media_on'):
  784. confirm = mc.ShowDialogConfirm('MLB.TV', 'Would you like to watch this game from the start or jump into the live broadcast?', 'Start', 'Live')
  785. live = int(confirm)
  786. item.SetProperty('IsLiveStream', str(live))
  787. playlist_url = ((playlist_url + '&live=') + str(live))
  788. content_type = 'application/vnd.apple.mpegurl'
  789. stream_type = mc.ListItem.MEDIA_VIDEO_OTHER
  790. alt_label = item.GetProperty('alt-label')
  791. title = alt_label.replace('#', '').replace('@mlbtv', '')
  792. title = (title.replace(' v ', ' @ ') + ' on MLB.TV')
  793. playlist_url = ((playlist_url + '&bx-ourl=') + urllib.quote_plus(web_url))
  794. ext = mc.ListItem(stream_type)
  795. ext.SetTitle(alt_label)
  796. ext.SetLabel(title)
  797. ext.SetDescription(item.GetDescription(), False)
  798. ext.SetContentType(content_type)
  799. ext.SetThumbnail(item.GetThumbnail())
  800. ext.SetProviderSource('MLB.TV')
  801. params = {'title': title,
  802. 'alt-label': alt_label,
  803. 'event-id': item.GetProperty('event-id'),
  804. 'content-id': item.GetProperty('content-id'),
  805. 'description': item.GetDescription(),
  806. 'bx-ourl': web_url,
  807. 'thumbnail': item.GetThumbnail(),
  808. 'audio-stream': play_audio,
  809. 'media-state': item.GetProperty('media-state')}
  810. if play_audio:
  811. params['audio-string'] = item.GetProperty('audio-string')
  812. rand_number = str(random.randint(10000, 100000000))
  813. tracking_url = (((((('http://mlbglobal08.112.2o7.net/b/ss/mlbglobal08/1/G.5--NS/' + rand_number) + '?ch=Media&pageName=BOXEE%20Media%20Return&c25=') + str(play_audio[2])) + '%7C') + self.underscore(audio_request_type)) + '&c27=Media%20Player&c43=BOXEE')
  814. notify = mc.Http().Get(tracking_url)
  815. del notify
  816. ext.SetPath(('app://mlb/launch?%s' % urllib.urlencode(params)))
  817. new_item = mc.ListItem(stream_type)
  818. new_item.SetLabel(title)
  819. new_item.SetTitle(alt_label)
  820. new_item.SetDescription(item.GetDescription(), False)
  821. new_item.SetPath(str(playlist_url))
  822. new_item.SetProviderSource('MLB.TV')
  823. new_item.SetContentType(content_type)
  824. if (media_data['game_id'] and new_item.SetProperty('game_id', media_data['game_id'])):
  825. pass
  826. new_item.SetProperty('live', str(live))
  827. new_item.SetThumbnail(item.GetThumbnail())
  828. if (play_audio and new_item.SetAddToHistory(False)):
  829. new_item.SetReportToServer(False)
  830. new_item.SetExternalItem(ext)
  831. mc.GetActiveWindow().ClearStateStack()
  832. myJobManager = MLBJobManager(media_data['game_id'])
  833. try:
  834. track_label = self.generateTrackerGameString(item)
  835. if (track_label and self.myTracker.trackEvent('Video', 'Play', track_label)):
  836. pass
  837. except:
  838. self.myTracker.trackEvent('Video', 'Play', title)
  839. myJobManager.addJob(MessageChecker(media_data['game_id']))
  840. myJobManager.start()
  841. mc.HideDialogWait()
  842. mc.GetPlayer().Play(new_item)
  843.  
  844.  
  845.  
  846. def generateTrackerGameString(self, item):
  847. try:
  848. desc = item.GetDescription()
  849. event = item.GetProperty('event-id')
  850. desc = desc.split('[CR]')[0]
  851. event = event.split('-')[-3:]
  852. title = desc.replace(')', ((' ' + '-'.join(event)) + ')'))
  853. self.info('generateTrackerGameString', title)
  854. return title
  855. except:
  856. return False
  857.  
  858.  
  859.  
  860. def playList(self, mlbList):
  861. try:
  862. cf = mc.GetApp().GetLocalConfig()
  863. list = mc.GetActiveWindow().GetList(mlbList)
  864. item = list.GetItem(list.GetFocusedItem())
  865. if (self.isLoggedIn() or mc.ShowDialogNotification('You must first log in before you can watch this game.', 'mlb-icon.png')):
  866. pass
  867. except Exception, e:
  868. self.error('playlist', e)
  869. mc.ShowDialogNotification('Sorry, we are currently unable to play this game.', 'mlb-icon.png')
  870. return False
  871.  
  872.  
  873.  
  874. def updateArchiveSpoiler(self):
  875. try:
  876. mc.ShowDialogWait()
  877. if self.isLoggedIn():
  878. response = self.callService('showhide')
  879. if ((response == 'T') and mc.GetApp().GetLocalConfig().SetValue('hide_scores', 'true')):
  880. pass
  881. else:
  882. mc.GetApp().GetLocalConfig().Reset('hide_scores')
  883. mc.HideDialogWait()
  884. except Exception, e:
  885. mc.GetApp().GetLocalConfig().Reset('hide_scores')
  886. return self.raiseError(log='updatearchivespoiler', error=e)
  887.  
  888.  
  889.  
  890. def saveArchiveSpoiler(self, value):
  891. try:
  892. mc.ShowDialogWait()
  893. if self.isLoggedIn():
  894. response = self.callService('showhidesave', {'value': value})
  895. if ((response == '1') and mc.ShowDialogNotification('Score spoiler settings saved successfully!', 'mlb-icon.png')):
  896. pass
  897. else:
  898. mc.ShowDialogNotification('You must be logged in to modify settings.', 'mlb-icon.png')
  899. mc.HideDialogWait()
  900. except Exception, e:
  901. return self.raiseError(log='savearchivespoiler', error=e)
  902.  
  903.  
  904.  
  905. def favoriteTeams(self):
  906. try:
  907. mc.ShowDialogWait()
  908. data = self.getJson('http://mlb.mlb.com/lookup/json/named.team_all.bam?sport_code=%27mlb%27&active_sw=%27Y%27&all_star_sw=%27N%27')
  909. data = data.get('team_all').get('queryResults').get('row')
  910. teamList = mc.ListItems()
  911. for team in data:
  912. item = mc.ListItem(mc.ListItem.MEDIA_UNKNOWN)
  913. item.SetLabel(str(team.get('name_display_full')))
  914. item.SetThumbnail(('http://mlb.mlb.com/images/logos/200x200/200x200_%s.png' % str(team.get('name_abbrev'))))
  915. item.SetProperty('team-id', str(team.get('team_id')))
  916. item.SetProperty('team-abbrev', str(team.get('team_abbrev')))
  917. teamList.append(item)
  918.  
  919. favList = []
  920. for (index, team,) in enumerate(teamList):
  921. if ((team.GetProperty('team-id') in favList) and teamList.SetSelected(index, True)):
  922. pass
  923.  
  924. mc.HideDialogWait()
  925. return teamList
  926. except Exception, e:
  927. self.raiseError(log='favoriteteams', error=e)
  928.  
  929.  
  930.  
  931. def selectFavorite(self, listId):
  932. try:
  933. found = False
  934. list = mc.GetWindow(14010).GetList(listId)
  935. itemNumber = list.GetFocusedItem()
  936. item = list.GetItem(itemNumber)
  937. selectedItems = list.GetSelected()
  938. for team in selectedItems:
  939. if (team.GetProperty('team-id') == item.GetProperty('team-id')):
  940. found = True
  941. list.SetSelected(itemNumber, False)
  942.  
  943. if (found or list.SetSelected(itemNumber, True)):
  944. pass
  945. except Exception, e:
  946. return self.raiseError(log='selectfavorite', error=e)
  947.  
  948.  
  949.  
  950. def saveFavorites(self):
  951. try:
  952. mc.ShowDialogWait()
  953. favs = []
  954. for div in range(200, 206):
  955. items = mc.GetWindow(14010).GetList(div).GetSelected()
  956. for team in items:
  957. favs.append(team.GetProperty('team-id'))
  958.  
  959.  
  960. favs = ';'.join(favs)
  961. response = self.callService('setfavorites', {'teamids': favs})
  962. if ((response == '1') and mc.ShowDialogNotification('Your favorite teams have been saved successfully.', 'mlb-icon.png')):
  963. pass
  964. mc.HideDialogWait()
  965. except Exception, e:
  966. mc.GetActiveWindow().PopState()
  967. return self.raiseError(log='savefavorites', error=e)
  968.  
  969.  
  970.  
  971. def loadFavorites(self):
  972. try:
  973. mc.ShowDialogWait()
  974. if self.isLoggedIn():
  975. response = self.callService('teams')
  976. data = json.loads(response)
  977. data = data.get('teams')
  978. division = {'200': mc.ListItems(),
  979. '201': mc.ListItems(),
  980. '202': mc.ListItems(),
  981. '203': mc.ListItems(),
  982. '204': mc.ListItems(),
  983. '205': mc.ListItems()}
  984. for team in data:
  985. item = mc.ListItem(mc.ListItem.MEDIA_UNKNOWN)
  986. item.SetLabel(str(team.get('title')))
  987. item.SetThumbnail(str(team.get('thumb')))
  988. item.SetProperty('team-id', str(team.get('team-id')))
  989. item.SetProperty('team-abbrev', str(team.get('team-abbrev')))
  990. item.SetProperty('team-leauge', str(team.get('team-leauge')))
  991. item.SetProperty('team-division', str(team.get('team-division')))
  992. item.SetProperty('team-fav', str(team.get('team-fav')))
  993. div = str(team.get('team-division'))
  994. division[div].append(item)
  995.  
  996. for div in division:
  997. mc.GetWindow(14010).GetList(int(div)).SetItems(division[div])
  998. list = mc.GetWindow(14010).GetList(int(div))
  999. for (i, v,) in enumerate(list.GetItems()):
  1000. if ((v.GetProperty('team-fav') == '1') and list.SetSelected(i, True)):
  1001. pass
  1002.  
  1003.  
  1004. else:
  1005. return self.raiseError('You must be logged in to access your favorite teams!')
  1006. mc.HideDialogWait()
  1007. except Exception, e:
  1008. import xbmc
  1009. xbmc.executebuiltin('Dialog.Close(14010)')
  1010. return self.raiseError('An error occured while accessing your favorite team settings. Are you logged in?', log='loadfavorites', error=e)
  1011.  
  1012.  
  1013.  
  1014. def getPlayers(self, game_id):
  1015. '\n Get players for game\n '
  1016. players_uri = (self.gameIdToEventURI(game_id) + 'players.xml')
  1017. xml = mc.Http().Get(players_uri)
  1018. if (xml or self.debug('playersdata', ('Could not retrieve players xml here - %s' % players_uri))):
  1019. return False
  1020.  
  1021.  
  1022.  
  1023. def parsePlayers(self, xml):
  1024. '\n Parse players from gdx xml\n '
  1025. players = {}
  1026. dom = parseString(xml)
  1027. for player in dom.getElementsByTagName('player'):
  1028. dict = {}
  1029. dict['teamname'] = player.parentNode.attributes['name'].value
  1030. dict['teamcode'] = player.parentNode.attributes['id'].value
  1031. dict['teamtype'] = player.parentNode.attributes['type'].value
  1032. for i in range(player.attributes.length):
  1033. attribute = player.attributes.item(i)
  1034. dict[attribute.name] = attribute.value
  1035.  
  1036. players[dict['id']] = dict
  1037.  
  1038. return players
  1039.  
  1040.  
  1041.  
  1042. def getGameMarkers(self, media_data):
  1043. if ((mc.App().GetLocalConfig().GetValue('chapters') == 'atbats') or (mc.App().GetLocalConfig().GetValue('chapters') == 'interesting')):
  1044. if media_data['game_id']:
  1045. self.players = self.getPlayers(media_data['game_id'])
  1046. if (mc.App().GetLocalConfig().GetValue('chapters') == 'interesting'):
  1047. events = self.getEvents(media_data['game_id'], self.players, True)
  1048. else:
  1049. events = self.getEvents(media_data['game_id'], self.players)
  1050. if events:
  1051. return self.setEvents(events)
  1052. else:
  1053. self.debug('chapters', 'Could not get game_id from media service request.')
  1054. return False
  1055.  
  1056.  
  1057.  
  1058. def setInnings(self, innings):
  1059. '\n Set innings for listitem\n '
  1060. chapters = []
  1061. for inning in innings:
  1062. chapters.append({'name': inning['inning'],
  1063. 'startTime': inning['start'],
  1064. 'duration': 0})
  1065.  
  1066. try:
  1067. chapters = self.formatChapters(chapters)
  1068. except Exception, e:
  1069. self.debug('innings', 'Encountered error formatting innings data.')
  1070. self.raiseError(log='innings', error=e)
  1071. return False
  1072. return chapters
  1073.  
  1074.  
  1075.  
  1076. def getInnings(self, innings_index):
  1077. '\n Retrieve innings for innings_index\n '
  1078. xml = mc.Http().Get(innings_index)
  1079. if (xml or self.debug('innings', ('Could not retrieve innings_index xml here - %s' % innings_index))):
  1080. return False
  1081.  
  1082.  
  1083.  
  1084. def parseInnings(self, xml):
  1085. '\n Parse innings index xml\n '
  1086. innings = []
  1087. self.debug('innings', 'Creating DOM...')
  1088. dom = parseString(xml)
  1089. for inning in dom.firstChild.childNodes:
  1090. self.debug('innings', 'Finding inning...')
  1091. dict = {}
  1092. self.debug('innings', 'Getting inning names...')
  1093. if (inning.attributes['top'].value == 'true'):
  1094. dict['inning'] = ('Top ' + self.ordinal(int(inning.attributes['inning_number'].value)))
  1095. else:
  1096. dict['inning'] = ('Bottom ' + self.ordinal(int(inning.attributes['inning_number'].value)))
  1097. self.debug('innings', 'Setting start and end times...')
  1098. time = inning.firstChild
  1099. try:
  1100. dict['start'] = time.attributes['start'].value
  1101. except:
  1102. dict['start'] = 0
  1103. try:
  1104. dict['stop'] = time.attributes['end'].value
  1105. except:
  1106. dict['stop'] = 0
  1107. self.debug('innings', 'Adding to innings...')
  1108. innings.append(dict)
  1109.  
  1110. return innings
  1111.  
  1112.  
  1113.  
  1114. def setEvents(self, events):
  1115. '\n Set Events chapters for listitem\n '
  1116. chapters = []
  1117. for event in events:
  1118. startTime = self.digitsToTimecode(str(event['start_tfs']))
  1119. if ((mc.GetApp().GetLocalConfig().GetValue('hide_scores') == 'true') and chapters.append({'name': ((((event['inning'] + ' ') + event['boxname']) + ' / ') + event['pitcher_boxname']),
  1120. 'startTime': startTime,
  1121. 'duration': 0})):
  1122. pass
  1123.  
  1124. try:
  1125. chapters = self.formatChapters(chapters)
  1126. except Exception, e:
  1127. self.debug('events', 'Encountered error formatting events data.')
  1128. self.raiseError(log='events', error=e)
  1129. return False
  1130. return chapters
  1131.  
  1132.  
  1133.  
  1134. def getEvents(self, game_id, players = None, interesting = False):
  1135. '\n Retrieve events for game_id\n '
  1136. event_uri = (self.gameIdToEventURI(game_id) + 'game_events.xml')
  1137. xml = mc.Http().Get(event_uri)
  1138. if (xml or self.debug('events', ('Could not retrieve events xml here - %s' % event_uri))):
  1139. return False
  1140.  
  1141.  
  1142.  
  1143. def parseInterestingEvents(self, events):
  1144. new_data = []
  1145. for event in events:
  1146. if ((event['event'] in self.event_filter) and new_data.append(event)):
  1147. pass
  1148.  
  1149. return new_data
  1150.  
  1151.  
  1152.  
  1153. def parseEvents(self, xml):
  1154. '\n Parse innings index xml\n '
  1155. events = []
  1156. dom = parseString(xml)
  1157. for event in dom.getElementsByTagName('atbat'):
  1158. dict = {}
  1159. dict['inning'] = ((event.parentNode.tagName.capitalize() + ' ') + self.ordinal(int(event.parentNode.parentNode.attributes['num'].value)))
  1160. for i in range(event.attributes.length):
  1161. attribute = event.attributes.item(i)
  1162. dict[attribute.name] = attribute.value
  1163.  
  1164. events.append(dict)
  1165.  
  1166. return events
  1167.  
  1168.  
  1169.  
  1170. def formatChapters(self, chapters):
  1171. '\n Accept chapters list of dictionaries and output into XML for Boxee client.\n '
  1172. xml = '<?xml version="1.0" encoding="UTF-8" ?><chapters>'
  1173. for chapter in chapters:
  1174. xml = (((((((xml + '<chapter name="') + str(chapter['name'])) + '" startTime="') + str(chapter['startTime'])) + '" duration="') + str(chapter['duration'])) + '" />')
  1175.  
  1176. xml = (xml + '</chapters>')
  1177. return xml
  1178.  
  1179.  
  1180.  
  1181. def getPitch(self, game_id):
  1182. '\n Get latest pitch\n '
  1183. event_uri = (self.gameIdToEventURI(game_id) + 'plays.xml')
  1184. xml = mc.Http().Get(event_uri)
  1185. if (xml or self.debug('pitch', ('Could not retrieve pitch xml here - %s' % event_uri))):
  1186. pass
  1187.  
  1188.  
  1189.  
  1190. def parsePitch(self, xml):
  1191. '\n Parse pitch from plays.xml\n '
  1192. pitches = []
  1193. dom = parseString(xml)
  1194. if dom.getElementsByTagName('p'):
  1195. for pitch in dom.getElementsByTagName('p'):
  1196. dict = {}
  1197. dict['pitcher'] = dom.getElementsByTagName('pitcher')[0].attributes['boxname'].value
  1198. if pitch.hasAttribute('nasty'):
  1199. dict['schema'] = 'detailed'
  1200. else:
  1201. dict['schema'] = 'small'
  1202. for i in range(pitch.attributes.length):
  1203. attribute = pitch.attributes.item(i)
  1204. dict[attribute.name] = attribute.value
  1205.  
  1206. if pitch.hasAttribute('des'):
  1207. dict['des'] = dict['des'].split(',')[0]
  1208. pitches.append(dict)
  1209.  
  1210. if pitches:
  1211. return pitches.pop()
  1212. else:
  1213. self.debug('pitches', 'No pitches found.')
  1214. return False
  1215.  
  1216.  
  1217.  
  1218. def processMessages(self):
  1219. '\n Process all the messages\n '
  1220. self.debug('messages', 'Processing messages...')
  1221. try:
  1222. self.debug('messages', 'Getting messages...')
  1223. messages = self.getMessages()
  1224. except Exception, e:
  1225. self.error('messages', 'Error getting messages.')
  1226. return False
  1227. if (messages and self.debug('messages', 'Messages found - processing')):
  1228. for message in messages:
  1229. if ((not self.messageAlreadyDisplayed(message)) and self.messageWithinWindow(message)):
  1230. try:
  1231. self.debug('messages', ('Displaying message: %s' % message['message']))
  1232. self.displayMessage(message)
  1233. except Exception, e:
  1234. self.error('messages', 'Error displaying messages.')
  1235. return False
  1236.  
  1237.  
  1238.  
  1239.  
  1240. def getMessages(self):
  1241. '\n Get message from message_uri\n '
  1242. xml = mc.Http().Get(self.message_uri)
  1243. if (xml or self.debug('messages', ('Could not retrieve message xml here - %s' % event_uri))):
  1244. pass
  1245.  
  1246.  
  1247.  
  1248. def displayMessage(self, message):
  1249. hash = md5.md5(str(message)).hexdigest()
  1250. mc.GetApp().GetLocalConfig().SetValue(hash, 'true')
  1251. if ((message['type'] == 'ok') and self.debug('messages', 'Displaying OK dialog')):
  1252. return mc.ShowDialogOk(str(message['source']), str(message['message']))
  1253.  
  1254.  
  1255.  
  1256. def messageAlreadyDisplayed(self, message):
  1257. self.debug('messages', 'Checking if message has already been displayed.')
  1258. hash = md5.md5(str(message)).hexdigest()
  1259. if (mc.GetApp().GetLocalConfig().GetValue(hash) and self.debug('messages', 'Message has already been displayed.')):
  1260. return True
  1261.  
  1262.  
  1263.  
  1264. def messageWithinWindow(self, message):
  1265. self.debug('messages', 'Checking if time is in current message window.')
  1266. now = self.getCurrentMLBTime()
  1267. self.debug('messages', ('Current MLB time is: %s.' % str(now)))
  1268. startTime = time.strptime(message['startDatetime'], '%d %b %Y %H:%M')
  1269. endTime = time.strptime(message['endDatetime'], '%d %b %Y %H:%M')
  1270. if ((now >= startTime) and (now <= endTime)):
  1271. self.debug('messages', 'Message is in window.')
  1272. return True
  1273.  
  1274.  
  1275.  
  1276. def parseMessages(self, xml):
  1277. self.debug('messages', 'Parsing messages.')
  1278. dom = parseString(xml)
  1279. if dom.getElementsByTagName('message'):
  1280. messageList = []
  1281. for message in dom.getElementsByTagName('message'):
  1282. dict = {}
  1283. for i in range(message.attributes.length):
  1284. attribute = message.attributes.item(i)
  1285. dict[attribute.name] = attribute.value
  1286.  
  1287. dict['message'] = message.firstChild.data
  1288. messageList.append(dict)
  1289.  
  1290. return messageList
  1291. else:
  1292. return False
  1293.  
  1294.  
  1295.  
  1296. class MLBJobManager(jobmanager.BoxeeJobManager):
  1297. __module__ = __name__
  1298.  
  1299. def __init__(self, game_id):
  1300. self.game_id = game_id
  1301. self.mlb = MLB()
  1302. self.videoFail = 0
  1303. jobmanager.BoxeeJobManager.__init__(self, 5)
  1304. self.log(('Setting game_id as: %s' % self.game_id))
  1305.  
  1306.  
  1307.  
  1308. def check(self):
  1309. if (self.videoFail >= 3):
  1310. return self.stop()
  1311. if (mc.GetPlayer().IsPlaying() or self.log('Video not playing - incrementing counter.')):
  1312. self.videoFail = (self.videoFail + 1)
  1313. return
  1314.  
  1315.  
  1316.  
  1317. class MLBJob(jobmanager.BoxeeJob):
  1318. __module__ = __name__
  1319.  
  1320. def __init__(self, name, game_id, interval = 10):
  1321. self.name = name
  1322. self.mlb = MLB()
  1323. self.game_id = game_id
  1324. self.hash =
  1325. self.log(('Setting game ID to: %s' % self.game_id))
  1326. self.players = self.mlb.getPlayers(self.game_id)
  1327. jobmanager.BoxeeJob.__init__(self, name, interval)
  1328.  
  1329.  
  1330.  
  1331. def xmlChanged(self, xml):
  1332. self.log('Checking if xml has changed.')
  1333. new_hash = md5.md5(str(xml)).hexdigest()
  1334. if ((new_hash == self.hash) and self.log('Xml has not changed.')):
  1335. return False
  1336.  
  1337.  
  1338.  
  1339. def getInHMS(self, seconds):
  1340. (m, s,) = divmod(seconds, 60)
  1341. (h, m,) = divmod(m, 60)
  1342. return ('%d%02d%02d' % (h,
  1343. m,
  1344. s))
  1345.  
  1346.  
  1347.  
  1348. class PitchChecker(MLBJob):
  1349. __module__ = __name__
  1350.  
  1351. def __init__(self, game_id):
  1352. self.firstRun = True
  1353. self.game_id = game_id
  1354. self.timecodeRetry = 0
  1355. self.setting = mc.GetApp().GetLocalConfig().GetValue('pitches')
  1356. self.timer = threading.Event()
  1357. self.lastPitch =
  1358. MLBJob.__init__(self, 'PitchChecker', self.game_id, 0)
  1359.  
  1360.  
  1361.  
  1362. def process(self):
  1363. xml = self.getXml()
  1364. if (self.firstRun or xml):
  1365. pitch = self.mlb.parsePitch(xml)
  1366. if (pitch and (pitch['id'] != self.lastPitch)):
  1367. if self.log('Received pitch - building message.'):
  1368. if (pitch['schema'] == 'detailed'):
  1369. pitch['string_type'] = self.mlb.pitches[pitch['pitch_type']][1]
  1370. if (self.setting == 'km/h'):
  1371. kmh = str((int(pitch['start_speed'].split('.')[0].strip()) * 1.609344))
  1372. pitch_message = str(('%s: %s - %s km/h' % (pitch['des'],
  1373. pitch['string_type'],
  1374. str(int(kmh.split('.')[0].strip())))))
  1375. elif (self.setting == 'nasty'):
  1376. pitch_message = str(('%s: %s - %s%%' % (pitch['des'],
  1377. pitch['string_type'],
  1378. pitch['nasty'])))
  1379. else:
  1380. pitch_message = str(('%s: %s - %s mph' % (pitch['des'],
  1381. pitch['string_type'],
  1382. str(int(pitch['start_speed'].split('.')[0].strip())))))
  1383. else:
  1384. pitch_message = str(('%s' % pitch['des']))
  1385. self.log(('Sending pitch to client: %s' % pitch_message))
  1386. mc.ShowDialogNotification(pitch_message, 'mlb-icon.png')
  1387. self.lastPitch = pitch['id']
  1388. self.log('Message sent.')
  1389.  
  1390.  
  1391.  
  1392. def getXml(self):
  1393. if mc.GetPlayer().IsPlaying():
  1394. rawtime = mc.GetPlayer().GetTime()
  1395. if ((rawtime == -1) and self.log('Did not get timecode from client! ')):
  1396. self.timecodeRetry = (self.timecodeRetry + 1)
  1397. if ((self.timecodeRetry <= 3) and self.timer.wait(1)):
  1398. return self.getXml()
  1399. timecode = self.getInHMS(rawtime)
  1400. timecode = str((int(timecode) + 3))
  1401. timecode = str(int((5 * round((float(int(timecode)) / 5)))))
  1402. self.log(('Timecode is %s' % timecode))
  1403. else:
  1404. return False
  1405. event_uri = ((self.mlb.gameIdToEventURI(self.game_id, True) + 'plays.xml&timecode=') + timecode)
  1406. self.log(('Fetching URI %s' % event_uri))
  1407. xml = mc.Http().Get(event_uri)
  1408. if (xml or self.log(('Could not retrieve innings xml here - %s' % self.innings_index))):
  1409. return False
  1410.  
  1411.  
  1412.  
  1413. def debugXml(self, xml):
  1414. self.log('Debug info for plays data:')
  1415. dom = parseString(xml)
  1416. boxscore = {}
  1417. for i in range(dom.getElementsByTagName('game')[0].attributes.length):
  1418. attribute = dom.getElementsByTagName('game')[0].attributes.item(i)
  1419. try:
  1420. boxscore[attribute.name] = boxscore[attribute.value]
  1421. except:
  1422. raise Exception
  1423.  
  1424. self.log(('Box: %s %s. Count: %s balls, %s strikes, %s outs.' % (boxscore['inning'],
  1425. boxscore['top_inning'],
  1426. boxscore['b'],
  1427. boxscore['s'],
  1428. boxscore['o'])))
  1429. for player in dom.getElementsByTagName('players')[0].childNodes:
  1430. dict = {}
  1431. if self.players:
  1432. for player in self.players:
  1433. dict['boxname'] = self.players[player['pid']]['boxname']
  1434.  
  1435. for i in range(player.attributes.length):
  1436. attribute = player.attributes.item(i)
  1437. dict[attribute.name] = attribute.value
  1438.  
  1439. self.log(('Data for %s: %s' % (player.tagName,
  1440. dict)))
  1441.  
  1442. return self.log('End debug info for plays data.')
  1443.  
  1444.  
  1445.  
  1446. class InningsChecker(MLBJob):
  1447. __module__ = __name__
  1448.  
  1449. def __init__(self, game_id, innings_index):
  1450. self.innings_index = innings_index
  1451. MLBJob.__init__(self, 'InningsChecker', game_id, 120)
  1452.  
  1453.  
  1454.  
  1455. def process(self):
  1456. xml = self.getXml()
  1457. if (xml and self.log('Received new xml to process.')):
  1458. self.log('Parsing xml...')
  1459. data = self.mlb.parseInnings(xml)
  1460. self.log('Formatting chapters...')
  1461. chapters = self.mlb.setInnings(data)
  1462. self.log('Sending chapters to client...')
  1463. return mc.GetApp().SendMessage('mlb:chapters', chapters)
  1464.  
  1465.  
  1466.  
  1467. def getXml(self):
  1468. xml = mc.Http().Get(self.innings_index)
  1469. if (xml or self.log(('Could not retrieve innings xml here - %s' % self.innings_index))):
  1470. return False
  1471.  
  1472.  
  1473.  
  1474. class EventChecker(MLBJob):
  1475. __module__ = __name__
  1476.  
  1477. def __init__(self, game_id):
  1478. MLBJob.__init__(self, 'EventsChecker', game_id, 60)
  1479.  
  1480.  
  1481.  
  1482. def process(self):
  1483. xml = self.getXml()
  1484. if (xml and self.log('Received new xml to process.')):
  1485. self.log('Parsing xml...')
  1486. data = self.mlb.parseEvents(xml)
  1487. if ((mc.App().GetLocalConfig().GetValue('chapters') == 'interesting') and self.log('Getting only big plays')):
  1488. data = self.mlb.parseInterestingEvents(data)
  1489. if (self.players and self.log('Matching up player box names to player ids...')):
  1490. for event in data:
  1491. event['boxname'] = self.players[event['batter']]['boxname']
  1492.  
  1493. if (data and self.log('Formatting into chapters...')):
  1494. chapters = self.mlb.setEvents(data)
  1495. self.log('Updating client with new events.')
  1496. return mc.GetApp().SendMessage('mlb:chapters', chapters)
  1497. return False
  1498.  
  1499.  
  1500.  
  1501. def getXml(self):
  1502. event_uri = (self.mlb.gameIdToEventURI(self.game_id) + 'game_events.xml')
  1503. xml = mc.Http().Get(event_uri)
  1504. if (xml or self.log(('Could not retrieve events xml here - %s' % event_uri))):
  1505. return False
  1506.  
  1507.  
  1508.  
  1509. class MessageChecker(MLBJob):
  1510. __module__ = __name__
  1511.  
  1512. def __init__(self, game_id):
  1513. MLBJob.__init__(self, 'MessageChecker', game_id, 900)
  1514.  
  1515.  
  1516.  
  1517. def process(self):
  1518. self.log('Processing messages...')
  1519. self.mlb.processMessages()
  1520. self.log('Done processing messages.')
Add Comment
Please, Sign In to add comment