1 from .common
import InfoExtractor
2 from ..networking
.exceptions
import HTTPError
13 class ThreeQSDNIE(InfoExtractor
):
16 _VALID_URL
= r
'https?://playout\.3qsdn\.com/(?P<id>[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})'
17 _EMBED_REGEX
= [rf
'<iframe[^>]+\b(?:data-)?src=(["\'])(?P<url>{_VALID_URL}.*?)\1']
19 # https://player.3qsdn.com/demo.html
20 'url': 'https://playout.3qsdn.com/7201c779-6b3c-11e7-a40e-002590c750be',
21 'md5': '64a57396b16fa011b15e0ea60edce918',
23 'id': '7201c779-6b3c-11e7-a40e-002590c750be',
27 'description': 'Video Ads Demo',
28 'timestamp': 1500334803,
29 'upload_date': '20170717',
35 'expected_warnings': ['Unknown MIME type application/mp4 in DASH manifest'],
38 'url': 'https://playout.3qsdn.com/66e68995-11ca-11e8-9273-002590c750be',
40 'id': '66e68995-11ca-11e8-9273-002590c750be',
42 'title': 're:^66e68995-11ca-11e8-9273-002590c750be [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
46 'skip_download': True, # m3u8 downloads
50 'url': 'http://playout.3qsdn.com/9edf36e0-6bf2-11e2-a16a-9acf09e2db48',
51 'only_matching': True,
53 # live audio stream with some 404 URLs
54 'url': 'http://playout.3qsdn.com/ac5c3186-777a-11e2-9c30-9acf09e2db48',
55 'only_matching': True,
57 # geo restricted with 'This content is not available in your country'
58 'url': 'http://playout.3qsdn.com/d63a3ffe-75e8-11e2-9c30-9acf09e2db48',
59 'only_matching': True,
61 # geo restricted with 'playout.3qsdn.com/forbidden'
62 'url': 'http://playout.3qsdn.com/8e330f26-6ae2-11e2-a16a-9acf09e2db48',
63 'only_matching': True,
65 # live video with rtmp link
66 'url': 'https://playout.3qsdn.com/6092bb9e-8f72-11e4-a173-002590c750be',
67 'only_matching': True,
69 # ondemand from http://www.philharmonie.tv/veranstaltung/26/
70 'url': 'http://playout.3qsdn.com/0280d6b9-1215-11e6-b427-0cc47a188158?protocol=http',
71 'only_matching': True,
74 'url': 'https://playout.3qsdn.com/d755d94b-4ab9-11e3-9162-0025907ad44f?js=true',
75 'only_matching': True,
78 def _extract_from_webpage(self
, url
, webpage
):
79 for res
in super()._extract
_from
_webpage
(url
, webpage
):
82 '_type': 'url_transparent',
83 'uploader': self
._search
_regex
(r
'^(?:https?://)?([^/]*)/.*', url
, 'video uploader'),
86 def _real_extract(self
, url
):
87 video_id
= self
._match
_id
(url
)
90 config
= self
._download
_json
(
91 url
.replace('://playout.3qsdn.com/', '://playout.3qsdn.com/config/'), video_id
)
92 except ExtractorError
as e
:
93 if isinstance(e
.cause
, HTTPError
) and e
.cause
.status
== 401:
94 self
.raise_geo_restricted()
97 live
= config
.get('streamContent') == 'live'
98 aspect
= float_or_none(config
.get('aspect'))
102 for source_type
, source
in (config
.get('sources') or {}).items():
105 if source_type
== 'dash':
106 fmts
, subs
= self
._extract
_mpd
_formats
_and
_subtitles
(
107 source
, video_id
, mpd_id
='mpd', fatal
=False)
109 subtitles
= self
._merge
_subtitles
(subtitles
, subs
)
110 elif source_type
== 'hls':
111 fmts
, subs
= self
._extract
_m
3u8_formats
_and
_subtitles
(
112 source
, video_id
, 'mp4', live
=live
, m3u8_id
='hls', fatal
=False)
114 subtitles
= self
._merge
_subtitles
(subtitles
, subs
)
115 elif source_type
== 'progressive':
118 if not (src
and self
._is
_valid
_url
(src
, video_id
)):
120 ext
= determine_ext(src
)
121 height
= int_or_none(s
.get('height'))
124 'format_id': join_nonempty('http', ext
, height
and f
'{height}p'),
126 'source_preference': 0,
128 'vcodec': 'none' if height
== 0 else None,
129 'width': int(height
* aspect
) if height
and aspect
else None,
132 for subtitle
in (config
.get('subtitles') or []):
133 src
= subtitle
.get('src')
136 subtitles
.setdefault(subtitle
.get('label') or 'eng', []).append({
140 title
= config
.get('title') or video_id
145 'thumbnail': config
.get('poster') or None,
146 'description': config
.get('description') or None,
147 'timestamp': parse_iso8601(config
.get('upload_date')),
148 'duration': float_or_none(config
.get('vlength')) or None,
151 'subtitles': subtitles
,
152 # It seems like this would be correctly handled by default
153 # However, unless someone can confirm this, the old
154 # behaviour is being kept as-is
155 '_format_sort_fields': ('res', 'source_preference'),