def descramble(self) -> None: """Descramble the stream data and build Stream instances. The initialization process takes advantage of Python's "call-by-reference evaluation," which allows dictionary transforms to be applied in-place, instead of holding references to mutations at each interstitial step. :rtype: None """ logger.info("init started") self.vid_info = dict(parse_qsl(self.vid_info_raw)) if self.age_restricted: self.player_config_args = self.vid_info else: assert self.watch_html is not None self.player_config_args = get_ytplayer_config(self.watch_html)["args"] # Fix for KeyError: 'title' issue #434 if "title" not in self.player_config_args: # type: ignore i_start = self.watch_html.lower().index("<title>") + len("<title>") i_end = self.watch_html.lower().index("</title>") title = self.watch_html[i_start:i_end].strip() index = title.lower().rfind(" - youtube") title = title[:index] if index > 0 else title self.player_config_args["title"] = unescape(title) # https://github.com/nficano/pytube/issues/165 stream_maps = ["url_encoded_fmt_stream_map"] if "adaptive_fmts" in self.player_config_args: stream_maps.append("adaptive_fmts") # unscramble the progressive and adaptive stream manifests. for fmt in stream_maps: if not self.age_restricted and fmt in self.vid_info: apply_descrambler(self.vid_info, fmt) apply_descrambler(self.player_config_args, fmt) if not self.js: if not self.embed_html: self.embed_html = request.get(url=self.embed_url) self.js_url = extract.js_url(self.embed_html) self.js = request.get(self.js_url) apply_signature(self.player_config_args, fmt, self.js) # build instances of :class:`Stream <Stream>` self.initialize_stream_objects(fmt) # load the player_response object (contains subtitle information) self.player_response = json.loads(self.player_config_args["player_response"]) del self.player_config_args["player_response"] self.stream_monostate.title = self.title self.stream_monostate.duration = self.length logger.info("init finished successfully")
def descramble(self) -> None: """Descramble the stream data and build Stream instances. The initialization process takes advantage of Python's "call-by-reference evaluation," which allows dictionary transforms to be applied in-place, instead of holding references to mutations at each interstitial step. :rtype: None """ self.vid_info = dict(parse_qsl(self.vid_info_raw)) self.player_config_args = self.vid_info self.player_response = json.loads(self.vid_info['player_response']) # On pre-signed videos, we need to use get_ytplayer_config to fix # the player_response item if 'streamingData' not in self.player_config_args['player_response']: config_response = get_ytplayer_config(self.watch_html) if 'args' in config_response: self.player_config_args['player_response'] = config_response['args']['player_response'] # noqa: E501 else: self.player_config_args['player_response'] = config_response # https://github.com/nficano/pytube/issues/165 stream_maps = ["url_encoded_fmt_stream_map"] if "adaptive_fmts" in self.player_config_args: stream_maps.append("adaptive_fmts") # unscramble the progressive and adaptive stream manifests. for fmt in stream_maps: if not self.age_restricted and fmt in self.vid_info: apply_descrambler(self.vid_info, fmt) apply_descrambler(self.player_config_args, fmt) if not self.js: if not self.embed_html: self.embed_html = request.get(url=self.embed_url) self.js_url = extract.js_url(self.embed_html) self.js = request.get(self.js_url) apply_signature(self.player_config_args, fmt, self.js) # build instances of :class:`Stream <Stream>` self.initialize_stream_objects(fmt) # load the player_response object (contains subtitle information) if isinstance(self.player_config_args["player_response"], str): self.player_response = json.loads( self.player_config_args["player_response"] ) else: self.player_response = self.player_config_args["player_response"] del self.player_config_args["player_response"] self.stream_monostate.title = self.title self.stream_monostate.duration = self.length
def fmt_streams(self): """Returns a list of streams if they have been initialized. If the streams have not been initialized, finds all relevant streams and initializes them. """ self.check_availability() if self._fmt_streams: return self._fmt_streams self._fmt_streams = [] # https://github.com/pytube/pytube/issues/165 stream_maps = ["url_encoded_fmt_stream_map"] if "adaptive_fmts" in self.player_config_args: stream_maps.append("adaptive_fmts") # unscramble the progressive and adaptive stream manifests. for fmt in stream_maps: if not self.age_restricted and fmt in self.vid_info: extract.apply_descrambler(self.vid_info, fmt) extract.apply_descrambler(self.player_config_args, fmt) # If the cached js doesn't work, try fetching a new js file # https://github.com/pytube/pytube/issues/1054 try: extract.apply_signature(self.player_config_args, fmt, self.js) except exceptions.ExtractError: # To force an update to the js file, we clear the cache and retry self._js = None self._js_url = None pytube.__js__ = None pytube.__js_url__ = None extract.apply_signature(self.player_config_args, fmt, self.js) # build instances of :class:`Stream <Stream>` # Initialize stream objects stream_manifest = self.player_config_args[fmt] for stream in stream_manifest: video = Stream( stream=stream, player_config_args=self.player_config_args, monostate=self.stream_monostate, ) self._fmt_streams.append(video) self.stream_monostate.title = self.title self.stream_monostate.duration = self.length return self._fmt_streams
def _decipher(self, retry: bool = False): if not pytube.__js__ or retry: self._getJS() try: ''' These two are the main methods being used from PyTube. Used to decipher the stream URLs using player JavaScript & the player_response passed from the getStream method of this derieved class. These methods operate on the value of "player_response" key in dictionary of self._player_response & save _deciphered information in the "url_encoded_fmt_stream_map" key. ''' apply_descrambler(self._player_response, "url_encoded_fmt_stream_map") apply_signature( self._player_response, "url_encoded_fmt_stream_map", pytube.__js__ ) except: ''' Fetch updated player JavaScript to get new cipher algorithm. ''' self._decipher(retry = False)
async def fmt_streams(self): """Returns a list of streams if they have been initialized. If the streams have not been initialized, finds all relevant streams and initializes them. """ await self.check_availability() if self._fmt_streams: return self._fmt_streams self._fmt_streams = [] # https://github.com/pytube/pytube/issues/165 stream_maps = ["url_encoded_fmt_stream_map"] if "adaptive_fmts" in (await self.player_config_args): stream_maps.append("adaptive_fmts") # unscramble the progressive and adaptive stream manifests. for fmt in stream_maps: if not (await self.age_restricted) and fmt in (await self.vid_info): apply_descrambler((await self.vid_info), fmt) apply_descrambler((await self.player_config_args), fmt) apply_signature((await self.player_config_args), fmt, (await self.js)) # build instances of :class:`Stream <Stream>` # Initialize stream objects stream_manifest = (await self.player_config_args)[fmt] for stream in stream_manifest: video = Stream( stream=stream, player_config_args=(await self.player_config_args), monostate=self.stream_monostate, session=self._client_session, ) self._fmt_streams.append(video) self.stream_monostate.title = await self.title self.stream_monostate.duration = await self.length return self._fmt_streams
def fmt_streams(self): """Returns a list of streams if they have been initialized. If the streams have not been initialized, finds all relevant streams and initializes them. """ self.check_availability() if self._fmt_streams: return self._fmt_streams self._fmt_streams = [] stream_manifest = extract.apply_descrambler(self.streaming_data) # If the cached js doesn't work, try fetching a new js file # https://github.com/pytube/pytube/issues/1054 try: extract.apply_signature(stream_manifest, self.vid_info, self.js) except exceptions.ExtractError: # To force an update to the js file, we clear the cache and retry self._js = None self._js_url = None pytube.__js__ = None pytube.__js_url__ = None extract.apply_signature(stream_manifest, self.vid_info, self.js) # build instances of :class:`Stream <Stream>` # Initialize stream objects for stream in stream_manifest: video = Stream( stream=stream, monostate=self.stream_monostate, ) self._fmt_streams.append(video) self.stream_monostate.title = self.title self.stream_monostate.duration = self.length return self._fmt_streams
def test_signature_cipher_does_not_error(stream_dict): config_args = extract.get_ytplayer_config(stream_dict)['args'] extract.apply_descrambler(config_args, "url_encoded_fmt_stream_map") assert "s" in config_args["url_encoded_fmt_stream_map"][0].keys()
def test_signature_cipher_does_not_error(stream_dict): extract.apply_descrambler(stream_dict, "url_encoded_fmt_stream_map") assert "s" in stream_dict["url_encoded_fmt_stream_map"][0].keys()