def on_message(self, reply_: dict): if reply_: reply('FROM TWITCH -> {0}'.format(reply_)) message_type = try_get(reply_, lambda x: x['type'], str) or '' message_data = parse_json( try_get(reply_, lambda x: x['data']['message'], str)) or {} if "MESSAGE" in message_type: data_message_type = try_get(message_data, lambda x: x['type']) or '' if 'stream-up' in data_message_type: self.live_streaming = True self.broadcast_id = try_get( message_data, lambda x: x['data']['broadcast_id'], int) self.live_streaming, hls = self.getTwitchStreamInfo() if hls is not None: self.StreamFormat = get_format_from_data( hls, self.cachedDataHandler.getValue( 'recordingResolution')) self.start_recording(self.StreamFormat) if 'stream-down' in data_message_type: self.live_streaming = False self.stop_recording() if 'viewcount' in data_message_type: self.viewers = try_get(message_data, lambda x: x['viewers'], int) if "RESPONSE" in message_type: pass
def get_yt_player_config(website: str) -> dict or None: """ Taken and have been edited from: https://github.com/ytdl-org/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L1386 """ config = re.findall(r';ytplayer\.config\s*=\s*({.+?});', website) if config: return parse_json(config[0])
def get_yt_config(website: str) -> dict or None: """ Gets YT Config. of course """ if type(website) is not str: return None config = re.findall(r'ytcfg\.set({.+?});', website) if config: return parse_json(config[0])
def get_yt_initial_data(website: str) -> dict or None: """ Gets Youtube Initial Data. of course """ if type(website) is not str: return None config = re.findall(r'window\[\"ytInitialData\"]\s=\s(.+);', website) if config: return parse_json(config[0])
def loadVideoData(self): url = "https://www.twitch.tv/{0}".format(self.channel_name) download_object = download_website(url, CookieDict=self.sharedCookieDict) if download_object.status_code == 404: return [ False, "Failed getting Twitch Data! \"{0}\" doesn't exist as a channel name!" .format(self.channel_name) ] if download_object.text is None: return [ False, "Failed getting Youtube Data from the internet! " "This means there is no good internet available!" ] if self.globalVariables.get("client_id") is None: verbose("Getting Client ID. [TWITCH]") okay, client_id = find_client_id(download_object.text) if okay is False: warning(client_id) self.globalVariables.set("client_id", client_id) verbose('Getting Channel ID. [TWITCH]') self.access_token = self.__callAPI__( 'api/channels/{0}/access_token?{1}&oauth_token'.format( self.channel_name, urlencode({ 'need_https': 'true', 'platform': 'web', 'player_backend': 'mediaplayer', 'player_type': 'site' }))) token = parse_json(self.access_token['token']) self.channel_id = try_get(token, lambda x: x['channel_id']) # website_dict = self.__callAPI__('kraken/channels/{0}'.format( # self.channel_name)) # self.channel_image = try_get(website_dict, lambda x: x['logo']) # self.channel_id = try_get(website_dict, lambda x: x['_id'], int) self.live_streaming, hls = self.getTwitchStreamInfo() if hls is not None: self.StreamFormat = get_format_from_data( hls, self.cachedDataHandler.getValue('recordingResolution')) if not self.channel_id: return [False, "Unable to find Channel ID."] return [True, "OK"]
def loadVideoData(self, video_id=None): self.video_id = video_id if video_id: url = "https://www.youtube.com/watch?v={0}".format(video_id) else: url = "https://www.youtube.com/channel/{0}/live".format( self.channel_id) website_object = download_website(url, CookieDict=self.sharedCookieDict) # self.sharedCookieDict.update(websiteClass.cookies) if website_object.text is None: return [ False, "Failed getting Youtube Data from the internet! " "This means there is no good internet available!" ] if website_object.status_code == 404: return [ False, "Failed getting Youtube Data! \"{0}\" doesn't exist as a channel id!" .format(self.channel_id) ] website_string = website_object.text endpoint_type = get_endpoint_type(website_string) if endpoint_type: if endpoint_type == 'browse': array = re.findall(r'property="og:title" content="(.+?)"', website_string) if array: channel_name = array[0] warning( "{0} has the live stream " "currently unlisted or private, or only for members. " "Using safeguard. This may not be the best to leave on.\n" .format(channel_name)) self.channel_name = channel_name self.video_id = None self.privateStream = True else: if not endpoint_type == 'watch': warning("Unrecognized endpoint type. Endpoint Type: {0}.". format(endpoint_type)) verbose("Getting Video ID.") youtube_initial_data = get_yt_initial_data(website_string) yt_player_config = try_get( get_yt_player_config(website_string), lambda x: x, dict) player_response = parse_json( try_get(yt_player_config, lambda x: x['args']['player_response'], str)) videoDetails = try_get(player_response, lambda x: x['videoDetails'], dict) if yt_player_config and videoDetails: if "isLiveContent" in videoDetails and \ videoDetails['isLiveContent'] and \ ("isLive" in videoDetails or "isUpcoming" in videoDetails): self.channel_name = try_get(videoDetails, lambda x: x['author'], str) self.video_id = try_get(videoDetails, lambda x: x['videoId'], str) self.privateStream = False if not self.channel_id: self.channel_id = try_get(videoDetails, lambda x: x['channelId'], str) else: return [ False, "Found a stream, the stream seemed to be a non-live stream." ] else: return [ False, "Unable to get yt player config, and videoDetails." ] contents = try_get( youtube_initial_data, lambda x: x['contents']['twoColumnWatchNextResults'][ 'results']['results']['contents'], list) videoSecondaryInfoRenderer = try_get([ content for content in contents if content.get("videoSecondaryInfoRenderer") is not None ], lambda x: x[0], dict).get("videoSecondaryInfoRenderer") channelImageFormats = try_get( videoSecondaryInfoRenderer, lambda x: x['owner'][ 'videoOwnerRenderer']['thumbnail']['thumbnails'], list) if channelImageFormats is not None: self.channel_image = max( channelImageFormats, key=lambda x: x.get("height")).get("url") if not self.privateStream: # TO AVOID REPEATING REQUESTS. if player_response: # playabilityStatus is legit heartbeat all over again.. playabilityStatus = try_get( player_response, lambda x: x['playabilityStatus'], dict) status = try_get(playabilityStatus, lambda x: x['status'], str) # type: str reason = try_get(playabilityStatus, lambda x: x['reason'], str) # type: str if playabilityStatus and status: if 'OK' in status.upper(): if reason and 'ended' in reason: return [False, reason] streamingData = try_get( player_response, lambda x: x['streamingData'], dict) if streamingData: if 'licenseInfos' in streamingData: licenseInfo = streamingData.get( 'licenseInfos') drmFamilies = map( lambda x: x.get('drmFamily'), licenseInfo) return [ False, "This live stream contains DRM and cannot be recorded.\n" "DRM Families: {0}".format( ', '.join(drmFamilies)) ] manifest_url = str( try_get(streamingData, lambda x: x['hlsManifestUrl'], str)) if not manifest_url: return [ False, "Unable to find HLS Manifest URL." ] downloadOBJECT = download_website( manifest_url, CookieDict=self.sharedCookieDict) hls = downloadOBJECT.parse_m3u8_formats() if len(hls.formats) == 0: return [ False, "There were no formats found! Even when the streamer is live." ] format_ = get_format_from_data( hls, self.cachedDataHandler.getValue( 'recordingResolution')) if not videoDetails: videoDetails = try_get( player_response, lambda x: x['videoDetails'], dict) thumbnails = try_get( videoDetails, lambda x: x['thumbnail']['thumbnails'], list) if thumbnails: self.thumbnail_url = get_highest_thumbnail( thumbnails) self.dvr_enabled = try_get( videoDetails, lambda x: x['isLiveDvrEnabled'], bool) self.StreamFormat = format_ self.title = try_get( videoDetails, lambda x: x['title'], str) self.description = videoDetails[ 'shortDescription'] else: return [ False, "No StreamingData, YouTube bugged out!" ] self.live_streaming = self.is_live( json=playabilityStatus) # GET YOUTUBE GLOBAL VARIABLES if self.globalVariables.get( "checkedYouTubeVariables") is None: def getSettingsValue(ServiceSettings, settings_nameLook, name=None): for service in ServiceSettings: service_name = try_get(service, lambda x: x['key'], str) if service_name is not None and service_name in settings_nameLook: value = try_get(service, lambda x: x['value'], str) if name: if not value: warning( "Something happened when finding the " + name) return None return value return None def getServiceSettings(serviceTrackingParamsList, service_nameLook): if serviceTrackingParamsList: for service in serviceTrackingParamsList: service_name = try_get( service, lambda x: x['service'], str) if service_name is not None and service_name in service_nameLook: return service return None if self.globalVariables.get( "alreadyChecked" ) is False or self.globalVariables.get( "alreadyChecked") is None: verbose("Getting Global YouTube Variables.") e_catcher = getServiceSettings( try_get( youtube_initial_data, lambda x: x['responseContext'][ 'serviceTrackingParams'], list), "ECATCHER") account_playback_token = try_get( yt_player_config, lambda x: x['args'][ 'account_playback_token'][:-1], str) ps = try_get(yt_player_config, lambda x: x['args']['ps'], str) cbr = try_get(yt_player_config, lambda x: x['args']['cbr']) client_os = try_get(yt_player_config, lambda x: x['args']['cos']) client_os_version = try_get( yt_player_config, lambda x: x['args']['cosver']) if account_playback_token is None: warning( "Unable to find account playback token in the YouTube player config." ) if ps is None: warning( "Unable to find ps in the YouTube player config." ) if cbr is None: warning( "Unable to find cbr in the YouTube player config." ) if client_os is None: warning( "Unable to find Client OS in the YouTube player config." ) if client_os_version is None: warning( "Unable to find Client OS Version in the YouTube player config." ) self.globalVariables.set("checkedYouTubeVariables", None) if not youtube_initial_data: warning( "Unable to get Youtube Initial Data. Cannot find all Youtube Variables." ) elif e_catcher is None: warning( "Unable to get ECATCHER service data in Youtube Initial Data. " "Cannot find all Youtube Variables.") else: params = try_get(e_catcher, lambda x: x['params'], list) page_build_label = getSettingsValue( params, 'innertube.build.label', name="Page Build Label") page_cl = getSettingsValue( params, 'innertube.build.changelist', name="Page CL") variants_checksum = getSettingsValue( params, 'innertube.build.variants.checksum', name="Variants Checksum") client_version = getSettingsValue( params, 'client.version', name="Client Version") client_name = getSettingsValue( params, 'client.name', name="Client Name") self.globalVariables.set( "page_build_label", page_build_label) self.globalVariables.set("page_cl", page_cl) self.globalVariables.set( "client_version", client_version) self.globalVariables.set( "client_name", client_name) self.globalVariables.set( "variants_checksum", variants_checksum) self.globalVariables.set("ps", ps) self.globalVariables.set("cbr", cbr) self.globalVariables.set("client_os", client_os) self.globalVariables.set("client_os_version", client_os_version) self.globalVariables.set("account_playback_token", account_playback_token) self.globalVariables.set("utf_offset", get_utc_offset()) self.globalVariables.set("timezone", getTimeZone()) self.globalVariables.set("alreadyChecked", True) # ONLY WORKS IF LOGGED IN self.sponsor_on_channel = self.get_sponsor_channel( html_code=website_string) self.cpn = self.generate_cpn() return [True, "OK"]
def get_video_info(self): """ Gets the stream info from channelClass. Looked at for reference: https://github.com/ytdl-org/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L1675 """ url_arguments = {'html5': 1, 'video_id': self.video_id} if self.globalVariables.get("ps") is not None: url_arguments.update({'ps': self.globalVariables.get("ps")}) url_arguments.update({'eurl': ''}) url_arguments.update({'hl': 'en_US'}) if self.globalVariables.get("client_name") is not None: url_arguments.update( {'c': self.globalVariables.get("client_name")}) if self.globalVariables.get("cbr") is not None: url_arguments.update({'cbr': self.globalVariables.get("cbr")}) if self.globalVariables.get("client_version") is not None: url_arguments.update( {'cver': self.globalVariables.get("client_version")}) if self.globalVariables.get("client_os") is not None: url_arguments.update( {'cos': self.globalVariables.get("client_os")}) if self.globalVariables.get("client_os_version") is not None: url_arguments.update( {'cosver': self.globalVariables.get("client_os_version")}) if self.cpn is not None: url_arguments.update({'cpn': self.cpn}) downloadClass = download_website( 'https://www.youtube.com/get_video_info?{0}'.format( urlencode(url_arguments)), CookieDict=self.sharedCookieDict) video_info_website = downloadClass.text video_info = parse_qs(video_info_website) player_response = parse_json( try_get(video_info, lambda x: x['player_response'][0], str)) if player_response: video_details = try_get(player_response, lambda x: x['videoDetails'], dict) if "streamingData" not in player_response: warning("No StreamingData, Youtube bugged out!") return None manifest_url = str( try_get(player_response, lambda x: x['streamingData']['hlsManifestUrl'], str)) if not manifest_url: warning("Unable to find HLS Manifest URL.") return None downloadOBJECT = download_website(manifest_url, CookieDict=self.sharedCookieDict) if downloadOBJECT.status_code != 200: return None hls = downloadOBJECT.parse_m3u8_formats() if len(hls.formats) == 0: warning( "There were no formats found! Even when the streamer is live." ) return None return { 'formats': hls, 'manifest_url': manifest_url, 'video_details': video_details, } return None
def on_message(self, ws, message): self.on_message_(parse_json(message))