2525import org .schabi .newpipe .extractor .localization .TimeAgoPatternsManager ;
2626import org .schabi .newpipe .extractor .services .youtube .ItagItem ;
2727import org .schabi .newpipe .extractor .services .youtube .YoutubeParsingHelper ;
28+ import org .schabi .newpipe .extractor .services .youtube .YoutubeThrottlingDecoder ;
2829import org .schabi .newpipe .extractor .services .youtube .linkHandler .YoutubeChannelLinkHandlerFactory ;
2930import org .schabi .newpipe .extractor .stream .*;
3031import org .schabi .newpipe .extractor .utils .JsonUtils ;
3940import java .time .OffsetDateTime ;
4041import java .time .format .DateTimeFormatter ;
4142import java .util .*;
42- import java .util .regex .Pattern ;
4343
4444import static org .schabi .newpipe .extractor .services .youtube .YoutubeParsingHelper .*;
4545import static org .schabi .newpipe .extractor .utils .Utils .EMPTY_STRING ;
@@ -80,13 +80,10 @@ public static class DeobfuscateException extends ParsingException {
8080
8181 @ Nullable
8282 private static String cachedDeobfuscationCode = null ;
83- @ Nullable
84- private String playerJsUrl = null ;
85-
86- private JsonArray initialAjaxJson ;
87- private JsonObject initialData ;
8883 @ Nonnull
8984 private final Map <String , String > videoInfoPage = new HashMap <>();
85+ private JsonArray initialAjaxJson ;
86+ private JsonObject initialData ;
9087 private JsonObject playerResponse ;
9188 private JsonObject videoPrimaryInfoRenderer ;
9289 private JsonObject videoSecondaryInfoRenderer ;
@@ -526,32 +523,18 @@ public List<AudioStream> getAudioStreams() throws ExtractionException {
526523 public List <VideoStream > getVideoStreams () throws ExtractionException {
527524 assertPageFetched ();
528525 final List <VideoStream > videoStreams = new ArrayList <>();
526+ YoutubeThrottlingDecoder throttlingDecoder = new YoutubeThrottlingDecoder (getId (), getExtractorLocalization ());
529527
530528 try {
531- getDeobfuscationCode ();
532- final String playerCode = NewPipe .getDownloader ()
533- .get (playerJsUrl , getExtractorLocalization ()).responseBody ();
534- Pattern pattern = Pattern .compile ("b=a\\ .get\\ (\" n\" \\ )\\ )&&\\ (b=(\\ w+)\\ (b\\ ),a\\ .set\\ (\" n\" ,b\\ )" );
535- String functionName = Parser .matchGroup1 (pattern , playerCode );
536- Pattern functionPattern = Pattern .compile (functionName + "=function(.*?;)\n " , Pattern .DOTALL );
537- String function = "function " + functionName + Parser .matchGroup1 (functionPattern , playerCode );
538-
539- Context context = Context .enter ();
540- context .setOptimizationLevel (-1 );
541- ScriptableObject scope = context .initSafeStandardObjects ();
542-
543529 for (final Map .Entry <String , ItagItem > entry : getItags (FORMATS , ItagItem .ItagType .VIDEO ).entrySet ()) {
544530 final ItagItem itag = entry .getValue ();
545531 final String url = entry .getKey ();
546- Pattern nValuePattern = Pattern .compile ("[&?]n=([^&]+)" );
547- String nValue = Parser .matchGroup1 (nValuePattern , url );
548-
549- context .evaluateString (scope , function , functionName , 1 , null );
550- final Function jsFunction = (Function ) scope .get (functionName , scope );
551- Object result = jsFunction .call (context , scope , scope , new Object []{nValue });
552- String newNValue = Objects .toString (result , nValue );
553- String newUrl = nValuePattern .matcher (url ).replaceFirst (newNValue );
554- System .out .println ("aaaaaa " + nValue + " - " + newNValue );
532+
533+ String oldNParam = throttlingDecoder .parseNParam (url );
534+ String newNParam = throttlingDecoder .decodeNParam (oldNParam );
535+ String newUrl = throttlingDecoder .replaceNParam (url , newNParam );
536+
537+ System .out .println ("aaaaaa " + oldNParam + " - " + newNParam );
555538 final VideoStream videoStream = new VideoStream (newUrl , false , itag );
556539 if (!Stream .containSimilarStream (videoStream , videoStreams )) {
557540 videoStreams .add (videoStream );
@@ -820,16 +803,15 @@ private void fetchVideoInfoPage() throws ParsingException, ReCaptchaException, I
820803 }
821804 }
822805
823- @ Nonnull
824- private String getEmbeddedInfoStsAndStorePlayerJsUrl () {
806+ private String extractPlayerJsUrl () throws ParsingException {
825807 try {
826808 final String embedUrl = "https://www.youtube.com/embed/" + getId ();
827809 final String embedPageContent = NewPipe .getDownloader ()
828810 .get (embedUrl , getExtractorLocalization ()).responseBody ();
829811
830812 try {
831813 final String assetsPattern = "\" assets\" :.+?\" js\" :\\ s*(\" [^\" ]+\" )" ;
832- playerJsUrl = Parser .matchGroup1 (assetsPattern , embedPageContent )
814+ return Parser .matchGroup1 (assetsPattern , embedPageContent )
833815 .replace ("\\ " , "" ).replace ("\" " , "" );
834816 } catch (final Parser .RegexException ex ) {
835817 // playerJsUrl is still available in the file, just somewhere else TODO
@@ -838,17 +820,25 @@ private String getEmbeddedInfoStsAndStorePlayerJsUrl() {
838820 final Elements elems = doc .select ("script" ).attr ("name" , "player_ias/base" );
839821 for (final Element elem : elems ) {
840822 if (elem .attr ("src" ).contains ("base.js" )) {
841- playerJsUrl = elem .attr ("src" );
842- break ;
823+ return elem .attr ("src" );
843824 }
844825 }
845826 }
846827
847- // Get embed sts
848- return Parser .matchGroup1 ("\" sts\" \\ s*:\\ s*(\\ d+)" , embedPageContent );
849828 } catch (final Exception i ) {
850- // if it fails we simply reply with no sts as then it does not seem to be necessary
851- return "" ;
829+ throw new ParsingException ("Embedded info did not provide YouTube player js url" );
830+ }
831+ throw new ParsingException ("Embedded info did not provide YouTube player js url" );
832+ }
833+
834+ private String cleanPlayerJsUrl (String playerJsUrl ) {
835+ if (playerJsUrl .startsWith ("//" )) {
836+ return HTTPS + playerJsUrl ;
837+ } else if (playerJsUrl .startsWith ("/" )) {
838+ // sometimes https://www.youtube.com part has to be added manually
839+ return HTTPS + "//www.youtube.com" + playerJsUrl ;
840+ } else {
841+ return playerJsUrl ;
852842 }
853843 }
854844
@@ -899,22 +889,7 @@ private String loadDeobfuscationCode(@Nonnull final String playerJsUrl)
899889 @ Nonnull
900890 private String getDeobfuscationCode () throws ParsingException {
901891 if (cachedDeobfuscationCode == null ) {
902- if (playerJsUrl == null ) {
903- // the currentPlayerJsUrl was not found in any page fetched so far and there is
904- // nothing cached, so try fetching embedded info
905- getEmbeddedInfoStsAndStorePlayerJsUrl ();
906- if (playerJsUrl == null ) {
907- throw new ParsingException (
908- "Embedded info did not provide YouTube player js url" );
909- }
910- }
911-
912- if (playerJsUrl .startsWith ("//" )) {
913- playerJsUrl = HTTPS + playerJsUrl ;
914- } else if (playerJsUrl .startsWith ("/" )) {
915- // sometimes https://www.youtube.com part has to be added manually
916- playerJsUrl = HTTPS + "//www.youtube.com" + playerJsUrl ;
917- }
892+ String playerJsUrl = cleanPlayerJsUrl (extractPlayerJsUrl ());
918893
919894 cachedDeobfuscationCode = loadDeobfuscationCode (playerJsUrl );
920895 }
0 commit comments