|
|
|
@ -1,20 +1,23 @@
|
|
|
|
|
# coding: utf-8
|
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
import datetime
|
|
|
|
|
import functools
|
|
|
|
|
import json
|
|
|
|
|
import math
|
|
|
|
|
|
|
|
|
|
from .common import InfoExtractor
|
|
|
|
|
from ..compat import (
|
|
|
|
|
compat_parse_qs,
|
|
|
|
|
compat_urlparse,
|
|
|
|
|
compat_urllib_parse_urlparse,
|
|
|
|
|
)
|
|
|
|
|
from ..utils import (
|
|
|
|
|
determine_ext,
|
|
|
|
|
dict_get,
|
|
|
|
|
ExtractorError,
|
|
|
|
|
int_or_none,
|
|
|
|
|
float_or_none,
|
|
|
|
|
InAdvancePagedList,
|
|
|
|
|
int_or_none,
|
|
|
|
|
parse_duration,
|
|
|
|
|
parse_iso8601,
|
|
|
|
|
remove_start,
|
|
|
|
@ -181,7 +184,7 @@ class NiconicoIE(InfoExtractor):
|
|
|
|
|
if urlh is False:
|
|
|
|
|
login_ok = False
|
|
|
|
|
else:
|
|
|
|
|
parts = compat_urlparse.urlparse(urlh.geturl())
|
|
|
|
|
parts = compat_urllib_parse_urlparse(urlh.geturl())
|
|
|
|
|
if compat_parse_qs(parts.query).get('message', [None])[0] == 'cant_login':
|
|
|
|
|
login_ok = False
|
|
|
|
|
if not login_ok:
|
|
|
|
@ -292,7 +295,7 @@ class NiconicoIE(InfoExtractor):
|
|
|
|
|
'http://flapi.nicovideo.jp/api/getflv/' + video_id + '?as3=1',
|
|
|
|
|
video_id, 'Downloading flv info')
|
|
|
|
|
|
|
|
|
|
flv_info = compat_urlparse.parse_qs(flv_info_webpage)
|
|
|
|
|
flv_info = compat_parse_qs(flv_info_webpage)
|
|
|
|
|
if 'url' not in flv_info:
|
|
|
|
|
if 'deleted' in flv_info:
|
|
|
|
|
raise ExtractorError('The video has been deleted.',
|
|
|
|
@ -437,34 +440,76 @@ class NiconicoIE(InfoExtractor):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class NiconicoPlaylistIE(InfoExtractor):
|
|
|
|
|
_VALID_URL = r'https?://(?:www\.)?nicovideo\.jp/mylist/(?P<id>\d+)'
|
|
|
|
|
_VALID_URL = r'https?://(?:www\.)?nicovideo\.jp/(?:user/\d+/)?mylist/(?P<id>\d+)'
|
|
|
|
|
|
|
|
|
|
_TEST = {
|
|
|
|
|
_TESTS = [{
|
|
|
|
|
'url': 'http://www.nicovideo.jp/mylist/27411728',
|
|
|
|
|
'info_dict': {
|
|
|
|
|
'id': '27411728',
|
|
|
|
|
'title': 'AKB48のオールナイトニッポン',
|
|
|
|
|
'description': 'md5:d89694c5ded4b6c693dea2db6e41aa08',
|
|
|
|
|
'uploader': 'のっく',
|
|
|
|
|
'uploader_id': '805442',
|
|
|
|
|
},
|
|
|
|
|
'playlist_mincount': 225,
|
|
|
|
|
}
|
|
|
|
|
}, {
|
|
|
|
|
'url': 'https://www.nicovideo.jp/user/805442/mylist/27411728',
|
|
|
|
|
'only_matching': True,
|
|
|
|
|
}]
|
|
|
|
|
_PAGE_SIZE = 100
|
|
|
|
|
|
|
|
|
|
def _real_extract(self, url):
|
|
|
|
|
list_id = self._match_id(url)
|
|
|
|
|
webpage = self._download_webpage(url, list_id)
|
|
|
|
|
def _call_api(self, list_id, resource, query):
|
|
|
|
|
return self._download_json(
|
|
|
|
|
'https://nvapi.nicovideo.jp/v2/mylists/' + list_id, list_id,
|
|
|
|
|
'Downloading %s JSON metatdata' % resource, query=query,
|
|
|
|
|
headers={'X-Frontend-Id': 6})['data']['mylist']
|
|
|
|
|
|
|
|
|
|
entries_json = self._search_regex(r'Mylist\.preload\(\d+, (\[.*\])\);',
|
|
|
|
|
webpage, 'entries')
|
|
|
|
|
entries = json.loads(entries_json)
|
|
|
|
|
entries = [{
|
|
|
|
|
def _parse_owner(self, item):
|
|
|
|
|
owner = item.get('owner') or {}
|
|
|
|
|
if owner:
|
|
|
|
|
return {
|
|
|
|
|
'uploader': owner.get('name'),
|
|
|
|
|
'uploader_id': owner.get('id'),
|
|
|
|
|
}
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
def _fetch_page(self, list_id, page):
|
|
|
|
|
page += 1
|
|
|
|
|
items = self._call_api(list_id, 'page %d' % page, {
|
|
|
|
|
'page': page,
|
|
|
|
|
'pageSize': self._PAGE_SIZE,
|
|
|
|
|
})['items']
|
|
|
|
|
for item in items:
|
|
|
|
|
video = item.get('video') or {}
|
|
|
|
|
video_id = video.get('id')
|
|
|
|
|
if not video_id:
|
|
|
|
|
continue
|
|
|
|
|
count = video.get('count') or {}
|
|
|
|
|
get_count = lambda x: int_or_none(count.get(x))
|
|
|
|
|
info = {
|
|
|
|
|
'_type': 'url',
|
|
|
|
|
'id': video_id,
|
|
|
|
|
'title': video.get('title'),
|
|
|
|
|
'url': 'https://www.nicovideo.jp/watch/' + video_id,
|
|
|
|
|
'description': video.get('shortDescription'),
|
|
|
|
|
'duration': int_or_none(video.get('duration')),
|
|
|
|
|
'view_count': get_count('view'),
|
|
|
|
|
'comment_count': get_count('comment'),
|
|
|
|
|
'ie_key': NiconicoIE.ie_key(),
|
|
|
|
|
'url': ('http://www.nicovideo.jp/watch/%s' %
|
|
|
|
|
entry['item_data']['video_id']),
|
|
|
|
|
} for entry in entries]
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'_type': 'playlist',
|
|
|
|
|
'title': self._search_regex(r'\s+name: "(.*?)"', webpage, 'title'),
|
|
|
|
|
'id': list_id,
|
|
|
|
|
'entries': entries,
|
|
|
|
|
}
|
|
|
|
|
info.update(self._parse_owner(video))
|
|
|
|
|
yield info
|
|
|
|
|
|
|
|
|
|
def _real_extract(self, url):
|
|
|
|
|
list_id = self._match_id(url)
|
|
|
|
|
mylist = self._call_api(list_id, 'list', {
|
|
|
|
|
'pageSize': 1,
|
|
|
|
|
})
|
|
|
|
|
entries = InAdvancePagedList(
|
|
|
|
|
functools.partial(self._fetch_page, list_id),
|
|
|
|
|
math.ceil(mylist['totalItemCount'] / self._PAGE_SIZE),
|
|
|
|
|
self._PAGE_SIZE)
|
|
|
|
|
result = self.playlist_result(
|
|
|
|
|
entries, list_id, mylist.get('name'), mylist.get('description'))
|
|
|
|
|
result.update(self._parse_owner(mylist))
|
|
|
|
|
return result
|
|
|
|
|