Compare commits
48 Commits
2017.02.17
...
2017.02.21
Author | SHA1 | Date | |
---|---|---|---|
8c6c88c7da | |||
159aaaa9d0 | |||
eea0716cae | |||
336a76551b | |||
dc0a869e5e | |||
e39b5d4ab8 | |||
e469ab2528 | |||
890d44b005 | |||
6926304472 | |||
3ccdde8cb7 | |||
da42ff0668 | |||
82f662182b | |||
2cc7fcd338 | |||
6d4c259765 | |||
c78dd35491 | |||
8ffb8e63fe | |||
983e9b7746 | |||
8936f68a0b | |||
c58b7ffef4 | |||
f1a78ee4ef | |||
de64e23c56 | |||
553f6dbac7 | |||
0aa10994f4 | |||
4248dad92b | |||
0a840f584c | |||
0016b84e16 | |||
18a0defab0 | |||
5d3fbf77d9 | |||
80b59020e0 | |||
71631862f4 | |||
89cc7fe770 | |||
04d906eae3 | |||
8ab8066cf0 | |||
01b1aa9ff4 | |||
ff4007891f | |||
28200e654b | |||
e633f21a96 | |||
d392005a79 | |||
773f291dcb | |||
bf5b9d859a | |||
049a0f4d6d | |||
ac33accd96 | |||
e84888b432 | |||
02d9b82a23 | |||
a2e3286676 | |||
f75caf059e | |||
bdabbc220c | |||
70bcc444a9 |
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.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.
|
### 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.
|
||||||
- [ ] I've **verified** and **I assure** that I'm running youtube-dl **2017.02.17**
|
- [ ] I've **verified** and **I assure** that I'm running youtube-dl **2017.02.21**
|
||||||
|
|
||||||
### 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.17
|
[debug] youtube-dl version 2017.02.21
|
||||||
[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: {}
|
||||||
|
46
ChangeLog
46
ChangeLog
@ -1,3 +1,49 @@
|
|||||||
|
version 2017.02.21
|
||||||
|
|
||||||
|
Core
|
||||||
|
* [extractor/common] Allow calling _initialize_geo_bypass from extractors
|
||||||
|
(#11970)
|
||||||
|
+ [adobepass] Add support for Time Warner Cable (#12191)
|
||||||
|
+ [travis] Run tests in parallel
|
||||||
|
+ [downloader/ism] Honor HTTP headers when downloading fragments
|
||||||
|
+ [downloader/dash] Honor HTTP headers when downloading fragments
|
||||||
|
+ [utils] Add GeoUtils class for working with geo tools and GeoUtils.random_ipv4
|
||||||
|
+ Add option --geo-bypass-country for explicit geo bypass on behalf of
|
||||||
|
specified country
|
||||||
|
+ Add options to control geo bypass mechanism --geo-bypass and --no-geo-bypass
|
||||||
|
+ Add experimental geo restriction bypass mechanism based on faking
|
||||||
|
X-Forwarded-For HTTP header
|
||||||
|
+ [utils] Introduce GeoRestrictedError for geo restricted videos
|
||||||
|
+ [utils] Introduce YoutubeDLError base class for all youtube-dl exceptions
|
||||||
|
|
||||||
|
Extractors
|
||||||
|
+ [ninecninemedia] Use geo bypass mechanism
|
||||||
|
* [spankbang] Make uploader optional (#12193)
|
||||||
|
+ [iprima] Improve geo restriction detection and disable geo bypass
|
||||||
|
* [iprima] Modernize
|
||||||
|
* [commonmistakes] Disable UnicodeBOM extractor test for python 3.2
|
||||||
|
+ [prosiebensat1] Throw ExtractionError on unsupported page type (#12180)
|
||||||
|
* [nrk] Update _API_HOST and relax _VALID_URL
|
||||||
|
+ [tv4] Bypass geo restriction and improve detection
|
||||||
|
* [tv4] Switch to hls3 protocol (#12177)
|
||||||
|
+ [viki] Improve geo restriction detection
|
||||||
|
+ [vgtv] Improve geo restriction detection
|
||||||
|
+ [srgssr] Improve geo restriction detection
|
||||||
|
+ [vbox7] Improve geo restriction detection and use geo bypass mechanism
|
||||||
|
+ [svt] Improve geo restriction detection and use geo bypass mechanism
|
||||||
|
+ [pbs] Improve geo restriction detection and use geo bypass mechanism
|
||||||
|
+ [ondemandkorea] Improve geo restriction detection and use geo bypass mechanism
|
||||||
|
+ [nrk] Improve geo restriction detection and use geo bypass mechanism
|
||||||
|
+ [itv] Improve geo restriction detection and use geo bypass mechanism
|
||||||
|
+ [go] Improve geo restriction detection and use geo bypass mechanism
|
||||||
|
+ [dramafever] Improve geo restriction detection and use geo bypass mechanism
|
||||||
|
* [brightcove:legacy] Restrict videoPlayer value (#12040)
|
||||||
|
+ [tvn24] Add support for tvn24.pl and tvn24bis.pl (#11679)
|
||||||
|
+ [thisav] Add support for HTML5 media (#11771)
|
||||||
|
* [metacafe] Bypass family filter (#10371)
|
||||||
|
* [viceland] Improve info extraction
|
||||||
|
|
||||||
|
|
||||||
version 2017.02.17
|
version 2017.02.17
|
||||||
|
|
||||||
Extractors
|
Extractors
|
||||||
|
34
README.md
34
README.md
@ -99,11 +99,21 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
--source-address IP Client-side IP address to bind to
|
--source-address IP Client-side IP address to bind to
|
||||||
-4, --force-ipv4 Make all connections via IPv4
|
-4, --force-ipv4 Make all connections via IPv4
|
||||||
-6, --force-ipv6 Make all connections via IPv6
|
-6, --force-ipv6 Make all connections via IPv6
|
||||||
|
|
||||||
|
## Geo Restriction:
|
||||||
--geo-verification-proxy URL Use this proxy to verify the IP address for
|
--geo-verification-proxy URL Use this proxy to verify the IP address for
|
||||||
some geo-restricted sites. The default
|
some geo-restricted sites. The default
|
||||||
proxy specified by --proxy (or none, if the
|
proxy specified by --proxy (or none, if the
|
||||||
options is not present) is used for the
|
options is not present) is used for the
|
||||||
actual downloading.
|
actual downloading.
|
||||||
|
--geo-bypass Bypass geographic restriction via faking
|
||||||
|
X-Forwarded-For HTTP header (experimental)
|
||||||
|
--no-geo-bypass Do not bypass geographic restriction via
|
||||||
|
faking X-Forwarded-For HTTP header
|
||||||
|
(experimental)
|
||||||
|
--geo-bypass-country CODE Force bypass geographic restriction with
|
||||||
|
explicitly provided two-letter ISO 3166-2
|
||||||
|
country code (experimental)
|
||||||
|
|
||||||
## Video Selection:
|
## Video Selection:
|
||||||
--playlist-start NUMBER Playlist video to start at (default is 1)
|
--playlist-start NUMBER Playlist video to start at (default is 1)
|
||||||
@ -140,17 +150,19 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo
|
|||||||
check if the key is not present, key >
|
check if the key is not present, key >
|
||||||
NUMBER (like "comment_count > 12", also
|
NUMBER (like "comment_count > 12", also
|
||||||
works with >=, <, <=, !=, =) to compare
|
works with >=, <, <=, !=, =) to compare
|
||||||
against a number, and & to require multiple
|
against a number, key = 'LITERAL' (like
|
||||||
matches. Values which are not known are
|
"uploader = 'Mike Smith'", also works with
|
||||||
excluded unless you put a question mark (?)
|
!=) to match against a string literal and &
|
||||||
after the operator. For example, to only
|
to require multiple matches. Values which
|
||||||
match videos that have been liked more than
|
are not known are excluded unless you put a
|
||||||
100 times and disliked less than 50 times
|
question mark (?) after the operator. For
|
||||||
(or the dislike functionality is not
|
example, to only match videos that have
|
||||||
available at the given service), but who
|
been liked more than 100 times and disliked
|
||||||
also have a description, use --match-filter
|
less than 50 times (or the dislike
|
||||||
"like_count > 100 & dislike_count <? 50 &
|
functionality is not available at the given
|
||||||
description" .
|
service), but who also have a description,
|
||||||
|
use --match-filter "like_count > 100 &
|
||||||
|
dislike_count <? 50 & description" .
|
||||||
--no-playlist Download only the video, if the URL refers
|
--no-playlist Download only the video, if the URL refers
|
||||||
to a video and a playlist.
|
to a video and a playlist.
|
||||||
--yes-playlist Download the playlist, if the URL refers to
|
--yes-playlist Download the playlist, if the URL refers to
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
DOWNLOAD_TESTS="age_restriction|download|subtitles|write_annotations|iqiyi_sdk_interpreter"
|
DOWNLOAD_TESTS="age_restriction|download|subtitles|write_annotations|iqiyi_sdk_interpreter"
|
||||||
|
|
||||||
test_set=""
|
test_set=""
|
||||||
|
multiprocess_args=""
|
||||||
|
|
||||||
case "$YTDL_TEST_SET" in
|
case "$YTDL_TEST_SET" in
|
||||||
core)
|
core)
|
||||||
@ -10,10 +11,11 @@ case "$YTDL_TEST_SET" in
|
|||||||
;;
|
;;
|
||||||
download)
|
download)
|
||||||
test_set="-I test_(?!$DOWNLOAD_TESTS).+\.py"
|
test_set="-I test_(?!$DOWNLOAD_TESTS).+\.py"
|
||||||
|
multiprocess_args="--processes=4 --process-timeout=540"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
nosetests test --verbose $test_set
|
nosetests test --verbose $test_set $multiprocess_args
|
||||||
|
@ -804,6 +804,7 @@
|
|||||||
- **TVCArticle**
|
- **TVCArticle**
|
||||||
- **tvigle**: Интернет-телевидение Tvigle.ru
|
- **tvigle**: Интернет-телевидение Tvigle.ru
|
||||||
- **tvland.com**
|
- **tvland.com**
|
||||||
|
- **TVN24**
|
||||||
- **TVNoe**
|
- **TVNoe**
|
||||||
- **tvp**: Telewizja Polska
|
- **tvp**: Telewizja Polska
|
||||||
- **tvp:embed**: Telewizja Polska
|
- **tvp:embed**: Telewizja Polska
|
||||||
|
@ -65,6 +65,10 @@ defs = gettestcases()
|
|||||||
|
|
||||||
|
|
||||||
class TestDownload(unittest.TestCase):
|
class TestDownload(unittest.TestCase):
|
||||||
|
# Parallel testing in nosetests. See
|
||||||
|
# http://nose.readthedocs.org/en/latest/doc_tests/test_multiprocess/multiprocess.html
|
||||||
|
_multiprocess_shared_ = True
|
||||||
|
|
||||||
maxDiff = None
|
maxDiff = None
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -73,7 +77,7 @@ class TestDownload(unittest.TestCase):
|
|||||||
# Dynamically generate tests
|
# Dynamically generate tests
|
||||||
|
|
||||||
|
|
||||||
def generator(test_case):
|
def generator(test_case, tname):
|
||||||
|
|
||||||
def test_template(self):
|
def test_template(self):
|
||||||
ie = youtube_dl.extractor.get_info_extractor(test_case['name'])
|
ie = youtube_dl.extractor.get_info_extractor(test_case['name'])
|
||||||
@ -102,6 +106,7 @@ def generator(test_case):
|
|||||||
return
|
return
|
||||||
|
|
||||||
params = get_params(test_case.get('params', {}))
|
params = get_params(test_case.get('params', {}))
|
||||||
|
params['outtmpl'] = tname + '_' + params['outtmpl']
|
||||||
if is_playlist and 'playlist' not in test_case:
|
if is_playlist and 'playlist' not in test_case:
|
||||||
params.setdefault('extract_flat', 'in_playlist')
|
params.setdefault('extract_flat', 'in_playlist')
|
||||||
params.setdefault('skip_download', True)
|
params.setdefault('skip_download', True)
|
||||||
@ -146,7 +151,7 @@ def generator(test_case):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
if try_num == RETRIES:
|
if try_num == RETRIES:
|
||||||
report_warning('Failed due to network errors, skipping...')
|
report_warning('%s failed due to network errors, skipping...' % tname)
|
||||||
return
|
return
|
||||||
|
|
||||||
print('Retrying: {0} failed tries\n\n##########\n\n'.format(try_num))
|
print('Retrying: {0} failed tries\n\n##########\n\n'.format(try_num))
|
||||||
@ -221,12 +226,12 @@ def generator(test_case):
|
|||||||
|
|
||||||
# And add them to TestDownload
|
# And add them to TestDownload
|
||||||
for n, test_case in enumerate(defs):
|
for n, test_case in enumerate(defs):
|
||||||
test_method = generator(test_case)
|
|
||||||
tname = 'test_' + str(test_case['name'])
|
tname = 'test_' + str(test_case['name'])
|
||||||
i = 1
|
i = 1
|
||||||
while hasattr(TestDownload, tname):
|
while hasattr(TestDownload, tname):
|
||||||
tname = 'test_%s_%d' % (test_case['name'], i)
|
tname = 'test_%s_%d' % (test_case['name'], i)
|
||||||
i += 1
|
i += 1
|
||||||
|
test_method = generator(test_case, tname)
|
||||||
test_method.__name__ = str(tname)
|
test_method.__name__ = str(tname)
|
||||||
setattr(TestDownload, test_method.__name__, test_method)
|
setattr(TestDownload, test_method.__name__, test_method)
|
||||||
del test_method
|
del test_method
|
||||||
|
@ -56,6 +56,8 @@ from .utils import (
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
format_bytes,
|
format_bytes,
|
||||||
formatSeconds,
|
formatSeconds,
|
||||||
|
GeoRestrictedError,
|
||||||
|
ISO3166Utils,
|
||||||
locked_file,
|
locked_file,
|
||||||
make_HTTPS_handler,
|
make_HTTPS_handler,
|
||||||
MaxDownloadsReached,
|
MaxDownloadsReached,
|
||||||
@ -272,6 +274,12 @@ class YoutubeDL(object):
|
|||||||
If it returns None, the video is downloaded.
|
If it returns None, the video is downloaded.
|
||||||
match_filter_func in utils.py is one example for this.
|
match_filter_func in utils.py is one example for this.
|
||||||
no_color: Do not emit color codes in output.
|
no_color: Do not emit color codes in output.
|
||||||
|
geo_bypass: Bypass geographic restriction via faking X-Forwarded-For
|
||||||
|
HTTP header (experimental)
|
||||||
|
geo_bypass_country:
|
||||||
|
Two-letter ISO 3166-2 country code that will be used for
|
||||||
|
explicit geographic restriction bypassing via faking
|
||||||
|
X-Forwarded-For HTTP header (experimental)
|
||||||
|
|
||||||
The following options determine which downloader is picked:
|
The following options determine which downloader is picked:
|
||||||
external_downloader: Executable of the external downloader to call.
|
external_downloader: Executable of the external downloader to call.
|
||||||
@ -707,6 +715,14 @@ class YoutubeDL(object):
|
|||||||
return self.process_ie_result(ie_result, download, extra_info)
|
return self.process_ie_result(ie_result, download, extra_info)
|
||||||
else:
|
else:
|
||||||
return ie_result
|
return ie_result
|
||||||
|
except GeoRestrictedError as e:
|
||||||
|
msg = e.msg
|
||||||
|
if e.countries:
|
||||||
|
msg += '\nThis video is available in %s.' % ', '.join(
|
||||||
|
map(ISO3166Utils.short2full, e.countries))
|
||||||
|
msg += '\nYou might want to use a VPN or a proxy server (with --proxy) to workaround.'
|
||||||
|
self.report_error(msg)
|
||||||
|
break
|
||||||
except ExtractorError as e: # An error we somewhat expected
|
except ExtractorError as e: # An error we somewhat expected
|
||||||
self.report_error(compat_str(e), e.format_traceback())
|
self.report_error(compat_str(e), e.format_traceback())
|
||||||
break
|
break
|
||||||
@ -847,8 +863,14 @@ class YoutubeDL(object):
|
|||||||
if self.params.get('playlistrandom', False):
|
if self.params.get('playlistrandom', False):
|
||||||
random.shuffle(entries)
|
random.shuffle(entries)
|
||||||
|
|
||||||
|
x_forwarded_for = ie_result.get('__x_forwarded_for_ip')
|
||||||
|
|
||||||
for i, entry in enumerate(entries, 1):
|
for i, entry in enumerate(entries, 1):
|
||||||
self.to_screen('[download] Downloading video %s of %s' % (i, n_entries))
|
self.to_screen('[download] Downloading video %s of %s' % (i, n_entries))
|
||||||
|
# This __x_forwarded_for_ip thing is a bit ugly but requires
|
||||||
|
# minimal changes
|
||||||
|
if x_forwarded_for:
|
||||||
|
entry['__x_forwarded_for_ip'] = x_forwarded_for
|
||||||
extra = {
|
extra = {
|
||||||
'n_entries': n_entries,
|
'n_entries': n_entries,
|
||||||
'playlist': playlist,
|
'playlist': playlist,
|
||||||
@ -1233,6 +1255,11 @@ class YoutubeDL(object):
|
|||||||
if cookies:
|
if cookies:
|
||||||
res['Cookie'] = cookies
|
res['Cookie'] = cookies
|
||||||
|
|
||||||
|
if 'X-Forwarded-For' not in res:
|
||||||
|
x_forwarded_for_ip = info_dict.get('__x_forwarded_for_ip')
|
||||||
|
if x_forwarded_for_ip:
|
||||||
|
res['X-Forwarded-For'] = x_forwarded_for_ip
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _calc_cookies(self, info_dict):
|
def _calc_cookies(self, info_dict):
|
||||||
@ -1375,6 +1402,9 @@ class YoutubeDL(object):
|
|||||||
full_format_info = info_dict.copy()
|
full_format_info = info_dict.copy()
|
||||||
full_format_info.update(format)
|
full_format_info.update(format)
|
||||||
format['http_headers'] = self._calc_headers(full_format_info)
|
format['http_headers'] = self._calc_headers(full_format_info)
|
||||||
|
# Remove private housekeeping stuff
|
||||||
|
if '__x_forwarded_for_ip' in info_dict:
|
||||||
|
del info_dict['__x_forwarded_for_ip']
|
||||||
|
|
||||||
# TODO Central sorting goes here
|
# TODO Central sorting goes here
|
||||||
|
|
||||||
|
@ -414,6 +414,8 @@ def _real_main(argv=None):
|
|||||||
'cn_verification_proxy': opts.cn_verification_proxy,
|
'cn_verification_proxy': opts.cn_verification_proxy,
|
||||||
'geo_verification_proxy': opts.geo_verification_proxy,
|
'geo_verification_proxy': opts.geo_verification_proxy,
|
||||||
'config_location': opts.config_location,
|
'config_location': opts.config_location,
|
||||||
|
'geo_bypass': opts.geo_bypass,
|
||||||
|
'geo_bypass_country': opts.geo_bypass_country,
|
||||||
}
|
}
|
||||||
|
|
||||||
with YoutubeDL(ydl_opts) as ydl:
|
with YoutubeDL(ydl_opts) as ydl:
|
||||||
|
@ -43,7 +43,10 @@ class DashSegmentsFD(FragmentFD):
|
|||||||
count = 0
|
count = 0
|
||||||
while count <= fragment_retries:
|
while count <= fragment_retries:
|
||||||
try:
|
try:
|
||||||
success = ctx['dl'].download(target_filename, {'url': segment_url})
|
success = ctx['dl'].download(target_filename, {
|
||||||
|
'url': segment_url,
|
||||||
|
'http_headers': info_dict.get('http_headers'),
|
||||||
|
})
|
||||||
if not success:
|
if not success:
|
||||||
return False
|
return False
|
||||||
down, target_sanitized = sanitize_open(target_filename, 'rb')
|
down, target_sanitized = sanitize_open(target_filename, 'rb')
|
||||||
|
@ -238,7 +238,10 @@ class IsmFD(FragmentFD):
|
|||||||
count = 0
|
count = 0
|
||||||
while count <= fragment_retries:
|
while count <= fragment_retries:
|
||||||
try:
|
try:
|
||||||
success = ctx['dl'].download(target_filename, {'url': segment_url})
|
success = ctx['dl'].download(target_filename, {
|
||||||
|
'url': segment_url,
|
||||||
|
'http_headers': info_dict.get('http_headers'),
|
||||||
|
})
|
||||||
if not success:
|
if not success:
|
||||||
return False
|
return False
|
||||||
down, target_sanitized = sanitize_open(target_filename, 'rb')
|
down, target_sanitized = sanitize_open(target_filename, 'rb')
|
||||||
|
@ -31,6 +31,11 @@ MSO_INFO = {
|
|||||||
'username_field': 'user',
|
'username_field': 'user',
|
||||||
'password_field': 'passwd',
|
'password_field': 'passwd',
|
||||||
},
|
},
|
||||||
|
'TWC': {
|
||||||
|
'name': 'Time Warner Cable | Spectrum',
|
||||||
|
'username_field': 'Ecom_User_ID',
|
||||||
|
'password_field': 'Ecom_Password',
|
||||||
|
},
|
||||||
'thr030': {
|
'thr030': {
|
||||||
'name': '3 Rivers Communications'
|
'name': '3 Rivers Communications'
|
||||||
},
|
},
|
||||||
|
@ -191,6 +191,10 @@ class BrightcoveLegacyIE(InfoExtractor):
|
|||||||
# These fields hold the id of the video
|
# These fields hold the id of the video
|
||||||
videoPlayer = find_param('@videoPlayer') or find_param('videoId') or find_param('videoID') or find_param('@videoList')
|
videoPlayer = find_param('@videoPlayer') or find_param('videoId') or find_param('videoID') or find_param('@videoList')
|
||||||
if videoPlayer is not None:
|
if videoPlayer is not None:
|
||||||
|
if isinstance(videoPlayer, list):
|
||||||
|
videoPlayer = videoPlayer[0]
|
||||||
|
if not (videoPlayer.isdigit() or videoPlayer.startswith('ref:')):
|
||||||
|
return None
|
||||||
params['@videoPlayer'] = videoPlayer
|
params['@videoPlayer'] = videoPlayer
|
||||||
linkBase = find_param('linkBaseURL')
|
linkBase = find_param('linkBaseURL')
|
||||||
if linkBase is not None:
|
if linkBase is not None:
|
||||||
|
@ -6,6 +6,7 @@ import hashlib
|
|||||||
import json
|
import json
|
||||||
import netrc
|
import netrc
|
||||||
import os
|
import os
|
||||||
|
import random
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
@ -39,6 +40,8 @@ from ..utils import (
|
|||||||
ExtractorError,
|
ExtractorError,
|
||||||
fix_xml_ampersands,
|
fix_xml_ampersands,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
|
GeoRestrictedError,
|
||||||
|
GeoUtils,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
js_to_json,
|
js_to_json,
|
||||||
parse_iso8601,
|
parse_iso8601,
|
||||||
@ -320,17 +323,34 @@ class InfoExtractor(object):
|
|||||||
_real_extract() methods and define a _VALID_URL regexp.
|
_real_extract() methods and define a _VALID_URL regexp.
|
||||||
Probably, they should also be added to the list of extractors.
|
Probably, they should also be added to the list of extractors.
|
||||||
|
|
||||||
|
_GEO_BYPASS attribute may be set to False in order to disable
|
||||||
|
geo restriction bypass mechanisms for a particular extractor.
|
||||||
|
Though it won't disable explicit geo restriction bypass based on
|
||||||
|
country code provided with geo_bypass_country. (experimental)
|
||||||
|
|
||||||
|
_GEO_COUNTRIES attribute may contain a list of presumably geo unrestricted
|
||||||
|
countries for this extractor. One of these countries will be used by
|
||||||
|
geo restriction bypass mechanism right away in order to bypass
|
||||||
|
geo restriction, of course, if the mechanism is not disabled. (experimental)
|
||||||
|
|
||||||
|
NB: both these geo attributes are experimental and may change in future
|
||||||
|
or be completely removed.
|
||||||
|
|
||||||
Finally, the _WORKING attribute should be set to False for broken IEs
|
Finally, the _WORKING attribute should be set to False for broken IEs
|
||||||
in order to warn the users and skip the tests.
|
in order to warn the users and skip the tests.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_ready = False
|
_ready = False
|
||||||
_downloader = None
|
_downloader = None
|
||||||
|
_x_forwarded_for_ip = None
|
||||||
|
_GEO_BYPASS = True
|
||||||
|
_GEO_COUNTRIES = None
|
||||||
_WORKING = True
|
_WORKING = True
|
||||||
|
|
||||||
def __init__(self, downloader=None):
|
def __init__(self, downloader=None):
|
||||||
"""Constructor. Receives an optional downloader."""
|
"""Constructor. Receives an optional downloader."""
|
||||||
self._ready = False
|
self._ready = False
|
||||||
|
self._x_forwarded_for_ip = None
|
||||||
self.set_downloader(downloader)
|
self.set_downloader(downloader)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -359,15 +379,59 @@ class InfoExtractor(object):
|
|||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
"""Initializes an instance (authentication, etc)."""
|
"""Initializes an instance (authentication, etc)."""
|
||||||
|
self._initialize_geo_bypass(self._GEO_COUNTRIES)
|
||||||
if not self._ready:
|
if not self._ready:
|
||||||
self._real_initialize()
|
self._real_initialize()
|
||||||
self._ready = True
|
self._ready = True
|
||||||
|
|
||||||
|
def _initialize_geo_bypass(self, countries):
|
||||||
|
"""
|
||||||
|
Initialize geo restriction bypass mechanism.
|
||||||
|
|
||||||
|
This method is used to initialize geo bypass mechanism based on faking
|
||||||
|
X-Forwarded-For HTTP header. A random country from provided country list
|
||||||
|
is selected and a random IP belonging to this country is generated. This
|
||||||
|
IP will be passed as X-Forwarded-For HTTP header in all subsequent
|
||||||
|
HTTP requests.
|
||||||
|
|
||||||
|
This method will be used for initial geo bypass mechanism initialization
|
||||||
|
during the instance initialization with _GEO_COUNTRIES.
|
||||||
|
|
||||||
|
You may also manually call it from extractor's code if geo countries
|
||||||
|
information is not available beforehand (e.g. obtained during
|
||||||
|
extraction) or due to some another reason.
|
||||||
|
"""
|
||||||
|
if not self._x_forwarded_for_ip:
|
||||||
|
country_code = self._downloader.params.get('geo_bypass_country', None)
|
||||||
|
# If there is no explicit country for geo bypass specified and
|
||||||
|
# the extractor is known to be geo restricted let's fake IP
|
||||||
|
# as X-Forwarded-For right away.
|
||||||
|
if (not country_code and
|
||||||
|
self._GEO_BYPASS and
|
||||||
|
self._downloader.params.get('geo_bypass', True) and
|
||||||
|
countries):
|
||||||
|
country_code = random.choice(countries)
|
||||||
|
if country_code:
|
||||||
|
self._x_forwarded_for_ip = GeoUtils.random_ipv4(country_code)
|
||||||
|
if self._downloader.params.get('verbose', False):
|
||||||
|
self._downloader.to_stdout(
|
||||||
|
'[debug] Using fake IP %s (%s) as X-Forwarded-For.'
|
||||||
|
% (self._x_forwarded_for_ip, country_code.upper()))
|
||||||
|
|
||||||
def extract(self, url):
|
def extract(self, url):
|
||||||
"""Extracts URL information and returns it in list of dicts."""
|
"""Extracts URL information and returns it in list of dicts."""
|
||||||
try:
|
try:
|
||||||
self.initialize()
|
for _ in range(2):
|
||||||
return self._real_extract(url)
|
try:
|
||||||
|
self.initialize()
|
||||||
|
ie_result = self._real_extract(url)
|
||||||
|
if self._x_forwarded_for_ip:
|
||||||
|
ie_result['__x_forwarded_for_ip'] = self._x_forwarded_for_ip
|
||||||
|
return ie_result
|
||||||
|
except GeoRestrictedError as e:
|
||||||
|
if self.__maybe_fake_ip_and_retry(e.countries):
|
||||||
|
continue
|
||||||
|
raise
|
||||||
except ExtractorError:
|
except ExtractorError:
|
||||||
raise
|
raise
|
||||||
except compat_http_client.IncompleteRead as e:
|
except compat_http_client.IncompleteRead as e:
|
||||||
@ -375,6 +439,21 @@ class InfoExtractor(object):
|
|||||||
except (KeyError, StopIteration) as e:
|
except (KeyError, StopIteration) as e:
|
||||||
raise ExtractorError('An extractor error has occurred.', cause=e)
|
raise ExtractorError('An extractor error has occurred.', cause=e)
|
||||||
|
|
||||||
|
def __maybe_fake_ip_and_retry(self, countries):
|
||||||
|
if (not self._downloader.params.get('geo_bypass_country', None) and
|
||||||
|
self._GEO_BYPASS and
|
||||||
|
self._downloader.params.get('geo_bypass', True) and
|
||||||
|
not self._x_forwarded_for_ip and
|
||||||
|
countries):
|
||||||
|
country_code = random.choice(countries)
|
||||||
|
self._x_forwarded_for_ip = GeoUtils.random_ipv4(country_code)
|
||||||
|
if self._x_forwarded_for_ip:
|
||||||
|
self.report_warning(
|
||||||
|
'Video is geo restricted. Retrying extraction with fake IP %s (%s) as X-Forwarded-For.'
|
||||||
|
% (self._x_forwarded_for_ip, country_code.upper()))
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def set_downloader(self, downloader):
|
def set_downloader(self, downloader):
|
||||||
"""Sets the downloader for this IE."""
|
"""Sets the downloader for this IE."""
|
||||||
self._downloader = downloader
|
self._downloader = downloader
|
||||||
@ -434,6 +513,15 @@ class InfoExtractor(object):
|
|||||||
if isinstance(url_or_request, (compat_str, str)):
|
if isinstance(url_or_request, (compat_str, str)):
|
||||||
url_or_request = url_or_request.partition('#')[0]
|
url_or_request = url_or_request.partition('#')[0]
|
||||||
|
|
||||||
|
# Some sites check X-Forwarded-For HTTP header in order to figure out
|
||||||
|
# the origin of the client behind proxy. This allows bypassing geo
|
||||||
|
# restriction by faking this header's value to IP that belongs to some
|
||||||
|
# geo unrestricted country. We will do so once we encounter any
|
||||||
|
# geo restriction error.
|
||||||
|
if self._x_forwarded_for_ip:
|
||||||
|
if 'X-Forwarded-For' not in headers:
|
||||||
|
headers['X-Forwarded-For'] = self._x_forwarded_for_ip
|
||||||
|
|
||||||
urlh = self._request_webpage(url_or_request, video_id, note, errnote, fatal, data=data, headers=headers, query=query)
|
urlh = self._request_webpage(url_or_request, video_id, note, errnote, fatal, data=data, headers=headers, query=query)
|
||||||
if urlh is False:
|
if urlh is False:
|
||||||
assert not fatal
|
assert not fatal
|
||||||
@ -609,10 +697,8 @@ class InfoExtractor(object):
|
|||||||
expected=True)
|
expected=True)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def raise_geo_restricted(msg='This video is not available from your location due to geo restriction'):
|
def raise_geo_restricted(msg='This video is not available from your location due to geo restriction', countries=None):
|
||||||
raise ExtractorError(
|
raise GeoRestrictedError(msg, countries=countries)
|
||||||
'%s. You might want to use --proxy to workaround.' % msg,
|
|
||||||
expected=True)
|
|
||||||
|
|
||||||
# Methods for following #608
|
# Methods for following #608
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import ExtractorError
|
from ..utils import ExtractorError
|
||||||
|
|
||||||
@ -33,7 +35,9 @@ class UnicodeBOMIE(InfoExtractor):
|
|||||||
IE_DESC = False
|
IE_DESC = False
|
||||||
_VALID_URL = r'(?P<bom>\ufeff)(?P<id>.*)$'
|
_VALID_URL = r'(?P<bom>\ufeff)(?P<id>.*)$'
|
||||||
|
|
||||||
_TESTS = [{
|
# Disable test for python 3.2 since BOM is broken in re in this version
|
||||||
|
# (see https://github.com/rg3/youtube-dl/issues/9751)
|
||||||
|
_TESTS = [] if (3, 0) < sys.version_info <= (3, 3) else [{
|
||||||
'url': '\ufeffhttp://www.youtube.com/watch?v=BaW_jenozKc',
|
'url': '\ufeffhttp://www.youtube.com/watch?v=BaW_jenozKc',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
@ -20,6 +20,7 @@ from ..utils import (
|
|||||||
class DramaFeverBaseIE(AMPIE):
|
class DramaFeverBaseIE(AMPIE):
|
||||||
_LOGIN_URL = 'https://www.dramafever.com/accounts/login/'
|
_LOGIN_URL = 'https://www.dramafever.com/accounts/login/'
|
||||||
_NETRC_MACHINE = 'dramafever'
|
_NETRC_MACHINE = 'dramafever'
|
||||||
|
_GEO_COUNTRIES = ['US', 'CA']
|
||||||
|
|
||||||
_CONSUMER_SECRET = 'DA59dtVXYLxajktV'
|
_CONSUMER_SECRET = 'DA59dtVXYLxajktV'
|
||||||
|
|
||||||
@ -116,8 +117,9 @@ class DramaFeverIE(DramaFeverBaseIE):
|
|||||||
'http://www.dramafever.com/amp/episode/feed.json?guid=%s' % video_id)
|
'http://www.dramafever.com/amp/episode/feed.json?guid=%s' % video_id)
|
||||||
except ExtractorError as e:
|
except ExtractorError as e:
|
||||||
if isinstance(e.cause, compat_HTTPError):
|
if isinstance(e.cause, compat_HTTPError):
|
||||||
raise ExtractorError(
|
self.raise_geo_restricted(
|
||||||
'Currently unavailable in your country.', expected=True)
|
msg='Currently unavailable in your country',
|
||||||
|
countries=self._GEO_COUNTRIES)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
series_id, episode_number = video_id.split('.')
|
series_id, episode_number = video_id.split('.')
|
||||||
|
@ -1009,6 +1009,7 @@ from .tvc import (
|
|||||||
)
|
)
|
||||||
from .tvigle import TvigleIE
|
from .tvigle import TvigleIE
|
||||||
from .tvland import TVLandIE
|
from .tvland import TVLandIE
|
||||||
|
from .tvn24 import TVN24IE
|
||||||
from .tvnoe import TVNoeIE
|
from .tvnoe import TVNoeIE
|
||||||
from .tvp import (
|
from .tvp import (
|
||||||
TVPEmbedIE,
|
TVPEmbedIE,
|
||||||
|
@ -37,6 +37,7 @@ class GoIE(AdobePassIE):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_VALID_URL = r'https?://(?:(?P<sub_domain>%s)\.)?go\.com/(?:[^/]+/)*(?:vdka(?P<id>\w+)|season-\d+/\d+-(?P<display_id>[^/?#]+))' % '|'.join(_SITE_INFO.keys())
|
_VALID_URL = r'https?://(?:(?P<sub_domain>%s)\.)?go\.com/(?:[^/]+/)*(?:vdka(?P<id>\w+)|season-\d+/\d+-(?P<display_id>[^/?#]+))' % '|'.join(_SITE_INFO.keys())
|
||||||
|
_GEO_COUNTRIES = ['US']
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://abc.go.com/shows/castle/video/most-recent/vdka0_g86w5onx',
|
'url': 'http://abc.go.com/shows/castle/video/most-recent/vdka0_g86w5onx',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -101,6 +102,10 @@ class GoIE(AdobePassIE):
|
|||||||
video_id, data=urlencode_postdata(data), headers=self.geo_verification_headers())
|
video_id, data=urlencode_postdata(data), headers=self.geo_verification_headers())
|
||||||
errors = entitlement.get('errors', {}).get('errors', [])
|
errors = entitlement.get('errors', {}).get('errors', [])
|
||||||
if errors:
|
if errors:
|
||||||
|
for error in errors:
|
||||||
|
if error.get('code') == 1002:
|
||||||
|
self.raise_geo_restricted(
|
||||||
|
error['message'], countries=self._GEO_COUNTRIES)
|
||||||
error_message = ', '.join([error['message'] for error in errors])
|
error_message = ', '.join([error['message'] for error in errors])
|
||||||
raise ExtractorError('%s said: %s' % (self.IE_NAME, error_message), expected=True)
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, error_message), expected=True)
|
||||||
asset_url += '?' + entitlement['uplynkData']['sessionKey']
|
asset_url += '?' + entitlement['uplynkData']['sessionKey']
|
||||||
|
@ -8,12 +8,12 @@ from .common import InfoExtractor
|
|||||||
from ..utils import (
|
from ..utils import (
|
||||||
determine_ext,
|
determine_ext,
|
||||||
js_to_json,
|
js_to_json,
|
||||||
sanitized_Request,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class IPrimaIE(InfoExtractor):
|
class IPrimaIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://play\.iprima\.cz/(?:.+/)?(?P<id>[^?#]+)'
|
_VALID_URL = r'https?://play\.iprima\.cz/(?:.+/)?(?P<id>[^?#]+)'
|
||||||
|
_GEO_BYPASS = False
|
||||||
|
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://play.iprima.cz/gondici-s-r-o-33',
|
'url': 'http://play.iprima.cz/gondici-s-r-o-33',
|
||||||
@ -29,6 +29,10 @@ class IPrimaIE(InfoExtractor):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'http://play.iprima.cz/particka/particka-92',
|
'url': 'http://play.iprima.cz/particka/particka-92',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# geo restricted
|
||||||
|
'url': 'http://play.iprima.cz/closer-nove-pripady/closer-nove-pripady-iv-1',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
@ -38,11 +42,13 @@ class IPrimaIE(InfoExtractor):
|
|||||||
|
|
||||||
video_id = self._search_regex(r'data-product="([^"]+)">', webpage, 'real id')
|
video_id = self._search_regex(r'data-product="([^"]+)">', webpage, 'real id')
|
||||||
|
|
||||||
req = sanitized_Request(
|
playerpage = self._download_webpage(
|
||||||
'http://play.iprima.cz/prehravac/init?_infuse=1'
|
'http://play.iprima.cz/prehravac/init',
|
||||||
'&_ts=%s&productId=%s' % (round(time.time()), video_id))
|
video_id, note='Downloading player', query={
|
||||||
req.add_header('Referer', url)
|
'_infuse': 1,
|
||||||
playerpage = self._download_webpage(req, video_id, note='Downloading player')
|
'_ts': round(time.time()),
|
||||||
|
'productId': video_id,
|
||||||
|
}, headers={'Referer': url})
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
|
|
||||||
@ -82,7 +88,7 @@ class IPrimaIE(InfoExtractor):
|
|||||||
extract_formats(src)
|
extract_formats(src)
|
||||||
|
|
||||||
if not formats and '>GEO_IP_NOT_ALLOWED<' in playerpage:
|
if not formats and '>GEO_IP_NOT_ALLOWED<' in playerpage:
|
||||||
self.raise_geo_restricted()
|
self.raise_geo_restricted(countries=['CZ'])
|
||||||
|
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ from ..utils import (
|
|||||||
|
|
||||||
class ITVIE(InfoExtractor):
|
class ITVIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?itv\.com/hub/[^/]+/(?P<id>[0-9a-zA-Z]+)'
|
_VALID_URL = r'https?://(?:www\.)?itv\.com/hub/[^/]+/(?P<id>[0-9a-zA-Z]+)'
|
||||||
|
_GEO_COUNTRIES = ['GB']
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.itv.com/hub/mr-bean-animated-series/2a2936a0053',
|
'url': 'http://www.itv.com/hub/mr-bean-animated-series/2a2936a0053',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -98,7 +99,11 @@ class ITVIE(InfoExtractor):
|
|||||||
headers=headers, data=etree.tostring(req_env))
|
headers=headers, data=etree.tostring(req_env))
|
||||||
playlist = xpath_element(resp_env, './/Playlist')
|
playlist = xpath_element(resp_env, './/Playlist')
|
||||||
if playlist is None:
|
if playlist is None:
|
||||||
|
fault_code = xpath_text(resp_env, './/faultcode')
|
||||||
fault_string = xpath_text(resp_env, './/faultstring')
|
fault_string = xpath_text(resp_env, './/faultstring')
|
||||||
|
if fault_code == 'InvalidGeoRegion':
|
||||||
|
self.raise_geo_restricted(
|
||||||
|
msg=fault_string, countries=self._GEO_COUNTRIES)
|
||||||
raise ExtractorError('%s said: %s' % (self.IE_NAME, fault_string))
|
raise ExtractorError('%s said: %s' % (self.IE_NAME, fault_string))
|
||||||
title = xpath_text(playlist, 'EpisodeTitle', fatal=True)
|
title = xpath_text(playlist, 'EpisodeTitle', fatal=True)
|
||||||
video_element = xpath_element(playlist, 'VideoEntries/Video', fatal=True)
|
video_element = xpath_element(playlist, 'VideoEntries/Video', fatal=True)
|
||||||
|
@ -6,12 +6,12 @@ from .common import InfoExtractor
|
|||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_parse_qs,
|
compat_parse_qs,
|
||||||
compat_urllib_parse_unquote,
|
compat_urllib_parse_unquote,
|
||||||
|
compat_urllib_parse_urlencode,
|
||||||
)
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
determine_ext,
|
determine_ext,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
urlencode_postdata,
|
|
||||||
get_element_by_attribute,
|
get_element_by_attribute,
|
||||||
mimetype2ext,
|
mimetype2ext,
|
||||||
)
|
)
|
||||||
@ -50,6 +50,21 @@ class MetacafeIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
'skip': 'Page is temporarily unavailable.',
|
'skip': 'Page is temporarily unavailable.',
|
||||||
},
|
},
|
||||||
|
# metacafe video with family filter
|
||||||
|
{
|
||||||
|
'url': 'http://www.metacafe.com/watch/2155630/adult_art_by_david_hart_156/',
|
||||||
|
'md5': 'b06082c5079bbdcde677a6291fbdf376',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '2155630',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': 'Adult Art By David Hart 156',
|
||||||
|
'uploader': '63346',
|
||||||
|
'description': 'md5:9afac8fc885252201ad14563694040fc',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
},
|
||||||
# AnyClip video
|
# AnyClip video
|
||||||
{
|
{
|
||||||
'url': 'http://www.metacafe.com/watch/an-dVVXnuY7Jh77J/the_andromeda_strain_1971_stop_the_bomb_part_3/',
|
'url': 'http://www.metacafe.com/watch/an-dVVXnuY7Jh77J/the_andromeda_strain_1971_stop_the_bomb_part_3/',
|
||||||
@ -112,22 +127,6 @@ class MetacafeIE(InfoExtractor):
|
|||||||
def report_disclaimer(self):
|
def report_disclaimer(self):
|
||||||
self.to_screen('Retrieving disclaimer')
|
self.to_screen('Retrieving disclaimer')
|
||||||
|
|
||||||
def _confirm_age(self):
|
|
||||||
# Retrieve disclaimer
|
|
||||||
self.report_disclaimer()
|
|
||||||
self._download_webpage(self._DISCLAIMER, None, False, 'Unable to retrieve disclaimer')
|
|
||||||
|
|
||||||
# Confirm age
|
|
||||||
self.report_age_confirmation()
|
|
||||||
self._download_webpage(
|
|
||||||
self._FILTER_POST, None, False, 'Unable to confirm age',
|
|
||||||
data=urlencode_postdata({
|
|
||||||
'filters': '0',
|
|
||||||
'submit': "Continue - I'm over 18",
|
|
||||||
}), headers={
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
})
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
# Extract id and simplified title from URL
|
# Extract id and simplified title from URL
|
||||||
video_id, display_id = re.match(self._VALID_URL, url).groups()
|
video_id, display_id = re.match(self._VALID_URL, url).groups()
|
||||||
@ -143,13 +142,15 @@ class MetacafeIE(InfoExtractor):
|
|||||||
if prefix == 'cb':
|
if prefix == 'cb':
|
||||||
return self.url_result('theplatform:%s' % ext_id, 'ThePlatform')
|
return self.url_result('theplatform:%s' % ext_id, 'ThePlatform')
|
||||||
|
|
||||||
# self._confirm_age()
|
headers = {
|
||||||
|
# Disable family filter
|
||||||
|
'Cookie': 'user=%s; ' % compat_urllib_parse_urlencode({'ffilter': False})
|
||||||
|
}
|
||||||
|
|
||||||
# AnyClip videos require the flashversion cookie so that we get the link
|
# AnyClip videos require the flashversion cookie so that we get the link
|
||||||
# to the mp4 file
|
# to the mp4 file
|
||||||
headers = {}
|
|
||||||
if video_id.startswith('an-'):
|
if video_id.startswith('an-'):
|
||||||
headers['Cookie'] = 'flashVersion=0;'
|
headers['Cookie'] += 'flashVersion=0; '
|
||||||
|
|
||||||
# Retrieve video webpage to extract further information
|
# Retrieve video webpage to extract further information
|
||||||
webpage = self._download_webpage(url, video_id, headers=headers)
|
webpage = self._download_webpage(url, video_id, headers=headers)
|
||||||
|
@ -19,6 +19,7 @@ class NineCNineMediaBaseIE(InfoExtractor):
|
|||||||
|
|
||||||
class NineCNineMediaStackIE(NineCNineMediaBaseIE):
|
class NineCNineMediaStackIE(NineCNineMediaBaseIE):
|
||||||
IE_NAME = '9c9media:stack'
|
IE_NAME = '9c9media:stack'
|
||||||
|
_GEO_COUNTRIES = ['CA']
|
||||||
_VALID_URL = r'9c9media:stack:(?P<destination_code>[^:]+):(?P<content_id>\d+):(?P<content_package>\d+):(?P<id>\d+)'
|
_VALID_URL = r'9c9media:stack:(?P<destination_code>[^:]+):(?P<content_id>\d+):(?P<content_package>\d+):(?P<id>\d+)'
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import random
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
@ -15,24 +14,7 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class NRKBaseIE(InfoExtractor):
|
class NRKBaseIE(InfoExtractor):
|
||||||
_faked_ip = None
|
_GEO_COUNTRIES = ['NO']
|
||||||
|
|
||||||
def _download_webpage_handle(self, *args, **kwargs):
|
|
||||||
# NRK checks X-Forwarded-For HTTP header in order to figure out the
|
|
||||||
# origin of the client behind proxy. This allows to bypass geo
|
|
||||||
# restriction by faking this header's value to some Norway IP.
|
|
||||||
# We will do so once we encounter any geo restriction error.
|
|
||||||
if self._faked_ip:
|
|
||||||
# NB: str is intentional
|
|
||||||
kwargs.setdefault(str('headers'), {})['X-Forwarded-For'] = self._faked_ip
|
|
||||||
return super(NRKBaseIE, self)._download_webpage_handle(*args, **kwargs)
|
|
||||||
|
|
||||||
def _fake_ip(self):
|
|
||||||
# Use fake IP from 37.191.128.0/17 in order to workaround geo
|
|
||||||
# restriction
|
|
||||||
def octet(lb=0, ub=255):
|
|
||||||
return random.randint(lb, ub)
|
|
||||||
self._faked_ip = '37.191.%d.%d' % (octet(128), octet())
|
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
@ -44,8 +26,6 @@ class NRKBaseIE(InfoExtractor):
|
|||||||
title = data.get('fullTitle') or data.get('mainTitle') or data['title']
|
title = data.get('fullTitle') or data.get('mainTitle') or data['title']
|
||||||
video_id = data.get('id') or video_id
|
video_id = data.get('id') or video_id
|
||||||
|
|
||||||
http_headers = {'X-Forwarded-For': self._faked_ip} if self._faked_ip else {}
|
|
||||||
|
|
||||||
entries = []
|
entries = []
|
||||||
|
|
||||||
conviva = data.get('convivaStatistics') or {}
|
conviva = data.get('convivaStatistics') or {}
|
||||||
@ -90,7 +70,6 @@ class NRKBaseIE(InfoExtractor):
|
|||||||
'duration': duration,
|
'duration': duration,
|
||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'http_headers': http_headers,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if not entries:
|
if not entries:
|
||||||
@ -107,19 +86,17 @@ class NRKBaseIE(InfoExtractor):
|
|||||||
}]
|
}]
|
||||||
|
|
||||||
if not entries:
|
if not entries:
|
||||||
message_type = data.get('messageType', '')
|
|
||||||
# Can be ProgramIsGeoBlocked or ChannelIsGeoBlocked*
|
|
||||||
if 'IsGeoBlocked' in message_type and not self._faked_ip:
|
|
||||||
self.report_warning(
|
|
||||||
'Video is geo restricted, trying to fake IP')
|
|
||||||
self._fake_ip()
|
|
||||||
return self._real_extract(url)
|
|
||||||
|
|
||||||
MESSAGES = {
|
MESSAGES = {
|
||||||
'ProgramRightsAreNotReady': 'Du kan dessverre ikke se eller høre programmet',
|
'ProgramRightsAreNotReady': 'Du kan dessverre ikke se eller høre programmet',
|
||||||
'ProgramRightsHasExpired': 'Programmet har gått ut',
|
'ProgramRightsHasExpired': 'Programmet har gått ut',
|
||||||
'ProgramIsGeoBlocked': 'NRK har ikke rettigheter til å vise dette programmet utenfor Norge',
|
'ProgramIsGeoBlocked': 'NRK har ikke rettigheter til å vise dette programmet utenfor Norge',
|
||||||
}
|
}
|
||||||
|
message_type = data.get('messageType', '')
|
||||||
|
# Can be ProgramIsGeoBlocked or ChannelIsGeoBlocked*
|
||||||
|
if 'IsGeoBlocked' in message_type:
|
||||||
|
self.raise_geo_restricted(
|
||||||
|
msg=MESSAGES.get('ProgramIsGeoBlocked'),
|
||||||
|
countries=self._GEO_COUNTRIES)
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
'%s said: %s' % (self.IE_NAME, MESSAGES.get(
|
'%s said: %s' % (self.IE_NAME, MESSAGES.get(
|
||||||
message_type, message_type)),
|
message_type, message_type)),
|
||||||
@ -188,12 +165,12 @@ class NRKIE(NRKBaseIE):
|
|||||||
https?://
|
https?://
|
||||||
(?:
|
(?:
|
||||||
(?:www\.)?nrk\.no/video/PS\*|
|
(?:www\.)?nrk\.no/video/PS\*|
|
||||||
v8-psapi\.nrk\.no/mediaelement/
|
v8[-.]psapi\.nrk\.no/mediaelement/
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
(?P<id>[^/?#&]+)
|
(?P<id>[^?#&]+)
|
||||||
'''
|
'''
|
||||||
_API_HOST = 'v8.psapi.nrk.no'
|
_API_HOST = 'v8-psapi.nrk.no'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
# video
|
# video
|
||||||
'url': 'http://www.nrk.no/video/PS*150533',
|
'url': 'http://www.nrk.no/video/PS*150533',
|
||||||
@ -219,6 +196,9 @@ class NRKIE(NRKBaseIE):
|
|||||||
}, {
|
}, {
|
||||||
'url': 'nrk:ecc1b952-96dc-4a98-81b9-5296dc7a98d9',
|
'url': 'nrk:ecc1b952-96dc-4a98-81b9-5296dc7a98d9',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'nrk:clip/7707d5a3-ebe7-434a-87d5-a3ebe7a34a70',
|
||||||
|
'only_matching': True,
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://v8-psapi.nrk.no/mediaelement/ecc1b952-96dc-4a98-81b9-5296dc7a98d9',
|
'url': 'https://v8-psapi.nrk.no/mediaelement/ecc1b952-96dc-4a98-81b9-5296dc7a98d9',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
@ -10,6 +10,7 @@ from ..utils import (
|
|||||||
|
|
||||||
class OnDemandKoreaIE(InfoExtractor):
|
class OnDemandKoreaIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?ondemandkorea\.com/(?P<id>[^/]+)\.html'
|
_VALID_URL = r'https?://(?:www\.)?ondemandkorea\.com/(?P<id>[^/]+)\.html'
|
||||||
|
_GEO_COUNTRIES = ['US', 'CA']
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'http://www.ondemandkorea.com/ask-us-anything-e43.html',
|
'url': 'http://www.ondemandkorea.com/ask-us-anything-e43.html',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -35,7 +36,8 @@ class OnDemandKoreaIE(InfoExtractor):
|
|||||||
|
|
||||||
if 'msg_block_01.png' in webpage:
|
if 'msg_block_01.png' in webpage:
|
||||||
self.raise_geo_restricted(
|
self.raise_geo_restricted(
|
||||||
'This content is not available in your region')
|
msg='This content is not available in your region',
|
||||||
|
countries=self._GEO_COUNTRIES)
|
||||||
|
|
||||||
if 'This video is only available to ODK PLUS members.' in webpage:
|
if 'This video is only available to ODK PLUS members.' in webpage:
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
|
@ -193,6 +193,8 @@ class PBSIE(InfoExtractor):
|
|||||||
)
|
)
|
||||||
''' % '|'.join(list(zip(*_STATIONS))[0])
|
''' % '|'.join(list(zip(*_STATIONS))[0])
|
||||||
|
|
||||||
|
_GEO_COUNTRIES = ['US']
|
||||||
|
|
||||||
_TESTS = [
|
_TESTS = [
|
||||||
{
|
{
|
||||||
'url': 'http://www.pbs.org/tpt/constitution-usa-peter-sagal/watch/a-more-perfect-union/',
|
'url': 'http://www.pbs.org/tpt/constitution-usa-peter-sagal/watch/a-more-perfect-union/',
|
||||||
@ -489,11 +491,13 @@ class PBSIE(InfoExtractor):
|
|||||||
headers=self.geo_verification_headers())
|
headers=self.geo_verification_headers())
|
||||||
|
|
||||||
if redirect_info['status'] == 'error':
|
if redirect_info['status'] == 'error':
|
||||||
|
message = self._ERRORS.get(
|
||||||
|
redirect_info['http_code'], redirect_info['message'])
|
||||||
|
if redirect_info['http_code'] == 403:
|
||||||
|
self.raise_geo_restricted(
|
||||||
|
msg=message, countries=self._GEO_COUNTRIES)
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
'%s said: %s' % (
|
'%s said: %s' % (self.IE_NAME, message), expected=True)
|
||||||
self.IE_NAME,
|
|
||||||
self._ERRORS.get(redirect_info['http_code'], redirect_info['message'])),
|
|
||||||
expected=True)
|
|
||||||
|
|
||||||
format_url = redirect_info.get('url')
|
format_url = redirect_info.get('url')
|
||||||
if not format_url:
|
if not format_url:
|
||||||
|
@ -424,3 +424,6 @@ class ProSiebenSat1IE(ProSiebenSat1BaseIE):
|
|||||||
return self._extract_clip(url, webpage)
|
return self._extract_clip(url, webpage)
|
||||||
elif page_type == 'playlist':
|
elif page_type == 'playlist':
|
||||||
return self._extract_playlist(url, webpage)
|
return self._extract_playlist(url, webpage)
|
||||||
|
else:
|
||||||
|
raise ExtractorError(
|
||||||
|
'Unsupported page type %s' % page_type, expected=True)
|
||||||
|
@ -23,6 +23,10 @@ class SpankBangIE(InfoExtractor):
|
|||||||
# 480p only
|
# 480p only
|
||||||
'url': 'http://spankbang.com/1vt0/video/solvane+gangbang',
|
'url': 'http://spankbang.com/1vt0/video/solvane+gangbang',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# no uploader
|
||||||
|
'url': 'http://spankbang.com/lklg/video/sex+with+anyone+wedding+edition+2',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
@ -48,7 +52,7 @@ class SpankBangIE(InfoExtractor):
|
|||||||
thumbnail = self._og_search_thumbnail(webpage)
|
thumbnail = self._og_search_thumbnail(webpage)
|
||||||
uploader = self._search_regex(
|
uploader = self._search_regex(
|
||||||
r'class="user"[^>]*><img[^>]+>([^<]+)',
|
r'class="user"[^>]*><img[^>]+>([^<]+)',
|
||||||
webpage, 'uploader', fatal=False)
|
webpage, 'uploader', default=None)
|
||||||
|
|
||||||
age_limit = self._rta_search(webpage)
|
age_limit = self._rta_search(webpage)
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ from ..utils import (
|
|||||||
|
|
||||||
class SRGSSRIE(InfoExtractor):
|
class SRGSSRIE(InfoExtractor):
|
||||||
_VALID_URL = r'(?:https?://tp\.srgssr\.ch/p(?:/[^/]+)+\?urn=urn|srgssr):(?P<bu>srf|rts|rsi|rtr|swi):(?:[^:]+:)?(?P<type>video|audio):(?P<id>[0-9a-f\-]{36}|\d+)'
|
_VALID_URL = r'(?:https?://tp\.srgssr\.ch/p(?:/[^/]+)+\?urn=urn|srgssr):(?P<bu>srf|rts|rsi|rtr|swi):(?:[^:]+:)?(?P<type>video|audio):(?P<id>[0-9a-f\-]{36}|\d+)'
|
||||||
|
_GEO_BYPASS = False
|
||||||
|
_GEO_COUNTRIES = ['CH']
|
||||||
|
|
||||||
_ERRORS = {
|
_ERRORS = {
|
||||||
'AGERATING12': 'To protect children under the age of 12, this video is only available between 8 p.m. and 6 a.m.',
|
'AGERATING12': 'To protect children under the age of 12, this video is only available between 8 p.m. and 6 a.m.',
|
||||||
@ -40,8 +42,12 @@ class SRGSSRIE(InfoExtractor):
|
|||||||
media_id)[media_type.capitalize()]
|
media_id)[media_type.capitalize()]
|
||||||
|
|
||||||
if media_data.get('block') and media_data['block'] in self._ERRORS:
|
if media_data.get('block') and media_data['block'] in self._ERRORS:
|
||||||
raise ExtractorError('%s said: %s' % (
|
message = self._ERRORS[media_data['block']]
|
||||||
self.IE_NAME, self._ERRORS[media_data['block']]), expected=True)
|
if media_data['block'] == 'GEOBLOCK':
|
||||||
|
self.raise_geo_restricted(
|
||||||
|
msg=message, countries=self._GEO_COUNTRIES)
|
||||||
|
raise ExtractorError(
|
||||||
|
'%s said: %s' % (self.IE_NAME, message), expected=True)
|
||||||
|
|
||||||
return media_data
|
return media_data
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@ from ..utils import (
|
|||||||
|
|
||||||
|
|
||||||
class SVTBaseIE(InfoExtractor):
|
class SVTBaseIE(InfoExtractor):
|
||||||
|
_GEO_COUNTRIES = ['SE']
|
||||||
|
|
||||||
def _extract_video(self, video_info, video_id):
|
def _extract_video(self, video_info, video_id):
|
||||||
formats = []
|
formats = []
|
||||||
for vr in video_info['videoReferences']:
|
for vr in video_info['videoReferences']:
|
||||||
@ -38,7 +40,9 @@ class SVTBaseIE(InfoExtractor):
|
|||||||
'url': vurl,
|
'url': vurl,
|
||||||
})
|
})
|
||||||
if not formats and video_info.get('rights', {}).get('geoBlockedSweden'):
|
if not formats and video_info.get('rights', {}).get('geoBlockedSweden'):
|
||||||
self.raise_geo_restricted('This video is only available in Sweden')
|
self.raise_geo_restricted(
|
||||||
|
'This video is only available in Sweden',
|
||||||
|
countries=self._GEO_COUNTRIES)
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
subtitles = {}
|
subtitles = {}
|
||||||
|
@ -10,6 +10,7 @@ from ..utils import remove_end
|
|||||||
class ThisAVIE(InfoExtractor):
|
class ThisAVIE(InfoExtractor):
|
||||||
_VALID_URL = r'https?://(?:www\.)?thisav\.com/video/(?P<id>[0-9]+)/.*'
|
_VALID_URL = r'https?://(?:www\.)?thisav\.com/video/(?P<id>[0-9]+)/.*'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
|
# jwplayer
|
||||||
'url': 'http://www.thisav.com/video/47734/%98%26sup1%3B%83%9E%83%82---just-fit.html',
|
'url': 'http://www.thisav.com/video/47734/%98%26sup1%3B%83%9E%83%82---just-fit.html',
|
||||||
'md5': '0480f1ef3932d901f0e0e719f188f19b',
|
'md5': '0480f1ef3932d901f0e0e719f188f19b',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -20,6 +21,7 @@ class ThisAVIE(InfoExtractor):
|
|||||||
'uploader_id': 'dj7970'
|
'uploader_id': 'dj7970'
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
# html5 media
|
||||||
'url': 'http://www.thisav.com/video/242352/nerdy-18yo-big-ass-tattoos-and-glasses.html',
|
'url': 'http://www.thisav.com/video/242352/nerdy-18yo-big-ass-tattoos-and-glasses.html',
|
||||||
'md5': 'ba90c076bd0f80203679e5b60bf523ee',
|
'md5': 'ba90c076bd0f80203679e5b60bf523ee',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
@ -48,8 +50,12 @@ class ThisAVIE(InfoExtractor):
|
|||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
info_dict = self._extract_jwplayer_data(
|
entries = self._parse_html5_media_entries(url, webpage, video_id)
|
||||||
webpage, video_id, require_title=False)
|
if entries:
|
||||||
|
info_dict = entries[0]
|
||||||
|
else:
|
||||||
|
info_dict = self._extract_jwplayer_data(
|
||||||
|
webpage, video_id, require_title=False)
|
||||||
uploader = self._html_search_regex(
|
uploader = self._html_search_regex(
|
||||||
r': <a href="http://www.thisav.com/user/[0-9]+/(?:[^"]+)">([^<]+)</a>',
|
r': <a href="http://www.thisav.com/user/[0-9]+/(?:[^"]+)">([^<]+)</a>',
|
||||||
webpage, 'uploader name', fatal=False)
|
webpage, 'uploader name', fatal=False)
|
||||||
|
@ -24,6 +24,7 @@ class TV4IE(InfoExtractor):
|
|||||||
sport/|
|
sport/|
|
||||||
)
|
)
|
||||||
)(?P<id>[0-9]+)'''
|
)(?P<id>[0-9]+)'''
|
||||||
|
_GEO_COUNTRIES = ['SE']
|
||||||
_TESTS = [
|
_TESTS = [
|
||||||
{
|
{
|
||||||
'url': 'http://www.tv4.se/kalla-fakta/klipp/kalla-fakta-5-english-subtitles-2491650',
|
'url': 'http://www.tv4.se/kalla-fakta/klipp/kalla-fakta-5-english-subtitles-2491650',
|
||||||
@ -71,16 +72,12 @@ class TV4IE(InfoExtractor):
|
|||||||
'http://www.tv4play.se/player/assets/%s.json' % video_id,
|
'http://www.tv4play.se/player/assets/%s.json' % video_id,
|
||||||
video_id, 'Downloading video info JSON')
|
video_id, 'Downloading video info JSON')
|
||||||
|
|
||||||
# If is_geo_restricted is true, it doesn't necessarily mean we can't download it
|
|
||||||
if info.get('is_geo_restricted'):
|
|
||||||
self.report_warning('This content might not be available in your country due to licensing restrictions.')
|
|
||||||
|
|
||||||
title = info['title']
|
title = info['title']
|
||||||
|
|
||||||
subtitles = {}
|
subtitles = {}
|
||||||
formats = []
|
formats = []
|
||||||
# http formats are linked with unresolvable host
|
# http formats are linked with unresolvable host
|
||||||
for kind in ('hls', ''):
|
for kind in ('hls3', ''):
|
||||||
data = self._download_json(
|
data = self._download_json(
|
||||||
'https://prima.tv4play.se/api/web/asset/%s/play.json' % video_id,
|
'https://prima.tv4play.se/api/web/asset/%s/play.json' % video_id,
|
||||||
video_id, 'Downloading sources JSON', query={
|
video_id, 'Downloading sources JSON', query={
|
||||||
@ -113,6 +110,10 @@ class TV4IE(InfoExtractor):
|
|||||||
'url': manifest_url,
|
'url': manifest_url,
|
||||||
'ext': 'vtt',
|
'ext': 'vtt',
|
||||||
}]})
|
}]})
|
||||||
|
|
||||||
|
if not formats and info.get('is_geo_restricted'):
|
||||||
|
self.raise_geo_restricted(countries=self._GEO_COUNTRIES)
|
||||||
|
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
76
youtube_dl/extractor/tvn24.py
Normal file
76
youtube_dl/extractor/tvn24.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from ..utils import (
|
||||||
|
int_or_none,
|
||||||
|
unescapeHTML,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TVN24IE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:(?:[^/]+)\.)?tvn24(?:bis)?\.pl/(?:[^/]+/)*(?P<id>[^/]+)\.html'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'http://www.tvn24.pl/wiadomosci-z-kraju,3/oredzie-artura-andrusa,702428.html',
|
||||||
|
'md5': 'fbdec753d7bc29d96036808275f2130c',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '1584444',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '"Święta mają być wesołe, dlatego, ludziska, wszyscy pod jemiołę"',
|
||||||
|
'description': 'Wyjątkowe orędzie Artura Andrusa, jednego z gości "Szkła kontaktowego".',
|
||||||
|
'thumbnail': 're:http://.*[.]jpeg',
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'url': 'http://fakty.tvn24.pl/ogladaj-online,60/53-konferencja-bezpieczenstwa-w-monachium,716431.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://sport.tvn24.pl/pilka-nozna,105/ligue-1-kamil-glik-rozcial-glowe-monaco-tylko-remisuje-z-bastia,716522.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://tvn24bis.pl/poranek,146,m/gen-koziej-w-tvn24-bis-wracamy-do-czasow-zimnej-wojny,715660.html',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
|
title = self._og_search_title(webpage)
|
||||||
|
|
||||||
|
def extract_json(attr, name, fatal=True):
|
||||||
|
return self._parse_json(
|
||||||
|
self._search_regex(
|
||||||
|
r'\b%s=(["\'])(?P<json>(?!\1).+?)\1' % attr, webpage,
|
||||||
|
name, group='json', fatal=fatal) or '{}',
|
||||||
|
video_id, transform_source=unescapeHTML, fatal=fatal)
|
||||||
|
|
||||||
|
quality_data = extract_json('data-quality', 'formats')
|
||||||
|
|
||||||
|
formats = []
|
||||||
|
for format_id, url in quality_data.items():
|
||||||
|
formats.append({
|
||||||
|
'url': url,
|
||||||
|
'format_id': format_id,
|
||||||
|
'height': int_or_none(format_id.rstrip('p')),
|
||||||
|
})
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
description = self._og_search_description(webpage)
|
||||||
|
thumbnail = self._og_search_thumbnail(
|
||||||
|
webpage, default=None) or self._html_search_regex(
|
||||||
|
r'\bdata-poster=(["\'])(?P<url>(?!\1).+?)\1', webpage,
|
||||||
|
'thumbnail', group='url')
|
||||||
|
|
||||||
|
share_params = extract_json(
|
||||||
|
'data-share-params', 'share params', fatal=False)
|
||||||
|
if isinstance(share_params, dict):
|
||||||
|
video_id = share_params.get('id') or video_id
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'thumbnail': thumbnail,
|
||||||
|
'formats': formats,
|
||||||
|
}
|
@ -20,6 +20,7 @@ class Vbox7IE(InfoExtractor):
|
|||||||
)
|
)
|
||||||
(?P<id>[\da-fA-F]+)
|
(?P<id>[\da-fA-F]+)
|
||||||
'''
|
'''
|
||||||
|
_GEO_COUNTRIES = ['BG']
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'http://vbox7.com/play:0946fff23c',
|
'url': 'http://vbox7.com/play:0946fff23c',
|
||||||
'md5': 'a60f9ab3a3a2f013ef9a967d5f7be5bf',
|
'md5': 'a60f9ab3a3a2f013ef9a967d5f7be5bf',
|
||||||
@ -78,7 +79,7 @@ class Vbox7IE(InfoExtractor):
|
|||||||
video_url = video['src']
|
video_url = video['src']
|
||||||
|
|
||||||
if '/na.mp4' in video_url:
|
if '/na.mp4' in video_url:
|
||||||
self.raise_geo_restricted()
|
self.raise_geo_restricted(countries=self._GEO_COUNTRIES)
|
||||||
|
|
||||||
uploader = video.get('uploader')
|
uploader = video.get('uploader')
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ from ..utils import (
|
|||||||
|
|
||||||
class VGTVIE(XstreamIE):
|
class VGTVIE(XstreamIE):
|
||||||
IE_DESC = 'VGTV, BTTV, FTV, Aftenposten and Aftonbladet'
|
IE_DESC = 'VGTV, BTTV, FTV, Aftenposten and Aftonbladet'
|
||||||
|
_GEO_BYPASS = False
|
||||||
|
|
||||||
_HOST_TO_APPNAME = {
|
_HOST_TO_APPNAME = {
|
||||||
'vgtv.no': 'vgtv',
|
'vgtv.no': 'vgtv',
|
||||||
@ -217,7 +218,8 @@ class VGTVIE(XstreamIE):
|
|||||||
properties = try_get(
|
properties = try_get(
|
||||||
data, lambda x: x['streamConfiguration']['properties'], list)
|
data, lambda x: x['streamConfiguration']['properties'], list)
|
||||||
if properties and 'geoblocked' in properties:
|
if properties and 'geoblocked' in properties:
|
||||||
raise self.raise_geo_restricted()
|
raise self.raise_geo_restricted(
|
||||||
|
countries=[host.rpartition('.')[-1].partition('/')[0].upper()])
|
||||||
|
|
||||||
self._sort_formats(info['formats'])
|
self._sort_formats(info['formats'])
|
||||||
|
|
||||||
|
@ -70,10 +70,10 @@ class ViceBaseIE(AdobePassIE):
|
|||||||
'url': uplynk_preplay_url,
|
'url': uplynk_preplay_url,
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'title': title,
|
'title': title,
|
||||||
'description': base.get('body'),
|
'description': base.get('body') or base.get('display_body'),
|
||||||
'thumbnail': watch_hub_data.get('cover-image') or watch_hub_data.get('thumbnail'),
|
'thumbnail': watch_hub_data.get('cover-image') or watch_hub_data.get('thumbnail'),
|
||||||
'duration': parse_duration(video_data.get('video_duration') or watch_hub_data.get('video-duration')),
|
'duration': int_or_none(video_data.get('video_duration')) or parse_duration(watch_hub_data.get('video-duration')),
|
||||||
'timestamp': int_or_none(video_data.get('created_at')),
|
'timestamp': int_or_none(video_data.get('created_at'), 1000),
|
||||||
'age_limit': parse_age_limit(video_data.get('video_rating')),
|
'age_limit': parse_age_limit(video_data.get('video_rating')),
|
||||||
'series': video_data.get('show_title') or watch_hub_data.get('show-title'),
|
'series': video_data.get('show_title') or watch_hub_data.get('show-title'),
|
||||||
'episode_number': int_or_none(episode.get('episode_number') or watch_hub_data.get('episode')),
|
'episode_number': int_or_none(episode.get('episode_number') or watch_hub_data.get('episode')),
|
||||||
|
@ -7,16 +7,16 @@ from .vice import ViceBaseIE
|
|||||||
class VicelandIE(ViceBaseIE):
|
class VicelandIE(ViceBaseIE):
|
||||||
_VALID_URL = r'https?://(?:www\.)?viceland\.com/[^/]+/video/[^/]+/(?P<id>[a-f0-9]+)'
|
_VALID_URL = r'https?://(?:www\.)?viceland\.com/[^/]+/video/[^/]+/(?P<id>[a-f0-9]+)'
|
||||||
_TEST = {
|
_TEST = {
|
||||||
'url': 'https://www.viceland.com/en_us/video/cyberwar-trailer/57608447973ee7705f6fbd4e',
|
'url': 'https://www.viceland.com/en_us/video/trapped/588a70d0dba8a16007de7316',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '57608447973ee7705f6fbd4e',
|
'id': '588a70d0dba8a16007de7316',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'CYBERWAR (Trailer)',
|
'title': 'TRAPPED (Series Trailer)',
|
||||||
'description': 'Tapping into the geopolitics of hacking and surveillance, Ben Makuch travels the world to meet with hackers, government officials, and dissidents to investigate the ecosystem of cyberwarfare.',
|
'description': 'md5:7a8e95c2b6cd86461502a2845e581ccf',
|
||||||
'age_limit': 14,
|
'age_limit': 14,
|
||||||
'timestamp': 1466008539,
|
'timestamp': 1485474122,
|
||||||
'upload_date': '20160615',
|
'upload_date': '20170126',
|
||||||
'uploader_id': '11',
|
'uploader_id': '57a204098cb727dec794c6a3',
|
||||||
'uploader': 'Viceland',
|
'uploader': 'Viceland',
|
||||||
},
|
},
|
||||||
'params': {
|
'params': {
|
||||||
|
@ -27,6 +27,7 @@ class VikiBaseIE(InfoExtractor):
|
|||||||
_APP_VERSION = '2.2.5.1428709186'
|
_APP_VERSION = '2.2.5.1428709186'
|
||||||
_APP_SECRET = '-$iJ}@p7!G@SyU/je1bEyWg}upLu-6V6-Lg9VD(]siH,r.,m-r|ulZ,U4LC/SeR)'
|
_APP_SECRET = '-$iJ}@p7!G@SyU/je1bEyWg}upLu-6V6-Lg9VD(]siH,r.,m-r|ulZ,U4LC/SeR)'
|
||||||
|
|
||||||
|
_GEO_BYPASS = False
|
||||||
_NETRC_MACHINE = 'viki'
|
_NETRC_MACHINE = 'viki'
|
||||||
|
|
||||||
_token = None
|
_token = None
|
||||||
@ -77,8 +78,11 @@ class VikiBaseIE(InfoExtractor):
|
|||||||
def _check_errors(self, data):
|
def _check_errors(self, data):
|
||||||
for reason, status in data.get('blocking', {}).items():
|
for reason, status in data.get('blocking', {}).items():
|
||||||
if status and reason in self._ERRORS:
|
if status and reason in self._ERRORS:
|
||||||
|
message = self._ERRORS[reason]
|
||||||
|
if reason == 'geo':
|
||||||
|
self.raise_geo_restricted(msg=message)
|
||||||
raise ExtractorError('%s said: %s' % (
|
raise ExtractorError('%s said: %s' % (
|
||||||
self.IE_NAME, self._ERRORS[reason]), expected=True)
|
self.IE_NAME, message), expected=True)
|
||||||
|
|
||||||
def _real_initialize(self):
|
def _real_initialize(self):
|
||||||
self._login()
|
self._login()
|
||||||
|
@ -228,17 +228,29 @@ def parseOpts(overrideArguments=None):
|
|||||||
action='store_const', const='::', dest='source_address',
|
action='store_const', const='::', dest='source_address',
|
||||||
help='Make all connections via IPv6',
|
help='Make all connections via IPv6',
|
||||||
)
|
)
|
||||||
network.add_option(
|
|
||||||
|
geo = optparse.OptionGroup(parser, 'Geo Restriction')
|
||||||
|
geo.add_option(
|
||||||
'--geo-verification-proxy',
|
'--geo-verification-proxy',
|
||||||
dest='geo_verification_proxy', default=None, metavar='URL',
|
dest='geo_verification_proxy', default=None, metavar='URL',
|
||||||
help='Use this proxy to verify the IP address for some geo-restricted sites. '
|
help='Use this proxy to verify the IP address for some geo-restricted sites. '
|
||||||
'The default proxy specified by --proxy (or none, if the options is not present) is used for the actual downloading.'
|
'The default proxy specified by --proxy (or none, if the options is not present) is used for the actual downloading.')
|
||||||
)
|
geo.add_option(
|
||||||
network.add_option(
|
|
||||||
'--cn-verification-proxy',
|
'--cn-verification-proxy',
|
||||||
dest='cn_verification_proxy', default=None, metavar='URL',
|
dest='cn_verification_proxy', default=None, metavar='URL',
|
||||||
help=optparse.SUPPRESS_HELP,
|
help=optparse.SUPPRESS_HELP)
|
||||||
)
|
geo.add_option(
|
||||||
|
'--geo-bypass',
|
||||||
|
action='store_true', dest='geo_bypass', default=True,
|
||||||
|
help='Bypass geographic restriction via faking X-Forwarded-For HTTP header (experimental)')
|
||||||
|
geo.add_option(
|
||||||
|
'--no-geo-bypass',
|
||||||
|
action='store_false', dest='geo_bypass', default=True,
|
||||||
|
help='Do not bypass geographic restriction via faking X-Forwarded-For HTTP header (experimental)')
|
||||||
|
geo.add_option(
|
||||||
|
'--geo-bypass-country', metavar='CODE',
|
||||||
|
dest='geo_bypass_country', default=None,
|
||||||
|
help='Force bypass geographic restriction with explicitly provided two-letter ISO 3166-2 country code (experimental)')
|
||||||
|
|
||||||
selection = optparse.OptionGroup(parser, 'Video Selection')
|
selection = optparse.OptionGroup(parser, 'Video Selection')
|
||||||
selection.add_option(
|
selection.add_option(
|
||||||
@ -302,8 +314,10 @@ def parseOpts(overrideArguments=None):
|
|||||||
'match if the key is present, '
|
'match if the key is present, '
|
||||||
'!key to check if the key is not present, '
|
'!key to check if the key is not present, '
|
||||||
'key > NUMBER (like "comment_count > 12", also works with '
|
'key > NUMBER (like "comment_count > 12", also works with '
|
||||||
'>=, <, <=, !=, =) to compare against a number, and '
|
'>=, <, <=, !=, =) to compare against a number, '
|
||||||
'& to require multiple matches. '
|
'key = \'LITERAL\' (like "uploader = \'Mike Smith\'", also works with !=) '
|
||||||
|
'to match against a string literal '
|
||||||
|
'and & to require multiple matches. '
|
||||||
'Values which are not known are excluded unless you '
|
'Values which are not known are excluded unless you '
|
||||||
'put a question mark (?) after the operator. '
|
'put a question mark (?) after the operator. '
|
||||||
'For example, to only match videos that have been liked more than '
|
'For example, to only match videos that have been liked more than '
|
||||||
@ -834,6 +848,7 @@ def parseOpts(overrideArguments=None):
|
|||||||
|
|
||||||
parser.add_option_group(general)
|
parser.add_option_group(general)
|
||||||
parser.add_option_group(network)
|
parser.add_option_group(network)
|
||||||
|
parser.add_option_group(geo)
|
||||||
parser.add_option_group(selection)
|
parser.add_option_group(selection)
|
||||||
parser.add_option_group(downloader)
|
parser.add_option_group(downloader)
|
||||||
parser.add_option_group(filesystem)
|
parser.add_option_group(filesystem)
|
||||||
|
@ -23,6 +23,7 @@ import operator
|
|||||||
import os
|
import os
|
||||||
import pipes
|
import pipes
|
||||||
import platform
|
import platform
|
||||||
|
import random
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
import ssl
|
import ssl
|
||||||
@ -701,7 +702,12 @@ def bug_reports_message():
|
|||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
class ExtractorError(Exception):
|
class YoutubeDLError(Exception):
|
||||||
|
"""Base exception for YoutubeDL errors."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ExtractorError(YoutubeDLError):
|
||||||
"""Error during info extraction."""
|
"""Error during info extraction."""
|
||||||
|
|
||||||
def __init__(self, msg, tb=None, expected=False, cause=None, video_id=None):
|
def __init__(self, msg, tb=None, expected=False, cause=None, video_id=None):
|
||||||
@ -742,7 +748,19 @@ class RegexNotFoundError(ExtractorError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DownloadError(Exception):
|
class GeoRestrictedError(ExtractorError):
|
||||||
|
"""Geographic restriction Error exception.
|
||||||
|
|
||||||
|
This exception may be thrown when a video is not available from your
|
||||||
|
geographic location due to geographic restrictions imposed by a website.
|
||||||
|
"""
|
||||||
|
def __init__(self, msg, countries=None):
|
||||||
|
super(GeoRestrictedError, self).__init__(msg, expected=True)
|
||||||
|
self.msg = msg
|
||||||
|
self.countries = countries
|
||||||
|
|
||||||
|
|
||||||
|
class DownloadError(YoutubeDLError):
|
||||||
"""Download Error exception.
|
"""Download Error exception.
|
||||||
|
|
||||||
This exception may be thrown by FileDownloader objects if they are not
|
This exception may be thrown by FileDownloader objects if they are not
|
||||||
@ -756,7 +774,7 @@ class DownloadError(Exception):
|
|||||||
self.exc_info = exc_info
|
self.exc_info = exc_info
|
||||||
|
|
||||||
|
|
||||||
class SameFileError(Exception):
|
class SameFileError(YoutubeDLError):
|
||||||
"""Same File exception.
|
"""Same File exception.
|
||||||
|
|
||||||
This exception will be thrown by FileDownloader objects if they detect
|
This exception will be thrown by FileDownloader objects if they detect
|
||||||
@ -765,7 +783,7 @@ class SameFileError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PostProcessingError(Exception):
|
class PostProcessingError(YoutubeDLError):
|
||||||
"""Post Processing exception.
|
"""Post Processing exception.
|
||||||
|
|
||||||
This exception may be raised by PostProcessor's .run() method to
|
This exception may be raised by PostProcessor's .run() method to
|
||||||
@ -773,15 +791,16 @@ class PostProcessingError(Exception):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, msg):
|
def __init__(self, msg):
|
||||||
|
super(PostProcessingError, self).__init__(msg)
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
|
||||||
|
|
||||||
class MaxDownloadsReached(Exception):
|
class MaxDownloadsReached(YoutubeDLError):
|
||||||
""" --max-downloads limit has been reached. """
|
""" --max-downloads limit has been reached. """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UnavailableVideoError(Exception):
|
class UnavailableVideoError(YoutubeDLError):
|
||||||
"""Unavailable Format exception.
|
"""Unavailable Format exception.
|
||||||
|
|
||||||
This exception will be thrown when a video is requested
|
This exception will be thrown when a video is requested
|
||||||
@ -790,7 +809,7 @@ class UnavailableVideoError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ContentTooShortError(Exception):
|
class ContentTooShortError(YoutubeDLError):
|
||||||
"""Content Too Short exception.
|
"""Content Too Short exception.
|
||||||
|
|
||||||
This exception may be raised by FileDownloader objects when a file they
|
This exception may be raised by FileDownloader objects when a file they
|
||||||
@ -799,12 +818,15 @@ class ContentTooShortError(Exception):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, downloaded, expected):
|
def __init__(self, downloaded, expected):
|
||||||
|
super(ContentTooShortError, self).__init__(
|
||||||
|
'Downloaded {0} bytes, expected {1} bytes'.format(downloaded, expected)
|
||||||
|
)
|
||||||
# Both in bytes
|
# Both in bytes
|
||||||
self.downloaded = downloaded
|
self.downloaded = downloaded
|
||||||
self.expected = expected
|
self.expected = expected
|
||||||
|
|
||||||
|
|
||||||
class XAttrMetadataError(Exception):
|
class XAttrMetadataError(YoutubeDLError):
|
||||||
def __init__(self, code=None, msg='Unknown error'):
|
def __init__(self, code=None, msg='Unknown error'):
|
||||||
super(XAttrMetadataError, self).__init__(msg)
|
super(XAttrMetadataError, self).__init__(msg)
|
||||||
self.code = code
|
self.code = code
|
||||||
@ -820,7 +842,7 @@ class XAttrMetadataError(Exception):
|
|||||||
self.reason = 'NOT_SUPPORTED'
|
self.reason = 'NOT_SUPPORTED'
|
||||||
|
|
||||||
|
|
||||||
class XAttrUnavailableError(Exception):
|
class XAttrUnavailableError(YoutubeDLError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -3018,6 +3040,260 @@ class ISO3166Utils(object):
|
|||||||
return cls._country_map.get(code.upper())
|
return cls._country_map.get(code.upper())
|
||||||
|
|
||||||
|
|
||||||
|
class GeoUtils(object):
|
||||||
|
# Major IPv4 address blocks per country
|
||||||
|
_country_ip_map = {
|
||||||
|
'AD': '85.94.160.0/19',
|
||||||
|
'AE': '94.200.0.0/13',
|
||||||
|
'AF': '149.54.0.0/17',
|
||||||
|
'AG': '209.59.64.0/18',
|
||||||
|
'AI': '204.14.248.0/21',
|
||||||
|
'AL': '46.99.0.0/16',
|
||||||
|
'AM': '46.70.0.0/15',
|
||||||
|
'AO': '105.168.0.0/13',
|
||||||
|
'AP': '159.117.192.0/21',
|
||||||
|
'AR': '181.0.0.0/12',
|
||||||
|
'AS': '202.70.112.0/20',
|
||||||
|
'AT': '84.112.0.0/13',
|
||||||
|
'AU': '1.128.0.0/11',
|
||||||
|
'AW': '181.41.0.0/18',
|
||||||
|
'AZ': '5.191.0.0/16',
|
||||||
|
'BA': '31.176.128.0/17',
|
||||||
|
'BB': '65.48.128.0/17',
|
||||||
|
'BD': '114.130.0.0/16',
|
||||||
|
'BE': '57.0.0.0/8',
|
||||||
|
'BF': '129.45.128.0/17',
|
||||||
|
'BG': '95.42.0.0/15',
|
||||||
|
'BH': '37.131.0.0/17',
|
||||||
|
'BI': '154.117.192.0/18',
|
||||||
|
'BJ': '137.255.0.0/16',
|
||||||
|
'BL': '192.131.134.0/24',
|
||||||
|
'BM': '196.12.64.0/18',
|
||||||
|
'BN': '156.31.0.0/16',
|
||||||
|
'BO': '161.56.0.0/16',
|
||||||
|
'BQ': '161.0.80.0/20',
|
||||||
|
'BR': '152.240.0.0/12',
|
||||||
|
'BS': '24.51.64.0/18',
|
||||||
|
'BT': '119.2.96.0/19',
|
||||||
|
'BW': '168.167.0.0/16',
|
||||||
|
'BY': '178.120.0.0/13',
|
||||||
|
'BZ': '179.42.192.0/18',
|
||||||
|
'CA': '99.224.0.0/11',
|
||||||
|
'CD': '41.243.0.0/16',
|
||||||
|
'CF': '196.32.200.0/21',
|
||||||
|
'CG': '197.214.128.0/17',
|
||||||
|
'CH': '85.0.0.0/13',
|
||||||
|
'CI': '154.232.0.0/14',
|
||||||
|
'CK': '202.65.32.0/19',
|
||||||
|
'CL': '152.172.0.0/14',
|
||||||
|
'CM': '165.210.0.0/15',
|
||||||
|
'CN': '36.128.0.0/10',
|
||||||
|
'CO': '181.240.0.0/12',
|
||||||
|
'CR': '201.192.0.0/12',
|
||||||
|
'CU': '152.206.0.0/15',
|
||||||
|
'CV': '165.90.96.0/19',
|
||||||
|
'CW': '190.88.128.0/17',
|
||||||
|
'CY': '46.198.0.0/15',
|
||||||
|
'CZ': '88.100.0.0/14',
|
||||||
|
'DE': '53.0.0.0/8',
|
||||||
|
'DJ': '197.241.0.0/17',
|
||||||
|
'DK': '87.48.0.0/12',
|
||||||
|
'DM': '192.243.48.0/20',
|
||||||
|
'DO': '152.166.0.0/15',
|
||||||
|
'DZ': '41.96.0.0/12',
|
||||||
|
'EC': '186.68.0.0/15',
|
||||||
|
'EE': '90.190.0.0/15',
|
||||||
|
'EG': '156.160.0.0/11',
|
||||||
|
'ER': '196.200.96.0/20',
|
||||||
|
'ES': '88.0.0.0/11',
|
||||||
|
'ET': '196.188.0.0/14',
|
||||||
|
'EU': '2.16.0.0/13',
|
||||||
|
'FI': '91.152.0.0/13',
|
||||||
|
'FJ': '144.120.0.0/16',
|
||||||
|
'FM': '119.252.112.0/20',
|
||||||
|
'FO': '88.85.32.0/19',
|
||||||
|
'FR': '90.0.0.0/9',
|
||||||
|
'GA': '41.158.0.0/15',
|
||||||
|
'GB': '25.0.0.0/8',
|
||||||
|
'GD': '74.122.88.0/21',
|
||||||
|
'GE': '31.146.0.0/16',
|
||||||
|
'GF': '161.22.64.0/18',
|
||||||
|
'GG': '62.68.160.0/19',
|
||||||
|
'GH': '45.208.0.0/14',
|
||||||
|
'GI': '85.115.128.0/19',
|
||||||
|
'GL': '88.83.0.0/19',
|
||||||
|
'GM': '160.182.0.0/15',
|
||||||
|
'GN': '197.149.192.0/18',
|
||||||
|
'GP': '104.250.0.0/19',
|
||||||
|
'GQ': '105.235.224.0/20',
|
||||||
|
'GR': '94.64.0.0/13',
|
||||||
|
'GT': '168.234.0.0/16',
|
||||||
|
'GU': '168.123.0.0/16',
|
||||||
|
'GW': '197.214.80.0/20',
|
||||||
|
'GY': '181.41.64.0/18',
|
||||||
|
'HK': '113.252.0.0/14',
|
||||||
|
'HN': '181.210.0.0/16',
|
||||||
|
'HR': '93.136.0.0/13',
|
||||||
|
'HT': '148.102.128.0/17',
|
||||||
|
'HU': '84.0.0.0/14',
|
||||||
|
'ID': '39.192.0.0/10',
|
||||||
|
'IE': '87.32.0.0/12',
|
||||||
|
'IL': '79.176.0.0/13',
|
||||||
|
'IM': '5.62.80.0/20',
|
||||||
|
'IN': '117.192.0.0/10',
|
||||||
|
'IO': '203.83.48.0/21',
|
||||||
|
'IQ': '37.236.0.0/14',
|
||||||
|
'IR': '2.176.0.0/12',
|
||||||
|
'IS': '82.221.0.0/16',
|
||||||
|
'IT': '79.0.0.0/10',
|
||||||
|
'JE': '87.244.64.0/18',
|
||||||
|
'JM': '72.27.0.0/17',
|
||||||
|
'JO': '176.29.0.0/16',
|
||||||
|
'JP': '126.0.0.0/8',
|
||||||
|
'KE': '105.48.0.0/12',
|
||||||
|
'KG': '158.181.128.0/17',
|
||||||
|
'KH': '36.37.128.0/17',
|
||||||
|
'KI': '103.25.140.0/22',
|
||||||
|
'KM': '197.255.224.0/20',
|
||||||
|
'KN': '198.32.32.0/19',
|
||||||
|
'KP': '175.45.176.0/22',
|
||||||
|
'KR': '175.192.0.0/10',
|
||||||
|
'KW': '37.36.0.0/14',
|
||||||
|
'KY': '64.96.0.0/15',
|
||||||
|
'KZ': '2.72.0.0/13',
|
||||||
|
'LA': '115.84.64.0/18',
|
||||||
|
'LB': '178.135.0.0/16',
|
||||||
|
'LC': '192.147.231.0/24',
|
||||||
|
'LI': '82.117.0.0/19',
|
||||||
|
'LK': '112.134.0.0/15',
|
||||||
|
'LR': '41.86.0.0/19',
|
||||||
|
'LS': '129.232.0.0/17',
|
||||||
|
'LT': '78.56.0.0/13',
|
||||||
|
'LU': '188.42.0.0/16',
|
||||||
|
'LV': '46.109.0.0/16',
|
||||||
|
'LY': '41.252.0.0/14',
|
||||||
|
'MA': '105.128.0.0/11',
|
||||||
|
'MC': '88.209.64.0/18',
|
||||||
|
'MD': '37.246.0.0/16',
|
||||||
|
'ME': '178.175.0.0/17',
|
||||||
|
'MF': '74.112.232.0/21',
|
||||||
|
'MG': '154.126.0.0/17',
|
||||||
|
'MH': '117.103.88.0/21',
|
||||||
|
'MK': '77.28.0.0/15',
|
||||||
|
'ML': '154.118.128.0/18',
|
||||||
|
'MM': '37.111.0.0/17',
|
||||||
|
'MN': '49.0.128.0/17',
|
||||||
|
'MO': '60.246.0.0/16',
|
||||||
|
'MP': '202.88.64.0/20',
|
||||||
|
'MQ': '109.203.224.0/19',
|
||||||
|
'MR': '41.188.64.0/18',
|
||||||
|
'MS': '208.90.112.0/22',
|
||||||
|
'MT': '46.11.0.0/16',
|
||||||
|
'MU': '105.16.0.0/12',
|
||||||
|
'MV': '27.114.128.0/18',
|
||||||
|
'MW': '105.234.0.0/16',
|
||||||
|
'MX': '187.192.0.0/11',
|
||||||
|
'MY': '175.136.0.0/13',
|
||||||
|
'MZ': '197.218.0.0/15',
|
||||||
|
'NA': '41.182.0.0/16',
|
||||||
|
'NC': '101.101.0.0/18',
|
||||||
|
'NE': '197.214.0.0/18',
|
||||||
|
'NF': '203.17.240.0/22',
|
||||||
|
'NG': '105.112.0.0/12',
|
||||||
|
'NI': '186.76.0.0/15',
|
||||||
|
'NL': '145.96.0.0/11',
|
||||||
|
'NO': '84.208.0.0/13',
|
||||||
|
'NP': '36.252.0.0/15',
|
||||||
|
'NR': '203.98.224.0/19',
|
||||||
|
'NU': '49.156.48.0/22',
|
||||||
|
'NZ': '49.224.0.0/14',
|
||||||
|
'OM': '5.36.0.0/15',
|
||||||
|
'PA': '186.72.0.0/15',
|
||||||
|
'PE': '186.160.0.0/14',
|
||||||
|
'PF': '123.50.64.0/18',
|
||||||
|
'PG': '124.240.192.0/19',
|
||||||
|
'PH': '49.144.0.0/13',
|
||||||
|
'PK': '39.32.0.0/11',
|
||||||
|
'PL': '83.0.0.0/11',
|
||||||
|
'PM': '70.36.0.0/20',
|
||||||
|
'PR': '66.50.0.0/16',
|
||||||
|
'PS': '188.161.0.0/16',
|
||||||
|
'PT': '85.240.0.0/13',
|
||||||
|
'PW': '202.124.224.0/20',
|
||||||
|
'PY': '181.120.0.0/14',
|
||||||
|
'QA': '37.210.0.0/15',
|
||||||
|
'RE': '139.26.0.0/16',
|
||||||
|
'RO': '79.112.0.0/13',
|
||||||
|
'RS': '178.220.0.0/14',
|
||||||
|
'RU': '5.136.0.0/13',
|
||||||
|
'RW': '105.178.0.0/15',
|
||||||
|
'SA': '188.48.0.0/13',
|
||||||
|
'SB': '202.1.160.0/19',
|
||||||
|
'SC': '154.192.0.0/11',
|
||||||
|
'SD': '154.96.0.0/13',
|
||||||
|
'SE': '78.64.0.0/12',
|
||||||
|
'SG': '152.56.0.0/14',
|
||||||
|
'SI': '188.196.0.0/14',
|
||||||
|
'SK': '78.98.0.0/15',
|
||||||
|
'SL': '197.215.0.0/17',
|
||||||
|
'SM': '89.186.32.0/19',
|
||||||
|
'SN': '41.82.0.0/15',
|
||||||
|
'SO': '197.220.64.0/19',
|
||||||
|
'SR': '186.179.128.0/17',
|
||||||
|
'SS': '105.235.208.0/21',
|
||||||
|
'ST': '197.159.160.0/19',
|
||||||
|
'SV': '168.243.0.0/16',
|
||||||
|
'SX': '190.102.0.0/20',
|
||||||
|
'SY': '5.0.0.0/16',
|
||||||
|
'SZ': '41.84.224.0/19',
|
||||||
|
'TC': '65.255.48.0/20',
|
||||||
|
'TD': '154.68.128.0/19',
|
||||||
|
'TG': '196.168.0.0/14',
|
||||||
|
'TH': '171.96.0.0/13',
|
||||||
|
'TJ': '85.9.128.0/18',
|
||||||
|
'TK': '27.96.24.0/21',
|
||||||
|
'TL': '180.189.160.0/20',
|
||||||
|
'TM': '95.85.96.0/19',
|
||||||
|
'TN': '197.0.0.0/11',
|
||||||
|
'TO': '175.176.144.0/21',
|
||||||
|
'TR': '78.160.0.0/11',
|
||||||
|
'TT': '186.44.0.0/15',
|
||||||
|
'TV': '202.2.96.0/19',
|
||||||
|
'TW': '120.96.0.0/11',
|
||||||
|
'TZ': '156.156.0.0/14',
|
||||||
|
'UA': '93.72.0.0/13',
|
||||||
|
'UG': '154.224.0.0/13',
|
||||||
|
'US': '3.0.0.0/8',
|
||||||
|
'UY': '167.56.0.0/13',
|
||||||
|
'UZ': '82.215.64.0/18',
|
||||||
|
'VA': '212.77.0.0/19',
|
||||||
|
'VC': '24.92.144.0/20',
|
||||||
|
'VE': '186.88.0.0/13',
|
||||||
|
'VG': '172.103.64.0/18',
|
||||||
|
'VI': '146.226.0.0/16',
|
||||||
|
'VN': '14.160.0.0/11',
|
||||||
|
'VU': '202.80.32.0/20',
|
||||||
|
'WF': '117.20.32.0/21',
|
||||||
|
'WS': '202.4.32.0/19',
|
||||||
|
'YE': '134.35.0.0/16',
|
||||||
|
'YT': '41.242.116.0/22',
|
||||||
|
'ZA': '41.0.0.0/11',
|
||||||
|
'ZM': '165.56.0.0/13',
|
||||||
|
'ZW': '41.85.192.0/19',
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def random_ipv4(cls, code):
|
||||||
|
block = cls._country_ip_map.get(code.upper())
|
||||||
|
if not block:
|
||||||
|
return None
|
||||||
|
addr, preflen = block.split('/')
|
||||||
|
addr_min = compat_struct_unpack('!L', socket.inet_aton(addr))[0]
|
||||||
|
addr_max = addr_min | (0xffffffff >> int(preflen))
|
||||||
|
return compat_str(socket.inet_ntoa(
|
||||||
|
compat_struct_pack('!L', random.randint(addr_min, addr_max))))
|
||||||
|
|
||||||
|
|
||||||
class PerRequestProxyHandler(compat_urllib_request.ProxyHandler):
|
class PerRequestProxyHandler(compat_urllib_request.ProxyHandler):
|
||||||
def __init__(self, proxies=None):
|
def __init__(self, proxies=None):
|
||||||
# Set default handlers
|
# Set default handlers
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
__version__ = '2017.02.17'
|
__version__ = '2017.02.21'
|
||||||
|
Reference in New Issue
Block a user