33import com .grack .nanojson .JsonArray ;
44import com .grack .nanojson .JsonObject ;
55import com .grack .nanojson .JsonParser ;
6+ import com .grack .nanojson .JsonParserException ;
67
7- import org .jsoup .nodes .Document ;
8- import org .jsoup .nodes .Element ;
98import org .mozilla .javascript .Context ;
109import org .mozilla .javascript .Function ;
1110import org .mozilla .javascript .ScriptableObject ;
1211import org .schabi .newpipe .extractor .MediaFormat ;
1312import org .schabi .newpipe .extractor .NewPipe ;
1413import org .schabi .newpipe .extractor .StreamingService ;
1514import org .schabi .newpipe .extractor .downloader .Downloader ;
16- import org .schabi .newpipe .extractor .downloader .Response ;
17- import org .schabi .newpipe .extractor .exceptions .ContentNotAvailableException ;
1815import org .schabi .newpipe .extractor .exceptions .ExtractionException ;
1916import org .schabi .newpipe .extractor .exceptions .ParsingException ;
2017import org .schabi .newpipe .extractor .exceptions .ReCaptchaException ;
@@ -91,7 +88,7 @@ public class DecryptException extends ParsingException {
9188
9289 /*//////////////////////////////////////////////////////////////////////////*/
9390
94- private Document doc ;
91+ private JsonArray initialAjaxJson ;
9592 @ Nullable
9693 private JsonObject playerArgs ;
9794 @ Nonnull
@@ -554,23 +551,8 @@ public StreamInfoItemsCollector getRelatedStreams() throws ExtractionException {
554551 */
555552 @ Override
556553 public String getErrorMessage () {
557- StringBuilder errorReason ;
558- Element errorElement = doc .select ("h1[id=\" unavailable-message\" ]" ).first ();
559-
560- if (errorElement == null ) {
561- errorReason = null ;
562- } else {
563- String errorMessage = errorElement .text ();
564- if (errorMessage == null || errorMessage .isEmpty ()) {
565- errorReason = null ;
566- } else {
567- errorReason = new StringBuilder (errorMessage );
568- errorReason .append (" " );
569- errorReason .append (doc .select ("[id=\" unavailable-submessage\" ]" ).first ().text ());
570- }
571- }
572-
573- return errorReason != null ? errorReason .toString () : "" ;
554+ return getTextFromObject (initialAjaxJson .getObject (2 ).getObject ("playerResponse" ).getObject ("playabilityStatus" )
555+ .getObject ("errorScreen" ).getObject ("playerErrorMessageRenderer" ).getObject ("reason" ));
574556 }
575557
576558 /*//////////////////////////////////////////////////////////////////////////
@@ -580,11 +562,8 @@ public String getErrorMessage() {
580562 private static final String FORMATS = "formats" ;
581563 private static final String ADAPTIVE_FORMATS = "adaptiveFormats" ;
582564 private static final String HTTPS = "https:" ;
583- private static final String CONTENT = "content" ;
584565 private static final String DECRYPTION_FUNC_NAME = "decrypt" ;
585566
586- private static final String VERIFIED_URL_PARAMS = "&has_verified=1&bpctr=9999999999" ;
587-
588567 private final static String DECRYPTION_SIGNATURE_FUNCTION_REGEX =
589568 "([\\ w$]+)\\ s*=\\ s*function\\ ((\\ w+)\\ )\\ {\\ s*\\ 2=\\ s*\\ 2\\ .split\\ (\" \" \\ )\\ s*;" ;
590569 private final static String DECRYPTION_SIGNATURE_FUNCTION_REGEX_2 =
@@ -596,29 +575,42 @@ public String getErrorMessage() {
596575
597576 private volatile String decryptionCode = "" ;
598577
599- private String pageHtml = null ;
600-
601578 @ Override
602579 public void onFetchPage (@ Nonnull Downloader downloader ) throws IOException , ExtractionException {
603- final String verifiedUrl = getUrl () + VERIFIED_URL_PARAMS ;
604- final Response response = downloader .get (verifiedUrl , getExtractorLocalization ());
605- pageHtml = response .responseBody ();
606- doc = YoutubeParsingHelper .parseAndCheckPage (verifiedUrl , response );
580+ final String url = getUrl () + "&pbj=1" ;
581+
582+ Map <String , List <String >> headers = new HashMap <>();
583+ headers .put ("X-YouTube-Client-Name" , Collections .singletonList ("1" ));
584+ headers .put ("X-YouTube-Client-Version" ,
585+ Collections .singletonList (YoutubeParsingHelper .getClientVersion ()));
586+ final String response = getDownloader ().get (url , headers , getExtractorLocalization ()).responseBody ();
587+ if (response .length () < 50 ) { // ensure to have a valid response
588+ throw new ParsingException ("Could not parse json data for next streams" );
589+ }
590+
591+ try {
592+ initialAjaxJson = JsonParser .array ().from (response );
593+ } catch (JsonParserException e ) {
594+ throw new ParsingException ("Could not parse json data for next streams" , e );
595+ }
607596
608597 final String playerUrl ;
609- initialData = YoutubeParsingHelper .getInitialData (pageHtml );
610- // Check if the video is age restricted
611- if (getAgeLimit () == 18 ) {
598+
599+ if (initialAjaxJson .getObject (2 ).getObject ("response" ) != null ) { // age-restricted videos
600+ initialData = initialAjaxJson .getObject (2 ).getObject ("response" );
601+
612602 final EmbeddedInfo info = getEmbeddedInfo ();
613603 final String videoInfoUrl = getVideoInfoUrl (getId (), info .sts );
614604 final String infoPageResponse = downloader .get (videoInfoUrl , getExtractorLocalization ()).responseBody ();
615605 videoInfoPage .putAll (Parser .compatParseMap (infoPageResponse ));
616606 playerUrl = info .url ;
617607 } else {
618- final JsonObject ytPlayerConfig = getPlayerConfig ();
619- playerArgs = getPlayerArgs (ytPlayerConfig );
620- playerUrl = getPlayerUrl (ytPlayerConfig );
608+ initialData = initialAjaxJson .getObject (3 ).getObject ("response" );
609+
610+ playerArgs = getPlayerArgs (initialAjaxJson .getObject (2 ).getObject ("player" ));
611+ playerUrl = getPlayerUrl (initialAjaxJson .getObject (2 ).getObject ("player" ));
621612 }
613+
622614 playerResponse = getPlayerResponse ();
623615
624616 if (decryptionCode .isEmpty ()) {
@@ -630,21 +622,6 @@ public void onFetchPage(@Nonnull Downloader downloader) throws IOException, Extr
630622 }
631623 }
632624
633- private JsonObject getPlayerConfig () throws ParsingException {
634- try {
635- String ytPlayerConfigRaw = Parser .matchGroup1 ("ytplayer.config\\ s*=\\ s*(\\ {.*?\\ });" , pageHtml );
636- return JsonParser .object ().from (ytPlayerConfigRaw );
637- } catch (Parser .RegexException e ) {
638- String errorReason = getErrorMessage ();
639- if (errorReason .isEmpty ()) {
640- throw new ContentNotAvailableException ("Content not available: player config empty" , e );
641- }
642- throw new ContentNotAvailableException ("Content not available" , e );
643- } catch (Exception e ) {
644- throw new ParsingException ("Could not parse yt player config" , e );
645- }
646- }
647-
648625 private JsonObject getPlayerArgs (JsonObject playerConfig ) throws ParsingException {
649626 JsonObject playerArgs ;
650627
@@ -950,17 +927,7 @@ private Map<String, ItagItem> getItags(String streamingDataKey, ItagItem.ItagTyp
950927 @ Override
951928 public List <Frameset > getFrames () throws ExtractionException {
952929 try {
953- final String script = doc .select ("#player-api" ).first ().siblingElements ().select ("script" ).html ();
954- int p = script .indexOf ("ytplayer.config" );
955- if (p == -1 ) {
956- return Collections .emptyList ();
957- }
958- p = script .indexOf ('{' , p );
959- int e = script .indexOf ("ytplayer.load" , p );
960- if (e == -1 ) {
961- return Collections .emptyList ();
962- }
963- JsonObject jo = JsonParser .object ().from (script .substring (p , e - 1 ));
930+ JsonObject jo = initialAjaxJson .getObject (2 ).getObject ("player" );
964931 final String resp = jo .getObject ("args" ).getString ("player_response" );
965932 jo = JsonParser .object ().from (resp );
966933 final String [] spec = jo .getObject ("storyboards" ).getObject ("playerStoryboardSpecRenderer" ).getString ("spec" ).split ("\\ |" );
0 commit comments