1 from .common
import InfoExtractor
11 class TubeTuGrazBaseIE(InfoExtractor
):
12 _NETRC_MACHINE
= 'tubetugraz'
14 _API_EPISODE
= 'https://tube.tugraz.at/search/episode.json'
15 _FORMAT_TYPES
= ('presentation', 'presenter')
17 def _perform_login(self
, username
, password
):
18 urlh
= self
._request
_webpage
(
19 'https://tube.tugraz.at/Shibboleth.sso/Login?target=/paella/ui/index.html',
20 None, fatal
=False, note
='downloading login page', errnote
='unable to fetch login page')
24 urlh
= self
._request
_webpage
(
25 urlh
.geturl(), None, fatal
=False, headers
={'referer': urlh.geturl()}
,
26 note
='logging in', errnote
='unable to log in', data
=urlencode_postdata({
28 '_eventId_proceed': '',
29 'j_username': username
,
30 'j_password': password
33 if urlh
and urlh
.geturl() != 'https://tube.tugraz.at/paella/ui/index.html':
34 self
.report_warning('unable to login: incorrect password')
36 def _extract_episode(self
, episode_info
):
37 id = episode_info
.get('id')
38 formats
= list(self
._extract
_formats
(
39 traverse_obj(episode_info
, ('mediapackage', 'media', 'track')), id))
41 title
= traverse_obj(episode_info
, ('mediapackage', 'title'), 'dcTitle')
42 series_title
= traverse_obj(episode_info
, ('mediapackage', 'seriestitle'))
43 creator
= ', '.join(variadic(traverse_obj(
44 episode_info
, ('mediapackage', 'creators', 'creator'), 'dcCreator', default
='')))
48 'creator': creator
or None,
49 'duration': traverse_obj(episode_info
, ('mediapackage', 'duration'), 'dcExtent'),
50 'series': series_title
,
51 'series_id': traverse_obj(episode_info
, ('mediapackage', 'series'), 'dcIsPartOf'),
52 'episode': series_title
and title
,
56 def _set_format_type(self
, formats
, type):
58 f
['format_note'] = type
59 if not type.startswith(self
._FORMAT
_TYPES
[0]):
63 def _extract_formats(self
, format_list
, id):
64 has_hls
, has_dash
= False, False
66 for format_info
in format_list
or []:
67 url
= traverse_obj(format_info
, ('tags', 'url'), 'url')
71 type = format_info
.get('type') or 'unknown'
72 transport
= (format_info
.get('transport') or 'https').lower()
74 if transport
== 'https':
77 'abr': float_or_none(traverse_obj(format_info
, ('audio', 'bitrate')), 1000),
78 'vbr': float_or_none(traverse_obj(format_info
, ('video', 'bitrate')), 1000),
79 'fps': traverse_obj(format_info
, ('video', 'framerate')),
80 **parse_resolution(traverse_obj(format_info
, ('video', 'resolution'))),
82 elif transport
== 'hls':
83 has_hls
, formats
= True, self
._extract
_m
3u8_formats
(
84 url
, id, 'mp4', fatal
=False, note
=f
'downloading {type} HLS manifest')
85 elif transport
== 'dash':
86 has_dash
, formats
= True, self
._extract
_mpd
_formats
(
87 url
, id, fatal
=False, note
=f
'downloading {type} DASH manifest')
89 # RTMP, HDS, SMOOTH, and unknown formats
90 # - RTMP url fails on every tested entry until now
91 # - HDS url 404's on every tested entry until now
92 # - SMOOTH url 404's on every tested entry until now
95 yield from self
._set
_format
_type
(formats
, type)
97 # TODO: Add test for these
98 for type in self
._FORMAT
_TYPES
:
100 hls_formats
= self
._extract
_m
3u8_formats
(
101 f
'https://wowza.tugraz.at/matterhorn_engage/smil:engage-player_{id}_{type}.smil/playlist.m3u8',
102 id, 'mp4', fatal
=False, note
=f
'Downloading {type} HLS manifest', errnote
=False) or []
103 yield from self
._set
_format
_type
(hls_formats
, type)
106 dash_formats
= self
._extract
_mpd
_formats
(
107 f
'https://wowza.tugraz.at/matterhorn_engage/smil:engage-player_{id}_{type}.smil/manifest_mpm4sav_mvlist.mpd',
108 id, fatal
=False, note
=f
'Downloading {type} DASH manifest', errnote
=False)
109 yield from self
._set
_format
_type
(dash_formats
, type)
112 class TubeTuGrazIE(TubeTuGrazBaseIE
):
113 IE_DESC
= 'tube.tugraz.at'
115 _VALID_URL
= r
'''(?x)
116 https?://tube\.tugraz\.at/paella/ui/watch.html\?id=
117 (?P<id>[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12})
121 'url': 'https://tube.tugraz.at/paella/ui/watch.html?id=f2634392-e40e-4ac7-9ddc-47764aa23d40',
122 'md5': 'a23a3d5c9aaca2b84932fdba66e17145',
124 'id': 'f2634392-e40e-4ac7-9ddc-47764aa23d40',
126 'title': '#6 (23.11.2017)',
127 'episode': '#6 (23.11.2017)',
128 'series': '[INB03001UF] Einführung in die strukturierte Programmierung',
129 'creator': 'Safran C',
131 'series_id': 'b1192fff-2aa7-4bf0-a5cf-7b15c3bd3b34',
134 'url': 'https://tube.tugraz.at/paella/ui/watch.html?id=2df6d787-e56a-428d-8ef4-d57f07eef238',
135 'md5': 'de0d854a56bf7318d2b693fe1adb89a5',
137 'id': '2df6d787-e56a-428d-8ef4-d57f07eef238',
138 'title': 'TubeTuGraz video #2df6d787-e56a-428d-8ef4-d57f07eef238',
141 'expected_warnings': ['Extractor failed to obtain "title"'],
145 def _real_extract(self
, url
):
146 video_id
= self
._match
_id
(url
)
147 episode_data
= self
._download
_json
(
148 self
._API
_EPISODE
, video_id
, query
={'id': video_id, 'limit': 1}
, note
='Downloading episode metadata')
150 episode_info
= traverse_obj(episode_data
, ('search-results', 'result'), default
={'id': video_id}
)
151 return self
._extract
_episode
(episode_info
)
154 class TubeTuGrazSeriesIE(TubeTuGrazBaseIE
):
155 _VALID_URL
= r
'''(?x)
156 https?://tube\.tugraz\.at/paella/ui/browse\.html\?series=
157 (?P<id>[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12})
160 'url': 'https://tube.tugraz.at/paella/ui/browse.html?series=0e6351b7-c372-491e-8a49-2c9b7e21c5a6',
161 'id': '0e6351b7-c372-491e-8a49-2c9b7e21c5a6',
163 'id': '0e6351b7-c372-491e-8a49-2c9b7e21c5a6',
164 'title': '[209351] Strassenwesen',
169 'id': 'ee17ce5d-34e2-48b7-a76a-fed148614e11',
170 'series_id': '0e6351b7-c372-491e-8a49-2c9b7e21c5a6',
172 'title': '#4 Detailprojekt',
173 'episode': '#4 Detailprojekt',
174 'series': '[209351] Strassenwesen',
175 'creator': 'Neuhold R',
181 'id': '87350498-799a-44d3-863f-d1518a98b114',
182 'series_id': '0e6351b7-c372-491e-8a49-2c9b7e21c5a6',
184 'title': '#3 Generelles Projekt',
185 'episode': '#3 Generelles Projekt',
186 'series': '[209351] Strassenwesen',
187 'creator': 'Neuhold R',
193 'id': '778599ea-489e-4189-9e05-3b4888e19bcd',
194 'series_id': '0e6351b7-c372-491e-8a49-2c9b7e21c5a6',
196 'title': '#2 Vorprojekt',
197 'episode': '#2 Vorprojekt',
198 'series': '[209351] Strassenwesen',
199 'creator': 'Neuhold R',
205 'id': '75e4c71c-d99d-4e56-b0e6-4f2bcdf11f29',
206 'series_id': '0e6351b7-c372-491e-8a49-2c9b7e21c5a6',
208 'title': '#1 Variantenstudium',
209 'episode': '#1 Variantenstudium',
210 'series': '[209351] Strassenwesen',
211 'creator': 'Neuhold R',
216 'min_playlist_count': 4
219 def _real_extract(self
, url
):
220 id = self
._match
_id
(url
)
221 episodes_data
= self
._download
_json
(self
._API
_EPISODE
, id, query
={'sid': id}
, note
='Downloading episode list')
222 series_data
= self
._download
_json
(
223 'https://tube.tugraz.at/series/series.json', id, fatal
=False,
224 note
='downloading series metadata', errnote
='failed to download series metadata',
231 return self
.playlist_result(
232 map(self
._extract
_episode
, episodes_data
['search-results']['result']), id,
233 traverse_obj(series_data
, ('catalogs', 0, 'http://purl.org/dc/terms/', 'title', 0, 'value')))