def __init__(self, *args, **kwargs): # maybe the answer for nexdef for basic subscribers self.use_wired_web = kwargs.get('use_wired_web') ymd_tuple = kwargs.get('ymd_tuple') time_shift = kwargs.get('time_shift') self.international = kwargs.get('international') self.cfg = kwargs.get('cfg') # Default to today if not ymd_tuple: now = datetime.datetime.now() dif = datetime.timedelta(1) # Now, we want the day to go until, say, 9 am the next # morning. This needs to be worked out, still... if now.hour < 9: now = now - dif ymd_tuple = (now.year, now.month, now.day) self.year = ymd_tuple[0] self.month = ymd_tuple[1] self.day = ymd_tuple[2] self.shift = time_shift self.http = MLBHttp(accept_gzip=True) self.grid = "http://gdx.mlb.com/components/game/mlb/year_"\ + padstr(self.year)\ + "/month_" + padstr(self.month)\ + "/day_" + padstr(self.day) + "/grid.xml" self.multiangle = "http://gdx.mlb.com/components/game/mlb/year_"\ + padstr(self.year)\ + "/month_" + padstr(self.month)\ + "/day_" + padstr(self.day) + "/multi_angle_epg.xml" self.log = MLBLog(LOGFILE) self.data = [] self.error_str = "Something went wrong. A more descriptive error should be here."
def __init__(self, user, passwd, debug=False): self.user = user if self.user is None: # if user= is commented out, cfg.get() returns None, normalize this self.user = "" self.passwd = passwd self.auth = True self.logged_in = None self.cookie_jar = None self.cookies = {} self.debug = debug if COOKIE_DEBUG: self.debug = True self.log = MLBLog(LOGFILE) try: self.session_key = self.readSessionKey() if self.debug: self.log.write("LOGIN> Read session key from file: " + str(self.session_key)) except: self.session_key = None
def __init__(self, user, passwd, debug=False): self.user = user if self.user is None: # if user= is commented out, cfg.get() returns None, normalize this self.user = "" self.passwd = passwd self.auth = True self.logged_in = None self.cookie_jar = None self.cookies = {} self.debug = debug if COOKIE_DEBUG: self.debug = True self.log = MLBLog(LOGFILE) self.log.write('MLBSession BEGIN') try: self.session_key = self.readSessionKey() self.log.write('init() session-key : ' + self.session_key) except: #raise self.log.write('init() session-key : None') self.session_key = None
def __init__(self,user,passwd,debug=False): self.user = user if self.user is None: # if user= is commented out, cfg.get() returns None, normalize this self.user = "" self.passwd = passwd self.auth = True self.logged_in = None self.cookie_jar = None self.cookies = {} self.debug = debug if COOKIE_DEBUG: self.debug = True self.log = MLBLog(LOGFILE) try: self.session_key = self.readSessionKey() if self.debug: self.log.write("LOGIN> Read session key from file: " + str(self.session_key)) except: self.session_key = None
def __init__(self,user,passwd,debug=False): self.user = user if self.user is None: # if user= is commented out, cfg.get() returns None, normalize this self.user = "" self.passwd = passwd self.auth = True self.logged_in = None self.cookie_jar = None self.cookies = {} self.debug = debug if COOKIE_DEBUG: self.debug = True self.log = MLBLog(LOGFILE) self.log.write('MLBSession BEGIN') try: self.session_key = self.readSessionKey() self.log.write('init() session-key : ' + self.session_key) except: #raise self.log.write('init() session-key : None') self.session_key = None
class MiLBMediaStream(MediaStream): def __init__(self, stream, session, cfg, coverage=None, streamtype='video', start_time=0): # Initialize basic object from instance variables self.stream = stream self.session = session self.cfg = cfg if coverage == None: self.coverage = 0 else: self.coverage = coverage self.start_time = start_time self.streamtype = streamtype # Need a few config items self.use_librtmp = self.cfg.get('use_librtmp') self.speed = self.cfg.get('speed') # milbtv is one flavor: vanilla. Not even French vanilla. self.use_nexdef = False self.streamtype = 'video' # Install the cookie received from MLBLogin and used for subsequent # media requests. This part should resolve the issue of login # restriction errors when each MediaStream request was its own login/ # logout sequence. try: opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.session.cookie_jar)) urllib2.install_opener(opener) except: raise self.log = MLBLog(LOGFILE) self.error_str = "What happened here?\nPlease enable debug with the d key and try your request again." # Break the stream argument into its components used for media location # requests. try: ( self.call_letters, self.team_id, self.content_id, self.event_id ) = self.stream except: self.error_str = "No stream available for selected game." self.log.write(str(datetime.datetime.now()) + '\n') self.session_key = None self.debug = cfg.get('debug') # The request format depends on the streamtype self.scenario = 'FLASH_1000K_640X360' self.subject = 'LIVE_EVENT_COVERAGE' # Media response needs to be parsed into components below. self.auth_chunk = None self.play_path = None self.tc_url = None self.app = None self.rtmp_url = None self.rtmp_host = None self.rtmp_port = None self.sub_path = None # TODO: Has this findUserVerifiedEvent been updated? Does this # url need to be changed to reflect that? self.base_url='http://www.milb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3?' def createMediaRequest(self,stream): if stream == None: self.error_str = "No event-id present to create media request." raise try: sessionKey = urllib.unquote(self.session.cookies['ftmu']) except: sessionKey = None # Query values query_values = { 'contentId': self.content_id, 'sessionKey': sessionKey, 'fingerprint': urllib.unquote(self.session.cookies['fprt']), 'identityPointId': self.session.cookies['ipid'], 'playbackScenario': self.scenario, 'subject': self.subject } # Build query url = self.base_url + urllib.urlencode(query_values) # And make the request req = urllib2.Request(url) response = urllib2.urlopen(req) reply = xml.dom.minidom.parse(response) return reply def locateMedia(self): game_url = None # 1. Make initial media request -- receive a reply with available media # 2. Update the session with current cookie values/session key. # 3. Get the content_list that matches the requested stream # 4. Strip out blacked out content or content that is not authorized. #reply = self.createMediaRequest(self.stream) #content_list = self.parseMediaReply(reply) game_url = self.requestSpecificMedia() #self.updateSession(reply) return game_url def updateSession(self,reply): try: self.session_key = reply.getElementsByTagName('session-key')[0].childNodes[0].data self.session_keys['ftmu'] = self.session_key self.session.writeSessionKey(self.session_key) except: pass def parseMediaReply(self,reply): # If status is not successful, make it easier to determine why status_code = str(reply.getElementsByTagName('status-code')[0].childNodes[0].data) if status_code != "1": self.log.write("UNSUCCESSFUL MEDIA REQUEST: status-code: %s , event-id = %s\n" % (status_code , self.event_id)) self.log.write("See %s for XML response.\n"%ERRORLOG_1) err1 = open(ERRORLOG_1, 'w') reply.writexml(err1) err1.close() self.error_str = SOAPCODES[status_code] raise Exception,self.error_str else: self.log.write("SUCCESSFUL MEDIA REQUEST: status-code: %s , event-id = %s\n" % (status_code , self.event_id)) self.log.write("See %s for XML response.\n"%MEDIALOG_1) med1 = open(MEDIALOG_1,'w') reply.writexml(med1) med1.close() # determine blackout status #self.determineBlackoutStatus(reply) # and now the meat of the parsing... content_list = [] for content in reply.getElementsByTagName('user-verified-content'): type = content.getElementsByTagName('type')[0].childNodes[0].data if type != self.streamtype: continue content_id = content.getElementsByTagName('content-id')[0].childNodes[0].data if content_id != self.content_id: continue # First, collect all the domain-attributes dict = {} for node in content.getElementsByTagName('domain-attribute'): name = str(node.getAttribute('name')) value = node.childNodes[0].data dict[name] = value # There are a series of checks to trim the content list # 1. Trim out 'in-market' listings like Yankees On Yes if dict.has_key('coverage_type'): if 'in-market' in dict['coverage_type']: continue # 2. Trim out all non-English language broadcasts if dict.has_key('language'): if dict['language'] != 'EN': continue # 3. For post-season, trim out multi-angle listings if self.cfg.get('postseason'): if dict['in_epg'] != 'mlb_multiangle_epg': continue else: if dict['in_epg'] == 'mlb_multiangle_epg': continue # 4. Get coverage association and call_letters try: cov_pat = re.compile(r'([0-9][0-9]*)') coverage = re.search(cov_pat, dict['coverage_association']).groups()[0] except: coverage = None try: call_letters = dict['call_letters'] except: if self.cfg.get('postseason') == False: raise Exception,repr(dict) else: call_letters = 'MiLB' for media in content.getElementsByTagName('user-verified-media-item'): state = media.getElementsByTagName('state')[0].childNodes[0].data scenario = media.getElementsByTagName('playback-scenario')[0].childNodes[0].data if scenario == self.scenario and \ state in ('MEDIA_ARCHIVE', 'MEDIA_ON', 'MEDIA_OFF'): content_list.append( ( call_letters, coverage, content_id, self.event_id ) ) return content_list def determineBlackoutStatus(self,reply): # Determine the blackout status try: blackout_status = reply.getElementsByTagName('blackout')[0].childNodes[0].data except: blackout_status = reply.getElementsByTagName('blackout-status')[0] try: success_status = blackout_status.getElementsByTagName('successStatus') blackout_status = None except: try: location_status = blackout_status.getElementsByTagName('locationCannotBeDeterminedStatus') except: blackout_status = 'LOCATION CANNOT BE DETERMINED.' media_type = reply.getElementsByTagName('type')[0].childNodes[0].data media_state = reply.getElementsByTagName('state')[0].childNodes[0].data self.media_state = media_state if blackout_status is not None and self.streamtype == 'video': inmarket_pat = re.compile(r'INMARKET') if re.search(inmarket_pat,blackout_status) is not None: pass elif media_state == 'MEDIA_ON' and not self.postseason: self.log.write('MEDIA STREAM BLACKOUT. See %s for XML response.' % BLACKFILE) self.error_str = 'BLACKOUT: ' + str(blackout_status) bf = open(BLACKFILE, 'w') reply.writexml(bf) bf.close() raise Exception,self.error_str def selectCoverage(self,content_list): # now iterate over the content_list with the following rules: # 1. if coverage association is zero, use it (likely a national broadcast) # 2. if preferred coverage is available use it # 3. if coverage association is non-zero and preferred not available, then what? for content in content_list: ( call_letters, coverage, content_id , event_id ) = content if coverage == '0': self.content_id = content_id self.call_letters = call_letters elif coverage == self.coverage: self.content_id = content_id self.call_letters = call_letters # if we preferred coverage and national coverage not available, # select any coverage available if self.content_id is None: try: ( call_letters, coverage, content_id, event_id ) = content_list[0] self.content_id = content_id self.call_letters = call_letters except: self.content_id = None self.call_letters = None if self.content_id is None: self.error_str = "Requested stream is not available." self.error_str += "\n\nRequested coverage association: " + str(self.coverage) self.error_str += "\n\nAvailable content list = \n" + repr(content_list) raise Exception,self.error_str if self.debug: self.log.write("DEBUG>> writing soap response\n") self.log.write(repr(reply) + '\n') if self.content_id is None: self.error_str = "Requested stream is not yet available." raise Exception,self.error_str if self.debug: self.log.write("DEBUG>> soap event-id:" + str(self.stream) + '\n') self.log.write("DEBUG>> soap content-id:" + str(self.content_id) + '\n') def requestSpecificMedia(self): query_values = { 'subject': self.subject, 'identityPointId': self.session.cookies['ipid'], 'contentId': self.content_id, 'playbackScenario': self.scenario, 'fingerprint': urllib.unquote(self.session.cookies['fprt']) } try: sessionkey = urllib.unquote(self.session.cookies['ftmu']) query_values['sessionKey'] = sessionkey except: sessionkey = None url = self.base_url + urllib.urlencode(query_values) req = urllib2.Request(url) response = urllib2.urlopen(req) reply = parse(response) status_code = str(reply.getElementsByTagName('status-code')[0].childNodes[0].data) if status_code != "1": # candidate for new procedure: this code block of writing # unsuccessful xml responses is being repeated... self.log.write("DEBUG (SOAPCODES!=1)>> writing unsuccessful soap response event_id = " + str(self.event_id) + " contend-id = " + self.content_id + "\n") df = open('/tmp/unsuccessful.xml','w') reply.writexml(df) df.close() df = open('/tmp/unsuccessful.xml') msg = df.read() df.close() self.error_str = SOAPCODES[status_code] raise Exception,self.error_str try: self.session_key = reply.getElementsByTagName('session-key')[0].childNodes[0].data self.session.cookies['ftmu'] = self.session_key self.session.writeSessionKey(self.session_key) except: #raise self.session_key = None try: game_url = reply.getElementsByTagName('url')[0].childNodes[0].data except: self.error_str = "Stream URL not found in reply. Stream may not be available yet." df = open(ERRORLOG_2,'w') reply.writexml(df) df.close() raise Exception,self.error_str else: df = open(MEDIALOG_2,'w') reply.writexml(df) df.close() self.log.write("DEBUG>> URL received: " + game_url + '\n') return game_url def parseFmsCloudResponse(self,url): auth_pat = re.compile(r'auth=(.*)') self.auth_chunk = '?auth=' + re.search(auth_pat,url).groups()[0] out = '' req = urllib2.Request(url) handle = urllib2.urlopen(req) rsp = parse(handle) rtmp_base = rsp.getElementsByTagName('meta')[0].getAttribute('base') for elem in rsp.getElementsByTagName('video'): speed = int(elem.getAttribute('system-bitrate'))/1000 if int(self.speed) == int(speed): vid_src = elem.getAttribute('src').replace('mp4:','/') out = rtmp_base + vid_src return out def prepareMediaStreamer(self,game_url): #if self.streamtype in ( 'video', ): # game_url = self.parseFmsCloudResponse(game_url) return self.prepareFmsUrl(game_url) # finally some url processing routines def prepareFmsUrl(self,game_url): try: #play_path_pat = re.compile(r'ondemand\/(.*)\?') play_path_pat = re.compile(r'ondemand\/(.*)$') self.play_path = re.search(play_path_pat,game_url).groups()[0] app_pat = re.compile(r'ondemand\/(.*)\?(.*)$') querystring = re.search(app_pat,game_url).groups()[1] self.app = "ondemand?_fcs_vhost=cp118053.edgefcs.net&akmfv=1.6" + querystring # not sure if we need this try: req = urllib2.Request('http://cp118053.edgefcs.net/fcs/ident') page = urllib2.urlopen(req) fp = parse(page) ip = fp.getElementsByTagName('ip')[0].childNodes[0].data self.tc_url = 'http://' + str(ip) + ':1935/' + self.app except: self.tc_url = None except: self.play_path = None try: live_pat = re.compile(r'live\/milb') if re.search(live_pat,game_url): if self.streamtype == 'audio': auth_pat = re.compile(r'auth=(.*)') self.auth_chunk = '?auth=' + re.search(auth_pat,game_url).groups()[0] live_sub_pat = re.compile(r'live\/mlb_audio(.*)\?') self.sub_path = re.search(live_sub_pat,game_url).groups()[0] self.sub_path = 'mlb_audio' + self.sub_path live_play_pat = re.compile(r'live\/mlb_audio(.*)$') self.play_path = re.search(live_play_pat,game_url).groups()[0] self.play_path = 'mlb_audio' + self.play_path app_auth = self.auth_chunk.replace('?','&') self.app = "live?_fcs_vhost=cp153281.live.edgefcs.net&akmfv=1.6&aifp=v0006" + app_auth else: try: live_sub_pat = re.compile(r'live\/milb_encap_rm(.*)') self.sub_path = re.search(live_sub_pat,game_url).groups()[0] self.sub_path = 'milb_encap_rm' + self.sub_path except Exception,detail: self.error_str = 'Could not parse the stream subscribe path: ' + str(detail) raise Exception,self.error_str try: live_path_pat = re.compile(r'live\/milb_encap_rm(.*)$') self.play_path = re.search(live_path_pat,game_url).groups()[0] self.play_path = 'milb_encap_rm' + self.play_path except Exception,detail: self.error_str = 'Could not parse the stream play path: ' + str(detail) raise Exception,self.error_str sec_pat = re.compile(r'mlbsecurelive') if re.search(sec_pat,game_url) is not None: self.app = 'mlbsecurelive-live' else: self.app = 'live?_fcs_vhost=cp118053.live.edgefcs.net&akmfv=1.6' if self.debug: self.log.write("DEBUG>> sub_path = " + str(self.sub_path) + "\n") self.log.write("DEBUG>> play_path = " + str(self.play_path) + "\n") self.log.write("DEBUG>> app = " + str(self.app) + "\n")
class MLBSession: def __init__(self,user,passwd,debug=False): self.user = user if self.user is None: # if user= is commented out, cfg.get() returns None, normalize this self.user = "" self.passwd = passwd self.auth = True self.logged_in = None self.cookie_jar = None self.cookies = {} self.debug = debug if COOKIE_DEBUG: self.debug = True self.log = MLBLog(LOGFILE) self.log.write('MLBSession BEGIN') try: self.session_key = self.readSessionKey() self.log.write('init() session-key : ' + self.session_key) except: #raise self.log.write('init() session-key : None') self.session_key = None def readSessionKey(self): sk = open(SESSIONKEY,"r") self.session_key = sk.read() sk.close() return self.session_key def writeSessionKey(self,session_key): self.session_key = session_key self.log.write('writeSessionKey(): ' + str(self.session_key)) sk = open(SESSIONKEY,"w") sk.write(self.session_key) sk.close() return self.session_key def extractCookies(self): for c in self.cookie_jar: self.cookies[c.name] = c.value self.printCookies() def printCookies(self): self.log.write('printCookies() : ') for name in self.cookies.keys(): if name in ('fprt', 'ftmu', 'ipid'): self.log.write(str(name) + ' = ' + str(self.cookies[name])) def readCookieFile(self): self.cookie_jar = cookielib.LWPCookieJar() if self.cookie_jar != None: if os.path.isfile(COOKIEFILE): self.cookie_jar.load(COOKIEFILE,ignore_discard=IGNORE_DISCARD) if self.debug: self.log.write('readCookieFile:\n') self.extractCookies() else: raise MLBNoCookieFileError else: self.error_str = "Couldn't open cookie jar" raise Exception,self.error_str def login(self): try: self.readCookieFile() except MLBNoCookieFileError: #pass if self.debug: self.log.write("LOGIN> No cookie file") opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookie_jar)) urllib2.install_opener(opener) # First visit the login page and get the session cookie callback = str(int(time.time() * 1000)) login_url = 'http://mlb.mlb.com/account/quick_login_hdr.jsp?'\ 'successRedirect=http://mlb.mlb.com/shared/account/v2/login_success.jsp'\ '%3Fcallback%3Dl' + callback + '&callback=l' + callback + \ '&stylesheet=/style/account_management/myAccountMini.css&submitImage='\ '/shared/components/gameday/v4/images/btn-login.gif&'\ 'errorRedirect=http://mlb.mlb.com/account/quick_login_hdr.jsp%3Ferror'\ '%3Dtrue%26successRedirect%3Dhttp%253A%252F%252Fmlb.mlb.com%252Fshared'\ '%252Faccount%252Fv2%252Flogin_success.jsp%25253Fcallback%25253Dl' +\ callback + '%26callback%3Dl' + callback + '%26stylesheet%3D%252Fstyle'\ '%252Faccount_management%252FmyAccountMini.css%26submitImage%3D%252F'\ 'shared%252Fcomponents%252Fgameday%252Fv4%252Fimages%252Fbtn-login.gif'\ '%26errorRedirect%3Dhttp%3A//mlb.mlb.com/account/quick_login_hdr.jsp'\ '%253Ferror%253Dtrue%2526successRedirect%253Dhttp%25253A%25252F%25252F'\ 'mlb.mlb.com%25252Fshared%25252Faccount%25252Fv2%25252Flogin_success.jsp'\ '%2525253Fcallback%2525253Dl' + callback + '%2526callback%253Dl' +\ callback + '%2526stylesheet%253D%25252Fstyle%25252Faccount_management'\ '%25252FmyAccountMini.css%2526submitImage%253D%25252Fshared%25252F'\ 'components%25252Fgameday%25252Fv4%25252Fimages%25252Fbtn-login.gif' txheaders = {'User-agent' : USERAGENT} data = None req = urllib2.Request(login_url,data,txheaders) # we might have cookie info by now?? if self.user=="": return try: handle = urllib2.urlopen(req) except: self.error_str = 'Error occurred in HTTP request to login page' raise Exception, self.error_str try: if self.debug: self.log.write('pre-login:'******'Did we receive a cookie from the wizard?\n') # for index, cookie in enumerate(self.cookie_jar): # print >> self.log, index, ' : ' , cookie self.cookie_jar.save(COOKIEFILE,ignore_discard=IGNORE_DISCARD) rdata = handle.read() # now authenticate auth_values = {'emailAddress' : self.user, 'password' : self.passwd, 'submit.x' : 25, 'submit.y' : 7} g = re.search('name="successRedirect" value="(?P<successRedirect>[^"]+)"', rdata) auth_values['successRedirect'] = g.group('successRedirect') g = re.search('name="errorRedirect" value="(?P<errorRedirect>[^"]+)"', rdata) auth_values['errorRedirect'] = g.group('errorRedirect') auth_data = urllib.urlencode(auth_values) auth_url = 'https://secure.mlb.com/account/topNavLogin.jsp' req = urllib2.Request(auth_url,auth_data,txheaders) try: handle = urllib2.urlopen(req) self.cookie_jar.save(COOKIEFILE,ignore_discard=IGNORE_DISCARD) if self.debug: self.log.write('post-login: (this gets saved to file)') self.extractCookies() except: self.error_str = 'Error occurred in HTTP request to auth page' raise Exception, self.error_str auth_page = handle.read() #if self.debug: # self.log.write('Did we receive a cookie from authenticate?\n') # for index, cookie in enumerate(self.cookie_jar): # print >> self.log, index, ' : ' , cookie self.cookie_jar.save(COOKIEFILE,ignore_discard=IGNORE_DISCARD) try: loggedin = re.search('Login Success', auth_page).groups() self.log.write('Logged in successfully!\n') self.logged_in = True except: self.error_str = 'Login was unsuccessful.' self.log.write(auth_page) os.remove(COOKIEFILE) raise MLBAuthError, self.error_str
def __init__(self, stream, session, cfg, coverage=None, streamtype='video', start_time=0): # Initialize basic object from instance variables self.stream = stream self.session = session self.cfg = cfg if coverage == None: self.coverage = 0 else: self.coverage = coverage self.start_time = start_time self.streamtype = streamtype # Need a few config items self.use_nexdef = self.cfg.get('use_nexdef') self.postseason = self.cfg.get('postseason') self.use_librtmp = self.cfg.get('use_librtmp') # EXPERIMENTAL: make this a default now #self.use_wired_web = self.cfg.get('use_wired_web') self.use_wired_web = 1 self.max_bps = int(self.cfg.get('max_bps')) self.min_bps = int(self.cfg.get('min_bps')) # allow max_bps and min_bps to be specified in kbps if self.min_bps < 128000: self.min_bps *= 1000 if self.max_bps < 128000: self.max_bps *= 1000 self.speed = self.cfg.get('speed') self.adaptive = self.cfg.get('adaptive_stream') # Install the cookie received from MLBLogin and used for subsequent # media requests. This part should resolve the issue of login # restriction errors when each MediaStream request was its own login/ # logout sequence. try: opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.session.cookie_jar)) urllib2.install_opener(opener) except: raise self.log = MLBLog(LOGFILE) self.error_str = "What happened here?\nPlease enable debug with the d key and try your request again." # Break the stream argument into its components used for media location # requests. try: ( self.call_letters, self.team_id, self.content_id, self.event_id ) = self.stream except: self.error_str = "No stream available for selected game." self.log.write(str(datetime.datetime.now()) + '\n') try: self.session_key = self.session.session_key except: self.session_key = None self.debug = cfg.get('debug') # The request format depends on the streamtype if self.streamtype in ( 'audio', 'alt_audio' ): self.scenario = "AUDIO_FMS_32K" self.subject = "MLBCOM_GAMEDAY_AUDIO" else: if self.use_nexdef: if self.use_wired_web: self.scenario = 'HTTP_CLOUD_WIRED_WEB' else: self.scenario = 'HTTP_CLOUD_WIRED' else: self.scenario = 'FMS_CLOUD' #self.subject = "LIVE_EVENT_COVERAGE" self.subject = "MLBTV" # Media response needs to be parsed into components below. self.auth_chunk = None self.play_path = None self.tc_url = None self.app = None self.rtmp_url = None self.rtmp_host = None self.rtmp_port = None self.sub_path = None # TODO: Has this findUserVerifiedEvent been updated? Does this # url need to be changed to reflect that? self.base_url='https://secure.mlb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3?'
class MediaStream: def __init__(self, stream, session, cfg, coverage=None, streamtype='video', start_time=0): # Initialize basic object from instance variables self.stream = stream self.session = session self.cfg = cfg if coverage == None: self.coverage = 0 else: self.coverage = coverage self.start_time = start_time self.streamtype = streamtype # Need a few config items self.use_nexdef = self.cfg.get('use_nexdef') self.postseason = self.cfg.get('postseason') self.use_librtmp = self.cfg.get('use_librtmp') self.use_wired_web = self.cfg.get('use_wired_web') self.max_bps = int(self.cfg.get('max_bps')) self.min_bps = int(self.cfg.get('min_bps')) # allow max_bps and min_bps to be specified in kbps if self.min_bps < 128000: self.min_bps *= 1000 if self.max_bps < 128000: self.max_bps *= 1000 self.speed = self.cfg.get('speed') self.adaptive = self.cfg.get('adaptive_stream') # Install the cookie received from MLBLogin and used for subsequent # media requests. This part should resolve the issue of login # restriction errors when each MediaStream request was its own login/ # logout sequence. try: opener = urllib2.build_opener( urllib2.HTTPCookieProcessor(self.session.cookie_jar)) urllib2.install_opener(opener) except: raise self.log = MLBLog(LOGFILE) self.error_str = "What happened here?\nPlease enable debug with the d key and try your request again." # Break the stream argument into its components used for media location # requests. try: (self.call_letters, self.team_id, self.content_id, self.event_id) = self.stream except: self.error_str = "No stream available for selected game." self.log.write(str(datetime.datetime.now()) + '\n') try: self.session_key = self.session.session_key except: self.session_key = None self.debug = cfg.get('debug') # The request format depends on the streamtype if self.streamtype in ('audio', 'alt_audio'): self.scenario = "AUDIO_FMS_32K" self.subject = "MLBCOM_GAMEDAY_AUDIO" else: if self.use_nexdef: if self.use_wired_web: self.scenario = 'HTTP_CLOUD_WIRED_WEB' else: self.scenario = 'HTTP_CLOUD_WIRED' else: self.scenario = 'FMS_CLOUD' #self.subject = "LIVE_EVENT_COVERAGE" self.subject = "MLBTV" # Media response needs to be parsed into components below. self.auth_chunk = None self.play_path = None self.tc_url = None self.app = None self.rtmp_url = None self.rtmp_host = None self.rtmp_port = None self.sub_path = None # TODO: Has this findUserVerifiedEvent been updated? Does this # url need to be changed to reflect that? self.base_url = 'https://secure.mlb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3?' def createMediaRequest(self, stream): if stream == None: self.error_str = "No event-id present to create media request." raise try: #sessionKey = urllib.unquote(self.session.cookies['ftmu']) sessionKey = self.session.session_key except: sessionKey = None # Query values query_values = { 'eventId': self.event_id, 'sessionKey': sessionKey, 'fingerprint': urllib.unquote(self.session.cookies['fprt']), 'identityPointId': self.session.cookies['ipid'], 'playbackScenario': self.scenario, 'subject': self.subject } # Build query url = self.base_url + urllib.urlencode(query_values) # And make the request req = urllib2.Request(url) try: response = urllib2.urlopen(req) except urllib2.HTTPError, err: self.log.write("Error (%s) for URL: %s" % (err.code, url)) raise reply = xml.dom.minidom.parse(response) return reply
class MLBSession: def __init__(self, user, passwd, debug=False): self.user = user if self.user is None: # if user= is commented out, cfg.get() returns None, normalize this self.user = "" self.passwd = passwd self.auth = True self.logged_in = None self.cookie_jar = None self.cookies = {} self.debug = debug if COOKIE_DEBUG: self.debug = True self.log = MLBLog(LOGFILE) self.log.write('MLBSession BEGIN') try: self.session_key = self.readSessionKey() self.log.write('init() session-key : ' + self.session_key) except: #raise self.log.write('init() session-key : None') self.session_key = None def readSessionKey(self): sk = open(SESSIONKEY, "r") self.session_key = sk.read() sk.close() return self.session_key def writeSessionKey(self, session_key): self.session_key = session_key self.log.write('writeSessionKey(): ' + str(self.session_key)) sk = open(SESSIONKEY, "w") sk.write(self.session_key) sk.close() return self.session_key def extractCookies(self): for c in self.cookie_jar: self.cookies[c.name] = c.value self.printCookies() def printCookies(self): self.log.write('printCookies() : ') for name in self.cookies.keys(): if name in ('fprt', 'ftmu', 'ipid'): self.log.write(str(name) + ' = ' + str(self.cookies[name])) def readCookieFile(self): self.cookie_jar = cookielib.LWPCookieJar() if self.cookie_jar != None: if os.path.isfile(COOKIEFILE): self.cookie_jar.load(COOKIEFILE, ignore_discard=IGNORE_DISCARD) if self.debug: self.log.write('readCookieFile:\n') self.extractCookies() else: raise MLBNoCookieFileError else: self.error_str = "Couldn't open cookie jar" raise Exception, self.error_str def login(self): try: self.readCookieFile() except MLBNoCookieFileError: #pass if self.debug: self.log.write("LOGIN> No cookie file") opener = urllib2.build_opener( urllib2.HTTPCookieProcessor(self.cookie_jar)) urllib2.install_opener(opener) # First visit the login page and get the session cookie callback = str(int(time.time() * 1000)) login_url = 'http://mlb.mlb.com/account/quick_login_hdr.jsp?'\ 'successRedirect=http://mlb.mlb.com/shared/account/v2/login_success.jsp'\ '%3Fcallback%3Dl' + callback + '&callback=l' + callback + \ '&stylesheet=/style/account_management/myAccountMini.css&submitImage='\ '/shared/components/gameday/v4/images/btn-login.gif&'\ 'errorRedirect=http://mlb.mlb.com/account/quick_login_hdr.jsp%3Ferror'\ '%3Dtrue%26successRedirect%3Dhttp%253A%252F%252Fmlb.mlb.com%252Fshared'\ '%252Faccount%252Fv2%252Flogin_success.jsp%25253Fcallback%25253Dl' +\ callback + '%26callback%3Dl' + callback + '%26stylesheet%3D%252Fstyle'\ '%252Faccount_management%252FmyAccountMini.css%26submitImage%3D%252F'\ 'shared%252Fcomponents%252Fgameday%252Fv4%252Fimages%252Fbtn-login.gif'\ '%26errorRedirect%3Dhttp%3A//mlb.mlb.com/account/quick_login_hdr.jsp'\ '%253Ferror%253Dtrue%2526successRedirect%253Dhttp%25253A%25252F%25252F'\ 'mlb.mlb.com%25252Fshared%25252Faccount%25252Fv2%25252Flogin_success.jsp'\ '%2525253Fcallback%2525253Dl' + callback + '%2526callback%253Dl' +\ callback + '%2526stylesheet%253D%25252Fstyle%25252Faccount_management'\ '%25252FmyAccountMini.css%2526submitImage%253D%25252Fshared%25252F'\ 'components%25252Fgameday%25252Fv4%25252Fimages%25252Fbtn-login.gif' txheaders = {'User-agent': USERAGENT} data = None req = urllib2.Request(login_url, data, txheaders) # we might have cookie info by now?? if self.user == "": return try: handle = urllib2.urlopen(req) except: self.error_str = 'Error occurred in HTTP request to login page' raise Exception, self.error_str try: if self.debug: self.log.write('pre-login:'******'Did we receive a cookie from the wizard?\n') # for index, cookie in enumerate(self.cookie_jar): # print >> self.log, index, ' : ' , cookie self.cookie_jar.save(COOKIEFILE, ignore_discard=IGNORE_DISCARD) rdata = handle.read() # now authenticate auth_values = { 'emailAddress': self.user, 'password': self.passwd, 'submit.x': 25, 'submit.y': 7 } g = re.search( 'name="successRedirect" value="(?P<successRedirect>[^"]+)"', rdata) auth_values['successRedirect'] = g.group('successRedirect') g = re.search('name="errorRedirect" value="(?P<errorRedirect>[^"]+)"', rdata) auth_values['errorRedirect'] = g.group('errorRedirect') auth_data = urllib.urlencode(auth_values) auth_url = 'https://secure.mlb.com/account/topNavLogin.jsp' req = urllib2.Request(auth_url, auth_data, txheaders) try: handle = urllib2.urlopen(req) self.cookie_jar.save(COOKIEFILE, ignore_discard=IGNORE_DISCARD) if self.debug: self.log.write('post-login: (this gets saved to file)') self.extractCookies() except: self.error_str = 'Error occurred in HTTP request to auth page' raise Exception, self.error_str auth_page = handle.read() #if self.debug: # self.log.write('Did we receive a cookie from authenticate?\n') # for index, cookie in enumerate(self.cookie_jar): # print >> self.log, index, ' : ' , cookie self.cookie_jar.save(COOKIEFILE, ignore_discard=IGNORE_DISCARD) try: loggedin = re.search('Login Success', auth_page).groups() self.log.write('Logged in successfully!\n') self.logged_in = True except: self.error_str = 'Login was unsuccessful.' self.log.write(auth_page) os.remove(COOKIEFILE) raise MLBAuthError, self.error_str
def __init__(self, stream, session, cfg, coverage=None, streamtype='video', start_time=0): # Initialize basic object from instance variables self.stream = stream self.session = session self.cfg = cfg if coverage == None: self.coverage = 0 else: self.coverage = coverage self.start_time = start_time self.streamtype = streamtype # Need a few config items self.use_librtmp = self.cfg.get('use_librtmp') self.speed = self.cfg.get('speed') # milbtv is one flavor: vanilla. Not even French vanilla. self.use_nexdef = False self.streamtype = 'video' # Install the cookie received from MLBLogin and used for subsequent # media requests. This part should resolve the issue of login # restriction errors when each MediaStream request was its own login/ # logout sequence. try: opener = urllib2.build_opener( urllib2.HTTPCookieProcessor(self.session.cookie_jar)) urllib2.install_opener(opener) except: raise self.log = MLBLog(LOGFILE) self.error_str = "What happened here?\nPlease enable debug with the d key and try your request again." # Break the stream argument into its components used for media location # requests. try: (self.call_letters, self.team_id, self.content_id, self.event_id) = self.stream except: self.error_str = "No stream available for selected game." self.log.write(str(datetime.datetime.now()) + '\n') self.session_key = None self.debug = cfg.get('debug') # The request format depends on the streamtype self.scenario = 'FLASH_1000K_640X360' self.subject = 'LIVE_EVENT_COVERAGE' # Media response needs to be parsed into components below. self.auth_chunk = None self.play_path = None self.tc_url = None self.app = None self.rtmp_url = None self.rtmp_host = None self.rtmp_port = None self.sub_path = None # TODO: Has this findUserVerifiedEvent been updated? Does this # url need to be changed to reflect that? self.base_url = 'http://www.milb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3?'
class MiLBMediaStream(MediaStream): def __init__(self, stream, session, cfg, coverage=None, streamtype='video', start_time=0): # Initialize basic object from instance variables self.stream = stream self.session = session self.cfg = cfg if coverage == None: self.coverage = 0 else: self.coverage = coverage self.start_time = start_time self.streamtype = streamtype # Need a few config items self.use_librtmp = self.cfg.get('use_librtmp') self.speed = self.cfg.get('speed') # milbtv is one flavor: vanilla. Not even French vanilla. self.use_nexdef = False self.streamtype = 'video' # Install the cookie received from MLBLogin and used for subsequent # media requests. This part should resolve the issue of login # restriction errors when each MediaStream request was its own login/ # logout sequence. try: opener = urllib2.build_opener( urllib2.HTTPCookieProcessor(self.session.cookie_jar)) urllib2.install_opener(opener) except: raise self.log = MLBLog(LOGFILE) self.error_str = "What happened here?\nPlease enable debug with the d key and try your request again." # Break the stream argument into its components used for media location # requests. try: (self.call_letters, self.team_id, self.content_id, self.event_id) = self.stream except: self.error_str = "No stream available for selected game." self.log.write(str(datetime.datetime.now()) + '\n') self.session_key = None self.debug = cfg.get('debug') # The request format depends on the streamtype self.scenario = 'FLASH_1000K_640X360' self.subject = 'LIVE_EVENT_COVERAGE' # Media response needs to be parsed into components below. self.auth_chunk = None self.play_path = None self.tc_url = None self.app = None self.rtmp_url = None self.rtmp_host = None self.rtmp_port = None self.sub_path = None # TODO: Has this findUserVerifiedEvent been updated? Does this # url need to be changed to reflect that? self.base_url = 'http://www.milb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3?' def createMediaRequest(self, stream): if stream == None: self.error_str = "No event-id present to create media request." raise try: sessionKey = urllib.unquote(self.session.cookies['ftmu']) except: sessionKey = None # Query values query_values = { 'contentId': self.content_id, 'sessionKey': sessionKey, 'fingerprint': urllib.unquote(self.session.cookies['fprt']), 'identityPointId': self.session.cookies['ipid'], 'playbackScenario': self.scenario, 'subject': self.subject } # Build query url = self.base_url + urllib.urlencode(query_values) # And make the request req = urllib2.Request(url) response = urllib2.urlopen(req) reply = xml.dom.minidom.parse(response) return reply def locateMedia(self): game_url = None # 1. Make initial media request -- receive a reply with available media # 2. Update the session with current cookie values/session key. # 3. Get the content_list that matches the requested stream # 4. Strip out blacked out content or content that is not authorized. #reply = self.createMediaRequest(self.stream) #content_list = self.parseMediaReply(reply) game_url = self.requestSpecificMedia() #self.updateSession(reply) return game_url def updateSession(self, reply): try: self.session_key = reply.getElementsByTagName( 'session-key')[0].childNodes[0].data self.session_keys['ftmu'] = self.session_key self.session.writeSessionKey(self.session_key) except: pass def parseMediaReply(self, reply): # If status is not successful, make it easier to determine why status_code = str( reply.getElementsByTagName('status-code')[0].childNodes[0].data) if status_code != "1": self.log.write( "UNSUCCESSFUL MEDIA REQUEST: status-code: %s , event-id = %s\n" % (status_code, self.event_id)) self.log.write("See %s for XML response.\n" % ERRORLOG_1) err1 = open(ERRORLOG_1, 'w') reply.writexml(err1) err1.close() self.error_str = SOAPCODES[status_code] raise Exception, self.error_str else: self.log.write( "SUCCESSFUL MEDIA REQUEST: status-code: %s , event-id = %s\n" % (status_code, self.event_id)) self.log.write("See %s for XML response.\n" % MEDIALOG_1) med1 = open(MEDIALOG_1, 'w') reply.writexml(med1) med1.close() # determine blackout status #self.determineBlackoutStatus(reply) # and now the meat of the parsing... content_list = [] for content in reply.getElementsByTagName('user-verified-content'): type = content.getElementsByTagName('type')[0].childNodes[0].data if type != self.streamtype: continue content_id = content.getElementsByTagName( 'content-id')[0].childNodes[0].data if content_id != self.content_id: continue # First, collect all the domain-attributes dict = {} for node in content.getElementsByTagName('domain-attribute'): name = str(node.getAttribute('name')) value = node.childNodes[0].data dict[name] = value # There are a series of checks to trim the content list # 1. Trim out 'in-market' listings like Yankees On Yes if dict.has_key('coverage_type'): if 'in-market' in dict['coverage_type']: continue # 2. Trim out all non-English language broadcasts if dict.has_key('language'): if dict['language'] != 'EN': continue # 3. For post-season, trim out multi-angle listings if self.cfg.get('postseason'): if dict['in_epg'] != 'mlb_multiangle_epg': continue else: if dict['in_epg'] == 'mlb_multiangle_epg': continue # 4. Get coverage association and call_letters try: cov_pat = re.compile(r'([0-9][0-9]*)') coverage = re.search(cov_pat, dict['coverage_association']).groups()[0] except: coverage = None try: call_letters = dict['call_letters'] except: if self.cfg.get('postseason') == False: raise Exception, repr(dict) else: call_letters = 'MiLB' for media in content.getElementsByTagName( 'user-verified-media-item'): state = media.getElementsByTagName( 'state')[0].childNodes[0].data scenario = media.getElementsByTagName( 'playback-scenario')[0].childNodes[0].data if scenario == self.scenario and \ state in ('MEDIA_ARCHIVE', 'MEDIA_ON', 'MEDIA_OFF'): content_list.append( (call_letters, coverage, content_id, self.event_id)) return content_list def determineBlackoutStatus(self, reply): # Determine the blackout status try: blackout_status = reply.getElementsByTagName( 'blackout')[0].childNodes[0].data except: blackout_status = reply.getElementsByTagName('blackout-status')[0] try: success_status = blackout_status.getElementsByTagName( 'successStatus') blackout_status = None except: try: location_status = blackout_status.getElementsByTagName( 'locationCannotBeDeterminedStatus') except: blackout_status = 'LOCATION CANNOT BE DETERMINED.' media_type = reply.getElementsByTagName('type')[0].childNodes[0].data media_state = reply.getElementsByTagName('state')[0].childNodes[0].data self.media_state = media_state if blackout_status is not None and self.streamtype == 'video': inmarket_pat = re.compile(r'INMARKET') if re.search(inmarket_pat, blackout_status) is not None: pass elif media_state == 'MEDIA_ON' and not self.postseason: self.log.write( 'MEDIA STREAM BLACKOUT. See %s for XML response.' % BLACKFILE) self.error_str = 'BLACKOUT: ' + str(blackout_status) bf = open(BLACKFILE, 'w') reply.writexml(bf) bf.close() raise Exception, self.error_str def selectCoverage(self, content_list): # now iterate over the content_list with the following rules: # 1. if coverage association is zero, use it (likely a national broadcast) # 2. if preferred coverage is available use it # 3. if coverage association is non-zero and preferred not available, then what? for content in content_list: (call_letters, coverage, content_id, event_id) = content if coverage == '0': self.content_id = content_id self.call_letters = call_letters elif coverage == self.coverage: self.content_id = content_id self.call_letters = call_letters # if we preferred coverage and national coverage not available, # select any coverage available if self.content_id is None: try: (call_letters, coverage, content_id, event_id) = content_list[0] self.content_id = content_id self.call_letters = call_letters except: self.content_id = None self.call_letters = None if self.content_id is None: self.error_str = "Requested stream is not available." self.error_str += "\n\nRequested coverage association: " + str( self.coverage) self.error_str += "\n\nAvailable content list = \n" + repr( content_list) raise Exception, self.error_str if self.debug: self.log.write("DEBUG>> writing soap response\n") self.log.write(repr(reply) + '\n') if self.content_id is None: self.error_str = "Requested stream is not yet available." raise Exception, self.error_str if self.debug: self.log.write("DEBUG>> soap event-id:" + str(self.stream) + '\n') self.log.write("DEBUG>> soap content-id:" + str(self.content_id) + '\n') def requestSpecificMedia(self): query_values = { 'subject': self.subject, 'identityPointId': self.session.cookies['ipid'], 'contentId': self.content_id, 'playbackScenario': self.scenario, 'fingerprint': urllib.unquote(self.session.cookies['fprt']) } try: sessionkey = urllib.unquote(self.session.cookies['ftmu']) query_values['sessionKey'] = sessionkey except: sessionkey = None url = self.base_url + urllib.urlencode(query_values) req = urllib2.Request(url) response = urllib2.urlopen(req) reply = parse(response) status_code = str( reply.getElementsByTagName('status-code')[0].childNodes[0].data) if status_code != "1": # candidate for new procedure: this code block of writing # unsuccessful xml responses is being repeated... self.log.write( "DEBUG (SOAPCODES!=1)>> writing unsuccessful soap response event_id = " + str(self.event_id) + " contend-id = " + self.content_id + "\n") df = open('/tmp/unsuccessful.xml', 'w') reply.writexml(df) df.close() df = open('/tmp/unsuccessful.xml') msg = df.read() df.close() self.error_str = SOAPCODES[status_code] raise Exception, self.error_str try: self.session_key = reply.getElementsByTagName( 'session-key')[0].childNodes[0].data self.session.cookies['ftmu'] = self.session_key self.session.writeSessionKey(self.session_key) except: #raise self.session_key = None try: game_url = reply.getElementsByTagName('url')[0].childNodes[0].data except: self.error_str = "Stream URL not found in reply. Stream may not be available yet." df = open(ERRORLOG_2, 'w') reply.writexml(df) df.close() raise Exception, self.error_str else: df = open(MEDIALOG_2, 'w') reply.writexml(df) df.close() self.log.write("DEBUG>> URL received: " + game_url + '\n') return game_url def parseFmsCloudResponse(self, url): auth_pat = re.compile(r'auth=(.*)') self.auth_chunk = '?auth=' + re.search(auth_pat, url).groups()[0] out = '' req = urllib2.Request(url) handle = urllib2.urlopen(req) rsp = parse(handle) rtmp_base = rsp.getElementsByTagName('meta')[0].getAttribute('base') for elem in rsp.getElementsByTagName('video'): speed = int(elem.getAttribute('system-bitrate')) / 1000 if int(self.speed) == int(speed): vid_src = elem.getAttribute('src').replace('mp4:', '/') out = rtmp_base + vid_src return out def prepareMediaStreamer(self, game_url): #if self.streamtype in ( 'video', ): # game_url = self.parseFmsCloudResponse(game_url) return self.prepareFmsUrl(game_url) # finally some url processing routines def prepareFmsUrl(self, game_url): try: #play_path_pat = re.compile(r'ondemand\/(.*)\?') play_path_pat = re.compile(r'ondemand\/(.*)$') self.play_path = re.search(play_path_pat, game_url).groups()[0] app_pat = re.compile(r'ondemand\/(.*)\?(.*)$') querystring = re.search(app_pat, game_url).groups()[1] self.app = "ondemand?_fcs_vhost=cp118053.edgefcs.net&akmfv=1.6" + querystring # not sure if we need this try: req = urllib2.Request('http://cp118053.edgefcs.net/fcs/ident') page = urllib2.urlopen(req) fp = parse(page) ip = fp.getElementsByTagName('ip')[0].childNodes[0].data self.tc_url = 'http://' + str(ip) + ':1935/' + self.app except: self.tc_url = None except: self.play_path = None try: live_pat = re.compile(r'live\/milb') if re.search(live_pat, game_url): if self.streamtype == 'audio': auth_pat = re.compile(r'auth=(.*)') self.auth_chunk = '?auth=' + re.search( auth_pat, game_url).groups()[0] live_sub_pat = re.compile(r'live\/mlb_audio(.*)\?') self.sub_path = re.search(live_sub_pat, game_url).groups()[0] self.sub_path = 'mlb_audio' + self.sub_path live_play_pat = re.compile(r'live\/mlb_audio(.*)$') self.play_path = re.search(live_play_pat, game_url).groups()[0] self.play_path = 'mlb_audio' + self.play_path app_auth = self.auth_chunk.replace('?', '&') self.app = "live?_fcs_vhost=cp153281.live.edgefcs.net&akmfv=1.6&aifp=v0006" + app_auth else: try: live_sub_pat = re.compile(r'live\/milb_encap_rm(.*)') self.sub_path = re.search(live_sub_pat, game_url).groups()[0] self.sub_path = 'milb_encap_rm' + self.sub_path except Exception, detail: self.error_str = 'Could not parse the stream subscribe path: ' + str( detail) raise Exception, self.error_str try: live_path_pat = re.compile(r'live\/milb_encap_rm(.*)$') self.play_path = re.search(live_path_pat, game_url).groups()[0] self.play_path = 'milb_encap_rm' + self.play_path except Exception, detail: self.error_str = 'Could not parse the stream play path: ' + str( detail) raise Exception, self.error_str sec_pat = re.compile(r'mlbsecurelive') if re.search(sec_pat, game_url) is not None: self.app = 'mlbsecurelive-live' else: self.app = 'live?_fcs_vhost=cp118053.live.edgefcs.net&akmfv=1.6' if self.debug: self.log.write("DEBUG>> sub_path = " + str(self.sub_path) + "\n") self.log.write("DEBUG>> play_path = " + str(self.play_path) + "\n") self.log.write("DEBUG>> app = " + str(self.app) + "\n")
class MediaStream: def __init__(self, stream, session, cfg, coverage=None, streamtype='video', start_time=0): # Initialize basic object from instance variables self.stream = stream self.session = session self.cfg = cfg if coverage == None: self.coverage = 0 else: self.coverage = coverage self.start_time = start_time self.streamtype = streamtype # Need a few config items self.use_nexdef = self.cfg.get('use_nexdef') self.postseason = self.cfg.get('postseason') self.use_librtmp = self.cfg.get('use_librtmp') # EXPERIMENTAL: make this a default now #self.use_wired_web = 1 self.use_wired_web = self.cfg.get('use_wired_web') self.max_bps = int(self.cfg.get('max_bps')) self.min_bps = int(self.cfg.get('min_bps')) # allow max_bps and min_bps to be specified in kbps if self.min_bps < 128000: self.min_bps *= 1000 if self.max_bps < 128000: self.max_bps *= 1000 self.speed = self.cfg.get('speed') self.adaptive = self.cfg.get('adaptive_stream') # Install the cookie received from MLBLogin and used for subsequent # media requests. This part should resolve the issue of login # restriction errors when each MediaStream request was its own login/ # logout sequence. try: opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.session.cookie_jar)) urllib2.install_opener(opener) except: raise self.log = MLBLog(LOGFILE) self.error_str = "What happened here?\nPlease enable debug with the d key and try your request again." # Break the stream argument into its components used for media location # requests. try: ( self.call_letters, self.team_id, self.content_id, self.event_id ) = self.stream except: self.error_str = "No stream available for selected game." self.log.write(str(datetime.datetime.now()) + '\n') try: self.session_key = self.session.session_key except: self.session_key = None self.debug = cfg.get('debug') # The request format depends on the streamtype if self.streamtype in ( 'audio', 'alt_audio' ): self.scenario = "AUDIO_FMS_32K" self.subject = "MLBCOM_GAMEDAY_AUDIO" else: if self.use_nexdef: if self.use_wired_web: self.scenario = 'HTTP_CLOUD_WIRED_WEB' else: self.scenario = 'HTTP_CLOUD_WIRED' else: self.scenario = 'FMS_CLOUD' #self.subject = "LIVE_EVENT_COVERAGE" self.subject = "MLBTV" # Media response needs to be parsed into components below. self.auth_chunk = None self.play_path = None self.tc_url = None self.app = None self.rtmp_url = None self.rtmp_host = None self.rtmp_port = None self.sub_path = None # TODO: Has this findUserVerifiedEvent been updated? Does this # url need to be changed to reflect that? # self.base_url= 'https://mlb-ws-mf.media.mlb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3?' # 😉 self.base_url='https://secure.mlb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3?' def createMediaRequest(self,stream): if stream == None: self.error_str = "No event-id present to create media request." raise try: # sessionKey = urllib.unquote(self.session.cookies['ftmu']) sessionKey = self.session.session_key except: sessionKey = None # Query values query_values = { 'eventId': self.event_id, 'sessionKey': sessionKey, 'fingerprint': urllib.unquote(self.session.cookies['fprt']), 'identityPointId': self.session.cookies['ipid'], # 'playbackScenario': 'HTTP_CLOUD_TABLET_60', # 😉 # 'subject': 'LIVE_EVENT_COVERAGE', # 😉 # 'platform': 'IPAD', # 😉 # 'postalCode': '{{postalCode}}', # 😉 # 'country': '{{country}}', # 😉 # 'latitude': '{{latitude}}', # 😉 # 'longitude': '{{longitude}}' # 😉 'playbackScenario': self.scenario, 'subject': self.subject } # Build query url = self.base_url + urllib.urlencode(query_values) # And make the request req = urllib2.Request(url) try: response = urllib2.urlopen(req) except urllib2.HTTPError, err: self.log.write("Error (%s) for URL: %s" % ( err.code, url )) raise reply = xml.dom.minidom.parse(response) return reply
def __init__(self, stream, session, cfg, coverage=None, streamtype='video', start_time=0): # Initialize basic object from instance variables self.stream = stream self.session = session self.cfg = cfg if coverage == None: self.coverage = 0 else: self.coverage = coverage self.start_time = start_time self.streamtype = streamtype # Need a few config items self.use_librtmp = self.cfg.get('use_librtmp') self.speed = self.cfg.get('speed') # milbtv is one flavor: vanilla. Not even French vanilla. self.use_nexdef = False self.streamtype = 'video' # Install the cookie received from MLBLogin and used for subsequent # media requests. This part should resolve the issue of login # restriction errors when each MediaStream request was its own login/ # logout sequence. try: opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.session.cookie_jar)) urllib2.install_opener(opener) except: raise self.log = MLBLog(LOGFILE) self.error_str = "What happened here?\nPlease enable debug with the d key and try your request again." # Break the stream argument into its components used for media location # requests. try: ( self.call_letters, self.team_id, self.content_id, self.event_id ) = self.stream except: self.error_str = "No stream available for selected game." self.log.write(str(datetime.datetime.now()) + '\n') self.session_key = None self.debug = cfg.get('debug') # The request format depends on the streamtype self.scenario = 'FLASH_1000K_640X360' self.subject = 'LIVE_EVENT_COVERAGE' # Media response needs to be parsed into components below. self.auth_chunk = None self.play_path = None self.tc_url = None self.app = None self.rtmp_url = None self.rtmp_host = None self.rtmp_port = None self.sub_path = None # TODO: Has this findUserVerifiedEvent been updated? Does this # url need to be changed to reflect that? self.base_url='http://www.milb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3?'
class MiLBSession: def __init__(self, user, passwd, debug=False): self.user = user if self.user is None: # if user= is commented out, cfg.get() returns None, normalize this self.user = "" self.passwd = passwd self.auth = True self.logged_in = None self.cookie_jar = None self.cookies = {} self.debug = debug if COOKIE_DEBUG: self.debug = True self.log = MLBLog(LOGFILE) try: self.session_key = self.readSessionKey() if self.debug: self.log.write("LOGIN> Read session key from file: " + str(self.session_key)) except: self.session_key = None def readSessionKey(self): sk = open(SESSIONKEY, "r") self.session_key = sk.read() sk.close() return session_key def writeSessionKey(self, session_key): if self.debug: self.log.write('Writing session-key to file: ' + str(self.session_key) + '\n') sk = open(SESSIONKEY, "w") sk.write(session_key) sk.close() return session_key def extractCookies(self): for c in self.cookie_jar: self.cookies[c.name] = c.value self.printCookies() def printCookies(self): if self.debug: self.log.write('Printing relevant cookie morsels...\n') for name in self.cookies.keys(): if name in ('fprt', 'ftmu', 'ipid'): self.log.write(str(name) + ' = ' + str(self.cookies[name])) self.log.write('\n') def readCookieFile(self): self.cookie_jar = cookielib.LWPCookieJar() if self.cookie_jar != None: if os.path.isfile(COOKIEFILE): self.cookie_jar.load(COOKIEFILE, ignore_discard=IGNORE_DISCARD) if self.debug: self.log.write('readCookieFile:\n') self.extractCookies() else: raise MLBNoCookieFileError else: self.error_str = "Couldn't open cookie jar" raise Exception, self.error_str def login(self): try: self.readCookieFile() except MLBNoCookieFileError: #pass if self.debug: self.log.write("LOGIN> No cookie file") opener = urllib2.build_opener( urllib2.HTTPCookieProcessor(self.cookie_jar)) urllib2.install_opener(opener) # First visit the login page and get the session cookie login_url = 'https://secure.milb.com/enterworkflow.do?flowId=registration.profile' txheaders = {'User-agent': USERAGENT} data = None req = urllib2.Request(login_url, data, txheaders) # we might have cookie info by now?? if self.user == "": return try: handle = urllib2.urlopen(req) except: self.error_str = 'Error occurred in HTTP request to login page' raise Exception, self.error_str try: if self.debug: self.log.write('pre-login:\n') self.extractCookies() except Exception, detail: raise Exception, detail #if self.debug: # self.log.write('Did we receive a cookie from the wizard?\n') # for index, cookie in enumerate(self.cookie_jar): # print >> self.log, index, ' : ' , cookie self.cookie_jar.save(COOKIEFILE, ignore_discard=IGNORE_DISCARD) rdata = handle.read() # now authenticate auth_values = { 'uri': '/account/login_register.jsp', 'registrationAction': 'identify', 'emailAddress': self.user, 'password': self.passwd } success_pat = re.compile( r'Account Management - Profile | MiLB.com Account |') auth_data = urllib.urlencode(auth_values) auth_url = 'https://secure.milb.com/authenticate.do' req = urllib2.Request(auth_url, auth_data, txheaders) try: handle = urllib2.urlopen(req) self.cookie_jar.save(COOKIEFILE, ignore_discard=IGNORE_DISCARD) if self.debug: self.log.write('post-login: (this gets saved to file)\n') self.extractCookies() except: self.error_str = 'Error occurred in HTTP request to auth page' raise Exception, self.error_str auth_page = handle.read() #if self.debug: # self.log.write('Did we receive a cookie from authenticate?\n') # for index, cookie in enumerate(self.cookie_jar): # print >> self.log, index, ' : ' , cookie self.cookie_jar.save(COOKIEFILE, ignore_discard=IGNORE_DISCARD) try: loggedin = re.search(success_pat, auth_page).groups() self.log.write('Logged in successfully!\n') self.logged_in = True except: self.error_str = 'Login was unsuccessful.' self.log.write(auth_page) os.remove(COOKIEFILE) raise MLBAuthError, self.error_str
def __init__(self, stream, session, cfg, coverage=None, streamtype='video', start_time=0): # Initialize basic object from instance variables self.stream = stream self.session = session self.cfg = cfg if coverage == None: self.coverage = 0 else: self.coverage = coverage self.start_time = start_time self.streamtype = streamtype # Need a few config items self.use_nexdef = self.cfg.get('use_nexdef') self.postseason = self.cfg.get('postseason') self.use_librtmp = self.cfg.get('use_librtmp') # EXPERIMENTAL: make this a default now #self.use_wired_web = self.cfg.get('use_wired_web') self.use_wired_web = 1 self.max_bps = int(self.cfg.get('max_bps')) self.min_bps = int(self.cfg.get('min_bps')) # allow max_bps and min_bps to be specified in kbps if self.min_bps < 128000: self.min_bps *= 1000 if self.max_bps < 128000: self.max_bps *= 1000 self.speed = self.cfg.get('speed') self.adaptive = self.cfg.get('adaptive_stream') # Install the cookie received from MLBLogin and used for subsequent # media requests. This part should resolve the issue of login # restriction errors when each MediaStream request was its own login/ # logout sequence. try: opener = urllib2.build_opener( urllib2.HTTPCookieProcessor(self.session.cookie_jar)) urllib2.install_opener(opener) except: raise self.log = MLBLog(LOGFILE) self.error_str = "What happened here?\nPlease enable debug with the d key and try your request again." # Break the stream argument into its components used for media location # requests. try: (self.call_letters, self.team_id, self.content_id, self.event_id) = self.stream except: self.error_str = "No stream available for selected game." self.log.write(str(datetime.datetime.now()) + '\n') try: self.session_key = self.session.session_key except: self.session_key = None self.debug = cfg.get('debug') # The request format depends on the streamtype if self.streamtype in ('audio', 'alt_audio'): self.scenario = "AUDIO_FMS_32K" self.subject = "MLBCOM_GAMEDAY_AUDIO" else: if self.use_nexdef: if self.use_wired_web: self.scenario = 'HTTP_CLOUD_WIRED_WEB' else: self.scenario = 'HTTP_CLOUD_WIRED' else: self.scenario = 'FMS_CLOUD' #self.subject = "LIVE_EVENT_COVERAGE" self.subject = "MLBTV" # Media response needs to be parsed into components below. self.auth_chunk = None self.play_path = None self.tc_url = None self.app = None self.rtmp_url = None self.rtmp_host = None self.rtmp_port = None self.sub_path = None # TODO: Has this findUserVerifiedEvent been updated? Does this # url need to be changed to reflect that? self.base_url = 'https://secure.mlb.com/pubajaxws/bamrest/MediaService2_0/op-findUserVerifiedEvent/v-2.3?'
class MiLBSession: def __init__(self,user,passwd,debug=False): self.user = user if self.user is None: # if user= is commented out, cfg.get() returns None, normalize this self.user = "" self.passwd = passwd self.auth = True self.logged_in = None self.cookie_jar = None self.cookies = {} self.debug = debug if COOKIE_DEBUG: self.debug = True self.log = MLBLog(LOGFILE) try: self.session_key = self.readSessionKey() if self.debug: self.log.write("LOGIN> Read session key from file: " + str(self.session_key)) except: self.session_key = None def readSessionKey(self): sk = open(SESSIONKEY,"r") self.session_key = sk.read() sk.close() return session_key def writeSessionKey(self,session_key): if self.debug: self.log.write('Writing session-key to file: ' + str(self.session_key) + '\n') sk = open(SESSIONKEY,"w") sk.write(session_key) sk.close() return session_key def extractCookies(self): for c in self.cookie_jar: self.cookies[c.name] = c.value self.printCookies() def printCookies(self): if self.debug: self.log.write('Printing relevant cookie morsels...\n') for name in self.cookies.keys(): if name in ('fprt', 'ftmu', 'ipid'): self.log.write(str(name) + ' = ' + str(self.cookies[name])) self.log.write('\n') def readCookieFile(self): self.cookie_jar = cookielib.LWPCookieJar() if self.cookie_jar != None: if os.path.isfile(COOKIEFILE): self.cookie_jar.load(COOKIEFILE,ignore_discard=IGNORE_DISCARD) if self.debug: self.log.write('readCookieFile:\n') self.extractCookies() else: raise MLBNoCookieFileError else: self.error_str = "Couldn't open cookie jar" raise Exception,self.error_str def login(self): try: self.readCookieFile() except MLBNoCookieFileError: #pass if self.debug: self.log.write("LOGIN> No cookie file") opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookie_jar)) urllib2.install_opener(opener) # First visit the login page and get the session cookie login_url = 'https://secure.milb.com/enterworkflow.do?flowId=registration.profile' txheaders = {'User-agent' : USERAGENT} data = None req = urllib2.Request(login_url,data,txheaders) # we might have cookie info by now?? if self.user=="": return try: handle = urllib2.urlopen(req) except: self.error_str = 'Error occurred in HTTP request to login page' raise Exception, self.error_str try: if self.debug: self.log.write('pre-login:\n') self.extractCookies() except Exception,detail: raise Exception,detail #if self.debug: # self.log.write('Did we receive a cookie from the wizard?\n') # for index, cookie in enumerate(self.cookie_jar): # print >> self.log, index, ' : ' , cookie self.cookie_jar.save(COOKIEFILE,ignore_discard=IGNORE_DISCARD) rdata = handle.read() # now authenticate auth_values = {'uri' : '/account/login_register.jsp', 'registrationAction' : 'identify', 'emailAddress' : self.user, 'password' : self.passwd } success_pat = re.compile(r'Account Management - Profile | MiLB.com Account |') auth_data = urllib.urlencode(auth_values) auth_url = 'https://secure.milb.com/authenticate.do' req = urllib2.Request(auth_url,auth_data,txheaders) try: handle = urllib2.urlopen(req) self.cookie_jar.save(COOKIEFILE,ignore_discard=IGNORE_DISCARD) if self.debug: self.log.write('post-login: (this gets saved to file)\n') self.extractCookies() except: self.error_str = 'Error occurred in HTTP request to auth page' raise Exception, self.error_str auth_page = handle.read() #if self.debug: # self.log.write('Did we receive a cookie from authenticate?\n') # for index, cookie in enumerate(self.cookie_jar): # print >> self.log, index, ' : ' , cookie self.cookie_jar.save(COOKIEFILE,ignore_discard=IGNORE_DISCARD) try: loggedin = re.search(success_pat, auth_page).groups() self.log.write('Logged in successfully!\n') self.logged_in = True except: self.error_str = 'Login was unsuccessful.' self.log.write(auth_page) os.remove(COOKIEFILE) raise MLBAuthError, self.error_str