|
@@ -6,11 +6,18 @@ import json
|
|
|
import sys
|
|
|
|
|
|
from .common import InfoExtractor
|
|
|
-from ..compat import compat_str
|
|
|
+from ..compat import (
|
|
|
+ compat_str,
|
|
|
+ compat_urlparse,
|
|
|
+)
|
|
|
from ..utils import (
|
|
|
+ clean_html,
|
|
|
ExtractorError,
|
|
|
+ get_element_by_class,
|
|
|
int_or_none,
|
|
|
orderedSet,
|
|
|
+ parse_duration,
|
|
|
+ remove_start,
|
|
|
str_to_int,
|
|
|
unescapeHTML,
|
|
|
unified_strdate,
|
|
@@ -20,7 +27,54 @@ from .vimeo import VimeoIE
|
|
|
from .pladform import PladformIE
|
|
|
|
|
|
|
|
|
-class VKIE(InfoExtractor):
|
|
|
+class VKBaseIE(InfoExtractor):
|
|
|
+ _NETRC_MACHINE = 'vk'
|
|
|
+
|
|
|
+ def _login(self):
|
|
|
+ (username, password) = self._get_login_info()
|
|
|
+ if username is None:
|
|
|
+ return
|
|
|
+
|
|
|
+ login_page, url_handle = self._download_webpage_handle(
|
|
|
+ 'https://vk.com', None, 'Downloading login page')
|
|
|
+
|
|
|
+ login_form = self._hidden_inputs(login_page)
|
|
|
+
|
|
|
+ login_form.update({
|
|
|
+ 'email': username.encode('cp1251'),
|
|
|
+ 'pass': password.encode('cp1251'),
|
|
|
+ })
|
|
|
+
|
|
|
+ # https://new.vk.com/ serves two same remixlhk cookies in Set-Cookie header
|
|
|
+ # and expects the first one to be set rather than second (see
|
|
|
+ # https://github.com/rg3/youtube-dl/issues/9841#issuecomment-227871201).
|
|
|
+ # As of RFC6265 the newer one cookie should be set into cookie store
|
|
|
+ # what actually happens.
|
|
|
+ # We will workaround this VK issue by resetting the remixlhk cookie to
|
|
|
+ # the first one manually.
|
|
|
+ cookies = url_handle.headers.get('Set-Cookie')
|
|
|
+ if sys.version_info[0] >= 3:
|
|
|
+ cookies = cookies.encode('iso-8859-1')
|
|
|
+ cookies = cookies.decode('utf-8')
|
|
|
+ remixlhk = re.search(r'remixlhk=(.+?);.*?\bdomain=(.+?)(?:[,;]|$)', cookies)
|
|
|
+ if remixlhk:
|
|
|
+ value, domain = remixlhk.groups()
|
|
|
+ self._set_cookie(domain, 'remixlhk', value)
|
|
|
+
|
|
|
+ login_page = self._download_webpage(
|
|
|
+ 'https://login.vk.com/?act=login', None,
|
|
|
+ note='Logging in as %s' % username,
|
|
|
+ data=urlencode_postdata(login_form))
|
|
|
+
|
|
|
+ if re.search(r'onLoginFailed', login_page):
|
|
|
+ raise ExtractorError(
|
|
|
+ 'Unable to login, incorrect username and/or password', expected=True)
|
|
|
+
|
|
|
+ def _real_initialize(self):
|
|
|
+ self._login()
|
|
|
+
|
|
|
+
|
|
|
+class VKIE(VKBaseIE):
|
|
|
IE_NAME = 'vk'
|
|
|
IE_DESC = 'VK'
|
|
|
_VALID_URL = r'''(?x)
|
|
@@ -38,8 +92,6 @@ class VKIE(InfoExtractor):
|
|
|
(?P<videoid>-?\d+_\d+)(?:.*\blist=(?P<list_id>[\da-f]+))?
|
|
|
)
|
|
|
'''
|
|
|
- _NETRC_MACHINE = 'vk'
|
|
|
-
|
|
|
_TESTS = [
|
|
|
{
|
|
|
'url': 'http://vk.com/videos-77521?z=video-77521_162222515%2Fclub77521',
|
|
@@ -189,49 +241,6 @@ class VKIE(InfoExtractor):
|
|
|
}
|
|
|
]
|
|
|
|
|
|
- def _login(self):
|
|
|
- (username, password) = self._get_login_info()
|
|
|
- if username is None:
|
|
|
- return
|
|
|
-
|
|
|
- login_page, url_handle = self._download_webpage_handle(
|
|
|
- 'https://vk.com', None, 'Downloading login page')
|
|
|
-
|
|
|
- login_form = self._hidden_inputs(login_page)
|
|
|
-
|
|
|
- login_form.update({
|
|
|
- 'email': username.encode('cp1251'),
|
|
|
- 'pass': password.encode('cp1251'),
|
|
|
- })
|
|
|
-
|
|
|
- # https://new.vk.com/ serves two same remixlhk cookies in Set-Cookie header
|
|
|
- # and expects the first one to be set rather than second (see
|
|
|
- # https://github.com/rg3/youtube-dl/issues/9841#issuecomment-227871201).
|
|
|
- # As of RFC6265 the newer one cookie should be set into cookie store
|
|
|
- # what actually happens.
|
|
|
- # We will workaround this VK issue by resetting the remixlhk cookie to
|
|
|
- # the first one manually.
|
|
|
- cookies = url_handle.headers.get('Set-Cookie')
|
|
|
- if sys.version_info[0] >= 3:
|
|
|
- cookies = cookies.encode('iso-8859-1')
|
|
|
- cookies = cookies.decode('utf-8')
|
|
|
- remixlhk = re.search(r'remixlhk=(.+?);.*?\bdomain=(.+?)(?:[,;]|$)', cookies)
|
|
|
- if remixlhk:
|
|
|
- value, domain = remixlhk.groups()
|
|
|
- self._set_cookie(domain, 'remixlhk', value)
|
|
|
-
|
|
|
- login_page = self._download_webpage(
|
|
|
- 'https://login.vk.com/?act=login', None,
|
|
|
- note='Logging in as %s' % username,
|
|
|
- data=urlencode_postdata(login_form))
|
|
|
-
|
|
|
- if re.search(r'onLoginFailed', login_page):
|
|
|
- raise ExtractorError(
|
|
|
- 'Unable to login, incorrect username and/or password', expected=True)
|
|
|
-
|
|
|
- def _real_initialize(self):
|
|
|
- self._login()
|
|
|
-
|
|
|
def _real_extract(self, url):
|
|
|
mobj = re.match(self._VALID_URL, url)
|
|
|
video_id = mobj.group('videoid')
|
|
@@ -355,7 +364,7 @@ class VKIE(InfoExtractor):
|
|
|
}
|
|
|
|
|
|
|
|
|
-class VKUserVideosIE(InfoExtractor):
|
|
|
+class VKUserVideosIE(VKBaseIE):
|
|
|
IE_NAME = 'vk:uservideos'
|
|
|
IE_DESC = "VK - User's Videos"
|
|
|
_VALID_URL = r'https?://(?:(?:m|new)\.)?vk\.com/videos(?P<id>-?[0-9]+)(?!\?.*\bz=video)(?:[/?#&]|$)'
|
|
@@ -396,3 +405,121 @@ class VKUserVideosIE(InfoExtractor):
|
|
|
webpage, 'title', default=page_id))
|
|
|
|
|
|
return self.playlist_result(entries, page_id, title)
|
|
|
+
|
|
|
+
|
|
|
+class VKWallPostIE(VKBaseIE):
|
|
|
+ IE_NAME = 'vk:wallpost'
|
|
|
+ _VALID_URL = r'https?://(?:(?:(?:(?:m|new)\.)?vk\.com/(?:[^?]+\?.*\bw=)?wall(?P<id>-?\d+_\d+)))'
|
|
|
+ _TESTS = [{
|
|
|
+ # public page URL, audio playlist
|
|
|
+ 'url': 'https://vk.com/bs.official?w=wall-23538238_35',
|
|
|
+ 'info_dict': {
|
|
|
+ 'id': '23538238_35',
|
|
|
+ 'title': 'Black Shadow - Wall post 23538238_35',
|
|
|
+ 'description': 'md5:3f84b9c4f9ef499731cf1ced9998cc0c',
|
|
|
+ },
|
|
|
+ 'playlist': [{
|
|
|
+ 'md5': '5ba93864ec5b85f7ce19a9af4af080f6',
|
|
|
+ 'info_dict': {
|
|
|
+ 'id': '135220665_111806521',
|
|
|
+ 'ext': 'mp3',
|
|
|
+ 'title': 'Black Shadow - Слепое Верование',
|
|
|
+ 'duration': 370,
|
|
|
+ 'uploader': 'Black Shadow',
|
|
|
+ 'artist': 'Black Shadow',
|
|
|
+ 'track': 'Слепое Верование',
|
|
|
+ },
|
|
|
+ }, {
|
|
|
+ 'md5': '4cc7e804579122b17ea95af7834c9233',
|
|
|
+ 'info_dict': {
|
|
|
+ 'id': '135220665_111802303',
|
|
|
+ 'ext': 'mp3',
|
|
|
+ 'title': 'Black Shadow - Война - Негасимое Бездны Пламя!',
|
|
|
+ 'duration': 423,
|
|
|
+ 'uploader': 'Black Shadow',
|
|
|
+ 'artist': 'Black Shadow',
|
|
|
+ 'track': 'Война - Негасимое Бездны Пламя!',
|
|
|
+ },
|
|
|
+ 'params': {
|
|
|
+ 'skip_download': True,
|
|
|
+ },
|
|
|
+ }],
|
|
|
+ 'skip': 'Requires vk account credentials',
|
|
|
+ }, {
|
|
|
+ # single YouTube embed, no leading -
|
|
|
+ 'url': 'https://vk.com/wall85155021_6319',
|
|
|
+ 'info_dict': {
|
|
|
+ 'id': '85155021_6319',
|
|
|
+ 'title': 'Sergey Gorbunov - Wall post 85155021_6319',
|
|
|
+ },
|
|
|
+ 'playlist_count': 1,
|
|
|
+ 'skip': 'Requires vk account credentials',
|
|
|
+ }, {
|
|
|
+ # wall page URL
|
|
|
+ 'url': 'https://vk.com/wall-23538238_35',
|
|
|
+ 'only_matching': True,
|
|
|
+ }, {
|
|
|
+ # mobile wall page URL
|
|
|
+ 'url': 'https://m.vk.com/wall-23538238_35',
|
|
|
+ 'only_matching': True,
|
|
|
+ }]
|
|
|
+
|
|
|
+ def _real_extract(self, url):
|
|
|
+ post_id = self._match_id(url)
|
|
|
+
|
|
|
+ wall_url = 'https://vk.com/wall%s' % post_id
|
|
|
+
|
|
|
+ post_id = remove_start(post_id, '-')
|
|
|
+
|
|
|
+ webpage = self._download_webpage(wall_url, post_id)
|
|
|
+
|
|
|
+ error = self._html_search_regex(
|
|
|
+ r'>Error</div>\s*<div[^>]+class=["\']body["\'][^>]*>([^<]+)',
|
|
|
+ webpage, 'error', default=None)
|
|
|
+ if error:
|
|
|
+ raise ExtractorError('VK said: %s' % error, expected=True)
|
|
|
+
|
|
|
+ description = clean_html(get_element_by_class('wall_post_text', webpage))
|
|
|
+ uploader = clean_html(get_element_by_class(
|
|
|
+ 'fw_post_author', webpage)) or self._og_search_description(webpage)
|
|
|
+ thumbnail = self._og_search_thumbnail(webpage)
|
|
|
+
|
|
|
+ entries = []
|
|
|
+
|
|
|
+ for audio in re.finditer(r'''(?sx)
|
|
|
+ <input[^>]+
|
|
|
+ id=(?P<q1>["\'])audio_info(?P<id>\d+_\d+).*?(?P=q1)[^>]+
|
|
|
+ value=(?P<q2>["\'])(?P<url>http.+?)(?P=q2)
|
|
|
+ .+?
|
|
|
+ </table>''', webpage):
|
|
|
+ audio_html = audio.group(0)
|
|
|
+ audio_id = audio.group('id')
|
|
|
+ duration = parse_duration(get_element_by_class('duration', audio_html))
|
|
|
+ track = self._html_search_regex(
|
|
|
+ r'<span[^>]+id=["\']title%s[^>]*>([^<]+)' % audio_id,
|
|
|
+ audio_html, 'title', default=None)
|
|
|
+ artist = self._html_search_regex(
|
|
|
+ r'>([^<]+)</a></b>\s*&ndash', audio_html,
|
|
|
+ 'artist', default=None)
|
|
|
+ entries.append({
|
|
|
+ 'id': audio_id,
|
|
|
+ 'url': audio.group('url'),
|
|
|
+ 'title': '%s - %s' % (artist, track) if artist and track else audio_id,
|
|
|
+ 'thumbnail': thumbnail,
|
|
|
+ 'duration': duration,
|
|
|
+ 'uploader': uploader,
|
|
|
+ 'artist': artist,
|
|
|
+ 'track': track,
|
|
|
+ })
|
|
|
+
|
|
|
+ for video in re.finditer(
|
|
|
+ r'<a[^>]+href=(["\'])(?P<url>/video(?:-?[\d_]+).*?)\1', webpage):
|
|
|
+ entries.append(self.url_result(
|
|
|
+ compat_urlparse.urljoin(url, video.group('url')), VKIE.ie_key()))
|
|
|
+
|
|
|
+ title = 'Wall post %s' % post_id
|
|
|
+
|
|
|
+ return self.playlist_result(
|
|
|
+ orderedSet(entries), post_id,
|
|
|
+ '%s - %s' % (uploader, title) if uploader else title,
|
|
|
+ description)
|