Skip to content

Commit ec3554a

Browse files
authored
Merge pull request #193 from Stypox/unavailable-video-fix
Fix TeamNewPipe/NewPipe#2615
2 parents 7169bcf + 24a37b8 commit ec3554a

1 file changed

Lines changed: 52 additions & 76 deletions

File tree

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java

Lines changed: 52 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)