Skip to content

Commit 266cd1f

Browse files
committed
[YouTube] Apply changes in YoutubeMusicSearchExtractor and split its InfoItemExtractors into separate classes
Splitting YoutubeMusicSearchExtractor's InfoItemExtractors into separate classes (YoutubeMusicSongOrVideoInfoItemExtractor, YoutubeMusicAlbumOrPlaylistInfoItemExtractor and YoutubeMusicArtistInfoItemExtractor) allows to simplify YoutubeMusicSearchExtractor,improves reading and applying changes to InfoItems (no more losing at least quarter of a line due to indentations). These InfoItems, in which the image changes have been applied, don't extend the YouTube ones anymore, as most methods were overridden and the few ones that are not don't apply in YouTube Music items responses, so it was useless to extend them. The code of YoutubeMusicSearchExtractor have been also improved a bit.
1 parent c1981ed commit 266cd1f

4 files changed

Lines changed: 467 additions & 315 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package org.schabi.newpipe.extractor.services.youtube.extractors;
2+
3+
import com.grack.nanojson.JsonArray;
4+
import com.grack.nanojson.JsonObject;
5+
import org.schabi.newpipe.extractor.Image;
6+
import org.schabi.newpipe.extractor.exceptions.ParsingException;
7+
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemExtractor;
8+
import org.schabi.newpipe.extractor.utils.Utils;
9+
10+
import javax.annotation.Nonnull;
11+
import javax.annotation.Nullable;
12+
import java.util.List;
13+
14+
import static org.schabi.newpipe.extractor.ListExtractor.ITEM_COUNT_MORE_THAN_100;
15+
import static org.schabi.newpipe.extractor.ListExtractor.ITEM_COUNT_UNKNOWN;
16+
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
17+
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getImagesFromThumbnailsArray;
18+
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
19+
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_ALBUMS;
20+
import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.MUSIC_PLAYLISTS;
21+
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
22+
23+
public class YoutubeMusicAlbumOrPlaylistInfoItemExtractor implements PlaylistInfoItemExtractor {
24+
private final JsonObject albumOrPlaylistInfoItem;
25+
private final JsonArray descriptionElements;
26+
private final String searchType;
27+
28+
public YoutubeMusicAlbumOrPlaylistInfoItemExtractor(final JsonObject albumOrPlaylistInfoItem,
29+
final JsonArray descriptionElements,
30+
final String searchType) {
31+
this.albumOrPlaylistInfoItem = albumOrPlaylistInfoItem;
32+
this.descriptionElements = descriptionElements;
33+
this.searchType = searchType;
34+
}
35+
36+
@Nonnull
37+
@Override
38+
public List<Image> getThumbnails() throws ParsingException {
39+
try {
40+
return getImagesFromThumbnailsArray(
41+
albumOrPlaylistInfoItem.getObject("thumbnail")
42+
.getObject("musicThumbnailRenderer")
43+
.getObject("thumbnail")
44+
.getArray("thumbnails"));
45+
} catch (final Exception e) {
46+
throw new ParsingException("Could not get thumbnails", e);
47+
}
48+
}
49+
50+
@Override
51+
public String getName() throws ParsingException {
52+
final String name = getTextFromObject(albumOrPlaylistInfoItem.getArray("flexColumns")
53+
.getObject(0)
54+
.getObject("musicResponsiveListItemFlexColumnRenderer")
55+
.getObject("text"));
56+
57+
if (!isNullOrEmpty(name)) {
58+
return name;
59+
}
60+
61+
throw new ParsingException("Could not get name");
62+
}
63+
64+
@Override
65+
public String getUrl() throws ParsingException {
66+
String playlistId = albumOrPlaylistInfoItem.getObject("menu")
67+
.getObject("menuRenderer")
68+
.getArray("items")
69+
.getObject(4)
70+
.getObject("toggleMenuServiceItemRenderer")
71+
.getObject("toggledServiceEndpoint")
72+
.getObject("likeEndpoint")
73+
.getObject("target")
74+
.getString("playlistId");
75+
76+
if (isNullOrEmpty(playlistId)) {
77+
playlistId = albumOrPlaylistInfoItem.getObject("overlay")
78+
.getObject("musicItemThumbnailOverlayRenderer")
79+
.getObject("content")
80+
.getObject("musicPlayButtonRenderer")
81+
.getObject("playNavigationEndpoint")
82+
.getObject("watchPlaylistEndpoint")
83+
.getString("playlistId");
84+
}
85+
86+
if (!isNullOrEmpty(playlistId)) {
87+
return "https://music.youtube.com/playlist?list=" + playlistId;
88+
}
89+
90+
throw new ParsingException("Could not get URL");
91+
}
92+
93+
@Override
94+
public String getUploaderName() throws ParsingException {
95+
final String name;
96+
if (searchType.equals(MUSIC_ALBUMS)) {
97+
name = descriptionElements.getObject(2).getString("text");
98+
} else {
99+
name = descriptionElements.getObject(0).getString("text");
100+
}
101+
102+
if (!isNullOrEmpty(name)) {
103+
return name;
104+
}
105+
106+
throw new ParsingException("Could not get uploader name");
107+
}
108+
109+
@Nullable
110+
@Override
111+
public String getUploaderUrl() throws ParsingException {
112+
if (searchType.equals(MUSIC_PLAYLISTS)) {
113+
return null;
114+
}
115+
116+
final JsonArray items = albumOrPlaylistInfoItem.getObject("menu")
117+
.getObject("menuRenderer")
118+
.getArray("items");
119+
for (final Object item : items) {
120+
final JsonObject menuNavigationItemRenderer =
121+
((JsonObject) item).getObject("menuNavigationItemRenderer");
122+
if (menuNavigationItemRenderer.getObject("icon")
123+
.getString("iconType", "")
124+
.equals("ARTIST")) {
125+
return getUrlFromNavigationEndpoint(
126+
menuNavigationItemRenderer.getObject("navigationEndpoint"));
127+
}
128+
}
129+
130+
throw new ParsingException("Could not get uploader URL");
131+
}
132+
133+
@Override
134+
public boolean isUploaderVerified() throws ParsingException {
135+
return false;
136+
}
137+
138+
@Override
139+
public long getStreamCount() throws ParsingException {
140+
if (searchType.equals(MUSIC_ALBUMS)) {
141+
return ITEM_COUNT_UNKNOWN;
142+
}
143+
144+
final String count = descriptionElements.getObject(2)
145+
.getString("text");
146+
147+
if (!isNullOrEmpty(count)) {
148+
if (count.contains("100+")) {
149+
return ITEM_COUNT_MORE_THAN_100;
150+
} else {
151+
return Long.parseLong(Utils.removeNonDigitCharacters(count));
152+
}
153+
}
154+
155+
throw new ParsingException("Could not get stream count");
156+
}
157+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package org.schabi.newpipe.extractor.services.youtube.extractors;
2+
3+
import com.grack.nanojson.JsonObject;
4+
import org.schabi.newpipe.extractor.Image;
5+
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
6+
import org.schabi.newpipe.extractor.exceptions.ParsingException;
7+
import org.schabi.newpipe.extractor.utils.Parser;
8+
import org.schabi.newpipe.extractor.utils.Utils;
9+
10+
import javax.annotation.Nonnull;
11+
import javax.annotation.Nullable;
12+
import java.util.List;
13+
14+
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
15+
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getImagesFromThumbnailsArray;
16+
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
17+
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
18+
19+
public class YoutubeMusicArtistInfoItemExtractor implements ChannelInfoItemExtractor {
20+
private final JsonObject artistInfoItem;
21+
22+
public YoutubeMusicArtistInfoItemExtractor(final JsonObject artistInfoItem) {
23+
this.artistInfoItem = artistInfoItem;
24+
}
25+
26+
@Nonnull
27+
@Override
28+
public List<Image> getThumbnails() throws ParsingException {
29+
try {
30+
return getImagesFromThumbnailsArray(
31+
artistInfoItem.getObject("thumbnail")
32+
.getObject("musicThumbnailRenderer")
33+
.getObject("thumbnail")
34+
.getArray("thumbnails"));
35+
} catch (final Exception e) {
36+
throw new ParsingException("Could not get thumbnails", e);
37+
}
38+
}
39+
40+
@Override
41+
public String getName() throws ParsingException {
42+
final String name = getTextFromObject(artistInfoItem.getArray("flexColumns")
43+
.getObject(0)
44+
.getObject("musicResponsiveListItemFlexColumnRenderer")
45+
.getObject("text"));
46+
if (!isNullOrEmpty(name)) {
47+
return name;
48+
}
49+
throw new ParsingException("Could not get name");
50+
}
51+
52+
@Override
53+
public String getUrl() throws ParsingException {
54+
final String url = getUrlFromNavigationEndpoint(
55+
artistInfoItem.getObject("navigationEndpoint"));
56+
if (!isNullOrEmpty(url)) {
57+
return url;
58+
}
59+
throw new ParsingException("Could not get URL");
60+
}
61+
62+
@Override
63+
public long getSubscriberCount() throws ParsingException {
64+
final String subscriberCount = getTextFromObject(artistInfoItem.getArray("flexColumns")
65+
.getObject(2)
66+
.getObject("musicResponsiveListItemFlexColumnRenderer")
67+
.getObject("text"));
68+
if (!isNullOrEmpty(subscriberCount)) {
69+
try {
70+
return Utils.mixedNumberWordToLong(subscriberCount);
71+
} catch (final Parser.RegexException ignored) {
72+
// probably subscriberCount == "No subscribers" or similar
73+
return 0;
74+
}
75+
}
76+
throw new ParsingException("Could not get subscriber count");
77+
}
78+
79+
@Override
80+
public long getStreamCount() {
81+
return -1;
82+
}
83+
84+
@Override
85+
public boolean isVerified() {
86+
// An artist on YouTube Music is always verified
87+
return true;
88+
}
89+
90+
@Nullable
91+
@Override
92+
public String getDescription() {
93+
return null;
94+
}
95+
}

0 commit comments

Comments
 (0)