33import com .grack .nanojson .JsonArray ;
44import com .grack .nanojson .JsonObject ;
55import com .grack .nanojson .JsonParser ;
6+ import org .jsoup .Jsoup ;
7+ import org .jsoup .nodes .Document ;
8+ import org .jsoup .nodes .Element ;
9+ import org .jsoup .select .Elements ;
610import org .mozilla .javascript .Context ;
711import org .mozilla .javascript .Function ;
812import org .mozilla .javascript .ScriptableObject ;
@@ -100,6 +104,7 @@ public class DeobfuscateException extends ParsingException {
100104 private JsonObject videoPrimaryInfoRenderer ;
101105 private JsonObject videoSecondaryInfoRenderer ;
102106 private int ageLimit ;
107+ private boolean newJsonScheme ;
103108
104109 @ Nonnull
105110 private List <SubtitlesInfo > subtitlesInfos = new ArrayList <>();
@@ -664,27 +669,23 @@ public void onFetchPage(@Nonnull Downloader downloader) throws IOException, Extr
664669 } else {
665670 ageLimit = NO_AGE_LIMIT ;
666671 JsonObject playerConfig ;
672+ initialData = initialAjaxJson .getObject (3 ).getObject ("response" );
667673
668- // sometimes at random YouTube does not provide the player config,
669- // so just retry the same request three times
670- int attempts = 2 ;
671- while (true ) {
672- playerConfig = initialAjaxJson .getObject (2 ).getObject ("player" , null );
673- if (playerConfig != null ) {
674- break ;
675- }
674+ // sometimes at random YouTube does not provide the player config
675+ playerConfig = initialAjaxJson .getObject (2 ).getObject ("player" , null );
676676
677- if (attempts <= 0 ) {
678- throw new ParsingException (
679- "YouTube did not provide player config even after three attempts" );
680- }
681- initialAjaxJson = getJsonResponse (url , getExtractorLocalization ());
682- --attempts ;
677+ if (playerConfig == null ) {
678+ newJsonScheme = true ;
679+ final EmbeddedInfo info = getEmbeddedInfo ();
680+ final String videoInfoUrl = getVideoInfoUrl (getId (), info .sts );
681+ final String infoPageResponse = downloader .get (videoInfoUrl , getExtractorLocalization ()).responseBody ();
682+ videoInfoPage .putAll (Parser .compatParseMap (infoPageResponse ));
683+ playerUrl = info .url ;
684+ } else {
685+ playerArgs = getPlayerArgs (playerConfig );
686+ playerUrl = getPlayerUrl (playerConfig );
683687 }
684- initialData = initialAjaxJson .getObject (3 ).getObject ("response" );
685688
686- playerArgs = getPlayerArgs (playerConfig );
687- playerUrl = getPlayerUrl (playerConfig );
688689 }
689690
690691 playerResponse = getPlayerResponse ();
@@ -732,6 +733,10 @@ private String getPlayerUrl(final JsonObject playerConfig) throws ParsingExcepti
732733 private JsonObject getPlayerResponse () throws ParsingException {
733734 try {
734735 String playerResponseStr ;
736+ if (newJsonScheme ) {
737+ return initialAjaxJson .getObject (2 ).getObject ("playerResponse" );
738+ }
739+
735740 if (playerArgs != null ) {
736741 playerResponseStr = playerArgs .getString ("player_response" );
737742 } else {
@@ -751,11 +756,30 @@ private EmbeddedInfo getEmbeddedInfo() throws ParsingException, ReCaptchaExcepti
751756 final String embedPageContent = downloader .get (embedUrl , getExtractorLocalization ()).responseBody ();
752757
753758 // Get player url
754- final String assetsPattern = "\" assets\" :.+?\" js\" :\\ s*(\" [^\" ]+\" )" ;
755- String playerUrl = Parser .matchGroup1 (assetsPattern , embedPageContent )
756- .replace ("\\ " , "" ).replace ("\" " , "" );
759+ String playerUrl = null ;
760+ try {
761+ final String assetsPattern = "\" assets\" :.+?\" js\" :\\ s*(\" [^\" ]+\" )" ;
762+ playerUrl = Parser .matchGroup1 (assetsPattern , embedPageContent )
763+ .replace ("\\ " , "" ).replace ("\" " , "" );
764+ } catch (Parser .RegexException ex ) {
765+ // playerUrl is still available in the file, just somewhere else
766+ final Document doc = Jsoup .parse (embedPageContent );
767+ final Elements elems = doc .select ("script" ).attr ("name" , "player_ias/base" );
768+ for (Element elem : elems ) {
769+ if (elem .attr ("src" ).contains ("base.js" )) {
770+ playerUrl = elem .attr ("src" );
771+ }
772+ }
773+
774+ if (playerUrl == null ) {
775+ throw new ParsingException ("Could not get playerUrl" );
776+ }
777+ }
778+
757779 if (playerUrl .startsWith ("//" )) {
758780 playerUrl = HTTPS + playerUrl ;
781+ } else if (playerUrl .startsWith ("/" )) {
782+ playerUrl = HTTPS + "//youtube.com" + playerUrl ;
759783 }
760784
761785 try {
0 commit comments