@ -62,7 +62,8 @@ class LivestreamIE(InfoExtractor):
_API_URL_TEMPLATE = ' http://livestream.com/api/accounts/ %s /events/ %s '
_API_URL_TEMPLATE = ' http://livestream.com/api/accounts/ %s /events/ %s '
def _parse_smil_formats ( self , smil , smil_url , video_id , namespace = None , f4m_params = None , transform_rtmp_url = None ) :
def _parse_smil_formats ( self , smil , smil_url , video_id , namespace = None , f4m_params = None , transform_rtmp_url = None ) :
base_ele = find_xpath_attr ( smil , self . _xpath_ns ( ' .//meta ' , namespace ) , ' name ' , ' httpBase ' )
base_ele = find_xpath_attr (
smil , self . _xpath_ns ( ' .//meta ' , namespace ) , ' name ' , ' httpBase ' )
base = base_ele . get ( ' content ' ) if base_ele else ' http://livestreamvod-f.akamaihd.net/ '
base = base_ele . get ( ' content ' ) if base_ele else ' http://livestreamvod-f.akamaihd.net/ '
formats = [ ]
formats = [ ]
@ -96,7 +97,8 @@ class LivestreamIE(InfoExtractor):
video_url = video_data . get ( key )
video_url = video_data . get ( key )
if video_url :
if video_url :
ext = determine_ext ( video_url )
ext = determine_ext ( video_url )
bitrate = int_or_none ( self . _search_regex ( r ' ( \ d+) \ . %s ' % ext , video_url , ' bitrate ' , default = None ) )
bitrate = int_or_none ( self . _search_regex (
r ' ( \ d+) \ . %s ' % ext , video_url , ' bitrate ' , default = None ) )
formats . append ( {
formats . append ( {
' url ' : video_url ,
' url ' : video_url ,
' format_id ' : format_id ,
' format_id ' : format_id ,
@ -119,7 +121,8 @@ class LivestreamIE(InfoExtractor):
f4m_url = video_data . get ( ' f4m_url ' )
f4m_url = video_data . get ( ' f4m_url ' )
if f4m_url :
if f4m_url :
f4m_formats = self . _extract_f4m_formats ( f4m_url , video_id , f4m_id = ' hds ' , fatal = False )
f4m_formats = self . _extract_f4m_formats (
f4m_url , video_id , f4m_id = ' hds ' , fatal = False )
if f4m_formats :
if f4m_formats :
formats . extend ( f4m_formats )
formats . extend ( f4m_formats )
self . _sort_formats ( formats )
self . _sort_formats ( formats )
@ -157,10 +160,11 @@ class LivestreamIE(InfoExtractor):
if smil_formats :
if smil_formats :
formats . extend ( smil_formats )
formats . extend ( smil_formats )
entry_protocol = ' m3u8 ' if is_live else ' m3u8_native '
m3u8_url = stream_info . get ( ' m3u8_url ' )
m3u8_url = stream_info . get ( ' m3u8_url ' )
if m3u8_url :
if m3u8_url :
m3u8_formats = self . _extract_m3u8_formats (
m3u8_formats = self . _extract_m3u8_formats (
m3u8_url , broadcast_id , ' mp4 ' , ' m3u8_native ' , m3u8_id = ' hls ' , fatal = False )
m3u8_url , broadcast_id , ' mp4 ' , entry_protocol , m3u8_id = ' hls ' , fatal = False )
if m3u8_formats :
if m3u8_formats :
formats . extend ( m3u8_formats )
formats . extend ( m3u8_formats )
@ -197,7 +201,8 @@ class LivestreamIE(InfoExtractor):
else :
else :
info_url = ' {root} ?&id= {id} &newer=-1&type=video ' . format (
info_url = ' {root} ?&id= {id} &newer=-1&type=video ' . format (
root = feed_root_url , id = last_video )
root = feed_root_url , id = last_video )
videos_info = self . _download_json ( info_url , event_id , ' Downloading page {0} ' . format ( i ) ) [ ' data ' ]
videos_info = self . _download_json (
info_url , event_id , ' Downloading page {0} ' . format ( i ) ) [ ' data ' ]
videos_info = [ v [ ' data ' ] for v in videos_info if v [ ' type ' ] == ' video ' ]
videos_info = [ v [ ' data ' ] for v in videos_info if v [ ' type ' ] == ' video ' ]
if not videos_info :
if not videos_info :
break
break
@ -215,7 +220,8 @@ class LivestreamIE(InfoExtractor):
account = mobj . group ( ' account_id ' ) or mobj . group ( ' account_name ' )
account = mobj . group ( ' account_id ' ) or mobj . group ( ' account_name ' )
api_url = self . _API_URL_TEMPLATE % ( account , event )
api_url = self . _API_URL_TEMPLATE % ( account , event )
if video_id :
if video_id :
video_data = self . _download_json ( api_url + ' /videos/ %s ' % video_id , video_id )
video_data = self . _download_json (
api_url + ' /videos/ %s ' % video_id , video_id )
return self . _extract_video_info ( video_data )
return self . _extract_video_info ( video_data )
else :
else :
event_data = self . _download_json ( api_url , video_id )
event_data = self . _download_json ( api_url , video_id )
@ -226,8 +232,8 @@ class LivestreamIE(InfoExtractor):
class LivestreamOriginalIE ( InfoExtractor ) :
class LivestreamOriginalIE ( InfoExtractor ) :
IE_NAME = ' livestream:original '
IE_NAME = ' livestream:original '
_VALID_URL = r ''' (?x)https?://original \ .livestream \ .com/
_VALID_URL = r ''' (?x)https?://original \ .livestream \ .com/
( ? P < user > [ ^ / ] + ) / ( ? P < type > video | folder )
( ? P < user > [ ^ / \? #]+)(?:/(?P<type>video|folder )
( ? : \? . * ? Id = | / ) ( ? P < id > . * ? ) ( & | $ )
( ? : ( ? : \? . * ? Id = | / ) ( ? P < id > . * ? ) ( & | $ ) ) ? ) ?
'''
'''
_TESTS = [ {
_TESTS = [ {
' url ' : ' http://original.livestream.com/dealbook/video?clipId=pla_8aa4a3f1-ba15-46a4-893b-902210e138fb ' ,
' url ' : ' http://original.livestream.com/dealbook/video?clipId=pla_8aa4a3f1-ba15-46a4-893b-902210e138fb ' ,
@ -244,52 +250,61 @@ class LivestreamOriginalIE(InfoExtractor):
' id ' : ' a07bf706-d0e4-4e75-a747-b021d84f2fd3 ' ,
' id ' : ' a07bf706-d0e4-4e75-a747-b021d84f2fd3 ' ,
} ,
} ,
' playlist_mincount ' : 4 ,
' playlist_mincount ' : 4 ,
} , {
# live stream
' url ' : ' http://www.livestream.com/znsbahamas ' ,
' only_matching ' : True ,
} ]
} ]
def _extract_video ( self , user , video_id ) :
def _extract_video_info ( self , user , video_id ) :
api_url = ' http://x {0} x.api.channel.livestream.com/2.0/clipdetails?extendedInfo=true&id= {1} ' . format ( user , video_id )
api_url = ' http://x %s x.api.channel.livestream.com/2.0/clipdetails?extendedInfo=true&id= %s ' % ( user , video_id )
info = self . _download_xml ( api_url , video_id )
info = self . _download_xml ( api_url , video_id )
# this url is used on mobile devices
stream_url = ' http://x {0} x.api.channel.livestream.com/3.0/getstream.json?id= {1} ' . format ( user , video_id )
stream_info = self . _download_json ( stream_url , video_id )
is_live = stream_info . get ( ' isLive ' )
item = info . find ( ' channel ' ) . find ( ' item ' )
item = info . find ( ' channel ' ) . find ( ' item ' )
title = xpath_text ( item , ' title ' )
media_ns = { ' media ' : ' http://search.yahoo.com/mrss ' }
media_ns = { ' media ' : ' http://search.yahoo.com/mrss ' }
thumbnail_url = xpath_attr ( item , xpath_with_ns ( ' media:thumbnail ' , media_ns ) , ' url ' )
thumbnail_url = xpath_attr (
duration = float_or_none ( xpath_attr ( item , xpath_with_ns ( ' media:content ' , media_ns ) , ' duration ' ) )
item , xpath_with_ns ( ' media:thumbnail ' , media_ns ) , ' url ' )
duration = float_or_none ( xpath_attr (
item , xpath_with_ns ( ' media:content ' , media_ns ) , ' duration ' ) )
ls_ns = { ' ls ' : ' http://api.channel.livestream.com/2.0 ' }
ls_ns = { ' ls ' : ' http://api.channel.livestream.com/2.0 ' }
view_count = int_or_none ( xpath_text ( item , xpath_with_ns ( ' ls:viewsCount ' , ls_ns ) ) )
view_count = int_or_none ( xpath_text (
item , xpath_with_ns ( ' ls:viewsCount ' , ls_ns ) ) )
formats = [ {
return {
' url ' : stream_info [ ' progressiveUrl ' ] ,
' id ' : video_id ,
' format_id ' : ' http ' ,
' title ' : title ,
} ]
' thumbnail ' : thumbnail_url ,
' duration ' : duration ,
' view_count ' : view_count ,
}
def _extract_video_formats ( self , video_data , video_id , entry_protocol ) :
formats = [ ]
progressive_url = video_data . get ( ' progressiveUrl ' )
if progressive_url :
formats . append ( {
' url ' : progressive_url ,
' format_id ' : ' http ' ,
} )
m3u8_url = stream_info . get ( ' httpUrl ' )
m3u8_url = video_data . get ( ' httpUrl ' )
if m3u8_url :
if m3u8_url :
m3u8_formats = self . _extract_m3u8_formats (
m3u8_formats = self . _extract_m3u8_formats (
m3u8_url , video_id , ' mp4 ' , ' m3u8_native ' , m3u8_id = ' hls ' , fatal = False )
m3u8_url , video_id , ' mp4 ' , entry_protocol , m3u8_id = ' hls ' , fatal = False )
if m3u8_formats :
if m3u8_formats :
formats . extend ( m3u8_formats )
formats . extend ( m3u8_formats )
rtsp_url = stream_info . get ( ' rtspUrl ' )
rtsp_url = video_data . get ( ' rtspUrl ' )
if rtsp_url :
if rtsp_url :
formats . append ( {
formats . append ( {
' url ' : rtsp_url ,
' url ' : rtsp_url ,
' format_id ' : ' rtsp ' ,
' format_id ' : ' rtsp ' ,
} )
} )
self . _sort_formats ( formats )
return {
self . _sort_formats ( formats )
' id ' : video_id ,
return formats
' title ' : self . _live_title ( xpath_text ( item , ' title ' ) ) if is_live else xpath_text ( item , ' title ' ) ,
' formats ' : formats ,
' thumbnail ' : thumbnail_url ,
' duration ' : duration ,
' view_count ' : view_count ,
' is_live ' : is_live ,
}
def _extract_folder ( self , url , folder_id ) :
def _extract_folder ( self , url , folder_id ) :
webpage = self . _download_webpage ( url , folder_id )
webpage = self . _download_webpage ( url , folder_id )
@ -308,13 +323,36 @@ class LivestreamOriginalIE(InfoExtractor):
def _real_extract ( self , url ) :
def _real_extract ( self , url ) :
mobj = re . match ( self . _VALID_URL , url )
mobj = re . match ( self . _VALID_URL , url )
id = mobj . group ( ' id ' )
user = mobj . group ( ' user ' )
user = mobj . group ( ' user ' )
url_type = mobj . group ( ' type ' )
url_type = mobj . group ( ' type ' )
content_id = mobj . group ( ' id ' )
if url_type == ' folder ' :
if url_type == ' folder ' :
return self . _extract_folder ( url , id)
return self . _extract_folder ( url , content_ id)
else :
else :
return self . _extract_video ( user , id )
# this url is used on mobile devices
stream_url = ' http://x %s x.api.channel.livestream.com/3.0/getstream.json ' % user
info = { }
if content_id :
stream_url + = ' ?id= %s ' % content_id
info = self . _extract_video_info ( user , content_id )
else :
content_id = user
webpage = self . _download_webpage ( url , content_id )
info = {
' title ' : self . _og_search_title ( webpage ) ,
' description ' : self . _og_search_description ( webpage ) ,
' thumbnail ' : self . _search_regex ( r ' channelLogo.src \ s*= \ s* " ([^ " ]+) " ' , webpage , ' thumbnail ' , None ) ,
}
video_data = self . _download_json ( stream_url , content_id )
is_live = video_data . get ( ' isLive ' )
entry_protocol = ' m3u8 ' if is_live else ' m3u8_native '
info . update ( {
' id ' : content_id ,
' title ' : self . _live_title ( info [ ' title ' ] ) if is_live else info [ ' title ' ] ,
' formats ' : self . _extract_video_formats ( video_data , content_id , entry_protocol ) ,
' is_live ' : is_live ,
} )
return info
# The server doesn't support HEAD request, the generic extractor can't detect
# The server doesn't support HEAD request, the generic extractor can't detect