Skip to content

Commit a6c94c7

Browse files
committed
Grub frames preview from youtube
1 parent cfc72d8 commit a6c94c7

3 files changed

Lines changed: 163 additions & 0 deletions

File tree

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,4 +1035,29 @@ public String getThumbnailUrl() throws ParsingException {
10351035
}
10361036
};
10371037
}
1038+
1039+
@Nullable
1040+
public StreamFrames getFrames() {
1041+
try {
1042+
final String script = doc.select("#player-api").first().siblingElements().select("script").html();
1043+
int p = script.indexOf("ytplayer.config");
1044+
if (p == -1) {
1045+
return null;
1046+
}
1047+
p = script.indexOf('{', p);
1048+
int e = script.indexOf("ytplayer.load", p);
1049+
if (e == -1) {
1050+
return null;
1051+
}
1052+
JsonObject jo = JsonParser.object().from(script.substring(p, e - 1));
1053+
final String resp = jo.getObject("args").getString("player_response");
1054+
jo = JsonParser.object().from(resp);
1055+
final String[] spec = jo.getObject("storyboards").getObject("playerStoryboardSpecRenderer").getString("spec").split("\\|");
1056+
final String url = spec[0];
1057+
final List<String> opts = Arrays.asList(spec).subList(1, spec.length);
1058+
return new StreamFrames(url, opts);
1059+
} catch (Exception e) {
1060+
return null;
1061+
}
1062+
}
10381063
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package org.schabi.newpipe.extractor.stream;
2+
3+
import javax.annotation.Nullable;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
7+
public class StreamFrames {
8+
9+
private final List<Frameset> frames;
10+
11+
public StreamFrames(String baseUrl, List<String> params) {
12+
frames = new ArrayList<>(params.size());
13+
for (int i = 0; i < params.size(); i++) {
14+
String param = params.get(i);
15+
final String[] parts = param.split("#");
16+
frames.add(new Frameset(
17+
baseUrl.replace("$L", String.valueOf(i)).replace("$N", parts[6]) + "&sigh=" + parts[7],
18+
Integer.parseInt(parts[0]),
19+
Integer.parseInt(parts[1]),
20+
Integer.parseInt(parts[2]),
21+
Integer.parseInt(parts[3]),
22+
Integer.parseInt(parts[4])
23+
));
24+
}
25+
}
26+
27+
public int getVariantsCount() {
28+
return frames.size();
29+
}
30+
31+
public Frameset getVariant(int index) {
32+
return frames.get(index);
33+
}
34+
35+
@Nullable
36+
public Frameset getDefaultVariant() {
37+
for (final Frameset f : frames) {
38+
if (f.getUrl().contains("default.jpg")) {
39+
return f;
40+
}
41+
}
42+
return null;
43+
}
44+
45+
public static class Frameset {
46+
47+
private String url;
48+
private int frameWidth;
49+
private int frameHeight;
50+
private int totalCount;
51+
private int framesPerPageX;
52+
private int framesPerPageY;
53+
54+
private Frameset(String url, int frameWidth, int frameHeight, int totalCount, int framesPerPageX, int framesPerPageY) {
55+
this.url = url;
56+
this.totalCount = totalCount;
57+
this.frameWidth = frameWidth;
58+
this.frameHeight = frameHeight;
59+
this.framesPerPageX = framesPerPageX;
60+
this.framesPerPageY = framesPerPageY;
61+
}
62+
63+
public String getUrl() {
64+
return url;
65+
}
66+
67+
public String getUrl(int page) {
68+
return url.replace("$M", String.valueOf(page));
69+
}
70+
71+
public int getTotalPages() {
72+
if (!url.contains("$M")) {
73+
return 0;
74+
}
75+
return (int) Math.ceil(totalCount / (double) (framesPerPageX * framesPerPageY));
76+
}
77+
78+
/**
79+
* @return total count of frames
80+
*/
81+
public int getTotalCount() {
82+
return totalCount;
83+
}
84+
85+
/**
86+
* @return maximum frames count by x
87+
*/
88+
public int getFramesPerPageX() {
89+
return framesPerPageX;
90+
}
91+
92+
/**
93+
* @return maximum frames count by y
94+
*/
95+
public int getFramesPerPageY() {
96+
return framesPerPageY;
97+
}
98+
99+
/**
100+
* @return width of a one frame, in pixels
101+
*/
102+
public int getFrameWidth() {
103+
return frameWidth;
104+
}
105+
106+
/**
107+
* @return height of a one frame, in pixels
108+
*/
109+
public int getFrameHeight() {
110+
return frameHeight;
111+
}
112+
}
113+
}

extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,4 +231,29 @@ public void testGetFullLinksInDescription() throws ParsingException {
231231
assertFalse(extractor.getDescription().contains("https://youtu.be/U-9tUEOFKNU?list=PL7..."));
232232
}
233233
}
234+
235+
public static class FramesTest {
236+
private static YoutubeStreamExtractor extractor;
237+
238+
@BeforeClass
239+
public static void setUp() throws Exception {
240+
NewPipe.init(Downloader.getInstance(), new Localization("GB", "en"));
241+
extractor = (YoutubeStreamExtractor) YouTube
242+
.getStreamExtractor("https://www.youtube.com/watch?v=HoK9shIJ2xQ");
243+
extractor.fetchPage();
244+
}
245+
246+
@Test
247+
public void testGetFrames() {
248+
final StreamFrames frames = extractor.getFrames();
249+
assertNotNull(frames);
250+
assertNotNull(frames.getDefaultVariant());
251+
for (int i=0;i<frames.getVariantsCount();i++) {
252+
final StreamFrames.Frameset frameset = frames.getVariant(i);
253+
final int pages = frameset.getTotalPages();
254+
final String url = pages == 0 ? frameset.getUrl() : frameset.getUrl(pages - 1);
255+
assertNotNull(url);
256+
}
257+
}
258+
}
234259
}

0 commit comments

Comments
 (0)