Skip to content

Commit 6383c51

Browse files
committed
Add dynamic itag support.
1 parent 22a4151 commit 6383c51

File tree

2 files changed

+84
-42
lines changed

2 files changed

+84
-42
lines changed

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

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

6164
new ItagItem(278, VIDEO_ONLY, WEBM, "144p"),
6265
new ItagItem(242, VIDEO_ONLY, WEBM, "240p"),
@@ -67,27 +70,17 @@ public class ItagItem {
6770
new ItagItem(247, VIDEO_ONLY, WEBM, "720p"),
6871
new ItagItem(248, VIDEO_ONLY, WEBM, "1080p"),
6972
new ItagItem(271, VIDEO_ONLY, WEBM, "1440p"),
70-
// #272 is either 3840x2160 (e.g. RtoitU2A-3E) or 7680x4320 (sLprVF6d7Ug)
71-
new ItagItem(272, VIDEO_ONLY, WEBM, "2160p"),
7273
new ItagItem(302, VIDEO_ONLY, WEBM, "720p60", 60),
7374
new ItagItem(303, VIDEO_ONLY, WEBM, "1080p60", 60),
7475
new ItagItem(308, VIDEO_ONLY, WEBM, "1440p60", 60),
7576
new ItagItem(313, VIDEO_ONLY, WEBM, "2160p"),
76-
new ItagItem(315, VIDEO_ONLY, WEBM, "2160p60", 60)
77+
new ItagItem(315, VIDEO_ONLY, WEBM, "2160p60", 60),
78+
new ItagItem(272, VIDEO_ONLY, WEBM, "4320p60", 60)
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-
9184
public static ItagItem getItag(int itagId) throws ParsingException {
9285
for (ItagItem item : ITAG_LIST) {
9386
if (itagId == item.id) {
@@ -97,6 +90,50 @@ public static ItagItem getItag(int itagId) throws ParsingException {
9790
throw new ParsingException("itag=" + itagId + " not supported");
9891
}
9992

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

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

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,21 @@ public static class DeobfuscateException extends ParsingException {
9898

9999
/*//////////////////////////////////////////////////////////////////////////*/
100100

101-
@Nullable private static String cachedDeobfuscationCode = null;
102-
@Nullable private String playerJsUrl = null;
101+
@Nullable
102+
private static String cachedDeobfuscationCode = null;
103+
@Nullable
104+
private String playerJsUrl = null;
103105

104106
private JsonArray initialAjaxJson;
105107
private JsonObject initialData;
106-
@Nonnull private final Map<String, String> videoInfoPage = new HashMap<>();
108+
@Nonnull
109+
private final Map<String, String> videoInfoPage = new HashMap<>();
107110
private JsonObject playerResponse;
108111
private JsonObject videoPrimaryInfoRenderer;
109112
private JsonObject videoSecondaryInfoRenderer;
110113
private int ageLimit = -1;
111-
@Nullable private List<SubtitlesStream> subtitles = null;
114+
@Nullable
115+
private List<SubtitlesStream> subtitles = null;
112116

113117
public YoutubeStreamExtractor(StreamingService service, LinkHandler linkHandler) {
114118
super(service, linkHandler);
@@ -776,8 +780,6 @@ private String getEmbeddedInfoStsAndStorePlayerJsUrl() {
776780
}
777781

778782

779-
780-
781783
private String getDeobfuscationFuncName(final String playerCode) throws DeobfuscateException {
782784
Parser.RegexException exception = null;
783785
for (final String regex : REGEXES) {
@@ -935,35 +937,38 @@ private Map<String, ItagItem> getItags(final String streamingDataKey,
935937
for (int i = 0; i != formats.size(); ++i) {
936938
JsonObject formatData = formats.getObject(i);
937939
int itag = formatData.getInt("itag");
940+
int averageBitrate = formatData.getInt("averageBitrate");
941+
int fps = formatData.getInt("fps");
942+
String qualityLabel = formatData.getString("qualityLabel");
943+
String mimeType = formatData.getString("mimeType");
938944

939-
if (ItagItem.isSupported(itag)) {
940-
try {
941-
ItagItem itagItem = ItagItem.getItag(itag);
942-
if (itagItem.itagType == itagTypeWanted) {
943-
// Ignore streams that are delivered using YouTube's OTF format,
944-
// as those only work with DASH and not with progressive HTTP.
945-
if (formatData.getString("type", EMPTY_STRING)
946-
.equalsIgnoreCase("FORMAT_STREAM_TYPE_OTF")) {
947-
continue;
948-
}
949945

950-
String streamUrl;
951-
if (formatData.has("url")) {
952-
streamUrl = formatData.getString("url");
953-
} else {
954-
// this url has an obfuscated signature
955-
final String cipherString = formatData.has("cipher")
956-
? formatData.getString("cipher")
957-
: formatData.getString("signatureCipher");
958-
final Map<String, String> cipher = Parser.compatParseMap(cipherString);
959-
streamUrl = cipher.get("url") + "&" + cipher.get("sp") + "="
960-
+ deobfuscateSignature(cipher.get("s"));
961-
}
946+
try {
947+
ItagItem itagItem = ItagItem.getItag(itag, averageBitrate, fps, qualityLabel, mimeType);
948+
if (itagItem.itagType == itagTypeWanted) {
949+
// Ignore streams that are delivered using YouTube's OTF format,
950+
// as those only work with DASH and not with progressive HTTP.
951+
if (formatData.getString("type", EMPTY_STRING)
952+
.equalsIgnoreCase("FORMAT_STREAM_TYPE_OTF")) {
953+
continue;
954+
}
962955

963-
urlAndItags.put(streamUrl, itagItem);
956+
String streamUrl;
957+
if (formatData.has("url")) {
958+
streamUrl = formatData.getString("url");
959+
} else {
960+
// this url has an obfuscated signature
961+
final String cipherString = formatData.has("cipher")
962+
? formatData.getString("cipher")
963+
: formatData.getString("signatureCipher");
964+
final Map<String, String> cipher = Parser.compatParseMap(cipherString);
965+
streamUrl = cipher.get("url") + "&" + cipher.get("sp") + "="
966+
+ deobfuscateSignature(cipher.get("s"));
964967
}
965-
} catch (UnsupportedEncodingException ignored) {
968+
969+
urlAndItags.put(streamUrl, itagItem);
966970
}
971+
} catch (UnsupportedEncodingException ignored) {
967972
}
968973
}
969974

0 commit comments

Comments
 (0)