+class NPODataMidEmbedIE(InfoExtractor):
+ def _real_extract(self, url):
+ display_id = self._match_id(url)
+ webpage = self._download_webpage(url, display_id)
+ video_id = self._search_regex(
+ r'data-mid=(["\'])(?P<id>(?:(?!\1).)+)\1', webpage, 'video_id', group='id')
+ return {
+ '_type': 'url_transparent',
+ 'ie_key': 'NPO',
+ 'url': 'npo:%s' % video_id,
+ 'display_id': display_id
+ }
+
+
+class SchoolTVIE(NPODataMidEmbedIE):
+ IE_NAME = 'schooltv'
+ _VALID_URL = r'https?://(?:www\.)?schooltv\.nl/video/(?P<id>[^/?#&]+)'
+
+ _TEST = {
+ 'url': 'http://www.schooltv.nl/video/ademhaling-de-hele-dag-haal-je-adem-maar-wat-gebeurt-er-dan-eigenlijk-in-je-lichaam/',
+ 'info_dict': {
+ 'id': 'WO_NTR_429477',
+ 'display_id': 'ademhaling-de-hele-dag-haal-je-adem-maar-wat-gebeurt-er-dan-eigenlijk-in-je-lichaam',
+ 'title': 'Ademhaling: De hele dag haal je adem. Maar wat gebeurt er dan eigenlijk in je lichaam?',
+ 'ext': 'mp4',
+ 'description': 'md5:abfa0ff690adb73fd0297fd033aaa631'
+ },
+ 'params': {
+ # Skip because of m3u8 download
+ 'skip_download': True
+ }
+ }
+
+
+class HetKlokhuisIE(NPODataMidEmbedIE):
+ IE_NAME = 'hetklokhuis'
+ _VALID_URL = r'https?://(?:www\.)?hetklokhuis\.nl/[^/]+/\d+/(?P<id>[^/?#&]+)'
+
+ _TEST = {
+ 'url': 'http://hetklokhuis.nl/tv-uitzending/3471/Zwaartekrachtsgolven',
+ 'info_dict': {
+ 'id': 'VPWON_1260528',
+ 'display_id': 'Zwaartekrachtsgolven',
+ 'ext': 'm4v',
+ 'title': 'Het Klokhuis: Zwaartekrachtsgolven',
+ 'description': 'md5:c94f31fb930d76c2efa4a4a71651dd48',
+ 'upload_date': '20170223',
+ },
+ 'params': {
+ 'skip_download': True
+ }
+ }
+
+
+class NPOPlaylistBaseIE(NPOIE):
+ def _real_extract(self, url):
+ playlist_id = self._match_id(url)
+
+ webpage = self._download_webpage(url, playlist_id)
+
+ entries = [
+ self.url_result('npo:%s' % video_id if not video_id.startswith('http') else video_id)
+ for video_id in orderedSet(re.findall(self._PLAYLIST_ENTRY_RE, webpage))
+ ]
+
+ playlist_title = self._html_search_regex(
+ self._PLAYLIST_TITLE_RE, webpage, 'playlist title',
+ default=None) or self._og_search_title(webpage)
+
+ return self.playlist_result(entries, playlist_id, playlist_title)
+
+
+class VPROIE(NPOPlaylistBaseIE):