aboutsummaryrefslogtreecommitdiff
path: root/youtube_dl/extractor
diff options
context:
space:
mode:
Diffstat (limited to 'youtube_dl/extractor')
-rw-r--r--youtube_dl/extractor/audiomack.py3
-rw-r--r--youtube_dl/extractor/dw.py2
-rw-r--r--youtube_dl/extractor/extractors.py3
-rw-r--r--youtube_dl/extractor/generic.py35
-rw-r--r--youtube_dl/extractor/godtv.py66
-rw-r--r--youtube_dl/extractor/lynda.py131
-rw-r--r--youtube_dl/extractor/nbc.py22
-rw-r--r--youtube_dl/extractor/openload.py7
-rw-r--r--youtube_dl/extractor/voicerepublic.py11
-rw-r--r--youtube_dl/extractor/vporn.py10
-rw-r--r--youtube_dl/extractor/vulture.py69
-rw-r--r--youtube_dl/extractor/wdr.py6
-rw-r--r--youtube_dl/extractor/weibo.py49
-rw-r--r--youtube_dl/extractor/xfileshare.py3
-rw-r--r--youtube_dl/extractor/xuite.py1
-rw-r--r--youtube_dl/extractor/yahoo.py2
-rw-r--r--youtube_dl/extractor/youtube.py5
17 files changed, 203 insertions, 222 deletions
diff --git a/youtube_dl/extractor/audiomack.py b/youtube_dl/extractor/audiomack.py
index a52d26cec..f3bd4d444 100644
--- a/youtube_dl/extractor/audiomack.py
+++ b/youtube_dl/extractor/audiomack.py
@@ -6,6 +6,7 @@ import time
from .common import InfoExtractor
from .soundcloud import SoundcloudIE
+from ..compat import compat_str
from ..utils import (
ExtractorError,
url_basename,
@@ -136,7 +137,7 @@ class AudiomackAlbumIE(InfoExtractor):
result[resultkey] = api_response[apikey]
song_id = url_basename(api_response['url']).rpartition('.')[0]
result['entries'].append({
- 'id': api_response.get('id', song_id),
+ 'id': compat_str(api_response.get('id', song_id)),
'uploader': api_response.get('artist'),
'title': api_response.get('title', song_id),
'url': api_response['url'],
diff --git a/youtube_dl/extractor/dw.py b/youtube_dl/extractor/dw.py
index 0f0f0b8d3..d740652f1 100644
--- a/youtube_dl/extractor/dw.py
+++ b/youtube_dl/extractor/dw.py
@@ -35,6 +35,7 @@ class DWIE(InfoExtractor):
'upload_date': '20160311',
}
}, {
+ # DW documentaries, only last for one or two weeks
'url': 'http://www.dw.com/en/documentaries-welcome-to-the-90s-2016-05-21/e-19220158-9798',
'md5': '56b6214ef463bfb9a3b71aeb886f3cf1',
'info_dict': {
@@ -44,6 +45,7 @@ class DWIE(InfoExtractor):
'description': 'Welcome to the 90s - The Golden Decade of Hip Hop',
'upload_date': '20160521',
},
+ 'skip': 'Video removed',
}]
def _real_extract(self, url):
diff --git a/youtube_dl/extractor/extractors.py b/youtube_dl/extractor/extractors.py
index aa98782a5..38708294a 100644
--- a/youtube_dl/extractor/extractors.py
+++ b/youtube_dl/extractor/extractors.py
@@ -292,6 +292,7 @@ from .globo import (
GloboArticleIE,
)
from .godtube import GodTubeIE
+from .godtv import GodTVIE
from .goldenmoustache import GoldenMoustacheIE
from .golem import GolemIE
from .googledrive import GoogleDriveIE
@@ -957,7 +958,6 @@ from .vporn import VpornIE
from .vrt import VRTIE
from .vube import VubeIE
from .vuclip import VuClipIE
-from .vulture import VultureIE
from .walla import WallaIE
from .washingtonpost import (
WashingtonPostIE,
@@ -973,7 +973,6 @@ from .webofstories import (
WebOfStoriesIE,
WebOfStoriesPlaylistIE,
)
-from .weibo import WeiboIE
from .weiqitv import WeiqiTVIE
from .wimp import WimpIE
from .wistia import WistiaIE
diff --git a/youtube_dl/extractor/generic.py b/youtube_dl/extractor/generic.py
index 90575ab0e..ef18ce3dc 100644
--- a/youtube_dl/extractor/generic.py
+++ b/youtube_dl/extractor/generic.py
@@ -627,13 +627,13 @@ class GenericIE(InfoExtractor):
},
# MTVSercices embed
{
- 'url': 'http://www.gametrailers.com/news-post/76093/north-america-europe-is-getting-that-mario-kart-8-mercedes-dlc-too',
- 'md5': '35727f82f58c76d996fc188f9755b0d5',
+ 'url': 'http://www.vulture.com/2016/06/new-key-peele-sketches-released.html',
+ 'md5': 'ca1aef97695ef2c1d6973256a57e5252',
'info_dict': {
- 'id': '0306a69b-8adf-4fb5-aace-75f8e8cbfca9',
+ 'id': '769f7ec0-0692-4d62-9b45-0d88074bffc1',
'ext': 'mp4',
- 'title': 'Review',
- 'description': 'Mario\'s life in the fast lane has never looked so good.',
+ 'title': 'Key and Peele|October 10, 2012|2|203|Liam Neesons - Uncensored',
+ 'description': 'Two valets share their love for movie star Liam Neesons.',
},
},
# YouTube embed via <data-embed-url="">
@@ -1032,6 +1032,17 @@ class GenericIE(InfoExtractor):
'timestamp': 1389118457,
},
},
+ # NBC News embed
+ {
+ 'url': 'http://www.vulture.com/2016/06/letterman-couldnt-care-less-about-late-night.html',
+ 'md5': '1aa589c675898ae6d37a17913cf68d66',
+ 'info_dict': {
+ 'id': '701714499682',
+ 'ext': 'mp4',
+ 'title': 'PREVIEW: On Assignment: David Letterman',
+ 'description': 'A preview of Tom Brokaw\'s interview with David Letterman as part of the On Assignment series powered by Dateline. Airs Sunday June 12 at 7/6c.',
+ },
+ },
# UDN embed
{
'url': 'https://video.udn.com/news/300346',
@@ -1846,14 +1857,6 @@ class GenericIE(InfoExtractor):
url = unescapeHTML(mobj.group('url'))
return self.url_result(url)
- # Look for embedded vulture.com player
- mobj = re.search(
- r'<iframe src="(?P<url>https?://video\.vulture\.com/[^"]+)"',
- webpage)
- if mobj is not None:
- url = unescapeHTML(mobj.group('url'))
- return self.url_result(url, ie='Vulture')
-
# Look for embedded mtvservices player
mtvservices_url = MTVServicesEmbeddedIE._extract_url(webpage)
if mtvservices_url:
@@ -1966,6 +1969,12 @@ class GenericIE(InfoExtractor):
if nbc_sports_url:
return self.url_result(nbc_sports_url, 'NBCSportsVPlayer')
+ # Look for NBC News embeds
+ nbc_news_embed_url = re.search(
+ r'<iframe[^>]+src=(["\'])(?P<url>(?:https?:)?//www\.nbcnews\.com/widget/video-embed/[^"\']+)\1', webpage)
+ if nbc_news_embed_url:
+ return self.url_result(nbc_news_embed_url.group('url'), 'NBCNews')
+
# Look for Google Drive embeds
google_drive_url = GoogleDriveIE._extract_url(webpage)
if google_drive_url:
diff --git a/youtube_dl/extractor/godtv.py b/youtube_dl/extractor/godtv.py
new file mode 100644
index 000000000..c5d3b4e6a
--- /dev/null
+++ b/youtube_dl/extractor/godtv.py
@@ -0,0 +1,66 @@
+from __future__ import unicode_literals
+
+from .common import InfoExtractor
+from .ooyala import OoyalaIE
+from ..utils import js_to_json
+
+
+class GodTVIE(InfoExtractor):
+ _VALID_URL = r'https?://(?:www\.)?god\.tv(?:/[^/]+)*/(?P<id>[^/?#&]+)'
+ _TESTS = [{
+ 'url': 'http://god.tv/jesus-image/video/jesus-conference-2016/randy-needham',
+ 'info_dict': {
+ 'id': 'lpd3g2MzE6D1g8zFAKz8AGpxWcpu6o_3',
+ 'ext': 'mp4',
+ 'title': 'Randy Needham',
+ 'duration': 3615.08,
+ },
+ 'params': {
+ 'skip_download': True,
+ }
+ }, {
+ 'url': 'http://god.tv/playlist/bible-study',
+ 'info_dict': {
+ 'id': 'bible-study',
+ },
+ 'playlist_mincount': 37,
+ }, {
+ 'url': 'http://god.tv/node/15097',
+ 'only_matching': True,
+ }, {
+ 'url': 'http://god.tv/live/africa',
+ 'only_matching': True,
+ }, {
+ 'url': 'http://god.tv/liveevents',
+ 'only_matching': True,
+ }]
+
+ def _real_extract(self, url):
+ display_id = self._match_id(url)
+
+ webpage = self._download_webpage(url, display_id)
+
+ settings = self._parse_json(
+ self._search_regex(
+ r'jQuery\.extend\(Drupal\.settings\s*,\s*({.+?})\);',
+ webpage, 'settings', default='{}'),
+ display_id, transform_source=js_to_json, fatal=False)
+
+ ooyala_id = None
+
+ if settings:
+ playlist = settings.get('playlist')
+ if playlist and isinstance(playlist, list):
+ entries = [
+ OoyalaIE._build_url_result(video['content_id'])
+ for video in playlist if video.get('content_id')]
+ if entries:
+ return self.playlist_result(entries, display_id)
+ ooyala_id = settings.get('ooyala', {}).get('content_id')
+
+ if not ooyala_id:
+ ooyala_id = self._search_regex(
+ r'["\']content_id["\']\s*:\s*(["\'])(?P<id>[\w-]+)\1',
+ webpage, 'ooyala id', group='id')
+
+ return OoyalaIE._build_url_result(ooyala_id)
diff --git a/youtube_dl/extractor/lynda.py b/youtube_dl/extractor/lynda.py
index c1bca5678..5b458d9bc 100644
--- a/youtube_dl/extractor/lynda.py
+++ b/youtube_dl/extractor/lynda.py
@@ -1,93 +1,94 @@
from __future__ import unicode_literals
import re
-import json
from .common import InfoExtractor
-from ..compat import compat_str
+from ..compat import (
+ compat_HTTPError,
+ compat_str,
+ compat_urlparse,
+)
from ..utils import (
ExtractorError,
- clean_html,
int_or_none,
- sanitized_Request,
urlencode_postdata,
)
class LyndaBaseIE(InfoExtractor):
- _LOGIN_URL = 'https://www.lynda.com/login/login.aspx'
+ _SIGNIN_URL = 'https://www.lynda.com/signin'
+ _PASSWORD_URL = 'https://www.lynda.com/signin/password'
+ _USER_URL = 'https://www.lynda.com/signin/user'
_ACCOUNT_CREDENTIALS_HINT = 'Use --username and --password options to provide lynda.com account credentials.'
_NETRC_MACHINE = 'lynda'
def _real_initialize(self):
self._login()
+ @staticmethod
+ def _check_error(json_string, key_or_keys):
+ keys = [key_or_keys] if isinstance(key_or_keys, compat_str) else key_or_keys
+ for key in keys:
+ error = json_string.get(key)
+ if error:
+ raise ExtractorError('Unable to login: %s' % error, expected=True)
+
+ def _login_step(self, form_html, fallback_action_url, extra_form_data, note, referrer_url):
+ action_url = self._search_regex(
+ r'<form[^>]+action=(["\'])(?P<url>.+?)\1', form_html,
+ 'post url', default=fallback_action_url, group='url')
+
+ if not action_url.startswith('http'):
+ action_url = compat_urlparse.urljoin(self._SIGNIN_URL, action_url)
+
+ form_data = self._hidden_inputs(form_html)
+ form_data.update(extra_form_data)
+
+ try:
+ response = self._download_json(
+ action_url, None, note,
+ data=urlencode_postdata(form_data),
+ headers={
+ 'Referer': referrer_url,
+ 'X-Requested-With': 'XMLHttpRequest',
+ })
+ except ExtractorError as e:
+ if isinstance(e.cause, compat_HTTPError) and e.cause.code == 500:
+ response = self._parse_json(e.cause.read().decode('utf-8'), None)
+ self._check_error(response, ('email', 'password'))
+ raise
+
+ self._check_error(response, 'ErrorMessage')
+
+ return response, action_url
+
def _login(self):
username, password = self._get_login_info()
if username is None:
return
- login_form = {
- 'username': username,
- 'password': password,
- 'remember': 'false',
- 'stayPut': 'false'
- }
- request = sanitized_Request(
- self._LOGIN_URL, urlencode_postdata(login_form))
- login_page = self._download_webpage(
- request, None, 'Logging in as %s' % username)
-
- # Not (yet) logged in
- m = re.search(r'loginResultJson\s*=\s*\'(?P<json>[^\']+)\';', login_page)
- if m is not None:
- response = m.group('json')
- response_json = json.loads(response)
- state = response_json['state']
-
- if state == 'notlogged':
- raise ExtractorError(
- 'Unable to login, incorrect username and/or password',
- expected=True)
-
- # This is when we get popup:
- # > You're already logged in to lynda.com on two devices.
- # > If you log in here, we'll log you out of another device.
- # So, we need to confirm this.
- if state == 'conflicted':
- confirm_form = {
- 'username': '',
- 'password': '',
- 'resolve': 'true',
- 'remember': 'false',
- 'stayPut': 'false',
- }
- request = sanitized_Request(
- self._LOGIN_URL, urlencode_postdata(confirm_form))
- login_page = self._download_webpage(
- request, None,
- 'Confirming log in and log out from another device')
-
- if all(not re.search(p, login_page) for p in ('isLoggedIn\s*:\s*true', r'logout\.aspx', r'>Log out<')):
- if 'login error' in login_page:
- mobj = re.search(
- r'(?s)<h1[^>]+class="topmost">(?P<title>[^<]+)</h1>\s*<div>(?P<description>.+?)</div>',
- login_page)
- if mobj:
- raise ExtractorError(
- 'lynda returned error: %s - %s'
- % (mobj.group('title'), clean_html(mobj.group('description'))),
- expected=True)
- raise ExtractorError('Unable to log in')
-
- def _logout(self):
- username, _ = self._get_login_info()
- if username is None:
+ # Step 1: download signin page
+ signin_page = self._download_webpage(
+ self._SIGNIN_URL, None, 'Downloading signin page')
+
+ # Already logged in
+ if any(re.search(p, signin_page) for p in (
+ 'isLoggedIn\s*:\s*true', r'logout\.aspx', r'>Log out<')):
return
- self._download_webpage(
- 'http://www.lynda.com/ajax/logout.aspx', None,
- 'Logging out', 'Unable to log out', fatal=False)
+ # Step 2: submit email
+ signin_form = self._search_regex(
+ r'(?s)(<form[^>]+data-form-name=["\']signin["\'][^>]*>.+?</form>)',
+ signin_page, 'signin form')
+ signin_page, signin_url = self._login_step(
+ signin_form, self._PASSWORD_URL, {'email': username},
+ 'Submitting email', self._SIGNIN_URL)
+
+ # Step 3: submit password
+ password_form = signin_page['body']
+ self._login_step(
+ password_form, self._USER_URL, {'email': username, 'password': password},
+ 'Submitting password', signin_url)
class LyndaIE(LyndaBaseIE):
@@ -212,8 +213,6 @@ class LyndaCourseIE(LyndaBaseIE):
'http://www.lynda.com/ajax/player?courseId=%s&type=course' % course_id,
course_id, 'Downloading course JSON')
- self._logout()
-
if course.get('Status') == 'NotFound':
raise ExtractorError(
'Course %s does not exist' % course_id, expected=True)
diff --git a/youtube_dl/extractor/nbc.py b/youtube_dl/extractor/nbc.py
index f27c7f139..6b7da1149 100644
--- a/youtube_dl/extractor/nbc.py
+++ b/youtube_dl/extractor/nbc.py
@@ -266,6 +266,11 @@ class NBCNewsIE(ThePlatformIE):
'url': 'http://www.nbcnews.com/watch/dateline/full-episode--deadly-betrayal-386250819952',
'only_matching': True,
},
+ {
+ # From http://www.vulture.com/2016/06/letterman-couldnt-care-less-about-late-night.html
+ 'url': 'http://www.nbcnews.com/widget/video-embed/701714499682',
+ 'only_matching': True,
+ },
]
def _real_extract(self, url):
@@ -289,18 +294,17 @@ class NBCNewsIE(ThePlatformIE):
webpage = self._download_webpage(url, display_id)
info = None
bootstrap_json = self._search_regex(
- r'(?m)var\s+(?:bootstrapJson|playlistData)\s*=\s*({.+});?\s*$',
+ [r'(?m)(?:var\s+(?:bootstrapJson|playlistData)|NEWS\.videoObj)\s*=\s*({.+});?\s*$',
+ r'videoObj\s*:\s*({.+})', r'data-video="([^"]+)"'],
webpage, 'bootstrap json', default=None)
- if bootstrap_json:
- bootstrap = self._parse_json(bootstrap_json, display_id)
+ bootstrap = self._parse_json(
+ bootstrap_json, display_id, transform_source=unescapeHTML)
+ if 'results' in bootstrap:
info = bootstrap['results'][0]['video']
+ elif 'video' in bootstrap:
+ info = bootstrap['video']
else:
- player_instance_json = self._search_regex(
- r'videoObj\s*:\s*({.+})', webpage, 'player instance', default=None)
- if not player_instance_json:
- player_instance_json = self._html_search_regex(
- r'data-video="([^"]+)"', webpage, 'video json')
- info = self._parse_json(player_instance_json, display_id)
+ info = bootstrap
video_id = info['mpxId']
title = info['title']
diff --git a/youtube_dl/extractor/openload.py b/youtube_dl/extractor/openload.py
index 5049b870e..6415b8fdc 100644
--- a/youtube_dl/extractor/openload.py
+++ b/youtube_dl/extractor/openload.py
@@ -14,7 +14,7 @@ from ..utils import (
class OpenloadIE(InfoExtractor):
- _VALID_URL = r'https://openload.(?:co|io)/(?:f|embed)/(?P<id>[a-zA-Z0-9-]+)'
+ _VALID_URL = r'https://openload.(?:co|io)/(?:f|embed)/(?P<id>[a-zA-Z0-9-_]+)'
_TESTS = [{
'url': 'https://openload.co/f/kUEfGclsU9o',
@@ -32,6 +32,9 @@ class OpenloadIE(InfoExtractor):
'url': 'https://openload.io/f/ZAn6oz-VZGE/',
'only_matching': True,
}, {
+ 'url': 'https://openload.co/f/_-ztPaZtMhM/',
+ 'only_matching': True,
+ }, {
# unavailable via https://openload.co/f/Sxz5sADo82g/, different layout
# for title and ext
'url': 'https://openload.co/embed/Sxz5sADo82g/',
@@ -100,7 +103,7 @@ class OpenloadIE(InfoExtractor):
raise ExtractorError('File not found', expected=True)
code = self._search_regex(
- r'</video>\s*</div>\s*<script[^>]+>([^<]+)</script>',
+ r'</video>\s*</div>\s*<script[^>]+>[^>]+</script>\s*<script[^>]+>([^<]+)</script>',
webpage, 'JS code')
decoded = self.openload_decode(code)
diff --git a/youtube_dl/extractor/voicerepublic.py b/youtube_dl/extractor/voicerepublic.py
index 93d15a556..4f1a99a89 100644
--- a/youtube_dl/extractor/voicerepublic.py
+++ b/youtube_dl/extractor/voicerepublic.py
@@ -3,7 +3,10 @@ from __future__ import unicode_literals
import re
from .common import InfoExtractor
-from ..compat import compat_urlparse
+from ..compat import (
+ compat_str,
+ compat_urlparse,
+)
from ..utils import (
ExtractorError,
determine_ext,
@@ -16,13 +19,13 @@ class VoiceRepublicIE(InfoExtractor):
_VALID_URL = r'https?://voicerepublic\.com/(?:talks|embed)/(?P<id>[0-9a-z-]+)'
_TESTS = [{
'url': 'http://voicerepublic.com/talks/watching-the-watchers-building-a-sousveillance-state',
- 'md5': '0554a24d1657915aa8e8f84e15dc9353',
+ 'md5': 'b9174d651323f17783000876347116e3',
'info_dict': {
'id': '2296',
'display_id': 'watching-the-watchers-building-a-sousveillance-state',
'ext': 'm4a',
'title': 'Watching the Watchers: Building a Sousveillance State',
- 'description': 'md5:715ba964958afa2398df615809cfecb1',
+ 'description': 'Secret surveillance programs have metadata too. The people and companies that operate secret surveillance programs can be surveilled.',
'thumbnail': 're:^https?://.*\.(?:png|jpg)$',
'duration': 1800,
'view_count': int,
@@ -52,7 +55,7 @@ class VoiceRepublicIE(InfoExtractor):
if data:
title = data['title']
description = data.get('teaser')
- talk_id = data.get('talk_id') or display_id
+ talk_id = compat_str(data.get('talk_id') or display_id)
talk = data['talk']
duration = int_or_none(talk.get('duration'))
formats = [{
diff --git a/youtube_dl/extractor/vporn.py b/youtube_dl/extractor/vporn.py
index 92c90e517..1557a0e04 100644
--- a/youtube_dl/extractor/vporn.py
+++ b/youtube_dl/extractor/vporn.py
@@ -4,6 +4,7 @@ import re
from .common import InfoExtractor
from ..utils import (
+ ExtractorError,
parse_duration,
str_to_int,
)
@@ -27,7 +28,8 @@ class VpornIE(InfoExtractor):
'duration': 393,
'age_limit': 18,
'view_count': int,
- }
+ },
+ 'skip': 'video removed',
},
{
'url': 'http://www.vporn.com/female/hana-shower/523564/',
@@ -40,7 +42,7 @@ class VpornIE(InfoExtractor):
'description': 'Hana showers at the bathroom.',
'thumbnail': 're:^https?://.*\.jpg$',
'uploader': 'Hmmmmm',
- 'categories': ['Big Boobs', 'Erotic', 'Teen', 'Female'],
+ 'categories': ['Big Boobs', 'Erotic', 'Teen', 'Female', '720p'],
'duration': 588,
'age_limit': 18,
'view_count': int,
@@ -55,6 +57,10 @@ class VpornIE(InfoExtractor):
webpage = self._download_webpage(url, display_id)
+ errmsg = 'This video has been deleted due to Copyright Infringement or by the account owner!'
+ if errmsg in webpage:
+ raise ExtractorError('%s said: %s' % (self.IE_NAME, errmsg), expected=True)
+
title = self._html_search_regex(
r'videoname\s*=\s*\'([^\']+)\'', webpage, 'title').strip()
description = self._html_search_regex(
diff --git a/youtube_dl/extractor/vulture.py b/youtube_dl/extractor/vulture.py
deleted file mode 100644
index faa167e65..000000000
--- a/youtube_dl/extractor/vulture.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from __future__ import unicode_literals
-
-import json
-import os.path
-import re
-
-from .common import InfoExtractor
-from ..utils import (
- int_or_none,
- parse_iso8601,
-)
-
-
-class VultureIE(InfoExtractor):
- IE_NAME = 'vulture.com'
- _VALID_URL = r'https?://video\.vulture\.com/video/(?P<display_id>[^/]+)/'
- _TEST = {
- 'url': 'http://video.vulture.com/video/Mindy-Kaling-s-Harvard-Speech/player?layout=compact&read_more=1',
- 'md5': '8d997845642a2b5152820f7257871bc8',
- 'info_dict': {
- 'id': '6GHRQL3RV7MSD1H4',
- 'ext': 'mp4',
- 'title': 'kaling-speech-2-MAGNIFY STANDARD CONTAINER REVISED',
- 'uploader_id': 'Sarah',
- 'thumbnail': 're:^http://.*\.jpg$',
- 'timestamp': 1401288564,
- 'upload_date': '20140528',
- 'description': 'Uplifting and witty, as predicted.',
- 'duration': 1015,
- }
- }
-
- def _real_extract(self, url):
- mobj = re.match(self._VALID_URL, url)
- display_id = mobj.group('display_id')
-
- webpage = self._download_webpage(url, display_id)
- query_string = self._search_regex(
- r"queryString\s*=\s*'([^']+)'", webpage, 'query string')
- video_id = self._search_regex(
- r'content=([^&]+)', query_string, 'video ID')
- query_url = 'http://video.vulture.com/embed/player/container/1000/1000/?%s' % query_string
-
- query_webpage = self._download_webpage(
- query_url, display_id, note='Downloading query page')
- params_json = self._search_regex(
- r'(?sm)new MagnifyEmbeddablePlayer\({.*?contentItem:\s*(\{.*?\})\n?,\n',
- query_webpage,
- 'player params')
- params = json.loads(params_json)
-
- upload_timestamp = parse_iso8601(params['posted'].replace(' ', 'T'))
- uploader_id = params.get('user', {}).get('handle')
-
- media_item = params['media_item']
- title = os.path.splitext(media_item['title'])[0]
- duration = int_or_none(media_item.get('duration_seconds'))
-
- return {
- 'id': video_id,
- 'display_id': display_id,
- 'url': media_item['pipeline_xid'],
- 'title': title,
- 'timestamp': upload_timestamp,
- 'thumbnail': params.get('thumbnail_url'),
- 'uploader_id': uploader_id,
- 'description': params.get('description'),
- 'duration': duration,
- }
diff --git a/youtube_dl/extractor/wdr.py b/youtube_dl/extractor/wdr.py
index a9238cbeb..6b83a2a04 100644
--- a/youtube_dl/extractor/wdr.py
+++ b/youtube_dl/extractor/wdr.py
@@ -34,7 +34,8 @@ class WDRIE(InfoExtractor):
'description': 'md5:87be8ff14d8dfd7a7ee46f0299b52318',
'is_live': False,
'subtitles': {'de': [{
- 'url': 'http://ondemand-ww.wdr.de/medp/fsk0/105/1058683/1058683_12220974.xml'
+ 'url': 'http://ondemand-ww.wdr.de/medp/fsk0/105/1058683/1058683_12220974.xml',
+ 'ext': 'ttml',
}]},
},
},
@@ -190,7 +191,8 @@ class WDRIE(InfoExtractor):
caption_url = metadata_media_resource.get('captionURL')
if caption_url:
subtitles['de'] = [{
- 'url': caption_url
+ 'url': caption_url,
+ 'ext': 'ttml',
}]
title = metadata_tracker_data.get('trackerClipTitle')
diff --git a/youtube_dl/extractor/weibo.py b/youtube_dl/extractor/weibo.py
deleted file mode 100644
index 20bb039d3..000000000
--- a/youtube_dl/extractor/weibo.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# coding: utf-8
-from __future__ import unicode_literals
-
-import re
-
-from .common import InfoExtractor
-
-
-class WeiboIE(InfoExtractor):
- """
- The videos in Weibo come from different sites, this IE just finds the link
- to the external video and returns it.
- """
- _VALID_URL = r'https?://video\.weibo\.com/v/weishipin/t_(?P<id>.+?)\.htm'
-
- _TEST = {
- 'url': 'http://video.weibo.com/v/weishipin/t_zjUw2kZ.htm',
- 'info_dict': {
- 'id': '98322879',
- 'ext': 'flv',
- 'title': '魔声耳机最新广告“All Eyes On Us”',
- },
- 'params': {
- 'skip_download': True,
- },
- 'add_ie': ['Sina'],
- }
-
- # Additional example videos from different sites
- # Youku: http://video.weibo.com/v/weishipin/t_zQGDWQ8.htm
- # 56.com: http://video.weibo.com/v/weishipin/t_zQ44HxN.htm
-
- def _real_extract(self, url):
- mobj = re.match(self._VALID_URL, url, flags=re.VERBOSE)
- video_id = mobj.group('id')
- info_url = 'http://video.weibo.com/?s=v&a=play_list&format=json&mix_video_id=t_%s' % video_id
- info = self._download_json(info_url, video_id)
-
- videos_urls = map(lambda v: v['play_page_url'], info['result']['data'])
- # Prefer sina video since they have thumbnails
- videos_urls = sorted(videos_urls, key=lambda u: 'video.sina.com' in u)
- player_url = videos_urls[-1]
- m_sina = re.match(r'https?://video\.sina\.com\.cn/v/b/(\d+)-\d+\.html',
- player_url)
- if m_sina is not None:
- self.to_screen('Sina video detected')
- sina_id = m_sina.group(1)
- player_url = 'http://you.video.sina.com.cn/swf/quotePlayer.swf?vid=%s' % sina_id
- return self.url_result(player_url)
diff --git a/youtube_dl/extractor/xfileshare.py b/youtube_dl/extractor/xfileshare.py
index 769003735..ee4d04c20 100644
--- a/youtube_dl/extractor/xfileshare.py
+++ b/youtube_dl/extractor/xfileshare.py
@@ -62,7 +62,8 @@ class XFileShareIE(InfoExtractor):
'ext': 'mp4',
'title': 'youtube-dl test video \'äBaW_jenozKc.mp4.mp4',
'thumbnail': 're:http://.*\.jpg',
- }
+ },
+ 'skip': 'Video removed',
}, {
'url': 'http://vidto.me/ku5glz52nqe1.html',
'info_dict': {
diff --git a/youtube_dl/extractor/xuite.py b/youtube_dl/extractor/xuite.py
index 2466410fa..0be8932ad 100644
--- a/youtube_dl/extractor/xuite.py
+++ b/youtube_dl/extractor/xuite.py
@@ -66,6 +66,7 @@ class XuiteIE(InfoExtractor):
'uploader_id': '242127761',
'categories': ['電玩動漫'],
},
+ 'skip': 'Video removed',
}, {
'url': 'http://vlog.xuite.net/play/S1dDUjdyLTMyOTc3NjcuZmx2/%E5%AD%AB%E7%87%95%E5%A7%BF-%E7%9C%BC%E6%B7%9A%E6%88%90%E8%A9%A9',
'only_matching': True,
diff --git a/youtube_dl/extractor/yahoo.py b/youtube_dl/extractor/yahoo.py
index b376f2b93..927a964a4 100644
--- a/youtube_dl/extractor/yahoo.py
+++ b/youtube_dl/extractor/yahoo.py
@@ -343,7 +343,7 @@ class YahooIE(InfoExtractor):
webpage, 'region', fatal=False, default='US')
data = compat_urllib_parse_urlencode({
'protocol': 'http',
- 'region': region,
+ 'region': region.upper(),
})
query_url = (
'https://video.media.yql.yahoo.com/v1/video/sapi/streams/'
diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py
index 6c9f77d95..00dd602ff 100644
--- a/youtube_dl/extractor/youtube.py
+++ b/youtube_dl/extractor/youtube.py
@@ -1988,7 +1988,7 @@ class YoutubeChannelIE(YoutubePlaylistBaseInfoExtractor):
class YoutubeUserIE(YoutubeChannelIE):
IE_DESC = 'YouTube.com user videos (URL or "ytuser" keyword)'
- _VALID_URL = r'(?:(?:https?://(?:\w+\.)?youtube\.com/(?:user/)?(?!(?:attribution_link|watch|results)(?:$|[^a-z_A-Z0-9-])))|ytuser:)(?!feed/)(?P<id>[A-Za-z0-9_-]+)'
+ _VALID_URL = r'(?:(?:https?://(?:\w+\.)?youtube\.com/(?:user/|c/)?(?!(?:attribution_link|watch|results)(?:$|[^a-z_A-Z0-9-])))|ytuser:)(?!feed/)(?P<id>[A-Za-z0-9_-]+)'
_TEMPLATE_URL = 'https://www.youtube.com/user/%s/videos'
IE_NAME = 'youtube:user'
@@ -2001,6 +2001,9 @@ class YoutubeUserIE(YoutubeChannelIE):
}, {
'url': 'ytuser:phihag',
'only_matching': True,
+ }, {
+ 'url': 'https://www.youtube.com/c/gametrailers',
+ 'only_matching': True,
}]
@classmethod