Skip to content

Commit fbb6a03

Browse files
committed
Add resolution support up to 4k
1 parent b587d17 commit fbb6a03

5 files changed

Lines changed: 148 additions & 52 deletions

File tree

DashMpdParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public static List<AudioStream> getAudioStreams(String dashManifestUrl)
8181
} else if(memeType.equals(MediaFormat.M4A.mimeType)) {
8282
format = MediaFormat.M4A.id;
8383
}
84-
audioStreams.add(new AudioStream(url, format, bandwidth, samplingRate));
84+
audioStreams.add(new AudioStream(url, format, 0, bandwidth, samplingRate));
8585
}
8686
}
8787
}

services/youtube/YoutubePlayListUrlIdHandler.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.schabi.newpipe.extractor.services.youtube;
22

3-
import android.net.UrlQuerySanitizer;
43

54
import org.schabi.newpipe.extractor.Parser;
65
import org.schabi.newpipe.extractor.UrlIdHandler;

services/youtube/YoutubeStreamExtractor.java

Lines changed: 119 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
import org.mozilla.javascript.Function;
1010
import org.mozilla.javascript.ScriptableObject;
1111
import org.schabi.newpipe.extractor.AbstractStreamInfo;
12-
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
13-
import org.schabi.newpipe.extractor.exceptions.ParsingException;
14-
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
15-
import org.schabi.newpipe.extractor.stream_info.AudioStream;
1612
import org.schabi.newpipe.extractor.Downloader;
13+
import org.schabi.newpipe.extractor.MediaFormat;
1714
import org.schabi.newpipe.extractor.NewPipe;
1815
import org.schabi.newpipe.extractor.Parser;
1916
import org.schabi.newpipe.extractor.UrlIdHandler;
20-
import org.schabi.newpipe.extractor.MediaFormat;
17+
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
18+
import org.schabi.newpipe.extractor.exceptions.ParsingException;
19+
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
20+
import org.schabi.newpipe.extractor.stream_info.AudioStream;
2121
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
2222
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
2323
import org.schabi.newpipe.extractor.stream_info.StreamInfoItemCollector;
@@ -105,49 +105,84 @@ public ItagItem(int id, ItagType type, MediaFormat format, String res, int fps)
105105
this.resolutionString = res;
106106
this.fps = fps;
107107
}
108+
108109
public ItagItem(int id, ItagType type, MediaFormat format, int samplingRate, int bandWidth) {
110+
this(id, type, format, 0, samplingRate, bandWidth);
111+
}
112+
113+
public ItagItem(int id, ItagType type, MediaFormat format, int avgBitrate, int samplingRate, int bandWidth) {
109114
this.id = id;
110115
this.itagType = type;
111116
this.mediaFormatId = format.id;
117+
this.avgBitrate = avgBitrate;
112118
this.samplingRate = samplingRate;
113119
this.bandWidth = bandWidth;
114120
}
121+
115122
public int id;
116123
public ItagType itagType;
117124
public int mediaFormatId;
118125
public String resolutionString;
119126
public int fps = -1;
127+
public int avgBitrate = -1;
120128
public int samplingRate = -1;
121129
public int bandWidth = -1;
122130
}
123131

124132
private static final ItagItem[] itagList = {
125-
// video streams
126-
// id, ItagType, MediaFormat, Resolution, fps
127-
new ItagItem(17, ItagType.VIDEO, MediaFormat.v3GPP, "144p", 12),
128-
new ItagItem(18, ItagType.VIDEO, MediaFormat.MPEG_4, "360p", 24),
129-
new ItagItem(22, ItagType.VIDEO, MediaFormat.MPEG_4, "720p", 24),
130-
new ItagItem(36, ItagType.VIDEO, MediaFormat.v3GPP, "240p", 24),
131-
new ItagItem(37, ItagType.VIDEO, MediaFormat.MPEG_4, "1080p", 24),
132-
new ItagItem(38, ItagType.VIDEO, MediaFormat.MPEG_4, "1080p", 24),
133-
new ItagItem(43, ItagType.VIDEO, MediaFormat.WEBM, "360p", 24),
134-
new ItagItem(44, ItagType.VIDEO, MediaFormat.WEBM, "480p", 24),
135-
new ItagItem(45, ItagType.VIDEO, MediaFormat.WEBM, "720p", 24),
136-
new ItagItem(46, ItagType.VIDEO, MediaFormat.WEBM, "1080p", 24),
137-
// audio streams
138-
// id, ItagType, MediaFormat, samplingR, bandwidth
139-
new ItagItem(249, ItagType.AUDIO, MediaFormat.WEBMA, 0, 0), // bandwith/samplingR 0 because not known
140-
new ItagItem(250, ItagType.AUDIO, MediaFormat.WEBMA, 0, 0),
141-
new ItagItem(171, ItagType.AUDIO, MediaFormat.WEBMA, 0, 0),
142-
new ItagItem(140, ItagType.AUDIO, MediaFormat.M4A, 0, 0),
143-
new ItagItem(251, ItagType.AUDIO, MediaFormat.WEBMA, 0, 0),
144-
// video only streams
145-
new ItagItem(160, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "144p", 24),
146-
new ItagItem(133, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "240p", 24),
147-
new ItagItem(134, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "360p", 24),
148-
new ItagItem(135, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "480p", 24),
149-
new ItagItem(136, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "720p", 24),
150-
new ItagItem(137, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "1080p", 24),
133+
//////////////////////////////////////////////////////////////////////////
134+
// VIDEO ID ItagType Format Resolution FPS ///
135+
////////////////////////////////////////////////////////////////////////
136+
new ItagItem(17, ItagType.VIDEO, MediaFormat.v3GPP, "144p" , 12),
137+
new ItagItem(18, ItagType.VIDEO, MediaFormat.MPEG_4, "360p" , 24),
138+
new ItagItem(22, ItagType.VIDEO, MediaFormat.MPEG_4, "720p" , 24),
139+
new ItagItem(36, ItagType.VIDEO, MediaFormat.v3GPP, "240p" , 24),
140+
new ItagItem(37, ItagType.VIDEO, MediaFormat.MPEG_4, "1080p" , 24),
141+
new ItagItem(38, ItagType.VIDEO, MediaFormat.MPEG_4, "1080p" , 24),
142+
new ItagItem(43, ItagType.VIDEO, MediaFormat.WEBM, "360p" , 24),
143+
new ItagItem(44, ItagType.VIDEO, MediaFormat.WEBM, "480p" , 24),
144+
new ItagItem(45, ItagType.VIDEO, MediaFormat.WEBM, "720p" , 24),
145+
new ItagItem(46, ItagType.VIDEO, MediaFormat.WEBM, "1080p" , 24),
146+
147+
//////////////////////////////////////////////////////////////////////////////////////////
148+
// AUDIO ID ItagType Format Bitrate SamplingR Bandwidth ///
149+
////////////////////////////////////////////////////////////////////////////////////////
150+
new ItagItem(249, ItagType.AUDIO, MediaFormat.WEBMA, 50, 0, 0),
151+
new ItagItem(250, ItagType.AUDIO, MediaFormat.WEBMA, 70, 0, 0),
152+
new ItagItem(251, ItagType.AUDIO, MediaFormat.WEBMA, 160, 0, 0),
153+
new ItagItem(171, ItagType.AUDIO, MediaFormat.WEBMA, 128, 0, 0),
154+
new ItagItem(172, ItagType.AUDIO, MediaFormat.WEBMA, 256, 0, 0),
155+
new ItagItem(140, ItagType.AUDIO, MediaFormat.M4A, 128, 0, 0),
156+
new ItagItem(141, ItagType.AUDIO, MediaFormat.M4A, 256, 0, 0),
157+
158+
/// VIDEO ONLY ///////////////////////////////////////////////////////////////////
159+
// ID ItagType Format Resolution FPS ///
160+
////////////////////////////////////////////////////////////////////////////////
161+
// Don't add VideoOnly streams that have normal variants
162+
// new ItagItem(160, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "144p" , 24),
163+
// new ItagItem(133, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "240p" , 24),
164+
// new ItagItem(134, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "360p" , 24),
165+
new ItagItem(135, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "480p" , 30),
166+
// new ItagItem(136, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "720p" , 30),
167+
new ItagItem(298, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "720p60" , 60),
168+
new ItagItem(137, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "1080p" , 30),
169+
new ItagItem(299, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "1080p60" , 60),
170+
new ItagItem(266, ItagType.VIDEO_ONLY, MediaFormat.MPEG_4, "2160p" , 30),
171+
172+
// new ItagItem(243, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "360p" , 30),
173+
new ItagItem(244, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "480p" , 30),
174+
new ItagItem(245, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "480p" , 30),
175+
new ItagItem(246, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "480p" , 30),
176+
new ItagItem(247, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "720p" , 30),
177+
new ItagItem(248, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "1080p" , 30),
178+
new ItagItem(271, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "1440p" , 30),
179+
// #272 is either 3840x2160 (e.g. RtoitU2A-3E) or 7680x4320 (sLprVF6d7Ug)
180+
new ItagItem(272, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "2160p" , 30),
181+
new ItagItem(302, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "720p60" , 60),
182+
new ItagItem(303, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "1080p60" , 60),
183+
new ItagItem(308, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "1440p60" , 60),
184+
new ItagItem(313, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "2160p" , 30),
185+
new ItagItem(315, ItagType.VIDEO_ONLY, MediaFormat.WEBM, "2160p60" , 60)
151186
};
152187

153188
/**These lists only contain itag formats that are supported by the common Android Video player.
@@ -483,6 +518,7 @@ public List<AudioStream> getAudioStreams() throws ParsingException {
483518

484519
audioStreams.add(new AudioStream(streamUrl,
485520
itagItem.mediaFormatId,
521+
itagItem.avgBitrate,
486522
itagItem.bandWidth,
487523
itagItem.samplingRate));
488524
}
@@ -549,7 +585,58 @@ public List<VideoStream> getVideoStreams() throws ParsingException {
549585

550586
@Override
551587
public List<VideoStream> getVideoOnlyStreams() throws ParsingException {
552-
return null;
588+
Vector<VideoStream> videoOnlyStreams = new Vector<>();
589+
590+
try {
591+
String encodedUrlMap;
592+
// playerArgs could be null if the video is age restricted
593+
if (playerArgs == null) {
594+
if (videoInfoPage.containsKey("adaptive_fmts")) {
595+
encodedUrlMap = videoInfoPage.get("adaptive_fmts");
596+
} else {
597+
return null;
598+
}
599+
} else {
600+
if (playerArgs.has("adaptive_fmts")) {
601+
encodedUrlMap = playerArgs.getString("adaptive_fmts");
602+
} else {
603+
return null;
604+
}
605+
}
606+
for (String url_data_str : encodedUrlMap.split(",")) {
607+
// This loop iterates through multiple streams, therefor tags
608+
// is related to one and the same stream at a time.
609+
Map<String, String> tags = Parser.compatParseMap(
610+
org.jsoup.parser.Parser.unescapeEntities(url_data_str, true));
611+
612+
int itag = Integer.parseInt(tags.get("itag"));
613+
614+
if (itagIsSupported(itag)) {
615+
ItagItem itagItem = getItagItem(itag);
616+
if (itagItem.itagType == ItagType.VIDEO_ONLY) {
617+
String streamUrl = tags.get("url");
618+
// if video has a signature: decrypt it and add it to the url
619+
if (tags.get("s") != null) {
620+
streamUrl = streamUrl + "&signature="
621+
+ decryptSignature(tags.get("s"), decryptionCode);
622+
}
623+
624+
videoOnlyStreams.add(new VideoStream(
625+
true, //isVideoOnly
626+
streamUrl,
627+
itagItem.mediaFormatId,
628+
itagItem.resolutionString));
629+
}
630+
}
631+
}
632+
} catch (Exception e) {
633+
throw new ParsingException("Failed to get video only streams", e);
634+
}
635+
636+
if (videoOnlyStreams.isEmpty()) {
637+
throw new ParsingException("Failed to get any video only stream");
638+
}
639+
return videoOnlyStreams;
553640
}
554641

555642
/**Attempts to parse (and return) the offset to start playing the video from.

stream_info/AudioStream.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,26 @@ public class AudioStream implements Serializable{
2727
public int format = -1;
2828
public int bandwidth = -1;
2929
public int sampling_rate = -1;
30+
public int avgBitrate = -1;
3031

31-
public AudioStream(String url, int format, int bandwidth, int samplingRate) {
32-
this.url = url; this.format = format;
33-
this.bandwidth = bandwidth; this.sampling_rate = samplingRate;
32+
public AudioStream(String url, int format, int avgBitrate, int bandwidth, int samplingRate) {
33+
this.url = url;
34+
this.format = format;
35+
this.avgBitrate = avgBitrate;
36+
this.bandwidth = bandwidth;
37+
this.sampling_rate = samplingRate;
3438
}
3539

3640
// reveals whether two streams are the same, but have different urls
3741
public boolean equalStats(AudioStream cmp) {
3842
return format == cmp.format
3943
&& bandwidth == cmp.bandwidth
40-
&& sampling_rate == cmp.sampling_rate;
44+
&& sampling_rate == cmp.sampling_rate
45+
&& avgBitrate == cmp.avgBitrate;
4146
}
4247

4348
// reveals whether two streams are equal
4449
public boolean equals(AudioStream cmp) {
45-
return cmp != null && equalStats(cmp)
46-
&& url == cmp.url;
50+
return cmp != null && equalStats(cmp) && url.equals(cmp.url);
4751
}
4852
}

stream_info/VideoStream.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,49 @@
44

55
/**
66
* Created by Christian Schabesberger on 04.03.16.
7-
*
7+
* <p>
88
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
99
* VideoStream.java is part of NewPipe.
10-
*
10+
* <p>
1111
* NewPipe is free software: you can redistribute it and/or modify
1212
* it under the terms of the GNU General Public License as published by
1313
* the Free Software Foundation, either version 3 of the License, or
1414
* (at your option) any later version.
15-
*
15+
* <p>
1616
* NewPipe is distributed in the hope that it will be useful,
1717
* but WITHOUT ANY WARRANTY; without even the implied warranty of
1818
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1919
* GNU General Public License for more details.
20-
*
20+
* <p>
2121
* You should have received a copy of the GNU General Public License
2222
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
2323
*/
2424

25-
public class VideoStream implements Serializable{
25+
public class VideoStream implements Serializable {
2626
//url of the stream
2727
public String url = "";
2828
public int format = -1;
2929
public String resolution = "";
30+
public boolean isVideoOnly = false;
3031

3132
public VideoStream(String url, int format, String res) {
32-
this.url = url; this.format = format; resolution = res;
33+
this(false, url, format, res);
3334
}
3435

35-
// reveals wether two streams are the same, but have diferent urls
36+
public VideoStream(boolean isVideoOnly, String url, int format, String res) {
37+
this.url = url;
38+
this.format = format;
39+
this.resolution = res;
40+
this.isVideoOnly = isVideoOnly;
41+
}
42+
43+
// reveals whether two streams are the same, but have diferent urls
3644
public boolean equalStats(VideoStream cmp) {
37-
return format == cmp.format
38-
&& resolution == cmp.resolution;
45+
return format == cmp.format && resolution.equals(cmp.resolution);
3946
}
4047

41-
// revelas wether two streams are equal
48+
// reveals whether two streams are equal
4249
public boolean equals(VideoStream cmp) {
43-
return cmp != null && equalStats(cmp)
44-
&& url == cmp.url;
50+
return cmp != null && equalStats(cmp) && url.equals(cmp.url);
4551
}
4652
}

0 commit comments

Comments
 (0)