@@ -85,6 +85,7 @@ public class SubtitlesException extends ContentNotAvailableException {
8585 private JsonObject playerArgs ;
8686 @ Nonnull
8787 private final Map <String , String > videoInfoPage = new HashMap <>();
88+ private JsonObject playerResponse ;
8889
8990 @ Nonnull
9091 private List <SubtitlesInfo > subtitlesInfos = new ArrayList <>();
@@ -253,20 +254,6 @@ public int getAgeLimit() throws ParsingException {
253254 public long getLength () throws ParsingException {
254255 assertPageFetched ();
255256
256- final JsonObject playerResponse ;
257- try {
258- final String pr ;
259- if (playerArgs != null ) {
260- pr = playerArgs .getString ("player_response" );
261- } else {
262- pr = videoInfoPage .get ("player_response" );
263- }
264- playerResponse = JsonParser .object ()
265- .from (pr );
266- } catch (Exception e ) {
267- throw new ParsingException ("Could not get playerResponse" , e );
268- }
269-
270257 // try getting duration from playerargs
271258 try {
272259 String durationMs = playerResponse
@@ -442,31 +429,24 @@ public String getDashMpdUrl() throws ParsingException {
442429 @ Override
443430 public String getHlsUrl () throws ParsingException {
444431 assertPageFetched ();
445- try {
446- String hlsvp = "" ;
447- if (playerArgs != null ) {
448- if ( playerArgs .isString ("hlsvp" ) ) {
449- hlsvp = playerArgs .getString ("hlsvp" , "" );
450- }else {
451- hlsvp = JsonParser .object ()
452- .from (playerArgs .getString ("player_response" , "{}" ))
453- .getObject ("streamingData" , new JsonObject ())
454- .getString ("hlsManifestUrl" , "" );
455- }
456- }
457432
458- return hlsvp ;
433+ try {
434+ return playerResponse .getObject ("streamingData" ).getString ("hlsManifestUrl" );
459435 } catch (Exception e ) {
460- throw new ParsingException ("Could not get hls manifest url" , e );
436+ if (playerArgs != null && playerArgs .isString ("hlsvp" )) {
437+ return playerArgs .getString ("hlsvp" );
438+ } else {
439+ throw new ParsingException ("Could not get hls manifest url" , e );
440+ }
461441 }
462442 }
463443
464444 @ Override
465- public List <AudioStream > getAudioStreams () throws IOException , ExtractionException {
445+ public List <AudioStream > getAudioStreams () throws ExtractionException {
466446 assertPageFetched ();
467447 List <AudioStream > audioStreams = new ArrayList <>();
468448 try {
469- for (Map .Entry <String , ItagItem > entry : getItags (ADAPTIVE_FMTS , ItagItem .ItagType .AUDIO ).entrySet ()) {
449+ for (Map .Entry <String , ItagItem > entry : getItags (ADAPTIVE_FORMATS , ItagItem .ItagType .AUDIO ).entrySet ()) {
470450 ItagItem itag = entry .getValue ();
471451
472452 AudioStream audioStream = new AudioStream (entry .getKey (), itag .getMediaFormat (), itag .avgBitrate );
@@ -482,11 +462,11 @@ public List<AudioStream> getAudioStreams() throws IOException, ExtractionExcepti
482462 }
483463
484464 @ Override
485- public List <VideoStream > getVideoStreams () throws IOException , ExtractionException {
465+ public List <VideoStream > getVideoStreams () throws ExtractionException {
486466 assertPageFetched ();
487467 List <VideoStream > videoStreams = new ArrayList <>();
488468 try {
489- for (Map .Entry <String , ItagItem > entry : getItags (URL_ENCODED_FMT_STREAM_MAP , ItagItem .ItagType .VIDEO ).entrySet ()) {
469+ for (Map .Entry <String , ItagItem > entry : getItags (FORMATS , ItagItem .ItagType .VIDEO ).entrySet ()) {
490470 ItagItem itag = entry .getValue ();
491471
492472 VideoStream videoStream = new VideoStream (entry .getKey (), itag .getMediaFormat (), itag .resolutionString );
@@ -506,7 +486,7 @@ public List<VideoStream> getVideoOnlyStreams() throws ExtractionException {
506486 assertPageFetched ();
507487 List <VideoStream > videoOnlyStreams = new ArrayList <>();
508488 try {
509- for (Map .Entry <String , ItagItem > entry : getItags (ADAPTIVE_FMTS , ItagItem .ItagType .VIDEO_ONLY ).entrySet ()) {
489+ for (Map .Entry <String , ItagItem > entry : getItags (ADAPTIVE_FORMATS , ItagItem .ItagType .VIDEO_ONLY ).entrySet ()) {
510490 ItagItem itag = entry .getValue ();
511491
512492 VideoStream videoStream = new VideoStream (entry .getKey (), itag .getMediaFormat (), itag .resolutionString , true );
@@ -543,7 +523,7 @@ public StreamType getStreamType() throws ParsingException {
543523 assertPageFetched ();
544524 try {
545525 if (playerArgs != null && (playerArgs .has ("ps" ) && playerArgs .get ("ps" ).toString ().equals ("live" ) ||
546- playerArgs . get ( URL_ENCODED_FMT_STREAM_MAP ). toString ( ).isEmpty ())) {
526+ playerResponse . getObject ( "streamingData" ). getArray ( FORMATS ).isEmpty ())) {
547527 return StreamType .LIVE_STREAM ;
548528 }
549529 } catch (Exception e ) {
@@ -619,8 +599,8 @@ public String getErrorMessage() {
619599 // Fetch page
620600 //////////////////////////////////////////////////////////////////////////*/
621601
622- private static final String URL_ENCODED_FMT_STREAM_MAP = "url_encoded_fmt_stream_map " ;
623- private static final String ADAPTIVE_FMTS = "adaptive_fmts " ;
602+ private static final String FORMATS = "formats " ;
603+ private static final String ADAPTIVE_FORMATS = "adaptiveFormats " ;
624604 private static final String HTTPS = "https:" ;
625605 private static final String CONTENT = "content" ;
626606 private static final String DECRYPTION_FUNC_NAME = "decrypt" ;
@@ -667,6 +647,7 @@ public void onFetchPage(@Nonnull Downloader downloader) throws IOException, Extr
667647 playerUrl = getPlayerUrl (ytPlayerConfig );
668648 isAgeRestricted = false ;
669649 }
650+ playerResponse = getPlayerResponse ();
670651
671652 if (decryptionCode .isEmpty ()) {
672653 decryptionCode = loadDecryptionCode (playerUrl );
@@ -728,6 +709,20 @@ private String getPlayerUrl(JsonObject playerConfig) throws ParsingException {
728709 }
729710 }
730711
712+ private JsonObject getPlayerResponse () throws ParsingException {
713+ try {
714+ String playerResponseStr ;
715+ if (playerArgs != null ) {
716+ playerResponseStr = playerArgs .getString ("player_response" );
717+ } else {
718+ playerResponseStr = videoInfoPage .get ("player_response" );
719+ }
720+ return JsonParser .object ().from (playerResponseStr );
721+ } catch (Exception e ) {
722+ throw new ParsingException ("Could not parse yt player response" , e );
723+ }
724+ }
725+
731726 @ Nonnull
732727 private EmbeddedInfo getEmbeddedInfo () throws ParsingException , ReCaptchaException {
733728 try {
@@ -843,19 +838,13 @@ private List<SubtitlesInfo> getAvailableSubtitlesInfo() throws SubtitlesExceptio
843838 } catch (IOException | ExtractionException e ) {
844839 throw new SubtitlesException ("Unable to download player configs" , e );
845840 }
846- final String playerResponse = playerConfig .getObject ("args" , new JsonObject ())
847- .getString ("player_response" );
848841
849842 final JsonObject captions ;
850- try {
851- if (playerResponse == null || !JsonParser .object ().from (playerResponse ).has ("captions" )) {
852- // Captions does not exist
853- return Collections .emptyList ();
854- }
855- captions = JsonParser .object ().from (playerResponse ).getObject ("captions" );
856- } catch (JsonParserException e ) {
857- throw new SubtitlesException ("Unable to parse subtitles listing" , e );
843+ if (!playerResponse .has ("captions" )) {
844+ // Captions does not exist
845+ return Collections .emptyList ();
858846 }
847+ captions = playerResponse .getObject ("captions" );
859848
860849 final JsonObject renderer = captions .getObject ("playerCaptionsTracklistRenderer" , new JsonObject ());
861850 final JsonArray captionsArray = renderer .getArray ("captionTracks" , new JsonArray ());
@@ -924,45 +913,32 @@ private static String getVideoInfoUrl(final String id, final String sts) {
924913 "&sts=" + sts + "&ps=default&gl=US&hl=en" ;
925914 }
926915
927- private Map <String , ItagItem > getItags (String encodedUrlMapKey , ItagItem .ItagType itagTypeWanted ) throws ParsingException {
916+ private Map <String , ItagItem > getItags (String streamingDataKey , ItagItem .ItagType itagTypeWanted ) throws ParsingException {
928917 Map <String , ItagItem > urlAndItags = new LinkedHashMap <>();
929918
930- String encodedUrlMap = "" ;
931- if (playerArgs != null && playerArgs .isString (encodedUrlMapKey )) {
932- encodedUrlMap = playerArgs .getString (encodedUrlMapKey , "" );
933- } else if (videoInfoPage .containsKey (encodedUrlMapKey )) {
934- encodedUrlMap = videoInfoPage .get (encodedUrlMapKey );
935- }
936-
937- for (String url_data_str : encodedUrlMap .split ("," )) {
938- try {
939- // This loop iterates through multiple streams, therefore tags
940- // is related to one and the same stream at a time.
941- Map <String , String > tags = Parser .compatParseMap (
942- org .jsoup .parser .Parser .unescapeEntities (url_data_str , true ));
943-
944- int itag = Integer .parseInt (tags .get ("itag" ));
919+ JsonArray formats = playerResponse .getObject ("streamingData" ).getArray (streamingDataKey );
920+ for (int i = 0 ; i != formats .size (); ++i ) {
921+ JsonObject formatData = formats .getObject (i );
922+ int itag = formatData .getInt ("itag" );
945923
946- if (ItagItem .isSupported (itag )) {
924+ if (ItagItem .isSupported (itag )) {
925+ try {
947926 ItagItem itagItem = ItagItem .getItag (itag );
948927 if (itagItem .itagType == itagTypeWanted ) {
949- String streamUrl = tags .get ("url" );
950- // if video has a signature: decrypt it and add it to the url
951- if (tags .get ("s" ) != null ) {
952- if (tags .get ("sp" ) == null ) {
953- // fallback for urls not conaining the "sp" tag
954- streamUrl = streamUrl + "&signature=" + decryptSignature (tags .get ("s" ), decryptionCode );
955- }
956- else {
957- streamUrl = streamUrl + "&" + tags .get ("sp" ) + "=" + decryptSignature (tags .get ("s" ), decryptionCode );
958- }
928+ String streamUrl ;
929+ if (formatData .has ("url" )) {
930+ streamUrl = formatData .getString ("url" );
931+ } else {
932+ // this url has an encrypted signature
933+ Map <String , String > cipher = Parser .compatParseMap (formatData .getString ("cipher" ));
934+ streamUrl = cipher .get ("url" ) + "&" + cipher .get ("sp" ) + "=" + decryptSignature (cipher .get ("s" ), decryptionCode );
959935 }
936+
960937 urlAndItags .put (streamUrl , itagItem );
961938 }
939+ } catch (UnsupportedEncodingException ignored ) {
940+
962941 }
963- } catch (DecryptException e ) {
964- throw e ;
965- } catch (Exception ignored ) {
966942 }
967943 }
968944
0 commit comments