# coding: utf-8 from __future__ import unicode_literals import itertools from .common import InfoExtractor from ..compat import compat_str from ..utils import ( ExtractorError, int_or_none, try_get, ) CDN_API_BASE = 'https://cdn.younow.com/php/api' MOMENT_URL_FORMAT = '%s/moment/fetch/id=%%s' % CDN_API_BASE class YouNowLiveIE(InfoExtractor): _VALID_URL = r'https?://(?:www\.)?younow\.com/(?P[^/?#&]+)' _TEST = { 'url': 'https://www.younow.com/AmandaPadeezy', 'info_dict': { 'id': 'AmandaPadeezy', 'ext': 'mp4', 'is_live': True, 'title': 'March 26, 2017', 'thumbnail': r're:^https?://.*\.jpg$', 'tags': ['girls'], 'categories': ['girls'], 'uploader': 'AmandaPadeezy', 'uploader_id': '6716501', 'uploader_url': 'https://www.younow.com/AmandaPadeezy', 'creator': 'AmandaPadeezy', }, 'skip': True, } @classmethod def suitable(cls, url): return (False if YouNowChannelIE.suitable(url) or YouNowMomentIE.suitable(url) else super(YouNowLiveIE, cls).suitable(url)) def _real_extract(self, url): username = self._match_id(url) data = self._download_json( 'https://api.younow.com/php/api/broadcast/info/curId=0/user=%s' % username, username) if data.get('errorCode') != 0: raise ExtractorError(data['errorMsg'], expected=True) uploader = try_get( data, lambda x: x['user']['profileUrlString'], compat_str) or username return { 'id': uploader, 'is_live': True, 'title': self._live_title(uploader), 'thumbnail': data.get('awsUrl'), 'tags': data.get('tags'), 'categories': data.get('tags'), 'uploader': uploader, 'uploader_id': data.get('userId'), 'uploader_url': 'https://www.younow.com/%s' % username, 'creator': uploader, 'view_count': int_or_none(data.get('viewers')), 'like_count': int_or_none(data.get('likes')), 'formats': [{ 'url': '%s/broadcast/videoPath/hls=1/broadcastId=%s/channelId=%s' % (CDN_API_BASE, data['broadcastId'], data['userId']), 'ext': 'mp4', 'protocol': 'm3u8', }], } def _extract_moment(item, fatal=True): moment_id = item.get('momentId') if not moment_id: if not fatal: return raise ExtractorError('Unable to extract moment id') moment_id = compat_str(moment_id) title = item.get('text') if not title: title = 'YouNow %s' % ( item.get('momentType') or item.get('titleType') or 'moment') uploader = try_get(item, lambda x: x['owner']['name'], compat_str) uploader_id = try_get(item, lambda x: x['owner']['userId']) uploader_url = 'https://www.younow.com/%s' % uploader if uploader else None entry = { 'extractor_key': 'YouNowMoment', 'id': moment_id, 'title': title, 'view_count': int_or_none(item.get('views')), 'like_count': int_or_none(item.get('likes')), 'timestamp': int_or_none(item.get('created')), 'creator': uploader, 'uploader': uploader, 'uploader_id': uploader_id, 'uploader_url': uploader_url, 'formats': [{ 'url': 'https://hls.younow.com/momentsplaylists/live/%s/%s.m3u8' % (moment_id, moment_id), 'ext': 'mp4', 'protocol': 'm3u8_native', }], } return entry class YouNowChannelIE(InfoExtractor): _VALID_URL = r'https?://(?:www\.)?younow\.com/(?P[^/]+)/channel' _TEST = { 'url': 'https://www.younow.com/its_Kateee_/channel', 'info_dict': { 'id': '14629760', 'title': 'its_Kateee_ moments' }, 'playlist_mincount': 8, } def _entries(self, username, channel_id): created_before = 0 for page_num in itertools.count(1): if created_before is None: break info = self._download_json( '%s/moment/profile/channelId=%s/createdBefore=%d/records=20' % (CDN_API_BASE, channel_id, created_before), username, note='Downloading moments page %d' % page_num) items = info.get('items') if not items or not isinstance(items, list): break for item in items: if not isinstance(item, dict): continue item_type = item.get('type') if item_type == 'moment': entry = _extract_moment(item, fatal=False) if entry: yield entry elif item_type == 'collection': moments = item.get('momentsIds') if isinstance(moments, list): for moment_id in moments: m = self._download_json( MOMENT_URL_FORMAT % moment_id, username, note='Downloading %s moment JSON' % moment_id, fatal=False) if m and isinstance(m, dict) and m.get('item'): entry = _extract_moment(m['item']) if entry: yield entry created_before = int_or_none(item.get('created')) def _real_extract(self, url): username = self._match_id(url) channel_id = compat_str(self._download_json( 'https://api.younow.com/php/api/broadcast/info/curId=0/user=%s' % username, username, note='Downloading user information')['userId']) return self.playlist_result( self._entries(username, channel_id), channel_id, '%s moments' % username) class YouNowMomentIE(InfoExtractor): _VALID_URL = r'https?://(?:www\.)?younow\.com/[^/]+/(?P[^/?#&]+)' _TEST = { 'url': 'https://www.younow.com/GABO.../20712117/36319236/3b316doc/m', 'md5': 'a30c70eadb9fb39a1aa3c8c0d22a0807', 'info_dict': { 'id': '20712117', 'ext': 'mp4', 'title': 'YouNow capture', 'view_count': int, 'like_count': int, 'timestamp': 1490432040, 'upload_date': '20170325', 'uploader': 'GABO...', 'uploader_id': 35917228, }, } @classmethod def suitable(cls, url): return (False if YouNowChannelIE.suitable(url) else super(YouNowMomentIE, cls).suitable(url)) def _real_extract(self, url): video_id = self._match_id(url) item = self._download_json(MOMENT_URL_FORMAT % video_id, video_id) return _extract_moment(item['item'])