3 from .common
import InfoExtractor
14 class CiscoLiveBaseIE(InfoExtractor
):
15 # These appear to be constant across all Cisco Live presentations
16 # and are not tied to any user session or event
17 RAINFOCUS_API_URL
= 'https://events.rainfocus.com/api/%s'
18 RAINFOCUS_API_PROFILE_ID
= 'Na3vqYdAlJFSxhYTYQGuMbpafMqftalz'
19 RAINFOCUS_WIDGET_ID
= 'n6l4Lo05R8fiy3RpUBm447dZN8uNWoye'
20 BRIGHTCOVE_URL_TEMPLATE
= 'http://players.brightcove.net/5647924234001/SyK2FdqjM_default/index.html?videoId=%s'
23 'Origin': 'https://ciscolive.cisco.com',
24 'rfApiProfileId': RAINFOCUS_API_PROFILE_ID
,
25 'rfWidgetId': RAINFOCUS_WIDGET_ID
,
28 def _call_api(self
, ep
, rf_id
, query
, referrer
, note
=None):
29 headers
= self
.HEADERS
.copy()
30 headers
['Referer'] = referrer
31 return self
._download
_json
(
32 self
.RAINFOCUS_API_URL
% ep
, rf_id
, note
=note
,
33 data
=urlencode_postdata(query
), headers
=headers
)
35 def _parse_rf_item(self
, rf_item
):
36 event_name
= rf_item
.get('eventName')
37 title
= rf_item
['title']
38 description
= clean_html(rf_item
.get('abstract'))
39 presenter_name
= try_get(rf_item
, lambda x
: x
['participants'][0]['fullName'])
40 bc_id
= rf_item
['videos'][0]['url']
41 bc_url
= self
.BRIGHTCOVE_URL_TEMPLATE
% bc_id
42 duration
= float_or_none(try_get(rf_item
, lambda x
: x
['times'][0]['length']))
43 location
= try_get(rf_item
, lambda x
: x
['times'][0]['room'])
46 duration
= duration
* 60
49 '_type': 'url_transparent',
51 'ie_key': 'BrightcoveNew',
53 'description': description
,
55 'creator': presenter_name
,
61 class CiscoLiveSessionIE(CiscoLiveBaseIE
):
62 _VALID_URL
= r
'https?://(?:www\.)?ciscolive(?:\.cisco)?\.com/[^#]*#/session/(?P<id>[^/?&]+)'
64 'url': 'https://ciscolive.cisco.com/on-demand-library/?#/session/1423353499155001FoSs',
65 'md5': 'c98acf395ed9c9f766941c70f5352e22',
67 'id': '5803694304001',
69 'title': '13 Smart Automations to Monitor Your Cisco IOS Network',
70 'description': 'md5:ec4a436019e09a918dec17714803f7cc',
71 'timestamp': 1530305395,
72 'upload_date': '20180629',
73 'uploader_id': '5647924234001',
74 'location': '16B Mezz.',
77 'url': 'https://www.ciscolive.com/global/on-demand-library.html?search.event=ciscoliveemea2019#/session/15361595531500013WOU',
78 'only_matching': True,
80 'url': 'https://www.ciscolive.com/global/on-demand-library.html?#/session/1490051371645001kNaS',
81 'only_matching': True,
84 def _real_extract(self
, url
):
85 rf_id
= self
._match
_id
(url
)
86 rf_result
= self
._call
_api
('session', rf_id
, {'id': rf_id
}, url
)
87 return self
._parse
_rf
_item
(rf_result
['items'][0])
90 class CiscoLiveSearchIE(CiscoLiveBaseIE
):
91 _VALID_URL
= r
'https?://(?:www\.)?ciscolive(?:\.cisco)?\.com/(?:global/)?on-demand-library(?:\.html|/)'
93 'url': 'https://ciscolive.cisco.com/on-demand-library/?search.event=ciscoliveus2018&search.technicallevel=scpsSkillLevel_aintroductory&search.focus=scpsSessionFocus_designAndDeployment#/',
95 'title': 'Search query',
99 'url': 'https://ciscolive.cisco.com/on-demand-library/?search.technology=scpsTechnology_applicationDevelopment&search.technology=scpsTechnology_ipv6&search.focus=scpsSessionFocus_troubleshootingTroubleshooting#/',
100 'only_matching': True,
102 'url': 'https://www.ciscolive.com/global/on-demand-library.html?search.technicallevel=scpsSkillLevel_aintroductory&search.event=ciscoliveemea2019&search.technology=scpsTechnology_dataCenter&search.focus=scpsSessionFocus_bestPractices#/',
103 'only_matching': True,
107 def suitable(cls
, url
):
108 return False if CiscoLiveSessionIE
.suitable(url
) else super().suitable(url
)
111 def _check_bc_id_exists(rf_item
):
112 return int_or_none(try_get(rf_item
, lambda x
: x
['videos'][0]['url'])) is not None
114 def _entries(self
, query
, url
):
117 for page_num
in itertools
.count(1):
118 results
= self
._call
_api
(
119 'search', None, query
, url
,
120 f
'Downloading search JSON page {page_num}')
121 sl
= try_get(results
, lambda x
: x
['sectionList'][0], dict)
124 items
= results
.get('items')
125 if not items
or not isinstance(items
, list):
128 if not isinstance(item
, dict):
130 if not self
._check
_bc
_id
_exists
(item
):
132 yield self
._parse
_rf
_item
(item
)
133 size
= int_or_none(results
.get('size'))
136 total
= int_or_none(results
.get('total'))
137 if total
is not None and query
['from'] + query
['size'] > total
:
139 query
['from'] += query
['size']
141 def _real_extract(self
, url
):
142 query
= parse_qs(url
)
143 query
['type'] = 'session'
144 return self
.playlist_result(
145 self
._entries
(query
, url
), playlist_title
='Search query')