Compare commits
33 Commits
2016.07.13
...
2016.07.17
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8188b923db | ||
![]() |
d993a1354d | ||
![]() |
e8882e7043 | ||
![]() |
1056821799 | ||
![]() |
890e6d3309 | ||
![]() |
246080d378 | ||
![]() |
b1ea680270 | ||
![]() |
45550d1039 | ||
![]() |
7cdfc4c90f | ||
![]() |
af21f56f98 | ||
![]() |
1a8f0773b6 | ||
![]() |
59cc5bd8bf | ||
![]() |
49bc16b95e | ||
![]() |
a2f9ca1e67 | ||
![]() |
371ddb14fe | ||
![]() |
998895dffa | ||
![]() |
aadd3ce21f | ||
![]() |
ae7b846203 | ||
![]() |
21ba7d0981 | ||
![]() |
691fbe7f98 | ||
![]() |
2e221ca3a8 | ||
![]() |
317f7ab634 | ||
![]() |
23495d6a39 | ||
![]() |
224db034ab | ||
![]() |
ad27649be3 | ||
![]() |
84571be645 | ||
![]() |
7b0d333a7e | ||
![]() |
342f0c3682 | ||
![]() |
38e0f16a94 | ||
![]() |
e910fe2fe4 | ||
![]() |
233b58dec7 | ||
![]() |
c39b2ed990 | ||
![]() |
35ec86689c |
6
.github/ISSUE_TEMPLATE.md
vendored
6
.github/ISSUE_TEMPLATE.md
vendored
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Make sure you are using the *latest* version: run `youtube-dl --version` and ensure your version is *2016.07.13*. If it's not read [this FAQ entry](https://github.com/rg3/youtube-dl/blob/master/README.md#how-do-i-update-youtube-dl) and update. Issues with outdated version will be rejected.
|
### Make sure you are using the *latest* version: run `youtube-dl --version` and ensure your version is *2016.07.17*. If it's not read [this FAQ entry](https://github.com/rg3/youtube-dl/blob/master/README.md#how-do-i-update-youtube-dl) and update. Issues with outdated version will be rejected.
|
||||||
- [ ] I've **verified** and **I assure** that I'm running youtube-dl **2016.07.13**
|
- [ ] I've **verified** and **I assure** that I'm running youtube-dl **2016.07.17**
|
||||||
|
|
||||||
### Before submitting an *issue* make sure you have:
|
### Before submitting an *issue* make sure you have:
|
||||||
- [ ] At least skimmed through [README](https://github.com/rg3/youtube-dl/blob/master/README.md) and **most notably** [FAQ](https://github.com/rg3/youtube-dl#faq) and [BUGS](https://github.com/rg3/youtube-dl#bugs) sections
|
- [ ] At least skimmed through [README](https://github.com/rg3/youtube-dl/blob/master/README.md) and **most notably** [FAQ](https://github.com/rg3/youtube-dl#faq) and [BUGS](https://github.com/rg3/youtube-dl#bugs) sections
|
||||||
@@ -35,7 +35,7 @@ $ youtube-dl -v <your command line>
|
|||||||
[debug] User config: []
|
[debug] User config: []
|
||||||
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
|
||||||
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
||||||
[debug] youtube-dl version 2016.07.13
|
[debug] youtube-dl version 2016.07.17
|
||||||
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
||||||
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
||||||
[debug] Proxy map: {}
|
[debug] Proxy map: {}
|
||||||
|
@@ -17,7 +17,7 @@ youtube-dl - download videos from youtube.com or other video platforms
|
|||||||
|
|
||||||
To install it right away for all UNIX users (Linux, OS X, etc.), type:
|
To install it right away for all UNIX users (Linux, OS X, etc.), type:
|
||||||
|
|
||||||
sudo curl -L https://yt-dl.org/latest/youtube-dl -o /usr/local/bin/youtube-dl
|
sudo curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl
|
||||||
sudo chmod a+rx /usr/local/bin/youtube-dl
|
sudo chmod a+rx /usr/local/bin/youtube-dl
|
||||||
|
|
||||||
If you do not have curl, you can alternatively use a recent wget:
|
If you do not have curl, you can alternatively use a recent wget:
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
- **8tracks**
|
- **8tracks**
|
||||||
- **91porn**
|
- **91porn**
|
||||||
- **9gag**
|
- **9gag**
|
||||||
|
- **9now.com.au**
|
||||||
- **abc.net.au**
|
- **abc.net.au**
|
||||||
- **Abc7News**
|
- **Abc7News**
|
||||||
- **abcnews**
|
- **abcnews**
|
||||||
@@ -448,6 +449,7 @@
|
|||||||
- **niconico**: ニコニコ動画
|
- **niconico**: ニコニコ動画
|
||||||
- **NiconicoPlaylist**
|
- **NiconicoPlaylist**
|
||||||
- **NineCNineMedia**
|
- **NineCNineMedia**
|
||||||
|
- **Nintendo**
|
||||||
- **njoy**: N-JOY
|
- **njoy**: N-JOY
|
||||||
- **njoy:embed**
|
- **njoy:embed**
|
||||||
- **Noco**
|
- **Noco**
|
||||||
@@ -567,6 +569,7 @@
|
|||||||
- **rtve.es:alacarta**: RTVE a la carta
|
- **rtve.es:alacarta**: RTVE a la carta
|
||||||
- **rtve.es:infantil**: RTVE infantil
|
- **rtve.es:infantil**: RTVE infantil
|
||||||
- **rtve.es:live**: RTVE.es live streams
|
- **rtve.es:live**: RTVE.es live streams
|
||||||
|
- **rtve.es:television**
|
||||||
- **RTVNH**
|
- **RTVNH**
|
||||||
- **Rudo**
|
- **Rudo**
|
||||||
- **RUHD**
|
- **RUHD**
|
||||||
@@ -643,6 +646,7 @@
|
|||||||
- **stanfordoc**: Stanford Open ClassRoom
|
- **stanfordoc**: Stanford Open ClassRoom
|
||||||
- **Steam**
|
- **Steam**
|
||||||
- **Stitcher**
|
- **Stitcher**
|
||||||
|
- **Streamable**
|
||||||
- **streamcloud.eu**
|
- **streamcloud.eu**
|
||||||
- **StreamCZ**
|
- **StreamCZ**
|
||||||
- **StreetVoice**
|
- **StreetVoice**
|
||||||
|
@@ -335,6 +335,40 @@ class TestFormatSelection(unittest.TestCase):
|
|||||||
downloaded = ydl.downloaded_info_dicts[0]
|
downloaded = ydl.downloaded_info_dicts[0]
|
||||||
self.assertEqual(downloaded['format_id'], f1['format_id'])
|
self.assertEqual(downloaded['format_id'], f1['format_id'])
|
||||||
|
|
||||||
|
def test_audio_only_extractor_format_selection(self):
|
||||||
|
# For extractors with incomplete formats (all formats are audio-only or
|
||||||
|
# video-only) best and worst should fallback to corresponding best/worst
|
||||||
|
# video-only or audio-only formats (as per
|
||||||
|
# https://github.com/rg3/youtube-dl/pull/5556)
|
||||||
|
formats = [
|
||||||
|
{'format_id': 'low', 'ext': 'mp3', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL},
|
||||||
|
{'format_id': 'high', 'ext': 'mp3', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL},
|
||||||
|
]
|
||||||
|
info_dict = _make_result(formats)
|
||||||
|
|
||||||
|
ydl = YDL({'format': 'best'})
|
||||||
|
ydl.process_ie_result(info_dict.copy())
|
||||||
|
downloaded = ydl.downloaded_info_dicts[0]
|
||||||
|
self.assertEqual(downloaded['format_id'], 'high')
|
||||||
|
|
||||||
|
ydl = YDL({'format': 'worst'})
|
||||||
|
ydl.process_ie_result(info_dict.copy())
|
||||||
|
downloaded = ydl.downloaded_info_dicts[0]
|
||||||
|
self.assertEqual(downloaded['format_id'], 'low')
|
||||||
|
|
||||||
|
def test_format_not_available(self):
|
||||||
|
formats = [
|
||||||
|
{'format_id': 'regular', 'ext': 'mp4', 'height': 360, 'url': TEST_URL},
|
||||||
|
{'format_id': 'video', 'ext': 'mp4', 'height': 720, 'acodec': 'none', 'url': TEST_URL},
|
||||||
|
]
|
||||||
|
info_dict = _make_result(formats)
|
||||||
|
|
||||||
|
# This must fail since complete video-audio format does not match filter
|
||||||
|
# and extractor does not provide incomplete only formats (i.e. only
|
||||||
|
# video-only or audio-only).
|
||||||
|
ydl = YDL({'format': 'best[height>360]'})
|
||||||
|
self.assertRaises(ExtractorError, ydl.process_ie_result, info_dict.copy())
|
||||||
|
|
||||||
def test_invalid_format_specs(self):
|
def test_invalid_format_specs(self):
|
||||||
def assert_syntax_error(format_spec):
|
def assert_syntax_error(format_spec):
|
||||||
ydl = YDL({'format': format_spec})
|
ydl = YDL({'format': format_spec})
|
||||||
|
@@ -5,6 +5,7 @@ from __future__ import absolute_import, unicode_literals
|
|||||||
|
|
||||||
import collections
|
import collections
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
import errno
|
import errno
|
||||||
import fileinput
|
import fileinput
|
||||||
@@ -1051,9 +1052,9 @@ class YoutubeDL(object):
|
|||||||
if isinstance(selector, list):
|
if isinstance(selector, list):
|
||||||
fs = [_build_selector_function(s) for s in selector]
|
fs = [_build_selector_function(s) for s in selector]
|
||||||
|
|
||||||
def selector_function(formats):
|
def selector_function(ctx):
|
||||||
for f in fs:
|
for f in fs:
|
||||||
for format in f(formats):
|
for format in f(ctx):
|
||||||
yield format
|
yield format
|
||||||
return selector_function
|
return selector_function
|
||||||
elif selector.type == GROUP:
|
elif selector.type == GROUP:
|
||||||
@@ -1061,17 +1062,17 @@ class YoutubeDL(object):
|
|||||||
elif selector.type == PICKFIRST:
|
elif selector.type == PICKFIRST:
|
||||||
fs = [_build_selector_function(s) for s in selector.selector]
|
fs = [_build_selector_function(s) for s in selector.selector]
|
||||||
|
|
||||||
def selector_function(formats):
|
def selector_function(ctx):
|
||||||
for f in fs:
|
for f in fs:
|
||||||
picked_formats = list(f(formats))
|
picked_formats = list(f(ctx))
|
||||||
if picked_formats:
|
if picked_formats:
|
||||||
return picked_formats
|
return picked_formats
|
||||||
return []
|
return []
|
||||||
elif selector.type == SINGLE:
|
elif selector.type == SINGLE:
|
||||||
format_spec = selector.selector
|
format_spec = selector.selector
|
||||||
|
|
||||||
def selector_function(formats):
|
def selector_function(ctx):
|
||||||
formats = list(formats)
|
formats = list(ctx['formats'])
|
||||||
if not formats:
|
if not formats:
|
||||||
return
|
return
|
||||||
if format_spec == 'all':
|
if format_spec == 'all':
|
||||||
@@ -1084,9 +1085,10 @@ class YoutubeDL(object):
|
|||||||
if f.get('vcodec') != 'none' and f.get('acodec') != 'none']
|
if f.get('vcodec') != 'none' and f.get('acodec') != 'none']
|
||||||
if audiovideo_formats:
|
if audiovideo_formats:
|
||||||
yield audiovideo_formats[format_idx]
|
yield audiovideo_formats[format_idx]
|
||||||
# for audio only (soundcloud) or video only (imgur) urls, select the best/worst audio format
|
# for extractors with incomplete formats (audio only (soundcloud)
|
||||||
elif (all(f.get('acodec') != 'none' for f in formats) or
|
# or video only (imgur)) we will fallback to best/worst
|
||||||
all(f.get('vcodec') != 'none' for f in formats)):
|
# {video,audio}-only format
|
||||||
|
elif ctx['incomplete_formats']:
|
||||||
yield formats[format_idx]
|
yield formats[format_idx]
|
||||||
elif format_spec == 'bestaudio':
|
elif format_spec == 'bestaudio':
|
||||||
audio_formats = [
|
audio_formats = [
|
||||||
@@ -1160,17 +1162,18 @@ class YoutubeDL(object):
|
|||||||
}
|
}
|
||||||
video_selector, audio_selector = map(_build_selector_function, selector.selector)
|
video_selector, audio_selector = map(_build_selector_function, selector.selector)
|
||||||
|
|
||||||
def selector_function(formats):
|
def selector_function(ctx):
|
||||||
formats = list(formats)
|
for pair in itertools.product(
|
||||||
for pair in itertools.product(video_selector(formats), audio_selector(formats)):
|
video_selector(copy.deepcopy(ctx)), audio_selector(copy.deepcopy(ctx))):
|
||||||
yield _merge(pair)
|
yield _merge(pair)
|
||||||
|
|
||||||
filters = [self._build_format_filter(f) for f in selector.filters]
|
filters = [self._build_format_filter(f) for f in selector.filters]
|
||||||
|
|
||||||
def final_selector(formats):
|
def final_selector(ctx):
|
||||||
|
ctx_copy = copy.deepcopy(ctx)
|
||||||
for _filter in filters:
|
for _filter in filters:
|
||||||
formats = list(filter(_filter, formats))
|
ctx_copy['formats'] = list(filter(_filter, ctx_copy['formats']))
|
||||||
return selector_function(formats)
|
return selector_function(ctx_copy)
|
||||||
return final_selector
|
return final_selector
|
||||||
|
|
||||||
stream = io.BytesIO(format_spec.encode('utf-8'))
|
stream = io.BytesIO(format_spec.encode('utf-8'))
|
||||||
@@ -1377,7 +1380,34 @@ class YoutubeDL(object):
|
|||||||
req_format_list.append('best')
|
req_format_list.append('best')
|
||||||
req_format = '/'.join(req_format_list)
|
req_format = '/'.join(req_format_list)
|
||||||
format_selector = self.build_format_selector(req_format)
|
format_selector = self.build_format_selector(req_format)
|
||||||
formats_to_download = list(format_selector(formats))
|
|
||||||
|
# While in format selection we may need to have an access to the original
|
||||||
|
# format set in order to calculate some metrics or do some processing.
|
||||||
|
# For now we need to be able to guess whether original formats provided
|
||||||
|
# by extractor are incomplete or not (i.e. whether extractor provides only
|
||||||
|
# video-only or audio-only formats) for proper formats selection for
|
||||||
|
# extractors with such incomplete formats (see
|
||||||
|
# https://github.com/rg3/youtube-dl/pull/5556).
|
||||||
|
# Since formats may be filtered during format selection and may not match
|
||||||
|
# the original formats the results may be incorrect. Thus original formats
|
||||||
|
# or pre-calculated metrics should be passed to format selection routines
|
||||||
|
# as well.
|
||||||
|
# We will pass a context object containing all necessary additional data
|
||||||
|
# instead of just formats.
|
||||||
|
# This fixes incorrect format selection issue (see
|
||||||
|
# https://github.com/rg3/youtube-dl/issues/10083).
|
||||||
|
incomplete_formats = (
|
||||||
|
# All formats are video-only or
|
||||||
|
all(f.get('vcodec') != 'none' and f.get('acodec') == 'none' for f in formats) or
|
||||||
|
# all formats are audio-only
|
||||||
|
all(f.get('vcodec') == 'none' and f.get('acodec') != 'none' for f in formats))
|
||||||
|
|
||||||
|
ctx = {
|
||||||
|
'formats': formats,
|
||||||
|
'incomplete_formats': incomplete_formats,
|
||||||
|
}
|
||||||
|
|
||||||
|
formats_to_download = list(format_selector(ctx))
|
||||||
if not formats_to_download:
|
if not formats_to_download:
|
||||||
raise ExtractorError('requested format not available',
|
raise ExtractorError('requested format not available',
|
||||||
expected=True)
|
expected=True)
|
||||||
|
@@ -20,7 +20,7 @@ from ..compat import compat_etree_fromstring
|
|||||||
|
|
||||||
class ARDMediathekIE(InfoExtractor):
|
class ARDMediathekIE(InfoExtractor):
|
||||||
IE_NAME = 'ARD:mediathek'
|
IE_NAME = 'ARD:mediathek'
|
||||||
_VALID_URL = r'^https?://(?:(?:www\.)?ardmediathek\.de|mediathek\.daserste\.de)/(?:.*/)(?P<video_id>[0-9]+|[^0-9][^/\?]+)[^/\?]*(?:\?.*)?'
|
_VALID_URL = r'^https?://(?:(?:www\.)?ardmediathek\.de|mediathek\.(?:daserste|rbb-online)\.de)/(?:.*/)(?P<video_id>[0-9]+|[^0-9][^/\?]+)[^/\?]*(?:\?.*)?'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.ardmediathek.de/tv/Dokumentation-und-Reportage/Ich-liebe-das-Leben-trotzdem/rbb-Fernsehen/Video?documentId=29582122&bcastId=3822114',
|
'url': 'http://www.ardmediathek.de/tv/Dokumentation-und-Reportage/Ich-liebe-das-Leben-trotzdem/rbb-Fernsehen/Video?documentId=29582122&bcastId=3822114',
|
||||||
|
@@ -44,8 +44,6 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
|
|
||||||
_MEDIASELECTION_NS = 'http://bbc.co.uk/2008/mp/mediaselection'
|
_MEDIASELECTION_NS = 'http://bbc.co.uk/2008/mp/mediaselection'
|
||||||
_EMP_PLAYLIST_NS = 'http://bbc.co.uk/2008/emp/playlist'
|
_EMP_PLAYLIST_NS = 'http://bbc.co.uk/2008/emp/playlist'
|
||||||
# Unified Streaming Platform
|
|
||||||
_USP_RE = r'/([^/]+)\.ism(?:\.hlsv2\.ism)?/[^/]+\.m3u8'
|
|
||||||
|
|
||||||
_NAMESPACES = (
|
_NAMESPACES = (
|
||||||
_MEDIASELECTION_NS,
|
_MEDIASELECTION_NS,
|
||||||
@@ -57,11 +55,12 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
'url': 'http://www.bbc.co.uk/programmes/b039g8p7',
|
'url': 'http://www.bbc.co.uk/programmes/b039g8p7',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'b039d07m',
|
'id': 'b039d07m',
|
||||||
'ext': 'mp4',
|
'ext': 'flv',
|
||||||
'title': 'Leonard Cohen, Kaleidoscope - BBC Radio 4',
|
'title': 'Leonard Cohen, Kaleidoscope - BBC Radio 4',
|
||||||
'description': 'The Canadian poet and songwriter reflects on his musical career.',
|
'description': 'The Canadian poet and songwriter reflects on his musical career.',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -93,7 +92,7 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
# rtmp download
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
'skip': 'this episode is not currently available',
|
'skip': 'Currently BBC iPlayer TV programmes are available to play in the UK only',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': 'http://www.bbc.co.uk/iplayer/episode/p026c7jt/tomorrows-worlds-the-unearthly-history-of-science-fiction-2-invasion',
|
'url': 'http://www.bbc.co.uk/iplayer/episode/p026c7jt/tomorrows-worlds-the-unearthly-history-of-science-fiction-2-invasion',
|
||||||
@@ -108,7 +107,7 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
# rtmp download
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
'skip': 'this episode is not currently available',
|
'skip': 'Currently BBC iPlayer TV programmes are available to play in the UK only',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.bbc.co.uk/programmes/b04v20dw',
|
'url': 'http://www.bbc.co.uk/programmes/b04v20dw',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -128,12 +127,13 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
'note': 'Audio',
|
'note': 'Audio',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'p022h44j',
|
'id': 'p022h44j',
|
||||||
'ext': 'mp4',
|
'ext': 'flv',
|
||||||
'title': 'BBC Proms Music Guides, Rachmaninov: Symphonic Dances',
|
'title': 'BBC Proms Music Guides, Rachmaninov: Symphonic Dances',
|
||||||
'description': "In this Proms Music Guide, Andrew McGregor looks at Rachmaninov's Symphonic Dances.",
|
'description': "In this Proms Music Guide, Andrew McGregor looks at Rachmaninov's Symphonic Dances.",
|
||||||
'duration': 227,
|
'duration': 227,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
@@ -141,12 +141,13 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
'note': 'Video',
|
'note': 'Video',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'p025c103',
|
'id': 'p025c103',
|
||||||
'ext': 'mp4',
|
'ext': 'flv',
|
||||||
'title': 'Reading and Leeds Festival, 2014, Rae Morris - Closer (Live on BBC Three)',
|
'title': 'Reading and Leeds Festival, 2014, Rae Morris - Closer (Live on BBC Three)',
|
||||||
'description': 'Rae Morris performs Closer for BBC Three at Reading 2014',
|
'description': 'Rae Morris performs Closer for BBC Three at Reading 2014',
|
||||||
'duration': 226,
|
'duration': 226,
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
@@ -162,7 +163,7 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
# rtmp download
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
'skip': 'this episode is not currently available',
|
'skip': 'geolocation',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://www.bbc.co.uk/iplayer/episode/b05zmgwn/royal-academy-summer-exhibition',
|
'url': 'http://www.bbc.co.uk/iplayer/episode/b05zmgwn/royal-academy-summer-exhibition',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -176,7 +177,7 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
# rtmp download
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
'skip': 'this episode is not currently available',
|
'skip': 'geolocation',
|
||||||
}, {
|
}, {
|
||||||
# iptv-all mediaset fails with geolocation however there is no geo restriction
|
# iptv-all mediaset fails with geolocation however there is no geo restriction
|
||||||
# for this programme at all
|
# for this programme at all
|
||||||
@@ -191,17 +192,18 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
# rtmp download
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
'skip': 'this episode is not currently available on BBC iPlayer Radio',
|
'skip': 'Now it\'s really geo-restricted',
|
||||||
}, {
|
}, {
|
||||||
# compact player (https://github.com/rg3/youtube-dl/issues/8147)
|
# compact player (https://github.com/rg3/youtube-dl/issues/8147)
|
||||||
'url': 'http://www.bbc.co.uk/programmes/p028bfkf/player',
|
'url': 'http://www.bbc.co.uk/programmes/p028bfkf/player',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'p028bfkj',
|
'id': 'p028bfkj',
|
||||||
'ext': 'mp4',
|
'ext': 'flv',
|
||||||
'title': 'Extract from BBC documentary Look Stranger - Giant Leeks and Magic Brews',
|
'title': 'Extract from BBC documentary Look Stranger - Giant Leeks and Magic Brews',
|
||||||
'description': 'Extract from BBC documentary Look Stranger - Giant Leeks and Magic Brews',
|
'description': 'Extract from BBC documentary Look Stranger - Giant Leeks and Magic Brews',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
@@ -246,15 +248,9 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
elif transfer_format == 'dash':
|
elif transfer_format == 'dash':
|
||||||
pass
|
pass
|
||||||
elif transfer_format == 'hls':
|
elif transfer_format == 'hls':
|
||||||
is_unified_streaming = re.search(self._USP_RE, href)
|
formats.extend(self._extract_m3u8_formats(
|
||||||
if is_unified_streaming:
|
|
||||||
href = re.sub(self._USP_RE, r'/\1.ism/\1.m3u8', href)
|
|
||||||
m3u8_formats = self._extract_m3u8_formats(
|
|
||||||
href, programme_id, ext='mp4', entry_protocol='m3u8_native',
|
href, programme_id, ext='mp4', entry_protocol='m3u8_native',
|
||||||
m3u8_id=supplier, fatal=False)
|
m3u8_id=supplier, fatal=False))
|
||||||
if is_unified_streaming:
|
|
||||||
self._check_formats(m3u8_formats, programme_id)
|
|
||||||
formats.extend(m3u8_formats)
|
|
||||||
# Direct link
|
# Direct link
|
||||||
else:
|
else:
|
||||||
formats.append({
|
formats.append({
|
||||||
@@ -309,14 +305,13 @@ class BBCCoUkIE(InfoExtractor):
|
|||||||
for connection in self._extract_connections(media):
|
for connection in self._extract_connections(media):
|
||||||
conn_formats = self._extract_connection(connection, programme_id)
|
conn_formats = self._extract_connection(connection, programme_id)
|
||||||
for format in conn_formats:
|
for format in conn_formats:
|
||||||
if format.get('protocol') != 'm3u8_native':
|
format.update({
|
||||||
format.update({
|
'width': width,
|
||||||
'width': width,
|
'height': height,
|
||||||
'height': height,
|
'vbr': vbr,
|
||||||
'vbr': vbr,
|
'vcodec': vcodec,
|
||||||
'vcodec': vcodec,
|
'filesize': file_size,
|
||||||
'filesize': file_size,
|
})
|
||||||
})
|
|
||||||
if service:
|
if service:
|
||||||
format['format_id'] = '%s_%s' % (service, format['format_id'])
|
format['format_id'] = '%s_%s' % (service, format['format_id'])
|
||||||
formats.extend(conn_formats)
|
formats.extend(conn_formats)
|
||||||
|
@@ -27,6 +27,7 @@ from ..utils import (
|
|||||||
unsmuggle_url,
|
unsmuggle_url,
|
||||||
update_url_query,
|
update_url_query,
|
||||||
clean_html,
|
clean_html,
|
||||||
|
mimetype2ext,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -545,14 +546,16 @@ class BrightcoveNewIE(InfoExtractor):
|
|||||||
formats = []
|
formats = []
|
||||||
for source in json_data.get('sources', []):
|
for source in json_data.get('sources', []):
|
||||||
container = source.get('container')
|
container = source.get('container')
|
||||||
source_type = source.get('type')
|
ext = mimetype2ext(source.get('type'))
|
||||||
src = source.get('src')
|
src = source.get('src')
|
||||||
if source_type == 'application/x-mpegURL' or container == 'M2TS':
|
if ext == 'ism':
|
||||||
|
continue
|
||||||
|
elif ext == 'm3u8' or container == 'M2TS':
|
||||||
if not src:
|
if not src:
|
||||||
continue
|
continue
|
||||||
formats.extend(self._extract_m3u8_formats(
|
formats.extend(self._extract_m3u8_formats(
|
||||||
src, video_id, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=False))
|
src, video_id, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=False))
|
||||||
elif source_type == 'application/dash+xml':
|
elif ext == 'mpd':
|
||||||
if not src:
|
if not src:
|
||||||
continue
|
continue
|
||||||
formats.extend(self._extract_mpd_formats(src, video_id, 'dash', fatal=False))
|
formats.extend(self._extract_mpd_formats(src, video_id, 'dash', fatal=False))
|
||||||
@@ -568,7 +571,7 @@ class BrightcoveNewIE(InfoExtractor):
|
|||||||
'tbr': tbr,
|
'tbr': tbr,
|
||||||
'filesize': int_or_none(source.get('size')),
|
'filesize': int_or_none(source.get('size')),
|
||||||
'container': container,
|
'container': container,
|
||||||
'ext': container.lower(),
|
'ext': ext or container.lower(),
|
||||||
}
|
}
|
||||||
if width == 0 and height == 0:
|
if width == 0 and height == 0:
|
||||||
f.update({
|
f.update({
|
||||||
|
@@ -25,6 +25,7 @@ class CBCIE(InfoExtractor):
|
|||||||
'upload_date': '20160203',
|
'upload_date': '20160203',
|
||||||
'uploader': 'CBCC-NEW',
|
'uploader': 'CBCC-NEW',
|
||||||
},
|
},
|
||||||
|
'skip': 'Geo-restricted to Canada',
|
||||||
}, {
|
}, {
|
||||||
# with clipId
|
# with clipId
|
||||||
'url': 'http://www.cbc.ca/archives/entry/1978-robin-williams-freestyles-on-90-minutes-live',
|
'url': 'http://www.cbc.ca/archives/entry/1978-robin-williams-freestyles-on-90-minutes-live',
|
||||||
@@ -64,6 +65,7 @@ class CBCIE(InfoExtractor):
|
|||||||
'uploader': 'CBCC-NEW',
|
'uploader': 'CBCC-NEW',
|
||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
|
'skip': 'Geo-restricted to Canada',
|
||||||
}]
|
}]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -104,6 +106,7 @@ class CBCPlayerIE(InfoExtractor):
|
|||||||
'upload_date': '20160210',
|
'upload_date': '20160210',
|
||||||
'uploader': 'CBCC-NEW',
|
'uploader': 'CBCC-NEW',
|
||||||
},
|
},
|
||||||
|
'skip': 'Geo-restricted to Canada',
|
||||||
}, {
|
}, {
|
||||||
# Redirected from http://www.cbc.ca/player/AudioMobile/All%20in%20a%20Weekend%20Montreal/ID/2657632011/
|
# Redirected from http://www.cbc.ca/player/AudioMobile/All%20in%20a%20Weekend%20Montreal/ID/2657632011/
|
||||||
'url': 'http://www.cbc.ca/player/play/2657631896',
|
'url': 'http://www.cbc.ca/player/play/2657631896',
|
||||||
|
@@ -26,6 +26,7 @@ class CBSNewsIE(CBSBaseIE):
|
|||||||
# rtmp download
|
# rtmp download
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
|
'skip': 'Subscribers only',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': 'http://www.cbsnews.com/videos/fort-hood-shooting-army-downplays-mental-illness-as-cause-of-attack/',
|
'url': 'http://www.cbsnews.com/videos/fort-hood-shooting-army-downplays-mental-illness-as-cause-of-attack/',
|
||||||
@@ -69,7 +70,7 @@ class CBSNewsLiveVideoIE(InfoExtractor):
|
|||||||
IE_DESC = 'CBS News Live Videos'
|
IE_DESC = 'CBS News Live Videos'
|
||||||
_VALID_URL = r'https?://(?:www\.)?cbsnews\.com/live/video/(?P<id>[\da-z_-]+)'
|
_VALID_URL = r'https?://(?:www\.)?cbsnews\.com/live/video/(?P<id>[\da-z_-]+)'
|
||||||
|
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'http://www.cbsnews.com/live/video/clinton-sanders-prepare-to-face-off-in-nh/',
|
'url': 'http://www.cbsnews.com/live/video/clinton-sanders-prepare-to-face-off-in-nh/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'clinton-sanders-prepare-to-face-off-in-nh',
|
'id': 'clinton-sanders-prepare-to-face-off-in-nh',
|
||||||
@@ -77,7 +78,15 @@ class CBSNewsLiveVideoIE(InfoExtractor):
|
|||||||
'title': 'Clinton, Sanders Prepare To Face Off In NH',
|
'title': 'Clinton, Sanders Prepare To Face Off In NH',
|
||||||
'duration': 334,
|
'duration': 334,
|
||||||
},
|
},
|
||||||
}
|
'skip': 'Video gone, redirected to http://www.cbsnews.com/live/',
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.cbsnews.com/live/video/video-shows-intense-paragliding-accident/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'video-shows-intense-paragliding-accident',
|
||||||
|
'ext': 'flv',
|
||||||
|
'title': 'Video Shows Intense Paragliding Accident',
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
@@ -23,7 +23,7 @@ class CliphunterIE(InfoExtractor):
|
|||||||
(?P<id>[0-9]+)/
|
(?P<id>[0-9]+)/
|
||||||
(?P<seo>.+?)(?:$|[#\?])
|
(?P<seo>.+?)(?:$|[#\?])
|
||||||
'''
|
'''
|
||||||
_TEST = {
|
_TESTS = [{
|
||||||
'url': 'http://www.cliphunter.com/w/1012420/Fun_Jynx_Maze_solo',
|
'url': 'http://www.cliphunter.com/w/1012420/Fun_Jynx_Maze_solo',
|
||||||
'md5': 'b7c9bbd4eb3a226ab91093714dcaa480',
|
'md5': 'b7c9bbd4eb3a226ab91093714dcaa480',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -32,8 +32,19 @@ class CliphunterIE(InfoExtractor):
|
|||||||
'title': 'Fun Jynx Maze solo',
|
'title': 'Fun Jynx Maze solo',
|
||||||
'thumbnail': 're:^https?://.*\.jpg$',
|
'thumbnail': 're:^https?://.*\.jpg$',
|
||||||
'age_limit': 18,
|
'age_limit': 18,
|
||||||
}
|
},
|
||||||
}
|
'skip': 'Video gone',
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.cliphunter.com/w/2019449/ShesNew__My_booty_girlfriend_Victoria_Paradices_pussy_filled_with_jizz',
|
||||||
|
'md5': '55a723c67bfc6da6b0cfa00d55da8a27',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '2019449',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'ShesNew - My booty girlfriend, Victoria Paradice\'s pussy filled with jizz',
|
||||||
|
'thumbnail': 're:^https?://.*\.jpg$',
|
||||||
|
'age_limit': 18,
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
@@ -6,7 +6,6 @@ import re
|
|||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_parse_qs,
|
compat_parse_qs,
|
||||||
compat_urllib_parse_urlencode,
|
|
||||||
compat_HTTPError,
|
compat_HTTPError,
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
@@ -17,37 +16,26 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class CloudyIE(InfoExtractor):
|
class CloudyIE(InfoExtractor):
|
||||||
_IE_DESC = 'cloudy.ec and videoraj.ch'
|
_IE_DESC = 'cloudy.ec'
|
||||||
_VALID_URL = r'''(?x)
|
_VALID_URL = r'''(?x)
|
||||||
https?://(?:www\.)?(?P<host>cloudy\.ec|videoraj\.(?:ch|to))/
|
https?://(?:www\.)?cloudy\.ec/
|
||||||
(?:v/|embed\.php\?id=)
|
(?:v/|embed\.php\?id=)
|
||||||
(?P<id>[A-Za-z0-9]+)
|
(?P<id>[A-Za-z0-9]+)
|
||||||
'''
|
'''
|
||||||
_EMBED_URL = 'http://www.%s/embed.php?id=%s'
|
_EMBED_URL = 'http://www.cloudy.ec/embed.php?id=%s'
|
||||||
_API_URL = 'http://www.%s/api/player.api.php?%s'
|
_API_URL = 'http://www.cloudy.ec/api/player.api.php'
|
||||||
_MAX_TRIES = 2
|
_MAX_TRIES = 2
|
||||||
_TESTS = [
|
_TEST = {
|
||||||
{
|
'url': 'https://www.cloudy.ec/v/af511e2527aac',
|
||||||
'url': 'https://www.cloudy.ec/v/af511e2527aac',
|
'md5': '5cb253ace826a42f35b4740539bedf07',
|
||||||
'md5': '5cb253ace826a42f35b4740539bedf07',
|
'info_dict': {
|
||||||
'info_dict': {
|
'id': 'af511e2527aac',
|
||||||
'id': 'af511e2527aac',
|
'ext': 'flv',
|
||||||
'ext': 'flv',
|
'title': 'Funny Cats and Animals Compilation june 2013',
|
||||||
'title': 'Funny Cats and Animals Compilation june 2013',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'url': 'http://www.videoraj.to/v/47f399fd8bb60',
|
|
||||||
'md5': '7d0f8799d91efd4eda26587421c3c3b0',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '47f399fd8bb60',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'Burning a New iPhone 5 with Gasoline - Will it Survive?',
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
|
|
||||||
def _extract_video(self, video_host, video_id, file_key, error_url=None, try_num=0):
|
def _extract_video(self, video_id, file_key, error_url=None, try_num=0):
|
||||||
|
|
||||||
if try_num > self._MAX_TRIES - 1:
|
if try_num > self._MAX_TRIES - 1:
|
||||||
raise ExtractorError('Unable to extract video URL', expected=True)
|
raise ExtractorError('Unable to extract video URL', expected=True)
|
||||||
@@ -64,9 +52,8 @@ class CloudyIE(InfoExtractor):
|
|||||||
'errorUrl': error_url,
|
'errorUrl': error_url,
|
||||||
})
|
})
|
||||||
|
|
||||||
data_url = self._API_URL % (video_host, compat_urllib_parse_urlencode(form))
|
|
||||||
player_data = self._download_webpage(
|
player_data = self._download_webpage(
|
||||||
data_url, video_id, 'Downloading player data')
|
self._API_URL, video_id, 'Downloading player data', query=form)
|
||||||
data = compat_parse_qs(player_data)
|
data = compat_parse_qs(player_data)
|
||||||
|
|
||||||
try_num += 1
|
try_num += 1
|
||||||
@@ -88,7 +75,7 @@ class CloudyIE(InfoExtractor):
|
|||||||
except ExtractorError as e:
|
except ExtractorError as e:
|
||||||
if isinstance(e.cause, compat_HTTPError) and e.cause.code in [404, 410]:
|
if isinstance(e.cause, compat_HTTPError) and e.cause.code in [404, 410]:
|
||||||
self.report_warning('Invalid video URL, requesting another', video_id)
|
self.report_warning('Invalid video URL, requesting another', video_id)
|
||||||
return self._extract_video(video_host, video_id, file_key, video_url, try_num)
|
return self._extract_video(video_id, file_key, video_url, try_num)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
@@ -98,14 +85,13 @@ class CloudyIE(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)
|
||||||
video_host = mobj.group('host')
|
|
||||||
video_id = mobj.group('id')
|
video_id = mobj.group('id')
|
||||||
|
|
||||||
url = self._EMBED_URL % (video_host, video_id)
|
url = self._EMBED_URL % video_id
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
file_key = self._search_regex(
|
file_key = self._search_regex(
|
||||||
[r'key\s*:\s*"([^"]+)"', r'filekey\s*=\s*"([^"]+)"'],
|
[r'key\s*:\s*"([^"]+)"', r'filekey\s*=\s*"([^"]+)"'],
|
||||||
webpage, 'file_key')
|
webpage, 'file_key')
|
||||||
|
|
||||||
return self._extract_video(video_host, video_id, file_key)
|
return self._extract_video(video_id, file_key)
|
||||||
|
@@ -273,3 +273,36 @@ class ComedyCentralShowsIE(MTVServicesInfoExtractor):
|
|||||||
'title': show_name + ' ' + title,
|
'title': show_name + ' ' + title,
|
||||||
'description': description,
|
'description': description,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ComedyCentralTVIE(MTVServicesInfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?comedycentral\.tv/(?:staffeln|shows)/(?P<id>[^/?#&]+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'http://www.comedycentral.tv/staffeln/7436-the-mindy-project-staffel-4',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'local_playlist-f99b626bdfe13568579a',
|
||||||
|
'ext': 'flv',
|
||||||
|
'title': 'Episode_the-mindy-project_shows_season-4_episode-3_full-episode_part1',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# rtmp download
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.comedycentral.tv/shows/1074-workaholics',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.comedycentral.tv/shows/1727-the-mindy-project/bonus',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
|
mrss_url = self._search_regex(
|
||||||
|
r'data-mrss=(["\'])(?P<url>(?:(?!\1).)+)\1',
|
||||||
|
webpage, 'mrss url', group='url')
|
||||||
|
|
||||||
|
return self._get_videos_info_from_url(mrss_url, video_id)
|
||||||
|
@@ -51,8 +51,11 @@ class CSpanIE(InfoExtractor):
|
|||||||
'url': 'http://www.c-span.org/video/?104517-1/immigration-reforms-needed-protect-skilled-american-workers',
|
'url': 'http://www.c-span.org/video/?104517-1/immigration-reforms-needed-protect-skilled-american-workers',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'judiciary031715',
|
'id': 'judiciary031715',
|
||||||
'ext': 'flv',
|
'ext': 'mp4',
|
||||||
'title': 'Immigration Reforms Needed to Protect Skilled American Workers',
|
'title': 'Immigration Reforms Needed to Protect Skilled American Workers',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True, # m3u8 downloads
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
@@ -17,8 +17,12 @@ class DreiSatIE(ZDFIE):
|
|||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Waidmannsheil',
|
'title': 'Waidmannsheil',
|
||||||
'description': 'md5:cce00ca1d70e21425e72c86a98a56817',
|
'description': 'md5:cce00ca1d70e21425e72c86a98a56817',
|
||||||
'uploader': '3sat',
|
'uploader': 'SCHWEIZWEIT',
|
||||||
|
'uploader_id': '100000210',
|
||||||
'upload_date': '20140913'
|
'upload_date': '20140913'
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True, # m3u8 downloads
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -537,6 +537,8 @@ from .nick import (
|
|||||||
from .niconico import NiconicoIE, NiconicoPlaylistIE
|
from .niconico import NiconicoIE, NiconicoPlaylistIE
|
||||||
from .ninecninemedia import NineCNineMediaIE
|
from .ninecninemedia import NineCNineMediaIE
|
||||||
from .ninegag import NineGagIE
|
from .ninegag import NineGagIE
|
||||||
|
from .ninenow import NineNowIE
|
||||||
|
from .nintendo import NintendoIE
|
||||||
from .noco import NocoIE
|
from .noco import NocoIE
|
||||||
from .normalboots import NormalbootsIE
|
from .normalboots import NormalbootsIE
|
||||||
from .nosvideo import NosVideoIE
|
from .nosvideo import NosVideoIE
|
||||||
@@ -689,7 +691,7 @@ from .rtlnl import RtlNlIE
|
|||||||
from .rtl2 import RTL2IE
|
from .rtl2 import RTL2IE
|
||||||
from .rtp import RTPIE
|
from .rtp import RTPIE
|
||||||
from .rts import RTSIE
|
from .rts import RTSIE
|
||||||
from .rtve import RTVEALaCartaIE, RTVELiveIE, RTVEInfantilIE
|
from .rtve import RTVEALaCartaIE, RTVELiveIE, RTVEInfantilIE, RTVELiveIE, RTVETelevisionIE
|
||||||
from .rtvnh import RTVNHIE
|
from .rtvnh import RTVNHIE
|
||||||
from .rudo import RudoIE
|
from .rudo import RudoIE
|
||||||
from .ruhd import RUHDIE
|
from .ruhd import RUHDIE
|
||||||
@@ -781,6 +783,7 @@ from .srmediathek import SRMediathekIE
|
|||||||
from .ssa import SSAIE
|
from .ssa import SSAIE
|
||||||
from .stanfordoc import StanfordOpenClassroomIE
|
from .stanfordoc import StanfordOpenClassroomIE
|
||||||
from .steam import SteamIE
|
from .steam import SteamIE
|
||||||
|
from .streamable import StreamableIE
|
||||||
from .streamcloud import StreamcloudIE
|
from .streamcloud import StreamcloudIE
|
||||||
from .streamcz import StreamCZIE
|
from .streamcz import StreamCZIE
|
||||||
from .streetvoice import StreetVoiceIE
|
from .streetvoice import StreetVoiceIE
|
||||||
|
@@ -28,10 +28,13 @@ class GameSpotIE(OnceIE):
|
|||||||
'url': 'http://www.gamespot.com/videos/the-witcher-3-wild-hunt-xbox-one-now-playing/2300-6424837/',
|
'url': 'http://www.gamespot.com/videos/the-witcher-3-wild-hunt-xbox-one-now-playing/2300-6424837/',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'gs-2300-6424837',
|
'id': 'gs-2300-6424837',
|
||||||
'ext': 'flv',
|
'ext': 'mp4',
|
||||||
'title': 'The Witcher 3: Wild Hunt [Xbox ONE] - Now Playing',
|
'title': 'Now Playing - The Witcher 3: Wild Hunt',
|
||||||
'description': 'Join us as we take a look at the early hours of The Witcher 3: Wild Hunt and more.',
|
'description': 'Join us as we take a look at the early hours of The Witcher 3: Wild Hunt and more.',
|
||||||
},
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True, # m3u8 downloads
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@@ -1249,6 +1249,20 @@ class GenericIE(InfoExtractor):
|
|||||||
'uploader': 'www.hudl.com',
|
'uploader': 'www.hudl.com',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
# twitter:player:stream embed
|
||||||
|
{
|
||||||
|
'url': 'http://www.rtl.be/info/video/589263.aspx?CategoryID=288',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'master',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Une nouvelle espèce de dinosaure découverte en Argentine',
|
||||||
|
'uploader': 'www.rtl.be',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 downloads
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
},
|
||||||
# twitter:player embed
|
# twitter:player embed
|
||||||
{
|
{
|
||||||
'url': 'http://www.theatlantic.com/video/index/484130/what-do-black-holes-sound-like/',
|
'url': 'http://www.theatlantic.com/video/index/484130/what-do-black-holes-sound-like/',
|
||||||
@@ -2184,11 +2198,6 @@ class GenericIE(InfoExtractor):
|
|||||||
'uploader': video_uploader,
|
'uploader': video_uploader,
|
||||||
}
|
}
|
||||||
|
|
||||||
# https://dev.twitter.com/cards/types/player#On_twitter.com_via_desktop_browser
|
|
||||||
embed_url = self._html_search_meta('twitter:player', webpage, default=None)
|
|
||||||
if embed_url:
|
|
||||||
return self.url_result(embed_url)
|
|
||||||
|
|
||||||
# Looking for http://schema.org/VideoObject
|
# Looking for http://schema.org/VideoObject
|
||||||
json_ld = self._search_json_ld(
|
json_ld = self._search_json_ld(
|
||||||
webpage, video_id, default=None, expected_type='VideoObject')
|
webpage, video_id, default=None, expected_type='VideoObject')
|
||||||
@@ -2245,6 +2254,9 @@ class GenericIE(InfoExtractor):
|
|||||||
r"cinerama\.embedPlayer\(\s*\'[^']+\',\s*'([^']+)'", webpage)
|
r"cinerama\.embedPlayer\(\s*\'[^']+\',\s*'([^']+)'", webpage)
|
||||||
if not found:
|
if not found:
|
||||||
# Try to find twitter cards info
|
# Try to find twitter cards info
|
||||||
|
# twitter:player:stream should be checked before twitter:player since
|
||||||
|
# it is expected to contain a raw stream (see
|
||||||
|
# https://dev.twitter.com/cards/types/player#On_twitter.com_via_desktop_browser)
|
||||||
found = filter_video(re.findall(
|
found = filter_video(re.findall(
|
||||||
r'<meta (?:property|name)="twitter:player:stream" (?:content|value)="(.+?)"', webpage))
|
r'<meta (?:property|name)="twitter:player:stream" (?:content|value)="(.+?)"', webpage))
|
||||||
if not found:
|
if not found:
|
||||||
@@ -2278,6 +2290,15 @@ class GenericIE(InfoExtractor):
|
|||||||
'_type': 'url',
|
'_type': 'url',
|
||||||
'url': new_url,
|
'url': new_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
# twitter:player is a https URL to iframe player that may or may not
|
||||||
|
# be supported by youtube-dl thus this is checked the very last (see
|
||||||
|
# https://dev.twitter.com/cards/types/player#On_twitter.com_via_desktop_browser)
|
||||||
|
embed_url = self._html_search_meta('twitter:player', webpage, default=None)
|
||||||
|
if embed_url:
|
||||||
|
return self.url_result(embed_url)
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
raise UnsupportedError(url)
|
raise UnsupportedError(url)
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@ from ..utils import (
|
|||||||
float_or_none,
|
float_or_none,
|
||||||
HEADRequest,
|
HEADRequest,
|
||||||
sanitized_Request,
|
sanitized_Request,
|
||||||
|
strip_or_none,
|
||||||
unescapeHTML,
|
unescapeHTML,
|
||||||
url_basename,
|
url_basename,
|
||||||
RegexNotFoundError,
|
RegexNotFoundError,
|
||||||
@@ -133,7 +134,7 @@ class MTVServicesInfoExtractor(InfoExtractor):
|
|||||||
message += item.text
|
message += item.text
|
||||||
raise ExtractorError(message, expected=True)
|
raise ExtractorError(message, expected=True)
|
||||||
|
|
||||||
description = xpath_text(itemdoc, 'description')
|
description = strip_or_none(xpath_text(itemdoc, 'description'))
|
||||||
|
|
||||||
title_el = None
|
title_el = None
|
||||||
if title_el is None:
|
if title_el is None:
|
||||||
|
72
youtube_dl/extractor/ninenow.py
Normal file
72
youtube_dl/extractor/ninenow.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..compat import compat_str
|
||||||
|
from ..utils import (
|
||||||
|
int_or_none,
|
||||||
|
float_or_none,
|
||||||
|
ExtractorError,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NineNowIE(InfoExtractor):
|
||||||
|
IE_NAME = '9now.com.au'
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?9now\.com\.au/(?:[^/]+/){2}(?P<id>[^/?#]+)'
|
||||||
|
_TESTS = [{
|
||||||
|
# clip
|
||||||
|
'url': 'https://www.9now.com.au/afl-footy-show/2016/clip-ciql02091000g0hp5oktrnytc',
|
||||||
|
'md5': '17cf47d63ec9323e562c9957a968b565',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '16801',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'St. Kilda\'s Joey Montagna on the potential for a player\'s strike',
|
||||||
|
'description': 'Is a boycott of the NAB Cup "on the table"?',
|
||||||
|
'uploader_id': '4460760524001',
|
||||||
|
'upload_date': '20160713',
|
||||||
|
'timestamp': 1468421266,
|
||||||
|
},
|
||||||
|
'skip': 'Only available in Australia',
|
||||||
|
}, {
|
||||||
|
# episode
|
||||||
|
'url': 'https://www.9now.com.au/afl-footy-show/2016/episode-19',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# DRM protected
|
||||||
|
'url': 'https://www.9now.com.au/andrew-marrs-history-of-the-world/season-1/episode-1',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/4460760524001/default_default/index.html?videoId=%s'
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
display_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
page_data = self._parse_json(self._search_regex(
|
||||||
|
r'window\.__data\s*=\s*({.*?});', webpage,
|
||||||
|
'page data'), display_id)
|
||||||
|
common_data = page_data.get('episode', {}).get('episode') or page_data.get('clip', {}).get('clip')
|
||||||
|
video_data = common_data['video']
|
||||||
|
|
||||||
|
if video_data.get('drm'):
|
||||||
|
raise ExtractorError('This video is DRM protected.', expected=True)
|
||||||
|
|
||||||
|
brightcove_id = video_data.get('brightcoveId') or 'ref:' + video_data['referenceId']
|
||||||
|
video_id = compat_str(video_data.get('id') or brightcove_id)
|
||||||
|
title = common_data['name']
|
||||||
|
|
||||||
|
thumbnails = [{
|
||||||
|
'id': thumbnail_id,
|
||||||
|
'url': thumbnail_url,
|
||||||
|
'width': int_or_none(thumbnail_id[1:])
|
||||||
|
} for thumbnail_id, thumbnail_url in common_data.get('image', {}).get('sizes', {}).items()]
|
||||||
|
|
||||||
|
return {
|
||||||
|
'_type': 'url_transparent',
|
||||||
|
'url': self.BRIGHTCOVE_URL_TEMPLATE % brightcove_id,
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'description': common_data.get('description'),
|
||||||
|
'duration': float_or_none(video_data.get('duration'), 1000),
|
||||||
|
'thumbnails': thumbnails,
|
||||||
|
'ie_key': 'BrightcoveNew',
|
||||||
|
}
|
46
youtube_dl/extractor/nintendo.py
Normal file
46
youtube_dl/extractor/nintendo.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from .ooyala import OoyalaIE
|
||||||
|
from ..utils import unescapeHTML
|
||||||
|
|
||||||
|
|
||||||
|
class NintendoIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?nintendo\.com/games/detail/(?P<id>[^/?#&]+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'http://www.nintendo.com/games/detail/yEiAzhU2eQI1KZ7wOHhngFoAHc1FpHwj',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'MzMmticjp0VPzO3CCj4rmFOuohEuEWoW',
|
||||||
|
'ext': 'flv',
|
||||||
|
'title': 'Duck Hunt Wii U VC NES - Trailer',
|
||||||
|
'duration': 60.326,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
'add_ie': ['Ooyala'],
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.nintendo.com/games/detail/tokyo-mirage-sessions-fe-wii-u',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'tokyo-mirage-sessions-fe-wii-u',
|
||||||
|
'title': 'Tokyo Mirage Sessions ♯FE',
|
||||||
|
},
|
||||||
|
'playlist_count': 3,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
page_id = self._match_id(url)
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, page_id)
|
||||||
|
|
||||||
|
entries = [
|
||||||
|
OoyalaIE._build_url_result(m.group('code'))
|
||||||
|
for m in re.finditer(
|
||||||
|
r'class=(["\'])embed-video\1[^>]+data-video-code=(["\'])(?P<code>(?:(?!\2).)+)\2',
|
||||||
|
webpage)]
|
||||||
|
|
||||||
|
return self.playlist_result(
|
||||||
|
entries, page_id, unescapeHTML(self._og_search_title(webpage, fatal=False)))
|
@@ -40,16 +40,16 @@ class ORFTVthekIE(InfoExtractor):
|
|||||||
'skip': 'Blocked outside of Austria / Germany',
|
'skip': 'Blocked outside of Austria / Germany',
|
||||||
}, {
|
}, {
|
||||||
'url': 'http://tvthek.orf.at/topic/Im-Wandel-der-Zeit/8002126/Best-of-Ingrid-Thurnher/7982256',
|
'url': 'http://tvthek.orf.at/topic/Im-Wandel-der-Zeit/8002126/Best-of-Ingrid-Thurnher/7982256',
|
||||||
'playlist': [{
|
'info_dict': {
|
||||||
'md5': '68f543909aea49d621dfc7703a11cfaf',
|
'id': '7982259',
|
||||||
'info_dict': {
|
'ext': 'mp4',
|
||||||
'id': '7982259',
|
'title': 'Best of Ingrid Thurnher',
|
||||||
'ext': 'mp4',
|
'upload_date': '20140527',
|
||||||
'title': 'Best of Ingrid Thurnher',
|
'description': 'Viele Jahre war Ingrid Thurnher das "Gesicht" der ZIB 2. Vor ihrem Wechsel zur ZIB 2 im Jahr 1995 moderierte sie unter anderem "Land und Leute", "Österreich-Bild" und "Niederösterreich heute".',
|
||||||
'upload_date': '20140527',
|
},
|
||||||
'description': 'Viele Jahre war Ingrid Thurnher das "Gesicht" der ZIB 2. Vor ihrem Wechsel zur ZIB 2 im jahr 1995 moderierte sie unter anderem "Land und Leute", "Österreich-Bild" und "Niederösterreich heute".',
|
'params': {
|
||||||
}
|
'skip_download': True, # rtsp downloads
|
||||||
}],
|
},
|
||||||
'_skip': 'Blocked outside of Austria / Germany',
|
'_skip': 'Blocked outside of Austria / Germany',
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
@@ -113,9 +113,7 @@ class RTVEALaCartaIE(InfoExtractor):
|
|||||||
png = self._download_webpage(png_request, video_id, 'Downloading url information')
|
png = self._download_webpage(png_request, video_id, 'Downloading url information')
|
||||||
video_url = _decrypt_url(png)
|
video_url = _decrypt_url(png)
|
||||||
if not video_url.endswith('.f4m'):
|
if not video_url.endswith('.f4m'):
|
||||||
video_url = video_url.replace(
|
video_url = video_url.replace('.net.rtve', '.multimedia.cdn.rtve')
|
||||||
'resources/', 'auth/resources/'
|
|
||||||
).replace('.net.rtve', '.multimedia.cdn.rtve')
|
|
||||||
|
|
||||||
subtitles = None
|
subtitles = None
|
||||||
if info.get('sbtFile') is not None:
|
if info.get('sbtFile') is not None:
|
||||||
@@ -222,3 +220,34 @@ class RTVELiveIE(InfoExtractor):
|
|||||||
'formats': formats,
|
'formats': formats,
|
||||||
'is_live': True,
|
'is_live': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RTVETelevisionIE(InfoExtractor):
|
||||||
|
IE_NAME = 'rtve.es:television'
|
||||||
|
_VALID_URL = r'https?://www\.rtve\.es/television/[^/]+/[^/]+/(?P<id>\d+).shtml'
|
||||||
|
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://www.rtve.es/television/20160628/revolucion-del-movil/1364141.shtml',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '3069778',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Documentos TV - La revolución del móvil',
|
||||||
|
'duration': 3496.948,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
page_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, page_id)
|
||||||
|
|
||||||
|
alacarta_url = self._search_regex(
|
||||||
|
r'data-location="alacarta_videos"[^<]+url":"(http://www\.rtve\.es/alacarta.+?)&',
|
||||||
|
webpage, 'alacarta url', default=None)
|
||||||
|
if alacarta_url is None:
|
||||||
|
raise ExtractorError(
|
||||||
|
'The webpage doesn\'t contain any video', expected=True)
|
||||||
|
|
||||||
|
return self.url_result(alacarta_url, ie=RTVEALaCartaIE.ie_key())
|
||||||
|
@@ -4,11 +4,8 @@ from .mtv import MTVServicesInfoExtractor
|
|||||||
|
|
||||||
|
|
||||||
class SpikeIE(MTVServicesInfoExtractor):
|
class SpikeIE(MTVServicesInfoExtractor):
|
||||||
_VALID_URL = r'''(?x)https?://
|
_VALID_URL = r'https?://(?:[^/]+\.)?spike\.com/[^/]+/[\da-z]{6}(?:[/?#&]|$)'
|
||||||
(?:www\.spike\.com/(?:video-(?:clips|playlists)|(?:full-)?episodes)/.+|
|
_TESTS = [{
|
||||||
m\.spike\.com/videos/video\.rbml\?id=(?P<id>[^&]+))
|
|
||||||
'''
|
|
||||||
_TEST = {
|
|
||||||
'url': 'http://www.spike.com/video-clips/lhtu8m/auction-hunters-can-allen-ride-a-hundred-year-old-motorcycle',
|
'url': 'http://www.spike.com/video-clips/lhtu8m/auction-hunters-can-allen-ride-a-hundred-year-old-motorcycle',
|
||||||
'md5': '1a9265f32b0c375793d6c4ce45255256',
|
'md5': '1a9265f32b0c375793d6c4ce45255256',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@@ -17,13 +14,19 @@ class SpikeIE(MTVServicesInfoExtractor):
|
|||||||
'title': 'Auction Hunters|Can Allen Ride A Hundred Year-Old Motorcycle?',
|
'title': 'Auction Hunters|Can Allen Ride A Hundred Year-Old Motorcycle?',
|
||||||
'description': 'md5:fbed7e82ed5fad493615b3094a9499cb',
|
'description': 'md5:fbed7e82ed5fad493615b3094a9499cb',
|
||||||
},
|
},
|
||||||
}
|
}, {
|
||||||
|
'url': 'http://www.spike.com/video-clips/lhtu8m/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.spike.com/video-clips/lhtu8m',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://bellator.spike.com/fight/atwr7k/bellator-158-michael-page-vs-evangelista-cyborg',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://bellator.spike.com/video-clips/bw6k7n/bellator-158-foundations-michael-venom-page',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
_FEED_URL = 'http://www.spike.com/feeds/mrss/'
|
_FEED_URL = 'http://www.spike.com/feeds/mrss/'
|
||||||
_MOBILE_TEMPLATE = 'http://m.spike.com/videos/video.rbml?id=%s'
|
_MOBILE_TEMPLATE = 'http://m.spike.com/videos/video.rbml?id=%s'
|
||||||
|
|
||||||
def _real_extract(self, url):
|
|
||||||
mobile_id = self._match_id(url)
|
|
||||||
if mobile_id:
|
|
||||||
url = 'http://www.spike.com/video-clips/%s' % mobile_id
|
|
||||||
return super(SpikeIE, self)._real_extract(url)
|
|
||||||
|
98
youtube_dl/extractor/streamable.py
Normal file
98
youtube_dl/extractor/streamable.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
ExtractorError,
|
||||||
|
float_or_none,
|
||||||
|
int_or_none,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class StreamableIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://streamable\.com/(?:e/)?(?P<id>\w+)'
|
||||||
|
_TESTS = [
|
||||||
|
{
|
||||||
|
'url': 'https://streamable.com/dnd1',
|
||||||
|
'md5': '3e3bc5ca088b48c2d436529b64397fef',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'dnd1',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Mikel Oiarzabal scores to make it 0-3 for La Real against Espanyol',
|
||||||
|
'thumbnail': 're:https?://.*\.jpg$',
|
||||||
|
'uploader': 'teabaker',
|
||||||
|
'timestamp': 1454964157.35115,
|
||||||
|
'upload_date': '20160208',
|
||||||
|
'duration': 61.516,
|
||||||
|
'view_count': int,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
# older video without bitrate, width/height, etc. info
|
||||||
|
{
|
||||||
|
'url': 'https://streamable.com/moo',
|
||||||
|
'md5': '2cf6923639b87fba3279ad0df3a64e73',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'moo',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '"Please don\'t eat me!"',
|
||||||
|
'thumbnail': 're:https?://.*\.jpg$',
|
||||||
|
'timestamp': 1426115495,
|
||||||
|
'upload_date': '20150311',
|
||||||
|
'duration': 12,
|
||||||
|
'view_count': int,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'url': 'https://streamable.com/e/dnd1',
|
||||||
|
'only_matching': True,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
# Note: Using the ajax API, as the public Streamable API doesn't seem
|
||||||
|
# to return video info like the title properly sometimes, and doesn't
|
||||||
|
# include info like the video duration
|
||||||
|
video = self._download_json(
|
||||||
|
'https://streamable.com/ajax/videos/%s' % video_id, video_id)
|
||||||
|
|
||||||
|
# Format IDs:
|
||||||
|
# 0 The video is being uploaded
|
||||||
|
# 1 The video is being processed
|
||||||
|
# 2 The video has at least one file ready
|
||||||
|
# 3 The video is unavailable due to an error
|
||||||
|
status = video.get('status')
|
||||||
|
if status != 2:
|
||||||
|
raise ExtractorError(
|
||||||
|
'This video is currently unavailable. It may still be uploading or processing.',
|
||||||
|
expected=True)
|
||||||
|
|
||||||
|
title = video.get('reddit_title') or video['title']
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for key, info in video['files'].items():
|
||||||
|
if not info.get('url'):
|
||||||
|
continue
|
||||||
|
formats.append({
|
||||||
|
'format_id': key,
|
||||||
|
'url': self._proto_relative_url(info['url']),
|
||||||
|
'width': int_or_none(info.get('width')),
|
||||||
|
'height': int_or_none(info.get('height')),
|
||||||
|
'filesize': int_or_none(info.get('size')),
|
||||||
|
'fps': int_or_none(info.get('framerate')),
|
||||||
|
'vbr': float_or_none(info.get('bitrate'), 1000)
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'description': video.get('description'),
|
||||||
|
'thumbnail': self._proto_relative_url(video.get('thumbnail_url')),
|
||||||
|
'uploader': video.get('owner', {}).get('user_name'),
|
||||||
|
'timestamp': float_or_none(video.get('date_added')),
|
||||||
|
'duration': float_or_none(video.get('duration')),
|
||||||
|
'view_count': int_or_none(video.get('plays')),
|
||||||
|
'formats': formats
|
||||||
|
}
|
@@ -1,46 +1,56 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import re
|
from .theplatform import ThePlatformIE
|
||||||
|
from ..utils import (
|
||||||
from .common import InfoExtractor
|
update_url_query,
|
||||||
|
smuggle_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SyfyIE(InfoExtractor):
|
class SyfyIE(ThePlatformIE):
|
||||||
_VALID_URL = r'https?://www\.syfy\.com/(?:videos/.+?vid:(?P<id>[0-9]+)|(?!videos)(?P<video_name>[^/]+)(?:$|[?#]))'
|
_VALID_URL = r'https?://www\.syfy\.com/(?:[^/]+/)?videos/(?P<id>[^/?#]+)'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.syfy.com/videos/Robot%20Combat%20League/Behind%20the%20Scenes/vid:2631458',
|
'url': 'http://www.syfy.com/theinternetruinedmylife/videos/the-internet-ruined-my-life-season-1-trailer',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'NmqMrGnXvmO1',
|
'id': '2968097',
|
||||||
'ext': 'flv',
|
'ext': 'mp4',
|
||||||
'title': 'George Lucas has Advice for his Daughter',
|
'title': 'The Internet Ruined My Life: Season 1 Trailer',
|
||||||
'description': 'Listen to what insights George Lucas give his daughter Amanda.',
|
'description': 'One tweet, one post, one click, can destroy everything.',
|
||||||
|
'uploader': 'NBCU-MPAT',
|
||||||
|
'upload_date': '20170113',
|
||||||
|
'timestamp': 1484345640,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# m3u8 download
|
||||||
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
'add_ie': ['ThePlatform'],
|
'add_ie': ['ThePlatform'],
|
||||||
}, {
|
|
||||||
'url': 'http://www.syfy.com/wilwheaton',
|
|
||||||
'md5': '94dfa54ee3ccb63295b276da08c415f6',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '4yoffOOXC767',
|
|
||||||
'ext': 'flv',
|
|
||||||
'title': 'The Wil Wheaton Project - Premiering May 27th at 10/9c.',
|
|
||||||
'description': 'The Wil Wheaton Project premieres May 27th at 10/9c. Don\'t miss it.',
|
|
||||||
},
|
|
||||||
'add_ie': ['ThePlatform'],
|
|
||||||
'skip': 'Blocked outside the US',
|
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
mobj = re.match(self._VALID_URL, url)
|
display_id = self._match_id(url)
|
||||||
video_name = mobj.group('video_name')
|
webpage = self._download_webpage(url, display_id)
|
||||||
if video_name:
|
syfy_mpx = list(self._parse_json(self._search_regex(
|
||||||
generic_webpage = self._download_webpage(url, video_name)
|
r'jQuery\.extend\([^,]+,\s*({.+})\);', webpage, 'drupal settings'),
|
||||||
video_id = self._search_regex(
|
display_id)['syfy']['syfy_mpx'].values())[0]
|
||||||
r'<iframe.*?class="video_iframe_page"\s+src="/_utils/video/thP_video_controller.php.*?_vid([0-9]+)">',
|
video_id = syfy_mpx['mpxGUID']
|
||||||
generic_webpage, 'video ID')
|
title = syfy_mpx['episodeTitle']
|
||||||
url = 'http://www.syfy.com/videos/%s/%s/vid:%s' % (
|
query = {
|
||||||
video_name, video_name, video_id)
|
'mbr': 'true',
|
||||||
else:
|
'manifest': 'm3u',
|
||||||
video_id = mobj.group('id')
|
}
|
||||||
webpage = self._download_webpage(url, video_id)
|
if syfy_mpx.get('entitlement') == 'auth':
|
||||||
return self.url_result(self._og_search_video_url(webpage))
|
resource = '<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>syfy</title><item><title><![CDATA[%s]]></title><guid>%s</guid><media:rating scheme="urn:v-chip">%s</media:rating></item></channel></rss>' % (title, video_id, syfy_mpx.get('mpxRating', 'TV-14'))
|
||||||
|
query['auth'] = self._extract_mvpd_auth(
|
||||||
|
url, video_id, 'syfy', resource)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'_type': 'url_transparent',
|
||||||
|
'ie_key': 'ThePlatform',
|
||||||
|
'url': smuggle_url(update_url_query(
|
||||||
|
self._proto_relative_url(syfy_mpx['releaseURL']), query),
|
||||||
|
{'force_smil_url': True}),
|
||||||
|
'title': title,
|
||||||
|
'id': video_id,
|
||||||
|
'display_id': display_id,
|
||||||
|
}
|
||||||
|
@@ -24,16 +24,20 @@ class ThreeQSDNIE(InfoExtractor):
|
|||||||
'title': '0280d6b9-1215-11e6-b427-0cc47a188158',
|
'title': '0280d6b9-1215-11e6-b427-0cc47a188158',
|
||||||
'is_live': False,
|
'is_live': False,
|
||||||
},
|
},
|
||||||
'expected_warnings': ['Failed to download MPD manifest'],
|
'expected_warnings': ['Failed to download MPD manifest', 'Failed to parse JSON'],
|
||||||
}, {
|
}, {
|
||||||
# live video stream
|
# live video stream
|
||||||
'url': 'https://playout.3qsdn.com/d755d94b-4ab9-11e3-9162-0025907ad44f?js=true',
|
'url': 'https://playout.3qsdn.com/d755d94b-4ab9-11e3-9162-0025907ad44f?js=true',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'd755d94b-4ab9-11e3-9162-0025907ad44f',
|
'id': 'd755d94b-4ab9-11e3-9162-0025907ad44f',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'd755d94b-4ab9-11e3-9162-0025907ad44f',
|
'title': 're:^d755d94b-4ab9-11e3-9162-0025907ad44f [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
|
||||||
'is_live': False,
|
'is_live': True,
|
||||||
},
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True, # m3u8 downloads
|
||||||
|
},
|
||||||
|
'expected_warnings': ['Failed to download MPD manifest'],
|
||||||
}, {
|
}, {
|
||||||
# live audio stream
|
# live audio stream
|
||||||
'url': 'http://playout.3qsdn.com/9edf36e0-6bf2-11e2-a16a-9acf09e2db48',
|
'url': 'http://playout.3qsdn.com/9edf36e0-6bf2-11e2-a16a-9acf09e2db48',
|
||||||
@@ -114,7 +118,7 @@ class ThreeQSDNIE(InfoExtractor):
|
|||||||
'vcodec': 'none' if stream_type == 'audio' else None,
|
'vcodec': 'none' if stream_type == 'audio' else None,
|
||||||
})
|
})
|
||||||
|
|
||||||
for item_js in re.findall(r'({.*?\b(?:src|source)\s*:\s*["\'].+?})', js):
|
for item_js in re.findall(r'({[^{]*?\b(?:src|source)\s*:\s*["\'].+?})', js):
|
||||||
f = self._parse_json(
|
f = self._parse_json(
|
||||||
item_js, video_id, transform_source=js_to_json, fatal=False)
|
item_js, video_id, transform_source=js_to_json, fatal=False)
|
||||||
if not f:
|
if not f:
|
||||||
|
@@ -130,7 +130,7 @@ class VikiIE(VikiBaseIE):
|
|||||||
}, {
|
}, {
|
||||||
# clip
|
# clip
|
||||||
'url': 'http://www.viki.com/videos/1067139v-the-avengers-age-of-ultron-press-conference',
|
'url': 'http://www.viki.com/videos/1067139v-the-avengers-age-of-ultron-press-conference',
|
||||||
'md5': 'feea2b1d7b3957f70886e6dfd8b8be84',
|
'md5': '86c0b5dbd4d83a6611a79987cc7a1989',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '1067139v',
|
'id': '1067139v',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@@ -156,15 +156,11 @@ class VikiIE(VikiBaseIE):
|
|||||||
'like_count': int,
|
'like_count': int,
|
||||||
'age_limit': 13,
|
'age_limit': 13,
|
||||||
},
|
},
|
||||||
'params': {
|
|
||||||
# m3u8 download
|
|
||||||
'skip_download': True,
|
|
||||||
},
|
|
||||||
'skip': 'Blocked in the US',
|
'skip': 'Blocked in the US',
|
||||||
}, {
|
}, {
|
||||||
# episode
|
# episode
|
||||||
'url': 'http://www.viki.com/videos/44699v-boys-over-flowers-episode-1',
|
'url': 'http://www.viki.com/videos/44699v-boys-over-flowers-episode-1',
|
||||||
'md5': '1f54697dabc8f13f31bf06bb2e4de6db',
|
'md5': '5fa476a902e902783ac7a4d615cdbc7a',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '44699v',
|
'id': '44699v',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@@ -200,7 +196,7 @@ class VikiIE(VikiBaseIE):
|
|||||||
}, {
|
}, {
|
||||||
# non-English description
|
# non-English description
|
||||||
'url': 'http://www.viki.com/videos/158036v-love-in-magic',
|
'url': 'http://www.viki.com/videos/158036v-love-in-magic',
|
||||||
'md5': '013dc282714e22acf9447cad14ff1208',
|
'md5': '1713ae35df5a521b31f6dc40730e7c9c',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '158036v',
|
'id': '158036v',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
@@ -281,9 +277,16 @@ class VikiIE(VikiBaseIE):
|
|||||||
r'^(\d+)[pP]$', format_id, 'height', default=None))
|
r'^(\d+)[pP]$', format_id, 'height', default=None))
|
||||||
for protocol, format_dict in stream_dict.items():
|
for protocol, format_dict in stream_dict.items():
|
||||||
if format_id == 'm3u8':
|
if format_id == 'm3u8':
|
||||||
formats.extend(self._extract_m3u8_formats(
|
m3u8_formats = self._extract_m3u8_formats(
|
||||||
format_dict['url'], video_id, 'mp4', 'm3u8_native',
|
format_dict['url'], video_id, 'mp4',
|
||||||
m3u8_id='m3u8-%s' % protocol, fatal=False))
|
entry_protocol='m3u8_native', preference=-1,
|
||||||
|
m3u8_id='m3u8-%s' % protocol, fatal=False)
|
||||||
|
# Despite CODECS metadata in m3u8 all video-only formats
|
||||||
|
# are actually video+audio
|
||||||
|
for f in m3u8_formats:
|
||||||
|
if f.get('acodec') == 'none' and f.get('vcodec') != 'none':
|
||||||
|
f['acodec'] = None
|
||||||
|
formats.extend(m3u8_formats)
|
||||||
else:
|
else:
|
||||||
formats.append({
|
formats.append({
|
||||||
'url': format_dict['url'],
|
'url': format_dict['url'],
|
||||||
|
@@ -363,10 +363,8 @@ class FFmpegEmbedSubtitlePP(FFmpegPostProcessor):
|
|||||||
input_files = [filename] + sub_filenames
|
input_files = [filename] + sub_filenames
|
||||||
|
|
||||||
opts = [
|
opts = [
|
||||||
'-map', '0:v',
|
'-map', '0',
|
||||||
'-c:v', 'copy',
|
'-c', 'copy',
|
||||||
'-map', '0:a',
|
|
||||||
'-c:a', 'copy',
|
|
||||||
# Don't copy the existing subtitles, we may be running the
|
# Don't copy the existing subtitles, we may be running the
|
||||||
# postprocessor a second time
|
# postprocessor a second time
|
||||||
'-map', '-0:s',
|
'-map', '-0:s',
|
||||||
|
@@ -2123,6 +2123,7 @@ def mimetype2ext(mt):
|
|||||||
'dash+xml': 'mpd',
|
'dash+xml': 'mpd',
|
||||||
'f4m': 'f4m',
|
'f4m': 'f4m',
|
||||||
'f4m+xml': 'f4m',
|
'f4m+xml': 'f4m',
|
||||||
|
'vnd.ms-sstr+xml': 'ism',
|
||||||
}.get(res, res)
|
}.get(res, res)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
__version__ = '2016.07.13'
|
__version__ = '2016.07.17'
|
||||||
|
Reference in New Issue
Block a user