Skip to content

Commit

Permalink
[YouTube] Detect deleted/nonexistent/invalid channels and playlists
Browse files Browse the repository at this point in the history
- Added tests for these cases.
  • Loading branch information
mauriciocolli authored and TobiGr committed Mar 1, 2020
1 parent 98e3594 commit e65333c
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ public void onFetchPage(@Nonnull Downloader downloader) throws IOException, Extr
final String url = super.getUrl() + "/videos?pbj=1&view=0&flow=grid";

final JsonArray ajaxJson = getJsonResponse(url, getExtractorLocalization());

initialData = ajaxJson.getObject(1).getObject("response");
YoutubeParsingHelper.defaultAlertsCheck(initialData);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.localization.TimeAgoParser;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.Utils;
Expand Down Expand Up @@ -39,6 +40,8 @@ public void onFetchPage(@Nonnull Downloader downloader) throws IOException, Extr
final JsonArray ajaxJson = getJsonResponse(url, getExtractorLocalization());

initialData = ajaxJson.getObject(1).getObject("response");
YoutubeParsingHelper.defaultAlertsCheck(initialData);

playlistInfo = getPlaylistInfo();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,10 +362,45 @@ public static JsonArray getJsonResponse(String url, Localization localization) t
throw new ParsingException("JSON response is too short");
}

// Check if the request was redirected to the error page.
final URL latestUrl = new URL(response.latestUrl());
if (latestUrl.getHost().equalsIgnoreCase("www.youtube.com")) {
final String path = latestUrl.getPath();
if (path.equalsIgnoreCase("/oops") || path.equalsIgnoreCase("/error")) {
throw new ContentNotAvailableException("Content unavailable");
}
}

final String responseContentType = response.getHeader("Content-Type");
if (responseContentType != null && responseContentType.toLowerCase().contains("text/html")) {
throw new ParsingException("Got HTML document, expected JSON response" +
" (latest url was: \"" + response.latestUrl() + "\")");
}

try {
return JsonParser.array().from(responseBody);
} catch (JsonParserException e) {
throw new ParsingException("Could not parse JSON", e);
}
}

/**
* Shared alert detection function, multiple endpoints return the error similarly structured.
* <p>
* Will check if the object has an alert of the type "ERROR".
*
* @param initialData the object which will be checked if an alert is present
* @throws ContentNotAvailableException if an alert is detected
*/
public static void defaultAlertsCheck(JsonObject initialData) throws ContentNotAvailableException {

This comment has been minimized.

Copy link
@PeterHindes

PeterHindes Mar 12, 2020

Specifically around here is where the issue is happening.

final JsonArray alerts = initialData.getArray("alerts");
if (alerts != null && !alerts.isEmpty()) {
final JsonObject alertRenderer = alerts.getObject(0).getObject("alertRenderer");
final String alertText = alertRenderer.getObject("text").getString("simpleText");
final String alertType = alertRenderer.getString("type");
if (alertType.equalsIgnoreCase("ERROR")) {
throw new ContentNotAvailableException("Got error: \"" + alertText + "\"");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.BaseChannelExtractorTest;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor;
Expand All @@ -19,6 +20,28 @@
* Test for {@link ChannelExtractor}
*/
public class YoutubeChannelExtractorTest {

public static class NotAvailable {
@BeforeClass
public static void setUp() {
NewPipe.init(DownloaderTestImpl.getInstance());
}

@Test(expected = ContentNotAvailableException.class)
public void deletedFetch() throws Exception {
final ChannelExtractor extractor =
YouTube.getChannelExtractor("https://www.youtube.com/channel/UCAUc4iz6edWerIjlnL8OSSw");
extractor.fetchPage();
}

@Test(expected = ContentNotAvailableException.class)
public void nonExistentFetch() throws Exception {
final ChannelExtractor extractor =
YouTube.getChannelExtractor("https://www.youtube.com/channel/DOESNT-EXIST");
extractor.fetchPage();
}
}

public static class Gronkh implements BaseChannelExtractorTest {
private static YoutubeChannelExtractor extractor;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.services.BasePlaylistExtractorTest;
Expand All @@ -23,6 +24,28 @@
* Test for {@link YoutubePlaylistExtractor}
*/
public class YoutubePlaylistExtractorTest {

public static class NotAvailable {
@BeforeClass
public static void setUp() {
NewPipe.init(DownloaderTestImpl.getInstance());
}

@Test(expected = ContentNotAvailableException.class)
public void nonExistentFetch() throws Exception {
final PlaylistExtractor extractor =
YouTube.getPlaylistExtractor("https://www.youtube.com/playlist?list=PL11111111111111111111111111111111");
extractor.fetchPage();
}

@Test(expected = ContentNotAvailableException.class)
public void invalidId() throws Exception {
final PlaylistExtractor extractor =
YouTube.getPlaylistExtractor("https://www.youtube.com/playlist?list=INVALID_ID");
extractor.fetchPage();
}
}

public static class TimelessPopHits implements BasePlaylistExtractorTest {
private static YoutubePlaylistExtractor extractor;

Expand Down

1 comment on commit e65333c

@PeterHindes
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, I think this is causing an issue

Please sign in to comment.