Merge remote-tracking branch 'upstream/master' into myversion
This commit is contained in:
commit
1004198099
@ -286,6 +286,9 @@ class YoutubeDL(object):
|
|||||||
Two-letter ISO 3166-2 country code that will be used for
|
Two-letter ISO 3166-2 country code that will be used for
|
||||||
explicit geographic restriction bypassing via faking
|
explicit geographic restriction bypassing via faking
|
||||||
X-Forwarded-For HTTP header (experimental)
|
X-Forwarded-For HTTP header (experimental)
|
||||||
|
geo_bypass_ip_block:
|
||||||
|
IP range in CIDR notation that will be used similarly to
|
||||||
|
geo_bypass_country (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.
|
||||||
|
@ -430,6 +430,7 @@ def _real_main(argv=None):
|
|||||||
'config_location': opts.config_location,
|
'config_location': opts.config_location,
|
||||||
'geo_bypass': opts.geo_bypass,
|
'geo_bypass': opts.geo_bypass,
|
||||||
'geo_bypass_country': opts.geo_bypass_country,
|
'geo_bypass_country': opts.geo_bypass_country,
|
||||||
|
'geo_bypass_ip_block': opts.geo_bypass_ip_block,
|
||||||
# just for deprecation check
|
# just for deprecation check
|
||||||
'autonumber': opts.autonumber if opts.autonumber is True else None,
|
'autonumber': opts.autonumber if opts.autonumber is True else None,
|
||||||
'usetitle': opts.usetitle if opts.usetitle is True else None,
|
'usetitle': opts.usetitle if opts.usetitle is True else None,
|
||||||
|
@ -277,7 +277,9 @@ class AnvatoIE(InfoExtractor):
|
|||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
url, smuggled_data = unsmuggle_url(url, {})
|
url, smuggled_data = unsmuggle_url(url, {})
|
||||||
self._initialize_geo_bypass(smuggled_data.get('geo_countries'))
|
self._initialize_geo_bypass({
|
||||||
|
'countries': smuggled_data.get('geo_countries'),
|
||||||
|
})
|
||||||
|
|
||||||
mobj = re.match(self._VALID_URL, url)
|
mobj = re.match(self._VALID_URL, url)
|
||||||
access_key, video_id = mobj.group('access_key_or_mcp', 'id')
|
access_key, video_id = mobj.group('access_key_or_mcp', 'id')
|
||||||
|
@ -669,7 +669,10 @@ class BrightcoveNewIE(AdobePassIE):
|
|||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
url, smuggled_data = unsmuggle_url(url, {})
|
url, smuggled_data = unsmuggle_url(url, {})
|
||||||
self._initialize_geo_bypass(smuggled_data.get('geo_countries'))
|
self._initialize_geo_bypass({
|
||||||
|
'countries': smuggled_data.get('geo_countries'),
|
||||||
|
'ip_blocks': smuggled_data.get('geo_ip_blocks'),
|
||||||
|
})
|
||||||
|
|
||||||
account_id, player_id, embed, video_id = re.match(self._VALID_URL, url).groups()
|
account_id, player_id, embed, video_id = re.match(self._VALID_URL, url).groups()
|
||||||
|
|
||||||
|
42
youtube_dl/extractor/businessinsider.py
Normal file
42
youtube_dl/extractor/businessinsider.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
from .jwplatform import JWPlatformIE
|
||||||
|
|
||||||
|
|
||||||
|
class BusinessInsiderIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:[^/]+\.)?businessinsider\.(?:com|nl)/(?:[^/]+/)*(?P<id>[^/?#&]+)'
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'http://uk.businessinsider.com/how-much-radiation-youre-exposed-to-in-everyday-life-2016-6',
|
||||||
|
'md5': 'ca237a53a8eb20b6dc5bd60564d4ab3e',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'hZRllCfw',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': "Here's how much radiation you're exposed to in everyday life",
|
||||||
|
'description': 'md5:9a0d6e2c279948aadaa5e84d6d9b99bd',
|
||||||
|
'upload_date': '20170709',
|
||||||
|
'timestamp': 1499606400,
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.businessinsider.nl/5-scientifically-proven-things-make-you-less-attractive-2017-7/',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'http://www.businessinsider.com/excel-index-match-vlookup-video-how-to-2015-2?IR=T',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
jwplatform_id = self._search_regex(
|
||||||
|
(r'data-media-id=["\']([a-zA-Z0-9]{8})',
|
||||||
|
r'id=["\']jwplayer_([a-zA-Z0-9]{8})',
|
||||||
|
r'id["\']?\s*:\s*["\']?([a-zA-Z0-9]{8})'),
|
||||||
|
webpage, 'jwplatform id')
|
||||||
|
return self.url_result(
|
||||||
|
'jwplatform:%s' % jwplatform_id, ie=JWPlatformIE.ie_key(),
|
||||||
|
video_id=video_id)
|
60
youtube_dl/extractor/cloudflarestream.py
Normal file
60
youtube_dl/extractor/cloudflarestream.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .common import InfoExtractor
|
||||||
|
|
||||||
|
|
||||||
|
class CloudflareStreamIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'''(?x)
|
||||||
|
https?://
|
||||||
|
(?:
|
||||||
|
(?:watch\.)?cloudflarestream\.com/|
|
||||||
|
embed\.cloudflarestream\.com/embed/[^/]+\.js\?.*?\bvideo=
|
||||||
|
)
|
||||||
|
(?P<id>[\da-f]+)
|
||||||
|
'''
|
||||||
|
_TESTS = [{
|
||||||
|
'url': 'https://embed.cloudflarestream.com/embed/we4g.fla9.latest.js?video=31c9291ab41fac05471db4e73aa11717',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '31c9291ab41fac05471db4e73aa11717',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '31c9291ab41fac05471db4e73aa11717',
|
||||||
|
},
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://watch.cloudflarestream.com/9df17203414fd1db3e3ed74abbe936c1',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
'url': 'https://cloudflarestream.com/31c9291ab41fac05471db4e73aa11717/manifest/video.mpd',
|
||||||
|
'only_matching': True,
|
||||||
|
}]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_urls(webpage):
|
||||||
|
return [
|
||||||
|
mobj.group('url')
|
||||||
|
for mobj in re.finditer(
|
||||||
|
r'<script[^>]+\bsrc=(["\'])(?P<url>(?:https?:)?//embed\.cloudflarestream\.com/embed/[^/]+\.js\?.*?\bvideo=[\da-f]+?.*?)\1',
|
||||||
|
webpage)]
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
video_id = self._match_id(url)
|
||||||
|
|
||||||
|
formats = self._extract_m3u8_formats(
|
||||||
|
'https://cloudflarestream.com/%s/manifest/video.m3u8' % video_id,
|
||||||
|
video_id, 'mp4', entry_protocol='m3u8_native', m3u8_id='hls',
|
||||||
|
fatal=False)
|
||||||
|
formats.extend(self._extract_mpd_formats(
|
||||||
|
'https://cloudflarestream.com/%s/manifest/video.mpd' % video_id,
|
||||||
|
video_id, mpd_id='dash', fatal=False))
|
||||||
|
self._sort_formats(formats)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': video_id,
|
||||||
|
'title': video_id,
|
||||||
|
'formats': formats,
|
||||||
|
}
|
@ -346,6 +346,11 @@ class InfoExtractor(object):
|
|||||||
geo restriction bypass mechanism right away in order to bypass
|
geo restriction bypass mechanism right away in order to bypass
|
||||||
geo restriction, of course, if the mechanism is not disabled. (experimental)
|
geo restriction, of course, if the mechanism is not disabled. (experimental)
|
||||||
|
|
||||||
|
_GEO_IP_BLOCKS attribute may contain a list of presumably geo unrestricted
|
||||||
|
IP blocks in CIDR notation for this extractor. One of these IP blocks
|
||||||
|
will be used by geo restriction bypass mechanism similarly
|
||||||
|
to _GEO_COUNTRIES. (experimental)
|
||||||
|
|
||||||
NB: both these geo attributes are experimental and may change in future
|
NB: both these geo attributes are experimental and may change in future
|
||||||
or be completely removed.
|
or be completely removed.
|
||||||
|
|
||||||
@ -358,6 +363,7 @@ class InfoExtractor(object):
|
|||||||
_x_forwarded_for_ip = None
|
_x_forwarded_for_ip = None
|
||||||
_GEO_BYPASS = True
|
_GEO_BYPASS = True
|
||||||
_GEO_COUNTRIES = None
|
_GEO_COUNTRIES = None
|
||||||
|
_GEO_IP_BLOCKS = None
|
||||||
_WORKING = True
|
_WORKING = True
|
||||||
|
|
||||||
def __init__(self, downloader=None):
|
def __init__(self, downloader=None):
|
||||||
@ -392,12 +398,15 @@ 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)
|
self._initialize_geo_bypass({
|
||||||
|
'countries': self._GEO_COUNTRIES,
|
||||||
|
'ip_blocks': self._GEO_IP_BLOCKS,
|
||||||
|
})
|
||||||
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):
|
def _initialize_geo_bypass(self, geo_bypass_context):
|
||||||
"""
|
"""
|
||||||
Initialize geo restriction bypass mechanism.
|
Initialize geo restriction bypass mechanism.
|
||||||
|
|
||||||
@ -408,28 +417,82 @@ class InfoExtractor(object):
|
|||||||
HTTP requests.
|
HTTP requests.
|
||||||
|
|
||||||
This method will be used for initial geo bypass mechanism initialization
|
This method will be used for initial geo bypass mechanism initialization
|
||||||
during the instance initialization with _GEO_COUNTRIES.
|
during the instance initialization with _GEO_COUNTRIES and
|
||||||
|
_GEO_IP_BLOCKS.
|
||||||
|
|
||||||
You may also manually call it from extractor's code if geo countries
|
You may also manually call it from extractor's code if geo bypass
|
||||||
information is not available beforehand (e.g. obtained during
|
information is not available beforehand (e.g. obtained during
|
||||||
extraction) or due to some another reason.
|
extraction) or due to some other reason. In this case you should pass
|
||||||
|
this information in geo bypass context passed as first argument. It may
|
||||||
|
contain following fields:
|
||||||
|
|
||||||
|
countries: List of geo unrestricted countries (similar
|
||||||
|
to _GEO_COUNTRIES)
|
||||||
|
ip_blocks: List of geo unrestricted IP blocks in CIDR notation
|
||||||
|
(similar to _GEO_IP_BLOCKS)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not self._x_forwarded_for_ip:
|
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
|
# Geo bypass mechanism is explicitly disabled by user
|
||||||
# the extractor is known to be geo restricted let's fake IP
|
if not self._downloader.params.get('geo_bypass', True):
|
||||||
# as X-Forwarded-For right away.
|
return
|
||||||
if (not country_code and
|
|
||||||
self._GEO_BYPASS and
|
if not geo_bypass_context:
|
||||||
self._downloader.params.get('geo_bypass', True) and
|
geo_bypass_context = {}
|
||||||
countries):
|
|
||||||
country_code = random.choice(countries)
|
# Backward compatibility: previously _initialize_geo_bypass
|
||||||
if country_code:
|
# expected a list of countries, some 3rd party code may still use
|
||||||
self._x_forwarded_for_ip = GeoUtils.random_ipv4(country_code)
|
# it this way
|
||||||
|
if isinstance(geo_bypass_context, (list, tuple)):
|
||||||
|
geo_bypass_context = {
|
||||||
|
'countries': geo_bypass_context,
|
||||||
|
}
|
||||||
|
|
||||||
|
# The whole point of geo bypass mechanism is to fake IP
|
||||||
|
# as X-Forwarded-For HTTP header based on some IP block or
|
||||||
|
# country code.
|
||||||
|
|
||||||
|
# Path 1: bypassing based on IP block in CIDR notation
|
||||||
|
|
||||||
|
# Explicit IP block specified by user, use it right away
|
||||||
|
# regardless of whether extractor is geo bypassable or not
|
||||||
|
ip_block = self._downloader.params.get('geo_bypass_ip_block', None)
|
||||||
|
|
||||||
|
# Otherwise use random IP block from geo bypass context but only
|
||||||
|
# if extractor is known as geo bypassable
|
||||||
|
if not ip_block:
|
||||||
|
ip_blocks = geo_bypass_context.get('ip_blocks')
|
||||||
|
if self._GEO_BYPASS and ip_blocks:
|
||||||
|
ip_block = random.choice(ip_blocks)
|
||||||
|
|
||||||
|
if ip_block:
|
||||||
|
self._x_forwarded_for_ip = GeoUtils.random_ipv4(ip_block)
|
||||||
|
if self._downloader.params.get('verbose', False):
|
||||||
|
self._downloader.to_screen(
|
||||||
|
'[debug] Using fake IP %s as X-Forwarded-For.'
|
||||||
|
% self._x_forwarded_for_ip)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Path 2: bypassing based on country code
|
||||||
|
|
||||||
|
# Explicit country code specified by user, use it right away
|
||||||
|
# regardless of whether extractor is geo bypassable or not
|
||||||
|
country = self._downloader.params.get('geo_bypass_country', None)
|
||||||
|
|
||||||
|
# Otherwise use random country code from geo bypass context but
|
||||||
|
# only if extractor is known as geo bypassable
|
||||||
|
if not country:
|
||||||
|
countries = geo_bypass_context.get('countries')
|
||||||
|
if self._GEO_BYPASS and countries:
|
||||||
|
country = random.choice(countries)
|
||||||
|
|
||||||
|
if country:
|
||||||
|
self._x_forwarded_for_ip = GeoUtils.random_ipv4(country)
|
||||||
if self._downloader.params.get('verbose', False):
|
if self._downloader.params.get('verbose', False):
|
||||||
self._downloader.to_screen(
|
self._downloader.to_screen(
|
||||||
'[debug] Using fake IP %s (%s) as X-Forwarded-For.'
|
'[debug] Using fake IP %s (%s) as X-Forwarded-For.'
|
||||||
% (self._x_forwarded_for_ip, country_code.upper()))
|
% (self._x_forwarded_for_ip, country.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."""
|
||||||
|
@ -5,7 +5,10 @@ import re
|
|||||||
import string
|
import string
|
||||||
|
|
||||||
from .discoverygo import DiscoveryGoBaseIE
|
from .discoverygo import DiscoveryGoBaseIE
|
||||||
from ..compat import compat_str
|
from ..compat import (
|
||||||
|
compat_str,
|
||||||
|
compat_urllib_parse_unquote,
|
||||||
|
)
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
try_get,
|
try_get,
|
||||||
@ -55,6 +58,18 @@ class DiscoveryIE(DiscoveryGoBaseIE):
|
|||||||
video = next(cb for cb in content_blocks if cb.get('type') == 'video')['content']['items'][0]
|
video = next(cb for cb in content_blocks if cb.get('type') == 'video')['content']['items'][0]
|
||||||
video_id = video['id']
|
video_id = video['id']
|
||||||
|
|
||||||
|
access_token = None
|
||||||
|
cookies = self._get_cookies(url)
|
||||||
|
|
||||||
|
# prefer Affiliate Auth Token over Anonymous Auth Token
|
||||||
|
auth_storage_cookie = cookies.get('eosAf') or cookies.get('eosAn')
|
||||||
|
if auth_storage_cookie and auth_storage_cookie.value:
|
||||||
|
auth_storage = self._parse_json(compat_urllib_parse_unquote(
|
||||||
|
compat_urllib_parse_unquote(auth_storage_cookie.value)),
|
||||||
|
video_id, fatal=False) or {}
|
||||||
|
access_token = auth_storage.get('a') or auth_storage.get('access_token')
|
||||||
|
|
||||||
|
if not access_token:
|
||||||
access_token = self._download_json(
|
access_token = self._download_json(
|
||||||
'https://www.%s.com/anonymous' % site, display_id, query={
|
'https://www.%s.com/anonymous' % site, display_id, query={
|
||||||
'authRel': 'authorization',
|
'authRel': 'authorization',
|
||||||
@ -72,7 +87,7 @@ class DiscoveryIE(DiscoveryGoBaseIE):
|
|||||||
'Authorization': 'Bearer ' + access_token,
|
'Authorization': 'Bearer ' + access_token,
|
||||||
})
|
})
|
||||||
except ExtractorError as e:
|
except ExtractorError as e:
|
||||||
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
|
if isinstance(e.cause, compat_HTTPError) and e.cause.code in (401, 403):
|
||||||
e_description = self._parse_json(
|
e_description = self._parse_json(
|
||||||
e.cause.read().decode(), display_id)['description']
|
e.cause.read().decode(), display_id)['description']
|
||||||
if 'resource not available for country' in e_description:
|
if 'resource not available for country' in e_description:
|
||||||
|
@ -102,7 +102,9 @@ class DPlayIE(InfoExtractor):
|
|||||||
display_id = mobj.group('id')
|
display_id = mobj.group('id')
|
||||||
domain = mobj.group('domain')
|
domain = mobj.group('domain')
|
||||||
|
|
||||||
self._initialize_geo_bypass([mobj.group('country').upper()])
|
self._initialize_geo_bypass({
|
||||||
|
'countries': [mobj.group('country').upper()],
|
||||||
|
})
|
||||||
|
|
||||||
webpage = self._download_webpage(url, display_id)
|
webpage = self._download_webpage(url, display_id)
|
||||||
|
|
||||||
|
@ -137,6 +137,7 @@ from .brightcove import (
|
|||||||
BrightcoveLegacyIE,
|
BrightcoveLegacyIE,
|
||||||
BrightcoveNewIE,
|
BrightcoveNewIE,
|
||||||
)
|
)
|
||||||
|
from .businessinsider import BusinessInsiderIE
|
||||||
from .buzzfeed import BuzzFeedIE
|
from .buzzfeed import BuzzFeedIE
|
||||||
from .byutv import BYUtvIE
|
from .byutv import BYUtvIE
|
||||||
from .c56 import C56IE
|
from .c56 import C56IE
|
||||||
@ -195,6 +196,7 @@ from .clippit import ClippitIE
|
|||||||
from .cliprs import ClipRsIE
|
from .cliprs import ClipRsIE
|
||||||
from .clipsyndicate import ClipsyndicateIE
|
from .clipsyndicate import ClipsyndicateIE
|
||||||
from .closertotruth import CloserToTruthIE
|
from .closertotruth import CloserToTruthIE
|
||||||
|
from .cloudflarestream import CloudflareStreamIE
|
||||||
from .cloudy import CloudyIE
|
from .cloudy import CloudyIE
|
||||||
from .clubic import ClubicIE
|
from .clubic import ClubicIE
|
||||||
from .clyp import ClypIE
|
from .clyp import ClypIE
|
||||||
@ -477,7 +479,10 @@ from .internetvideoarchive import InternetVideoArchiveIE
|
|||||||
from .iprima import IPrimaIE
|
from .iprima import IPrimaIE
|
||||||
from .iqiyi import IqiyiIE
|
from .iqiyi import IqiyiIE
|
||||||
from .ir90tv import Ir90TvIE
|
from .ir90tv import Ir90TvIE
|
||||||
from .itv import ITVIE
|
from .itv import (
|
||||||
|
ITVIE,
|
||||||
|
ITVBTCCIE,
|
||||||
|
)
|
||||||
from .ivi import (
|
from .ivi import (
|
||||||
IviIE,
|
IviIE,
|
||||||
IviCompilationIE
|
IviCompilationIE
|
||||||
|
@ -107,6 +107,7 @@ from .springboardplatform import SpringboardPlatformIE
|
|||||||
from .yapfiles import YapFilesIE
|
from .yapfiles import YapFilesIE
|
||||||
from .vice import ViceIE
|
from .vice import ViceIE
|
||||||
from .xfileshare import XFileShareIE
|
from .xfileshare import XFileShareIE
|
||||||
|
from .cloudflarestream import CloudflareStreamIE
|
||||||
|
|
||||||
|
|
||||||
class GenericIE(InfoExtractor):
|
class GenericIE(InfoExtractor):
|
||||||
@ -1471,21 +1472,6 @@ class GenericIE(InfoExtractor):
|
|||||||
},
|
},
|
||||||
'expected_warnings': ['Failed to parse JSON Expecting value'],
|
'expected_warnings': ['Failed to parse JSON Expecting value'],
|
||||||
},
|
},
|
||||||
# Ooyala embed
|
|
||||||
{
|
|
||||||
'url': 'http://www.businessinsider.com/excel-index-match-vlookup-video-how-to-2015-2?IR=T',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '50YnY4czr4ms1vJ7yz3xzq0excz_pUMs',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'description': 'Index/Match versus VLOOKUP.',
|
|
||||||
'title': 'This is what separates the Excel masters from the wannabes',
|
|
||||||
'duration': 191.933,
|
|
||||||
},
|
|
||||||
'params': {
|
|
||||||
# m3u8 downloads
|
|
||||||
'skip_download': True,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
# Brightcove URL in single quotes
|
# Brightcove URL in single quotes
|
||||||
{
|
{
|
||||||
'url': 'http://www.sportsnet.ca/baseball/mlb/sn-presents-russell-martin-world-citizen/',
|
'url': 'http://www.sportsnet.ca/baseball/mlb/sn-presents-russell-martin-world-citizen/',
|
||||||
@ -2013,6 +1999,19 @@ class GenericIE(InfoExtractor):
|
|||||||
'skip_download': True,
|
'skip_download': True,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
# CloudflareStream embed
|
||||||
|
'url': 'https://www.cloudflare.com/products/cloudflare-stream/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': '31c9291ab41fac05471db4e73aa11717',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'title': '31c9291ab41fac05471db4e73aa11717',
|
||||||
|
},
|
||||||
|
'add_ie': [CloudflareStreamIE.ie_key()],
|
||||||
|
'params': {
|
||||||
|
'skip_download': True,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'url': 'http://share-videos.se/auto/video/83645793?uid=13',
|
'url': 'http://share-videos.se/auto/video/83645793?uid=13',
|
||||||
'md5': 'b68d276de422ab07ee1d49388103f457',
|
'md5': 'b68d276de422ab07ee1d49388103f457',
|
||||||
@ -3025,6 +3024,11 @@ class GenericIE(InfoExtractor):
|
|||||||
return self.playlist_from_matches(
|
return self.playlist_from_matches(
|
||||||
xfileshare_urls, video_id, video_title, ie=XFileShareIE.ie_key())
|
xfileshare_urls, video_id, video_title, ie=XFileShareIE.ie_key())
|
||||||
|
|
||||||
|
cloudflarestream_urls = CloudflareStreamIE._extract_urls(webpage)
|
||||||
|
if cloudflarestream_urls:
|
||||||
|
return self.playlist_from_matches(
|
||||||
|
cloudflarestream_urls, video_id, video_title, ie=CloudflareStreamIE.ie_key())
|
||||||
|
|
||||||
sharevideos_urls = [mobj.group('url') for mobj in re.finditer(
|
sharevideos_urls = [mobj.group('url') for mobj in re.finditer(
|
||||||
r'<iframe[^>]+?\bsrc\s*=\s*(["\'])(?P<url>(?:https?:)?//embed\.share-videos\.se/auto/embed/\d+\?.*?\buid=\d+.*?)\1',
|
r'<iframe[^>]+?\bsrc\s*=\s*(["\'])(?P<url>(?:https?:)?//embed\.share-videos\.se/auto/embed/\d+\?.*?\buid=\d+.*?)\1',
|
||||||
webpage)]
|
webpage)]
|
||||||
|
@ -123,7 +123,7 @@ class GoIE(AdobePassIE):
|
|||||||
'adobe_requestor_id': requestor_id,
|
'adobe_requestor_id': requestor_id,
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
self._initialize_geo_bypass(['US'])
|
self._initialize_geo_bypass({'countries': ['US']})
|
||||||
entitlement = self._download_json(
|
entitlement = self._download_json(
|
||||||
'https://api.entitlement.watchabc.go.com/vp2/ws-secure/entitlement/2020/authorize.json',
|
'https://api.entitlement.watchabc.go.com/vp2/ws-secure/entitlement/2020/authorize.json',
|
||||||
video_id, data=urlencode_postdata(data))
|
video_id, data=urlencode_postdata(data))
|
||||||
|
@ -7,6 +7,7 @@ import json
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
|
from .brightcove import BrightcoveNewIE
|
||||||
from ..compat import (
|
from ..compat import (
|
||||||
compat_str,
|
compat_str,
|
||||||
compat_etree_register_namespace,
|
compat_etree_register_namespace,
|
||||||
@ -18,6 +19,7 @@ from ..utils import (
|
|||||||
xpath_text,
|
xpath_text,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
parse_duration,
|
parse_duration,
|
||||||
|
smuggle_url,
|
||||||
ExtractorError,
|
ExtractorError,
|
||||||
determine_ext,
|
determine_ext,
|
||||||
)
|
)
|
||||||
@ -41,6 +43,14 @@ class ITVIE(InfoExtractor):
|
|||||||
# unavailable via data-playlist-url
|
# unavailable via data-playlist-url
|
||||||
'url': 'https://www.itv.com/hub/through-the-keyhole/2a2271a0033',
|
'url': 'https://www.itv.com/hub/through-the-keyhole/2a2271a0033',
|
||||||
'only_matching': True,
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# InvalidVodcrid
|
||||||
|
'url': 'https://www.itv.com/hub/james-martins-saturday-morning/2a5159a0034',
|
||||||
|
'only_matching': True,
|
||||||
|
}, {
|
||||||
|
# ContentUnavailable
|
||||||
|
'url': 'https://www.itv.com/hub/whos-doing-the-dishes/2a2898a0024',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
@ -127,7 +137,8 @@ class ITVIE(InfoExtractor):
|
|||||||
if fault_code == 'InvalidGeoRegion':
|
if fault_code == 'InvalidGeoRegion':
|
||||||
self.raise_geo_restricted(
|
self.raise_geo_restricted(
|
||||||
msg=fault_string, countries=self._GEO_COUNTRIES)
|
msg=fault_string, countries=self._GEO_COUNTRIES)
|
||||||
elif fault_code != 'InvalidEntity':
|
elif fault_code not in (
|
||||||
|
'InvalidEntity', 'InvalidVodcrid', 'ContentUnavailable'):
|
||||||
raise ExtractorError(
|
raise ExtractorError(
|
||||||
'%s said: %s' % (self.IE_NAME, fault_string), expected=True)
|
'%s said: %s' % (self.IE_NAME, fault_string), expected=True)
|
||||||
info.update({
|
info.update({
|
||||||
@ -251,3 +262,38 @@ class ITVIE(InfoExtractor):
|
|||||||
'subtitles': subtitles,
|
'subtitles': subtitles,
|
||||||
})
|
})
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
class ITVBTCCIE(InfoExtractor):
|
||||||
|
_VALID_URL = r'https?://(?:www\.)?itv\.com/btcc/(?:[^/]+/)*(?P<id>[^/?#&]+)'
|
||||||
|
_TEST = {
|
||||||
|
'url': 'http://www.itv.com/btcc/races/btcc-2018-all-the-action-from-brands-hatch',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'btcc-2018-all-the-action-from-brands-hatch',
|
||||||
|
'title': 'BTCC 2018: All the action from Brands Hatch',
|
||||||
|
},
|
||||||
|
'playlist_mincount': 9,
|
||||||
|
}
|
||||||
|
BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/1582188683001/HkiHLnNRx_default/index.html?videoId=%s'
|
||||||
|
|
||||||
|
def _real_extract(self, url):
|
||||||
|
playlist_id = self._match_id(url)
|
||||||
|
|
||||||
|
webpage = self._download_webpage(url, playlist_id)
|
||||||
|
|
||||||
|
entries = [
|
||||||
|
self.url_result(
|
||||||
|
smuggle_url(self.BRIGHTCOVE_URL_TEMPLATE % video_id, {
|
||||||
|
# ITV does not like some GB IP ranges, so here are some
|
||||||
|
# IP blocks it accepts
|
||||||
|
'geo_ip_blocks': [
|
||||||
|
'193.113.0.0/16', '54.36.162.0/23', '159.65.16.0/21'
|
||||||
|
],
|
||||||
|
'referrer': url,
|
||||||
|
}),
|
||||||
|
ie=BrightcoveNewIE.ie_key(), video_id=video_id)
|
||||||
|
for video_id in re.findall(r'data-video-id=["\'](\d+)', webpage)]
|
||||||
|
|
||||||
|
title = self._og_search_title(webpage, fatal=False)
|
||||||
|
|
||||||
|
return self.playlist_result(entries, playlist_id, title)
|
||||||
|
@ -282,7 +282,9 @@ 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'))
|
self._initialize_geo_bypass({
|
||||||
|
'countries': smuggled_data.get('geo_countries'),
|
||||||
|
})
|
||||||
|
|
||||||
pc, mobile, metadata = self._extract(
|
pc, mobile, metadata = self._extract(
|
||||||
video_id, 'getPlaylistByMediaId',
|
video_id, 'getPlaylistByMediaId',
|
||||||
|
@ -62,7 +62,7 @@ class TuneInBaseIE(InfoExtractor):
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
'id': content_id,
|
'id': content_id,
|
||||||
'title': title,
|
'title': self._live_title(title) if is_live else title,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'thumbnail': thumbnail,
|
'thumbnail': thumbnail,
|
||||||
'location': location,
|
'location': location,
|
||||||
|
@ -227,14 +227,16 @@ class TVPlayIE(InfoExtractor):
|
|||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
url, smuggled_data = unsmuggle_url(url, {})
|
url, smuggled_data = unsmuggle_url(url, {})
|
||||||
self._initialize_geo_bypass(smuggled_data.get('geo_countries'))
|
self._initialize_geo_bypass({
|
||||||
|
'countries': smuggled_data.get('geo_countries'),
|
||||||
|
})
|
||||||
|
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
geo_country = self._search_regex(
|
geo_country = self._search_regex(
|
||||||
r'https?://[^/]+\.([a-z]{2})', url,
|
r'https?://[^/]+\.([a-z]{2})', url,
|
||||||
'geo country', default=None)
|
'geo country', default=None)
|
||||||
if geo_country:
|
if geo_country:
|
||||||
self._initialize_geo_bypass([geo_country.upper()])
|
self._initialize_geo_bypass({'countries': [geo_country.upper()]})
|
||||||
video = self._download_json(
|
video = self._download_json(
|
||||||
'http://playapi.mtgx.tv/v3/videos/%s' % video_id, video_id, 'Downloading video JSON')
|
'http://playapi.mtgx.tv/v3/videos/%s' % video_id, video_id, 'Downloading video JSON')
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ class WatchBoxIE(InfoExtractor):
|
|||||||
|
|
||||||
source = self._parse_json(
|
source = self._parse_json(
|
||||||
self._search_regex(
|
self._search_regex(
|
||||||
r'(?s)source\s*:\s*({.+?})\s*,\s*\n', webpage, 'source',
|
r'(?s)source["\']?\s*:\s*({.+?})\s*[,}]', webpage, 'source',
|
||||||
default='{}'),
|
default='{}'),
|
||||||
video_id, transform_source=js_to_json, fatal=False) or {}
|
video_id, transform_source=js_to_json, fatal=False) or {}
|
||||||
|
|
||||||
|
@ -249,6 +249,10 @@ def parseOpts(overrideArguments=None):
|
|||||||
'--geo-bypass-country', metavar='CODE',
|
'--geo-bypass-country', metavar='CODE',
|
||||||
dest='geo_bypass_country', default=None,
|
dest='geo_bypass_country', default=None,
|
||||||
help='Force bypass geographic restriction with explicitly provided two-letter ISO 3166-2 country code (experimental)')
|
help='Force bypass geographic restriction with explicitly provided two-letter ISO 3166-2 country code (experimental)')
|
||||||
|
geo.add_option(
|
||||||
|
'--geo-bypass-ip-block', metavar='IP_BLOCK',
|
||||||
|
dest='geo_bypass_ip_block', default=None,
|
||||||
|
help='Force bypass geographic restriction with explicitly provided IP block in CIDR notation (experimental)')
|
||||||
|
|
||||||
selection = optparse.OptionGroup(parser, 'Video Selection')
|
selection = optparse.OptionGroup(parser, 'Video Selection')
|
||||||
selection.add_option(
|
selection.add_option(
|
||||||
|
@ -3569,10 +3569,13 @@ class GeoUtils(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def random_ipv4(cls, code):
|
def random_ipv4(cls, code_or_block):
|
||||||
block = cls._country_ip_map.get(code.upper())
|
if len(code_or_block) == 2:
|
||||||
|
block = cls._country_ip_map.get(code_or_block.upper())
|
||||||
if not block:
|
if not block:
|
||||||
return None
|
return None
|
||||||
|
else:
|
||||||
|
block = code_or_block
|
||||||
addr, preflen = block.split('/')
|
addr, preflen = block.split('/')
|
||||||
addr_min = compat_struct_unpack('!L', socket.inet_aton(addr))[0]
|
addr_min = compat_struct_unpack('!L', socket.inet_aton(addr))[0]
|
||||||
addr_max = addr_min | (0xffffffff >> int(preflen))
|
addr_max = addr_min | (0xffffffff >> int(preflen))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user