|
|
|
@ -46,6 +46,10 @@ from ..utils import (
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_qs(url):
|
|
|
|
|
return compat_urlparse.parse_qs(compat_urlparse.urlparse(url).query)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class YoutubeBaseInfoExtractor(InfoExtractor):
|
|
|
|
|
"""Provide base functions for Youtube extractors"""
|
|
|
|
|
_LOGIN_URL = 'https://accounts.google.com/ServiceLogin'
|
|
|
|
@ -413,16 +417,9 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|
|
|
|
|(?:www\.)?cleanvideosearch\.com/media/action/yt/watch\?videoId=
|
|
|
|
|
)
|
|
|
|
|
)? # all until now is optional -> you can pass the naked ID
|
|
|
|
|
(?P<id>[0-9A-Za-z_-]{11}) # here is it! the YouTube video ID
|
|
|
|
|
(?!.*?\blist=
|
|
|
|
|
(?:
|
|
|
|
|
%(playlist_id)s| # combined list/video URLs are handled by the playlist IE
|
|
|
|
|
WL # WL are handled by the watch later IE
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
(?P<id>[0-9A-Za-z_-]{11}) # here is it! the YouTube video ID
|
|
|
|
|
(?(1).+)? # if we found the ID, everything can follow
|
|
|
|
|
$""" % {
|
|
|
|
|
'playlist_id': YoutubeBaseInfoExtractor._PLAYLIST_ID_RE,
|
|
|
|
|
'invidious': '|'.join(_INVIDIOUS_SITES),
|
|
|
|
|
}
|
|
|
|
|
_PLAYER_INFO_RE = (
|
|
|
|
@ -1208,6 +1205,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|
|
|
|
'397': {'acodec': 'none', 'vcodec': 'av01.0.05M.08'},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def suitable(cls, url):
|
|
|
|
|
qs = parse_qs(url)
|
|
|
|
|
if qs.get('list', [None])[0]:
|
|
|
|
|
return False
|
|
|
|
|
return super(YoutubeIE, cls).suitable(url)
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super(YoutubeIE, self).__init__(*args, **kwargs)
|
|
|
|
|
self._code_cache = {}
|
|
|
|
@ -2275,6 +2279,9 @@ class YoutubeTabIE(YoutubeBaseInfoExtractor):
|
|
|
|
|
'title': '#cctv9',
|
|
|
|
|
},
|
|
|
|
|
'playlist_mincount': 350,
|
|
|
|
|
}, {
|
|
|
|
|
'url': 'https://www.youtube.com/watch?list=PLW4dVinRY435CBE_JD3t-0SRXKfnZHS1P&feature=youtu.be&v=M9cJMXmQ_ZU',
|
|
|
|
|
'only_matching': True,
|
|
|
|
|
}]
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@ -2764,7 +2771,7 @@ class YoutubeTabIE(YoutubeBaseInfoExtractor):
|
|
|
|
|
url = compat_urlparse.urlunparse(
|
|
|
|
|
compat_urlparse.urlparse(url)._replace(netloc='www.youtube.com'))
|
|
|
|
|
# Handle both video/playlist URLs
|
|
|
|
|
qs = compat_urlparse.parse_qs(compat_urlparse.urlparse(url).query)
|
|
|
|
|
qs = parse_qs(url)
|
|
|
|
|
video_id = qs.get('v', [None])[0]
|
|
|
|
|
playlist_id = qs.get('list', [None])[0]
|
|
|
|
|
if video_id and playlist_id:
|
|
|
|
@ -2860,12 +2867,16 @@ class YoutubePlaylistIE(InfoExtractor):
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def suitable(cls, url):
|
|
|
|
|
return False if YoutubeTabIE.suitable(url) else super(
|
|
|
|
|
YoutubePlaylistIE, cls).suitable(url)
|
|
|
|
|
if YoutubeTabIE.suitable(url):
|
|
|
|
|
return False
|
|
|
|
|
qs = parse_qs(url)
|
|
|
|
|
if qs.get('v', [None])[0]:
|
|
|
|
|
return False
|
|
|
|
|
return super(YoutubePlaylistIE, cls).suitable(url)
|
|
|
|
|
|
|
|
|
|
def _real_extract(self, url):
|
|
|
|
|
playlist_id = self._match_id(url)
|
|
|
|
|
qs = compat_urlparse.parse_qs(compat_urlparse.urlparse(url).query)
|
|
|
|
|
qs = parse_qs(url)
|
|
|
|
|
if not qs:
|
|
|
|
|
qs = {'list': playlist_id}
|
|
|
|
|
return self.url_result(
|
|
|
|
|