Compare commits
16 Commits
2017.02.21
...
2017.02.22
Author | SHA1 | Date | |
---|---|---|---|
345b24538b | |||
63a29b6118 | |||
b5869560a4 | |||
527ef85fe9 | |||
58ad6995cd | |||
a86e416088 | |||
71e9577b94 | |||
0d427c8304 | |||
139d8ac106 | |||
abd29a2ced | |||
31615ac279 | |||
fc320a40d9 | |||
7345d6d465 | |||
86466a8b6f | |||
33dc173cdc | |||
3444844b04 |
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 *2017.02.21*. 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 *2017.02.22*. 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 **2017.02.21**
|
- [ ] I've **verified** and **I assure** that I'm running youtube-dl **2017.02.22**
|
||||||
|
|
||||||
### 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 2017.02.21
|
[debug] youtube-dl version 2017.02.22
|
||||||
[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: {}
|
||||||
|
15
ChangeLog
15
ChangeLog
@ -1,3 +1,18 @@
|
|||||||
|
version 2017.02.22
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
* [crunchyroll] Fix descriptions with double quotes (#12124)
|
||||||
|
* [dailymotion] Make comment count optional (#12209)
|
||||||
|
+ [vidzi] Add support for vidzi.cc (#12213)
|
||||||
|
+ [24video] Add support for 24video.tube (#12217)
|
||||||
|
+ [crackle] Use geo bypass mechanism
|
||||||
|
+ [viewster] Use geo verification headers
|
||||||
|
+ [tfo] Improve geo restriction detection and use geo bypass mechanism
|
||||||
|
+ [telequebec] Use geo bypass mechanism
|
||||||
|
+ [limelight] Extract PlaylistService errors and improve geo restriction
|
||||||
|
detection
|
||||||
|
|
||||||
|
|
||||||
version 2017.02.21
|
version 2017.02.21
|
||||||
|
|
||||||
Core
|
Core
|
||||||
|
5
setup.py
5
setup.py
@ -107,8 +107,8 @@ setup(
|
|||||||
url='https://github.com/rg3/youtube-dl',
|
url='https://github.com/rg3/youtube-dl',
|
||||||
author='Ricardo Garcia',
|
author='Ricardo Garcia',
|
||||||
author_email='ytdl@yt-dl.org',
|
author_email='ytdl@yt-dl.org',
|
||||||
maintainer='Philipp Hagemeister',
|
maintainer='Sergey M.',
|
||||||
maintainer_email='phihag@phihag.de',
|
maintainer_email='dstftw@gmail.com',
|
||||||
packages=[
|
packages=[
|
||||||
'youtube_dl',
|
'youtube_dl',
|
||||||
'youtube_dl.extractor', 'youtube_dl.downloader',
|
'youtube_dl.extractor', 'youtube_dl.downloader',
|
||||||
@ -130,6 +130,7 @@ setup(
|
|||||||
'Programming Language :: Python :: 3.3',
|
'Programming Language :: Python :: 3.3',
|
||||||
'Programming Language :: Python :: 3.4',
|
'Programming Language :: Python :: 3.4',
|
||||||
'Programming Language :: Python :: 3.5',
|
'Programming Language :: Python :: 3.5',
|
||||||
|
'Programming Language :: Python :: 3.6',
|
||||||
],
|
],
|
||||||
|
|
||||||
cmdclass={'build_lazy_extractors': build_lazy_extractors},
|
cmdclass={'build_lazy_extractors': build_lazy_extractors},
|
||||||
|
@ -6,6 +6,7 @@ from ..utils import int_or_none
|
|||||||
|
|
||||||
|
|
||||||
class CrackleIE(InfoExtractor):
|
class CrackleIE(InfoExtractor):
|
||||||
|
_GEO_COUNTRIES = ['US']
|
||||||
_VALID_URL = r'(?:crackle:|https?://(?:(?:www|m)\.)?crackle\.com/(?:playlist/\d+/|(?:[^/]+/)+))(?P<id>\d+)'
|
_VALID_URL = r'(?:crackle:|https?://(?:(?:www|m)\.)?crackle\.com/(?:playlist/\d+/|(?:[^/]+/)+))(?P<id>\d+)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.crackle.com/comedians-in-cars-getting-coffee/2498934',
|
'url': 'http://www.crackle.com/comedians-in-cars-getting-coffee/2498934',
|
||||||
|
@ -123,7 +123,7 @@ class CrunchyrollIE(CrunchyrollBaseIE):
|
|||||||
'url': 'http://www.crunchyroll.com/wanna-be-the-strongest-in-the-world/episode-1-an-idol-wrestler-is-born-645513',
|
'url': 'http://www.crunchyroll.com/wanna-be-the-strongest-in-the-world/episode-1-an-idol-wrestler-is-born-645513',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '645513',
|
'id': '645513',
|
||||||
'ext': 'flv',
|
'ext': 'mp4',
|
||||||
'title': 'Wanna be the Strongest in the World Episode 1 – An Idol-Wrestler is Born!',
|
'title': 'Wanna be the Strongest in the World Episode 1 – An Idol-Wrestler is Born!',
|
||||||
'description': 'md5:2d17137920c64f2f49981a7797d275ef',
|
'description': 'md5:2d17137920c64f2f49981a7797d275ef',
|
||||||
'thumbnail': 'http://img1.ak.crunchyroll.com/i/spire1-tmb/20c6b5e10f1a47b10516877d3c039cae1380951166_full.jpg',
|
'thumbnail': 'http://img1.ak.crunchyroll.com/i/spire1-tmb/20c6b5e10f1a47b10516877d3c039cae1380951166_full.jpg',
|
||||||
@ -192,6 +192,21 @@ class CrunchyrollIE(CrunchyrollBaseIE):
|
|||||||
# geo-restricted (US), 18+ maturity wall, non-premium available
|
# geo-restricted (US), 18+ maturity wall, non-premium available
|
||||||
'url': 'http://www.crunchyroll.com/cosplay-complex-ova/episode-1-the-birth-of-the-cosplay-club-565617',
|
'url': 'http://www.crunchyroll.com/cosplay-complex-ova/episode-1-the-birth-of-the-cosplay-club-565617',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# A description with double quotes
|
||||||
|
'url': 'http://www.crunchyroll.com/11eyes/episode-1-piros-jszaka-red-night-535080',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '535080',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '11eyes Episode 1 – Piros éjszaka - Red Night',
|
||||||
|
'description': 'Kakeru and Yuka are thrown into an alternate nightmarish world they call "Red Night".',
|
||||||
|
'uploader': 'Marvelous AQL Inc.',
|
||||||
|
'upload_date': '20091021',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
# Just test metadata extraction
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
_FORMAT_IDS = {
|
_FORMAT_IDS = {
|
||||||
@ -362,9 +377,9 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
|||||||
r'(?s)<h1[^>]*>((?:(?!<h1).)*?<span[^>]+itemprop=["\']title["\'][^>]*>(?:(?!<h1).)+?)</h1>',
|
r'(?s)<h1[^>]*>((?:(?!<h1).)*?<span[^>]+itemprop=["\']title["\'][^>]*>(?:(?!<h1).)+?)</h1>',
|
||||||
webpage, 'video_title')
|
webpage, 'video_title')
|
||||||
video_title = re.sub(r' {2,}', ' ', video_title)
|
video_title = re.sub(r' {2,}', ' ', video_title)
|
||||||
video_description = self._html_search_regex(
|
video_description = self._parse_json(self._html_search_regex(
|
||||||
r'<script[^>]*>\s*.+?\[media_id=%s\].+?"description"\s*:\s*"([^"]+)' % video_id,
|
r'<script[^>]*>\s*.+?\[media_id=%s\].+?({.+?"description"\s*:.+?})\);' % video_id,
|
||||||
webpage, 'description', default=None)
|
webpage, 'description', default='{}'), video_id).get('description')
|
||||||
if video_description:
|
if video_description:
|
||||||
video_description = lowercase_escape(video_description.replace(r'\r\n', '\n'))
|
video_description = lowercase_escape(video_description.replace(r'\r\n', '\n'))
|
||||||
video_upload_date = self._html_search_regex(
|
video_upload_date = self._html_search_regex(
|
||||||
|
@ -66,7 +66,6 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|||||||
'uploader_id': 'xijv66',
|
'uploader_id': 'xijv66',
|
||||||
'age_limit': 0,
|
'age_limit': 0,
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
'comment_count': int,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
# Vevo video
|
# Vevo video
|
||||||
@ -140,7 +139,7 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|||||||
view_count = str_to_int(view_count_str)
|
view_count = str_to_int(view_count_str)
|
||||||
comment_count = int_or_none(self._search_regex(
|
comment_count = int_or_none(self._search_regex(
|
||||||
r'<meta[^>]+itemprop="interactionCount"[^>]+content="UserComments:(\d+)"',
|
r'<meta[^>]+itemprop="interactionCount"[^>]+content="UserComments:(\d+)"',
|
||||||
webpage, 'comment count', fatal=False))
|
webpage, 'comment count', default=None))
|
||||||
|
|
||||||
player_v5 = self._search_regex(
|
player_v5 = self._search_regex(
|
||||||
[r'buildPlayer\(({.+?})\);\n', # See https://github.com/rg3/youtube-dl/issues/7826
|
[r'buildPlayer\(({.+?})\);\n', # See https://github.com/rg3/youtube-dl/issues/7826
|
||||||
|
@ -4,11 +4,13 @@ from __future__ import unicode_literals
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from ..compat import compat_HTTPError
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
determine_ext,
|
determine_ext,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
unsmuggle_url,
|
unsmuggle_url,
|
||||||
|
ExtractorError,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -20,9 +22,17 @@ class LimelightBaseIE(InfoExtractor):
|
|||||||
headers = {}
|
headers = {}
|
||||||
if referer:
|
if referer:
|
||||||
headers['Referer'] = referer
|
headers['Referer'] = referer
|
||||||
return self._download_json(
|
try:
|
||||||
self._PLAYLIST_SERVICE_URL % (self._PLAYLIST_SERVICE_PATH, item_id, method),
|
return self._download_json(
|
||||||
item_id, 'Downloading PlaylistService %s JSON' % method, fatal=fatal, headers=headers)
|
self._PLAYLIST_SERVICE_URL % (self._PLAYLIST_SERVICE_PATH, item_id, method),
|
||||||
|
item_id, 'Downloading PlaylistService %s JSON' % method, fatal=fatal, headers=headers)
|
||||||
|
except ExtractorError as e:
|
||||||
|
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
|
||||||
|
error = self._parse_json(e.cause.read().decode(), item_id)['detail']['contentAccessPermission']
|
||||||
|
if error == 'CountryDisabled':
|
||||||
|
self.raise_geo_restricted()
|
||||||
|
raise ExtractorError(error, expected=True)
|
||||||
|
raise
|
||||||
|
|
||||||
def _call_api(self, organization_id, item_id, method):
|
def _call_api(self, organization_id, item_id, method):
|
||||||
return self._download_json(
|
return self._download_json(
|
||||||
@ -213,6 +223,7 @@ class LimelightMediaIE(LimelightBaseIE):
|
|||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
url, smuggled_data = unsmuggle_url(url, {})
|
url, smuggled_data = unsmuggle_url(url, {})
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
|
self._initialize_geo_bypass(smuggled_data.get('geo_countries'))
|
||||||
|
|
||||||
pc, mobile, metadata = self._extract(
|
pc, mobile, metadata = self._extract(
|
||||||
video_id, 'getPlaylistByMediaId',
|
video_id, 'getPlaylistByMediaId',
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import int_or_none
|
from ..utils import (
|
||||||
|
int_or_none,
|
||||||
|
smuggle_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TeleQuebecIE(InfoExtractor):
|
class TeleQuebecIE(InfoExtractor):
|
||||||
@ -28,7 +31,7 @@ class TeleQuebecIE(InfoExtractor):
|
|||||||
return {
|
return {
|
||||||
'_type': 'url_transparent',
|
'_type': 'url_transparent',
|
||||||
'id': media_id,
|
'id': media_id,
|
||||||
'url': 'limelight:media:' + media_data['streamInfo']['sourceId'],
|
'url': smuggle_url('limelight:media:' + media_data['streamInfo']['sourceId'], {'geo_countries': ['CA']}),
|
||||||
'title': media_data['title'],
|
'title': media_data['title'],
|
||||||
'description': media_data.get('descriptions', [{'text': None}])[0].get('text'),
|
'description': media_data.get('descriptions', [{'text': None}])[0].get('text'),
|
||||||
'duration': int_or_none(media_data.get('durationInMilliseconds'), 1000),
|
'duration': int_or_none(media_data.get('durationInMilliseconds'), 1000),
|
||||||
|
@ -8,10 +8,12 @@ from ..utils import (
|
|||||||
HEADRequest,
|
HEADRequest,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
|
clean_html,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TFOIE(InfoExtractor):
|
class TFOIE(InfoExtractor):
|
||||||
|
_GEO_COUNTRIES = ['CA']
|
||||||
_VALID_URL = r'https?://(?:www\.)?tfo\.org/(?:en|fr)/(?:[^/]+/){2}(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www\.)?tfo\.org/(?:en|fr)/(?:[^/]+/){2}(?P<id>\d+)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.tfo.org/en/universe/tfo-247/100463871/video-game-hackathon',
|
'url': 'http://www.tfo.org/en/universe/tfo-247/100463871/video-game-hackathon',
|
||||||
@ -36,7 +38,9 @@ class TFOIE(InfoExtractor):
|
|||||||
'X-tfo-session': self._get_cookies('http://www.tfo.org/')['tfo-session'].value,
|
'X-tfo-session': self._get_cookies('http://www.tfo.org/')['tfo-session'].value,
|
||||||
})
|
})
|
||||||
if infos.get('success') == 0:
|
if infos.get('success') == 0:
|
||||||
raise ExtractorError('%s said: %s' % (self.IE_NAME, infos['msg']), expected=True)
|
if infos.get('code') == 'ErrGeoBlocked':
|
||||||
|
self.raise_geo_restricted(countries=self._GEO_COUNTRIES)
|
||||||
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, clean_html(infos['msg'])), expected=True)
|
||||||
video_data = infos['data']
|
video_data = infos['data']
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -12,7 +12,7 @@ from ..utils import (
|
|||||||
|
|
||||||
class TwentyFourVideoIE(InfoExtractor):
|
class TwentyFourVideoIE(InfoExtractor):
|
||||||
IE_NAME = '24video'
|
IE_NAME = '24video'
|
||||||
_VALID_URL = r'https?://(?:www\.)?24video\.(?:net|me|xxx|sex)/(?:video/(?:view|xml)/|player/new24_play\.swf\?id=)(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www\.)?24video\.(?:net|me|xxx|sex|tube)/(?:video/(?:view|xml)/|player/new24_play\.swf\?id=)(?P<id>\d+)'
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://www.24video.net/video/view/1044982',
|
'url': 'http://www.24video.net/video/view/1044982',
|
||||||
@ -37,6 +37,9 @@ class TwentyFourVideoIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://www.24video.me/video/view/1044982',
|
'url': 'http://www.24video.me/video/view/1044982',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.24video.tube/video/view/2363750',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@ -13,7 +13,7 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class VidziIE(InfoExtractor):
|
class VidziIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?vidzi\.tv/(?:embed-)?(?P<id>[0-9a-zA-Z]+)'
|
_VALID_URL = r'https?://(?:www\.)?vidzi\.(?:tv|cc)/(?:embed-)?(?P<id>[0-9a-zA-Z]+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://vidzi.tv/cghql9yq6emu.html',
|
'url': 'http://vidzi.tv/cghql9yq6emu.html',
|
||||||
'md5': '4f16c71ca0c8c8635ab6932b5f3f1660',
|
'md5': '4f16c71ca0c8c8635ab6932b5f3f1660',
|
||||||
@ -29,6 +29,9 @@ class VidziIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://vidzi.tv/embed-4z2yb0rzphe9-600x338.html',
|
'url': 'http://vidzi.tv/embed-4z2yb0rzphe9-600x338.html',
|
||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://vidzi.cc/cghql9yq6emu.html',
|
||||||
|
'skip_download': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@ -86,7 +86,9 @@ class ViewsterIE(InfoExtractor):
|
|||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
# Get 'api_token' cookie
|
# Get 'api_token' cookie
|
||||||
self._request_webpage(HEADRequest('http://www.viewster.com/'), video_id)
|
self._request_webpage(
|
||||||
|
HEADRequest('http://www.viewster.com/'),
|
||||||
|
video_id, headers=self.geo_verification_headers())
|
||||||
cookies = self._get_cookies('http://www.viewster.com/')
|
cookies = self._get_cookies('http://www.viewster.com/')
|
||||||
self._AUTH_TOKEN = compat_urllib_parse_unquote(cookies['api_token'].value)
|
self._AUTH_TOKEN = compat_urllib_parse_unquote(cookies['api_token'].value)
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
__version__ = '2017.02.21'
|
__version__ = '2017.02.22'
|
||||||
|
Reference in New Issue
Block a user