[ie/niconico] accurately check live status; add availability check

This commit is contained in:
Mozi 2024-05-30 15:16:27 +00:00
parent 31c13e92e2
commit 53a7fcc231

View file

@ -911,6 +911,8 @@ def _real_extract(self, url):
class NiconicoLiveIE(InfoExtractor):
IE_NAME = 'niconico:live'
IE_DESC = 'ニコニコ生放送'
_GEO_COUNTRIES = ['JP']
_GEO_BYPASS = False
_VALID_URL = r'https?://(?:sp\.)?live2?\.nicovideo\.jp/(?:watch|gate)/(?P<id>lv\d+)'
_TESTS = [{
'note': 'this test case includes invisible characters for title, pasting them as-is',
@ -959,7 +961,7 @@ def _yield_formats(self, ws_url, headers, latency, video_id, is_live):
ws = self._request_webpage(
Request(ws_url, headers=headers), video_id, note='Connecting to WebSocket server')
self.write_debug('[debug] Sending HLS server request')
self.write_debug('Sending HLS server request')
ws.send(json.dumps({
'type': 'startWatching',
'data': {
@ -1023,8 +1025,6 @@ def _real_extract(self, url):
ws_url = update_url_query(ws_url, {
'frontend_id': traverse_obj(embedded_data, ('site', 'frontendId')) or '9',
})
else:
self.raise_no_formats('The live hasn\'t started yet or already ended.', expected=True)
title = traverse_obj(embedded_data, ('program', 'title')) or self._html_search_meta(
('og:title', 'twitter:title'), webpage, 'live title', fatal=False)
@ -1050,11 +1050,7 @@ def _real_extract(self, url):
**res,
})
live_status = {
'Before': 'is_live',
'Open': 'was_live',
'End': 'was_live',
}.get(traverse_obj(embedded_data, ('programTimeshift', 'publication', 'status', {str})), 'is_live')
live_status, availability = self._check_status_and_availability(embedded_data, video_id)
latency = try_get(self._configuration_arg('latency'), lambda x: x[0])
if latency not in self._KNOWN_LATENCY:
@ -1074,6 +1070,7 @@ def _real_extract(self, url):
'description': clean_html(traverse_obj(embedded_data, ('program', 'description'))),
'timestamp': int_or_none(traverse_obj(embedded_data, ('program', 'openTime'))),
'live_status': live_status,
'availability': availability,
'thumbnails': thumbnails,
'formats': [*self._yield_formats(
ws_url, headers, latency, video_id, live_status == 'is_live')] if ws_url else None,
@ -1083,3 +1080,64 @@ def _real_extract(self, url):
'ws_url': ws_url,
},
}
def _check_status_and_availability(self, embedded_data, video_id):
live_status = {
'Before': 'is_live',
'Open': 'was_live',
'End': 'was_live',
}.get(traverse_obj(embedded_data, ('programTimeshift', 'publication', 'status', {str})), 'is_live')
if traverse_obj(embedded_data, ('userProgramWatch', 'canWatch', {bool})):
if needs_subscription := traverse_obj(embedded_data, ('program', 'isMemberFree', {bool})):
msg = 'You have no right to access the paid content. '
if not traverse_obj(embedded_data, ('program', 'trialWatch', 'isShown', {bool})):
msg += 'This video may be completely blank'
else:
# TODO: get the exact duration of the free part
msg += 'There may be some blank parts in this video'
self.report_warning(msg, video_id)
return live_status, self._availability(needs_subscription=needs_subscription)
if traverse_obj(embedded_data, ('userProgramWatch', 'isCountryRestrictionTarget', {bool})):
self.raise_geo_restricted(countries=self._GEO_COUNTRIES, metadata_available=True)
return live_status, self._availability()
rejected_reasons = traverse_obj(embedded_data, ('userProgramWatch', 'rejectedReasons', ..., {str}))
self.write_debug(f'userProgramWatch.rejectedReasons: {rejected_reasons!r}')
if 'programNotBegun' in rejected_reasons:
live_status = 'is_upcoming'
elif 'timeshiftBeforeOpen' in rejected_reasons:
live_status = 'post_live'
elif 'noTimeshiftProgram' in rejected_reasons:
self.report_warning('Timeshift is disabled', video_id)
live_status = 'was_live'
elif any(x in ['timeshiftClosed', 'timeshiftClosedAndNotFollow'] for x in rejected_reasons):
self.report_warning('Timeshift viewing period has ended', video_id)
live_status = 'was_live'
availability = self._availability(**{
'needs_premium': 'notLogin' in rejected_reasons,
'needs_subscription': any(x in [
'notSocialGroupMember',
'notCommunityMember',
'notChannelMember',
'notCommunityMemberAndNotHaveTimeshiftTicket',
'notChannelMemberAndNotHaveTimeshiftTicket',
] for x in rejected_reasons),
'needs_auth': any(x in [
'timeshiftTicketExpired',
'notHaveTimeshiftTicket',
'notCommunityMemberAndNotHaveTimeshiftTicket',
'notChannelMemberAndNotHaveTimeshiftTicket',
'notHavePayTicket',
'notActivatedBySerial',
'notHavePayTicketAndNotActivatedBySerial',
'notUseTimeshiftTicket',
'notUseTimeshiftTicketOnOnceTimeshift',
'notUseTimeshiftTicketOnUnlimitedTimeshift',
] for x in rejected_reasons),
})
return live_status, availability