From a6c94c7a9dc400bad6e0d7064d9b2ce00a401bbe Mon Sep 17 00:00:00 2001 From: Vasiliy Date: Mon, 26 Aug 2019 19:14:09 +0300 Subject: [PATCH 1/4] Grub frames preview from youtube --- .../extractors/YoutubeStreamExtractor.java | 25 ++++ .../extractor/stream/StreamFrames.java | 113 ++++++++++++++++++ .../YoutubeStreamExtractorDefaultTest.java | 25 ++++ 3 files changed, 163 insertions(+) create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamFrames.java diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java index 44e77b01e..0f443d9f1 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java @@ -1035,4 +1035,29 @@ public class YoutubeStreamExtractor extends StreamExtractor { } }; } + + @Nullable + public StreamFrames getFrames() { + try { + final String script = doc.select("#player-api").first().siblingElements().select("script").html(); + int p = script.indexOf("ytplayer.config"); + if (p == -1) { + return null; + } + p = script.indexOf('{', p); + int e = script.indexOf("ytplayer.load", p); + if (e == -1) { + return null; + } + JsonObject jo = JsonParser.object().from(script.substring(p, e - 1)); + final String resp = jo.getObject("args").getString("player_response"); + jo = JsonParser.object().from(resp); + final String[] spec = jo.getObject("storyboards").getObject("playerStoryboardSpecRenderer").getString("spec").split("\\|"); + final String url = spec[0]; + final List opts = Arrays.asList(spec).subList(1, spec.length); + return new StreamFrames(url, opts); + } catch (Exception e) { + return null; + } + } } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamFrames.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamFrames.java new file mode 100644 index 000000000..e4eb73319 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamFrames.java @@ -0,0 +1,113 @@ +package org.schabi.newpipe.extractor.stream; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +public class StreamFrames { + + private final List frames; + + public StreamFrames(String baseUrl, List params) { + frames = new ArrayList<>(params.size()); + for (int i = 0; i < params.size(); i++) { + String param = params.get(i); + final String[] parts = param.split("#"); + frames.add(new Frameset( + baseUrl.replace("$L", String.valueOf(i)).replace("$N", parts[6]) + "&sigh=" + parts[7], + Integer.parseInt(parts[0]), + Integer.parseInt(parts[1]), + Integer.parseInt(parts[2]), + Integer.parseInt(parts[3]), + Integer.parseInt(parts[4]) + )); + } + } + + public int getVariantsCount() { + return frames.size(); + } + + public Frameset getVariant(int index) { + return frames.get(index); + } + + @Nullable + public Frameset getDefaultVariant() { + for (final Frameset f : frames) { + if (f.getUrl().contains("default.jpg")) { + return f; + } + } + return null; + } + + public static class Frameset { + + private String url; + private int frameWidth; + private int frameHeight; + private int totalCount; + private int framesPerPageX; + private int framesPerPageY; + + private Frameset(String url, int frameWidth, int frameHeight, int totalCount, int framesPerPageX, int framesPerPageY) { + this.url = url; + this.totalCount = totalCount; + this.frameWidth = frameWidth; + this.frameHeight = frameHeight; + this.framesPerPageX = framesPerPageX; + this.framesPerPageY = framesPerPageY; + } + + public String getUrl() { + return url; + } + + public String getUrl(int page) { + return url.replace("$M", String.valueOf(page)); + } + + public int getTotalPages() { + if (!url.contains("$M")) { + return 0; + } + return (int) Math.ceil(totalCount / (double) (framesPerPageX * framesPerPageY)); + } + + /** + * @return total count of frames + */ + public int getTotalCount() { + return totalCount; + } + + /** + * @return maximum frames count by x + */ + public int getFramesPerPageX() { + return framesPerPageX; + } + + /** + * @return maximum frames count by y + */ + public int getFramesPerPageY() { + return framesPerPageY; + } + + /** + * @return width of a one frame, in pixels + */ + public int getFrameWidth() { + return frameWidth; + } + + /** + * @return height of a one frame, in pixels + */ + public int getFrameHeight() { + return frameHeight; + } + } +} diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java index 9bf0344a1..8da0da0a6 100644 --- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java @@ -231,4 +231,29 @@ public class YoutubeStreamExtractorDefaultTest { assertFalse(extractor.getDescription().contains("https://youtu.be/U-9tUEOFKNU?list=PL7...")); } } + + public static class FramesTest { + private static YoutubeStreamExtractor extractor; + + @BeforeClass + public static void setUp() throws Exception { + NewPipe.init(Downloader.getInstance(), new Localization("GB", "en")); + extractor = (YoutubeStreamExtractor) YouTube + .getStreamExtractor("https://www.youtube.com/watch?v=HoK9shIJ2xQ"); + extractor.fetchPage(); + } + + @Test + public void testGetFrames() { + final StreamFrames frames = extractor.getFrames(); + assertNotNull(frames); + assertNotNull(frames.getDefaultVariant()); + for (int i=0;i Date: Tue, 10 Sep 2019 19:38:51 +0300 Subject: [PATCH 2/4] Refactor frames extraction --- .../extractors/YoutubeStreamExtractor.java | 80 +++++++++---- .../newpipe/extractor/stream/Frameset.java | 63 ++++++++++ .../extractor/stream/StreamExtractor.java | 12 ++ .../extractor/stream/StreamFrames.java | 113 ------------------ .../YoutubeStreamExtractorDefaultTest.java | 17 +-- 5 files changed, 140 insertions(+), 145 deletions(-) create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/stream/Frameset.java delete mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamFrames.java diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java index 0f443d9f1..33419d7cf 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java @@ -1036,28 +1036,60 @@ public class YoutubeStreamExtractor extends StreamExtractor { }; } - @Nullable - public StreamFrames getFrames() { - try { - final String script = doc.select("#player-api").first().siblingElements().select("script").html(); - int p = script.indexOf("ytplayer.config"); - if (p == -1) { - return null; - } - p = script.indexOf('{', p); - int e = script.indexOf("ytplayer.load", p); - if (e == -1) { - return null; - } - JsonObject jo = JsonParser.object().from(script.substring(p, e - 1)); - final String resp = jo.getObject("args").getString("player_response"); - jo = JsonParser.object().from(resp); - final String[] spec = jo.getObject("storyboards").getObject("playerStoryboardSpecRenderer").getString("spec").split("\\|"); - final String url = spec[0]; - final List opts = Arrays.asList(spec).subList(1, spec.length); - return new StreamFrames(url, opts); - } catch (Exception e) { - return null; - } - } + @Nonnull + @Override + public List getFrames() throws ExtractionException { + try { + final String script = doc.select("#player-api").first().siblingElements().select("script").html(); + int p = script.indexOf("ytplayer.config"); + if (p == -1) { + return Collections.emptyList(); + } + p = script.indexOf('{', p); + int e = script.indexOf("ytplayer.load", p); + if (e == -1) { + return Collections.emptyList(); + } + JsonObject jo = JsonParser.object().from(script.substring(p, e - 1)); + final String resp = jo.getObject("args").getString("player_response"); + jo = JsonParser.object().from(resp); + final String[] spec = jo.getObject("storyboards").getObject("playerStoryboardSpecRenderer").getString("spec").split("\\|"); + final String url = spec[0]; + final ArrayList result = new ArrayList<>(spec.length - 1); + for (int i = 1; i < spec.length; ++i) { + final String[] parts = spec[i].split("#"); + if (parts.length != 8) { + continue; + } + final int frameWidth = Integer.parseInt(parts[0]); + final int frameHeight = Integer.parseInt(parts[1]); + final int totalCount = Integer.parseInt(parts[2]); + final int framesPerPageX = Integer.parseInt(parts[3]); + final int framesPerPageY = Integer.parseInt(parts[4]); + final String baseUrl = url.replace("$L", String.valueOf(i - 1)).replace("$N", parts[6]) + "&sigh=" + parts[7]; + final List urls; + if (baseUrl.contains("$M")) { + final int totalPages = (int) Math.ceil(totalCount / (double) (framesPerPageX * framesPerPageY)); + urls = new ArrayList<>(totalPages); + for (int j = 0; j < totalPages; j++) { + urls.add(baseUrl.replace("$M", String.valueOf(j))); + } + } else { + urls = Collections.singletonList(baseUrl); + } + result.add(new Frameset( + urls, + frameWidth, + frameHeight, + totalCount, + framesPerPageX, + framesPerPageY + )); + } + result.trimToSize(); + return result; + } catch (Exception e) { + throw new ExtractionException(e); + } + } } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Frameset.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Frameset.java new file mode 100644 index 000000000..5543d6fcb --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Frameset.java @@ -0,0 +1,63 @@ +package org.schabi.newpipe.extractor.stream; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.List; + +public final class Frameset { + + private List urls; + private int frameWidth; + private int frameHeight; + private int totalCount; + private int framesPerPageX; + private int framesPerPageY; + + public Frameset(List urls, int frameWidth, int frameHeight, int totalCount, int framesPerPageX, int framesPerPageY) { + this.urls = urls; + this.totalCount = totalCount; + this.frameWidth = frameWidth; + this.frameHeight = frameHeight; + this.framesPerPageX = framesPerPageX; + this.framesPerPageY = framesPerPageY; + } + + public List getUrls() { + return urls; + } + + /** + * @return total count of frames + */ + public int getTotalCount() { + return totalCount; + } + + /** + * @return maximum frames count by x + */ + public int getFramesPerPageX() { + return framesPerPageX; + } + + /** + * @return maximum frames count by y + */ + public int getFramesPerPageY() { + return framesPerPageY; + } + + /** + * @return width of a one frame, in pixels + */ + public int getFrameWidth() { + return frameWidth; + } + + /** + * @return height of a one frame, in pixels + */ + public int getFrameHeight() { + return frameHeight; + } +} \ No newline at end of file diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java index 3b9f06532..59a1c158b 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java @@ -30,7 +30,10 @@ import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.extractor.utils.Parser; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -255,6 +258,15 @@ public abstract class StreamExtractor extends Extractor { */ public abstract StreamInfoItemsCollector getRelatedStreams() throws IOException, ExtractionException; + /** + * Should return a list of frames + * @return + */ + @Nonnull + public List getFrames() throws IOException, ExtractionException { + return Collections.emptyList(); + } + /** * Should analyse the webpage's document and extracts any error message there might be. (e.g. GEMA block) * diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamFrames.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamFrames.java deleted file mode 100644 index e4eb73319..000000000 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamFrames.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.schabi.newpipe.extractor.stream; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; - -public class StreamFrames { - - private final List frames; - - public StreamFrames(String baseUrl, List params) { - frames = new ArrayList<>(params.size()); - for (int i = 0; i < params.size(); i++) { - String param = params.get(i); - final String[] parts = param.split("#"); - frames.add(new Frameset( - baseUrl.replace("$L", String.valueOf(i)).replace("$N", parts[6]) + "&sigh=" + parts[7], - Integer.parseInt(parts[0]), - Integer.parseInt(parts[1]), - Integer.parseInt(parts[2]), - Integer.parseInt(parts[3]), - Integer.parseInt(parts[4]) - )); - } - } - - public int getVariantsCount() { - return frames.size(); - } - - public Frameset getVariant(int index) { - return frames.get(index); - } - - @Nullable - public Frameset getDefaultVariant() { - for (final Frameset f : frames) { - if (f.getUrl().contains("default.jpg")) { - return f; - } - } - return null; - } - - public static class Frameset { - - private String url; - private int frameWidth; - private int frameHeight; - private int totalCount; - private int framesPerPageX; - private int framesPerPageY; - - private Frameset(String url, int frameWidth, int frameHeight, int totalCount, int framesPerPageX, int framesPerPageY) { - this.url = url; - this.totalCount = totalCount; - this.frameWidth = frameWidth; - this.frameHeight = frameHeight; - this.framesPerPageX = framesPerPageX; - this.framesPerPageY = framesPerPageY; - } - - public String getUrl() { - return url; - } - - public String getUrl(int page) { - return url.replace("$M", String.valueOf(page)); - } - - public int getTotalPages() { - if (!url.contains("$M")) { - return 0; - } - return (int) Math.ceil(totalCount / (double) (framesPerPageX * framesPerPageY)); - } - - /** - * @return total count of frames - */ - public int getTotalCount() { - return totalCount; - } - - /** - * @return maximum frames count by x - */ - public int getFramesPerPageX() { - return framesPerPageX; - } - - /** - * @return maximum frames count by y - */ - public int getFramesPerPageY() { - return framesPerPageY; - } - - /** - * @return width of a one frame, in pixels - */ - public int getFrameWidth() { - return frameWidth; - } - - /** - * @return height of a one frame, in pixels - */ - public int getFrameHeight() { - return frameHeight; - } - } -} diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java index 8da0da0a6..b1968d76b 100644 --- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java @@ -3,6 +3,7 @@ package org.schabi.newpipe.extractor.services.youtube; import org.junit.BeforeClass; import org.junit.Test; import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.extractor.ExtractorAsserts; import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.exceptions.ExtractionException; @@ -13,6 +14,7 @@ import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.extractor.utils.Utils; import java.io.IOException; +import java.util.List; import static org.junit.Assert.*; import static org.schabi.newpipe.extractor.ExtractorAsserts.assertIsSecureUrl; @@ -244,15 +246,14 @@ public class YoutubeStreamExtractorDefaultTest { } @Test - public void testGetFrames() { - final StreamFrames frames = extractor.getFrames(); + public void testGetFrames() throws ExtractionException { + final List frames = extractor.getFrames(); assertNotNull(frames); - assertNotNull(frames.getDefaultVariant()); - for (int i=0;i Date: Tue, 10 Sep 2019 19:42:55 +0300 Subject: [PATCH 3/4] Update frameset extractor test --- .../services/youtube/YoutubeStreamExtractorDefaultTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java index b1968d76b..f4bd8eeb2 100644 --- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/YoutubeStreamExtractorDefaultTest.java @@ -253,6 +253,7 @@ public class YoutubeStreamExtractorDefaultTest { for (final Frameset f : frames) { for (final String url : f.getUrls()) { ExtractorAsserts.assertIsValidUrl(url); + ExtractorAsserts.assertIsSecureUrl(url); } } } From ecb8ad85a177992c10e65ca4708c76523d0bb26c Mon Sep 17 00:00:00 2001 From: Vasiliy Date: Wed, 11 Sep 2019 19:03:53 +0300 Subject: [PATCH 4/4] Update comments --- .../java/org/schabi/newpipe/extractor/stream/Frameset.java | 3 +++ .../schabi/newpipe/extractor/stream/StreamExtractor.java | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Frameset.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Frameset.java index 5543d6fcb..2d4010dd9 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Frameset.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/Frameset.java @@ -22,6 +22,9 @@ public final class Frameset { this.framesPerPageY = framesPerPageY; } + /** + * @return list of urls to images with frames + */ public List getUrls() { return urls; } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java index 59a1c158b..e34007672 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamExtractor.java @@ -259,8 +259,10 @@ public abstract class StreamExtractor extends Extractor { public abstract StreamInfoItemsCollector getRelatedStreams() throws IOException, ExtractionException; /** - * Should return a list of frames - * @return + * Should return a list of Frameset object that contains preview of stream frames + * @return list of preview frames or empty list if frames preview is not supported or not found for specified stream + * @throws IOException + * @throws ExtractionException */ @Nonnull public List getFrames() throws IOException, ExtractionException {