Skip to content

Commit f9e05b9

Browse files
committed
Add dynamic itag support.
1 parent 78f2192 commit f9e05b9

File tree

2 files changed

+105
-64
lines changed

2 files changed

+105
-64
lines changed

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public class ItagItem {
5656
new ItagItem(137, VIDEO_ONLY, MPEG_4, "1080p"),
5757
new ItagItem(299, VIDEO_ONLY, MPEG_4, "1080p60", 60),
5858
new ItagItem(266, VIDEO_ONLY, MPEG_4, "2160p"),
59+
new ItagItem(402, VIDEO_ONLY, MPEG_4, "4320p"), // can be 4320p60 as well
60+
new ItagItem(571, VIDEO_ONLY, MPEG_4, "4320p"), // can be 4320p60 HDR as well (1La4QzGeaaQ)
61+
new ItagItem(402, VIDEO_ONLY, MPEG_4, "4320p60"),
5962

6063
new ItagItem(278, VIDEO_ONLY, WEBM, "144p"),
6164
new ItagItem(242, VIDEO_ONLY, WEBM, "240p"),
@@ -66,28 +69,19 @@ public class ItagItem {
6669
new ItagItem(247, VIDEO_ONLY, WEBM, "720p"),
6770
new ItagItem(248, VIDEO_ONLY, WEBM, "1080p"),
6871
new ItagItem(271, VIDEO_ONLY, WEBM, "1440p"),
69-
// #272 is either 3840x2160 (e.g. RtoitU2A-3E) or 7680x4320 (sLprVF6d7Ug)
70-
new ItagItem(272, VIDEO_ONLY, WEBM, "2160p"),
7172
new ItagItem(302, VIDEO_ONLY, WEBM, "720p60", 60),
7273
new ItagItem(303, VIDEO_ONLY, WEBM, "1080p60", 60),
7374
new ItagItem(308, VIDEO_ONLY, WEBM, "1440p60", 60),
7475
new ItagItem(313, VIDEO_ONLY, WEBM, "2160p"),
75-
new ItagItem(315, VIDEO_ONLY, WEBM, "2160p60", 60)
76+
new ItagItem(315, VIDEO_ONLY, WEBM, "2160p60", 60),
77+
new ItagItem(272, VIDEO_ONLY, WEBM, "4320p60", 60)
7678
};
7779

7880
/*//////////////////////////////////////////////////////////////////////////
7981
// Utils
8082
//////////////////////////////////////////////////////////////////////////*/
8183

82-
public static boolean isSupported(int itag) {
83-
for (ItagItem item : ITAG_LIST) {
84-
if (itag == item.id) {
85-
return true;
86-
}
87-
}
88-
return false;
89-
}
90-
84+
@Deprecated
9185
public static ItagItem getItag(int itagId) throws ParsingException {
9286
for (ItagItem item : ITAG_LIST) {
9387
if (itagId == item.id) {
@@ -97,6 +91,52 @@ public static ItagItem getItag(int itagId) throws ParsingException {
9791
throw new ParsingException("itag=" + itagId + " not supported");
9892
}
9993

94+
public static ItagItem getItag(int itagId, int averageBitrate, int fps, String qualityLabel, String mimeType) throws ParsingException {
95+
96+
String[] split = mimeType.split(";")[0].split("/");
97+
String streamType = split[0];
98+
String fileType = split[1];
99+
String codec = mimeType.split("\"")[1];
100+
101+
MediaFormat format = null;
102+
ItagType itagType = null;
103+
104+
if (codec.contains(",")) // muxed streams have both an audio and video codec
105+
itagType = VIDEO;
106+
else {
107+
if (streamType.equals("video"))
108+
itagType = VIDEO_ONLY;
109+
if (streamType.equals("audio"))
110+
itagType = AUDIO;
111+
}
112+
113+
if (itagType == AUDIO) {
114+
if (fileType.equals("mp4") && (codec.startsWith("m4a") || codec.startsWith("mp4a") ))
115+
format = M4A;
116+
if (fileType.startsWith("webm") && codec.equals("opus"))
117+
format = WEBMA_OPUS;
118+
}
119+
120+
if (itagType == VIDEO) {
121+
if (fileType.equals("mp4"))
122+
format = MPEG_4;
123+
if(fileType.equals("3gpp"))
124+
format = v3GPP;
125+
}
126+
127+
if (itagType == VIDEO_ONLY) {
128+
if (fileType.equals("mp4"))
129+
format = MPEG_4;
130+
if (fileType.equals("webm"))
131+
format = WEBM;
132+
}
133+
134+
if (itagType == null || format == null)
135+
throw new ParsingException("Unknown mimeType: " + mimeType);
136+
137+
return itagType == AUDIO ? new ItagItem(itagId, itagType, format, Math.round(averageBitrate / 1024f)) : new ItagItem(itagId, itagType, format, qualityLabel, fps);
138+
}
139+
100140
/*//////////////////////////////////////////////////////////////////////////
101141
// Contructors and misc
102142
//////////////////////////////////////////////////////////////////////////*/

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

Lines changed: 53 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -834,8 +834,8 @@ private void fetchAndroidMobileJsonPlayer(final ContentCountry contentCountry,
834834
throws IOException, ExtractionException {
835835
final byte[] mobileBody = JsonWriter.string(prepareAndroidMobileJsonBuilder(
836836
localization, contentCountry)
837-
.value("videoId", videoId)
838-
.done())
837+
.value("videoId", videoId)
838+
.done())
839839
.getBytes(UTF_8);
840840
final JsonObject mobilePlayerResponse = getJsonMobilePostResponse("player",
841841
mobileBody, contentCountry, localization);
@@ -1126,59 +1126,60 @@ private Map<String, ItagItem> getStreamsFromStreamingDataKey(
11261126
if (streamingData != null && streamingData.has(streamingDataKey)) {
11271127
final JsonArray formats = streamingData.getArray(streamingDataKey);
11281128
for (int i = 0; i != formats.size(); ++i) {
1129-
JsonObject formatData = formats.getObject(i);
1130-
int itag = formatData.getInt("itag");
1131-
1132-
if (ItagItem.isSupported(itag)) {
1133-
try {
1134-
final ItagItem itagItem = ItagItem.getItag(itag);
1135-
if (itagItem.itagType == itagTypeWanted) {
1136-
// Ignore streams that are delivered using YouTube's OTF format,
1137-
// as those only work with DASH and not with progressive HTTP.
1138-
if (formatData.getString("type", EMPTY_STRING)
1139-
.equalsIgnoreCase("FORMAT_STREAM_TYPE_OTF")) {
1140-
continue;
1141-
}
1142-
1143-
final String streamUrl;
1144-
if (formatData.has("url")) {
1145-
streamUrl = formatData.getString("url");
1146-
} else {
1147-
// This url has an obfuscated signature
1148-
final String cipherString = formatData.has("cipher")
1149-
? formatData.getString("cipher")
1150-
: formatData.getString("signatureCipher");
1151-
final Map<String, String> cipher = Parser.compatParseMap(
1152-
cipherString);
1153-
streamUrl = cipher.get("url") + "&" + cipher.get("sp") + "="
1154-
+ deobfuscateSignature(cipher.get("s"));
1155-
}
1129+
final JsonObject formatData = formats.getObject(i);
1130+
final int itag = formatData.getInt("itag");
1131+
final int averageBitrate = formatData.getInt("averageBitrate");
1132+
final int fps = formatData.getInt("fps");
1133+
final String qualityLabel = formatData.getString("qualityLabel");
1134+
final String mimeType = formatData.getString("mimeType", EMPTY_STRING);
1135+
1136+
try {
1137+
final ItagItem itagItem = ItagItem.getItag(itag, averageBitrate, fps, qualityLabel, mimeType);
1138+
if (itagItem.itagType == itagTypeWanted) {
1139+
// Ignore streams that are delivered using YouTube's OTF format,
1140+
// as those only work with DASH and not with progressive HTTP.
1141+
if (formatData.getString("type", EMPTY_STRING)
1142+
.equalsIgnoreCase("FORMAT_STREAM_TYPE_OTF")) {
1143+
continue;
1144+
}
11561145

1157-
final JsonObject initRange = formatData.getObject("initRange");
1158-
final JsonObject indexRange = formatData.getObject("indexRange");
1159-
final String mimeType = formatData.getString("mimeType", EMPTY_STRING);
1160-
final String codec = mimeType.contains("codecs")
1161-
? mimeType.split("\"")[1] : EMPTY_STRING;
1162-
1163-
itagItem.setBitrate(formatData.getInt("bitrate"));
1164-
itagItem.setWidth(formatData.getInt("width"));
1165-
itagItem.setHeight(formatData.getInt("height"));
1166-
itagItem.setInitStart(Integer.parseInt(initRange.getString("start",
1167-
"-1")));
1168-
itagItem.setInitEnd(Integer.parseInt(initRange.getString("end",
1169-
"-1")));
1170-
itagItem.setIndexStart(Integer.parseInt(indexRange.getString("start",
1171-
"-1")));
1172-
itagItem.setIndexEnd(Integer.parseInt(indexRange.getString("end",
1173-
"-1")));
1174-
itagItem.fps = formatData.getInt("fps");
1175-
itagItem.setQuality(formatData.getString("quality"));
1176-
itagItem.setCodec(codec);
1177-
1178-
urlAndItagsFromStreamingDataObject.put(streamUrl, itagItem);
1146+
final String streamUrl;
1147+
if (formatData.has("url")) {
1148+
streamUrl = formatData.getString("url");
1149+
} else {
1150+
// This url has an obfuscated signature
1151+
final String cipherString = formatData.has("cipher")
1152+
? formatData.getString("cipher")
1153+
: formatData.getString("signatureCipher");
1154+
final Map<String, String> cipher = Parser.compatParseMap(
1155+
cipherString);
1156+
streamUrl = cipher.get("url") + "&" + cipher.get("sp") + "="
1157+
+ deobfuscateSignature(cipher.get("s"));
11791158
}
1180-
} catch (final UnsupportedEncodingException | ParsingException ignored) {
1159+
1160+
final JsonObject initRange = formatData.getObject("initRange");
1161+
final JsonObject indexRange = formatData.getObject("indexRange");
1162+
final String codec = mimeType.contains("codecs")
1163+
? mimeType.split("\"")[1] : EMPTY_STRING;
1164+
1165+
itagItem.setBitrate(formatData.getInt("bitrate"));
1166+
itagItem.setWidth(formatData.getInt("width"));
1167+
itagItem.setHeight(formatData.getInt("height"));
1168+
itagItem.setInitStart(Integer.parseInt(initRange.getString("start",
1169+
"-1")));
1170+
itagItem.setInitEnd(Integer.parseInt(initRange.getString("end",
1171+
"-1")));
1172+
itagItem.setIndexStart(Integer.parseInt(indexRange.getString("start",
1173+
"-1")));
1174+
itagItem.setIndexEnd(Integer.parseInt(indexRange.getString("end",
1175+
"-1")));
1176+
itagItem.fps = formatData.getInt("fps");
1177+
itagItem.setQuality(formatData.getString("quality"));
1178+
itagItem.setCodec(codec);
1179+
1180+
urlAndItagsFromStreamingDataObject.put(streamUrl, itagItem);
11811181
}
1182+
} catch (final UnsupportedEncodingException | ParsingException ignored) {
11821183
}
11831184
}
11841185
}

0 commit comments

Comments
 (0)