|
1 | 1 | package org.schabi.newpipe.extractor.services.youtube; |
2 | 2 |
|
| 3 | +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.defaultAlertsCheck; |
| 4 | +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonPostResponse; |
| 5 | +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject; |
| 6 | +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.hasArtistOrVerifiedIconBadgeAttachment; |
| 7 | +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareDesktopJsonBuilder; |
| 8 | +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; |
| 9 | + |
3 | 10 | import com.grack.nanojson.JsonObject; |
4 | 11 | import com.grack.nanojson.JsonWriter; |
| 12 | + |
5 | 13 | import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; |
6 | 14 | import org.schabi.newpipe.extractor.exceptions.ExtractionException; |
7 | 15 | import org.schabi.newpipe.extractor.exceptions.ParsingException; |
8 | 16 | import org.schabi.newpipe.extractor.localization.ContentCountry; |
9 | 17 | import org.schabi.newpipe.extractor.localization.Localization; |
10 | 18 |
|
11 | | -import javax.annotation.Nonnull; |
12 | | -import javax.annotation.Nullable; |
13 | 19 | import java.io.IOException; |
14 | 20 | import java.io.Serializable; |
15 | 21 | import java.nio.charset.StandardCharsets; |
16 | 22 | import java.util.Optional; |
17 | 23 |
|
18 | | -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.defaultAlertsCheck; |
19 | | -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getJsonPostResponse; |
20 | | -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject; |
21 | | -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.hasArtistOrVerifiedIconBadgeAttachment; |
22 | | -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareDesktopJsonBuilder; |
23 | | -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; |
| 24 | +import javax.annotation.Nonnull; |
| 25 | +import javax.annotation.Nullable; |
24 | 26 |
|
25 | 27 | /** |
26 | 28 | * Shared functions for extracting YouTube channel pages and tabs. |
@@ -65,35 +67,59 @@ public static String resolveChannelId(@Nonnull final String idOrPath) |
65 | 67 | // URL, then no information about the channel associated with this URL was found, |
66 | 68 | // so the unresolved url will be returned. |
67 | 69 | if (!channelId[0].equals("channel")) { |
68 | | - final byte[] body = JsonWriter.string( |
69 | | - prepareDesktopJsonBuilder(Localization.DEFAULT, ContentCountry.DEFAULT) |
70 | | - .value("url", "https://www.youtube.com/" + idOrPath) |
| 70 | + String urlToResolve = "https://www.youtube.com/" + idOrPath; |
| 71 | + |
| 72 | + JsonObject endpoint = new JsonObject(); |
| 73 | + String webPageType = ""; |
| 74 | + // Try to resolve YT channel redirects |
| 75 | + // It works like that: |
| 76 | + // @TheDailyShow |
| 77 | + // -> resolves to thedailyshow |
| 78 | + // -> resolves to the id: UCwWhs_6x42TyRM4Wstoq8HA |
| 79 | + // Please note that this is not always the case, some handles |
| 80 | + // e.g. @google or @Gronkh directly resolve the id |
| 81 | + for (int tries = 0; |
| 82 | + urlToResolve != null && tries < 3; |
| 83 | + tries++) { |
| 84 | + final byte[] body = JsonWriter.string( |
| 85 | + prepareDesktopJsonBuilder(Localization.DEFAULT, ContentCountry.DEFAULT) |
| 86 | + .value("url", urlToResolve) |
71 | 87 | .done()) |
72 | 88 | .getBytes(StandardCharsets.UTF_8); |
73 | 89 |
|
74 | | - final JsonObject jsonResponse = getJsonPostResponse( |
| 90 | + final JsonObject jsonResponse = getJsonPostResponse( |
75 | 91 | "navigation/resolve_url", body, Localization.DEFAULT); |
76 | 92 |
|
77 | | - checkIfChannelResponseIsValid(jsonResponse); |
| 93 | + checkIfChannelResponseIsValid(jsonResponse); |
78 | 94 |
|
79 | | - final JsonObject endpoint = jsonResponse.getObject("endpoint"); |
| 95 | + endpoint = jsonResponse.getObject("endpoint"); |
80 | 96 |
|
81 | | - final String webPageType = endpoint.getObject("commandMetadata") |
| 97 | + webPageType = endpoint.getObject("commandMetadata") |
82 | 98 | .getObject("webCommandMetadata") |
83 | | - .getString("webPageType", ""); |
| 99 | + .getString("webPageType"); |
| 100 | + |
| 101 | + urlToResolve = "WEB_PAGE_TYPE_UNKNOWN".equals(webPageType) |
| 102 | + ? endpoint.getObject("urlEndpoint").getString("url") |
| 103 | + : null; |
| 104 | + } |
84 | 105 |
|
85 | | - final JsonObject browseEndpoint = endpoint.getObject(BROWSE_ENDPOINT); |
86 | | - final String browseId = browseEndpoint.getString(BROWSE_ID, ""); |
| 106 | + final String browseId = endpoint.getObject(BROWSE_ENDPOINT) |
| 107 | + .getString(BROWSE_ID, ""); |
87 | 108 |
|
88 | | - if (webPageType.equalsIgnoreCase("WEB_PAGE_TYPE_BROWSE") |
89 | | - || webPageType.equalsIgnoreCase("WEB_PAGE_TYPE_CHANNEL") |
| 109 | + if (("WEB_PAGE_TYPE_BROWSE".equalsIgnoreCase(webPageType) |
| 110 | + || "WEB_PAGE_TYPE_CHANNEL".equalsIgnoreCase(webPageType)) |
90 | 111 | && !browseId.isEmpty()) { |
91 | 112 | if (!browseId.startsWith("UC")) { |
92 | 113 | throw new ExtractionException("Redirected id is not pointing to a channel"); |
93 | 114 | } |
94 | 115 |
|
95 | 116 | return browseId; |
96 | 117 | } |
| 118 | + |
| 119 | + // Otherwise, the code after that will run into an IndexOutOfBoundsException |
| 120 | + if (channelId.length < 2) { |
| 121 | + throw new ExtractionException("Failed to resolve channelId for " + idOrPath); |
| 122 | + } |
97 | 123 | } |
98 | 124 |
|
99 | 125 | // return the unresolved URL |
@@ -175,13 +201,13 @@ public static ChannelResponseData getChannelResponse(@Nonnull final String chann |
175 | 201 |
|
176 | 202 | final String webPageType = endpoint.getObject("commandMetadata") |
177 | 203 | .getObject("webCommandMetadata") |
178 | | - .getString("webPageType", ""); |
| 204 | + .getString("webPageType"); |
179 | 205 |
|
180 | 206 | final String browseId = endpoint.getObject(BROWSE_ENDPOINT) |
181 | 207 | .getString(BROWSE_ID, ""); |
182 | 208 |
|
183 | | - if (webPageType.equalsIgnoreCase("WEB_PAGE_TYPE_BROWSE") |
184 | | - || webPageType.equalsIgnoreCase("WEB_PAGE_TYPE_CHANNEL") |
| 209 | + if (("WEB_PAGE_TYPE_BROWSE".equalsIgnoreCase(webPageType) |
| 210 | + || "WEB_PAGE_TYPE_CHANNEL".equalsIgnoreCase(webPageType)) |
185 | 211 | && !browseId.isEmpty()) { |
186 | 212 | if (!browseId.startsWith("UC")) { |
187 | 213 | throw new ExtractionException("Redirected id is not pointing to a channel"); |
|
0 commit comments