Skip to content

Commit 03a6551

Browse files
committed
[YouTube Music] Fix uploader and stream count for album/playlist info items
- handles autogenerated albums instead of returning an error for invalid uploader url - extracts uploader url of playlists too using another method - getStreamCount() now returns ITEM_COUNT_UNKNOWN because no info is included in the JSON
1 parent 260ba47 commit 03a6551

5 files changed

Lines changed: 417 additions & 114 deletions

File tree

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

Lines changed: 22 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,34 @@
55
import org.schabi.newpipe.extractor.Image;
66
import org.schabi.newpipe.extractor.exceptions.ParsingException;
77
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItemExtractor;
8-
import org.schabi.newpipe.extractor.utils.Utils;
98

109
import javax.annotation.Nonnull;
1110
import javax.annotation.Nullable;
1211
import java.util.List;
1312

14-
import static org.schabi.newpipe.extractor.ListExtractor.ITEM_COUNT_MORE_THAN_100;
1513
import static org.schabi.newpipe.extractor.ListExtractor.ITEM_COUNT_UNKNOWN;
1614
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getTextFromObject;
1715
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getImagesFromThumbnailsArray;
1816
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getUrlFromNavigationEndpoint;
1917
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;
2118
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
2219

2320
public class YoutubeMusicAlbumOrPlaylistInfoItemExtractor implements PlaylistInfoItemExtractor {
2421
private final JsonObject albumOrPlaylistInfoItem;
25-
private final JsonArray descriptionElements;
26-
private final String searchType;
22+
private final JsonObject descriptionElementUploader;
2723

2824
public YoutubeMusicAlbumOrPlaylistInfoItemExtractor(final JsonObject albumOrPlaylistInfoItem,
2925
final JsonArray descriptionElements,
3026
final String searchType) {
3127
this.albumOrPlaylistInfoItem = albumOrPlaylistInfoItem;
32-
this.descriptionElements = descriptionElements;
33-
this.searchType = searchType;
28+
29+
if (searchType.equals(MUSIC_ALBUMS)) {
30+
// "Album", " • ", uploader, " • ", year
31+
this.descriptionElementUploader = descriptionElements.getObject(2);
32+
} else {
33+
// uploader, " • ", view count
34+
this.descriptionElementUploader = descriptionElements.getObject(0);
35+
}
3436
}
3537

3638
@Nonnull
@@ -92,12 +94,7 @@ public String getUrl() throws ParsingException {
9294

9395
@Override
9496
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-
}
97+
final String name = descriptionElementUploader.getString("text");
10198

10299
if (!isNullOrEmpty(name)) {
103100
return name;
@@ -109,10 +106,7 @@ public String getUploaderName() throws ParsingException {
109106
@Nullable
110107
@Override
111108
public String getUploaderUrl() throws ParsingException {
112-
if (searchType.equals(MUSIC_PLAYLISTS)) {
113-
return null;
114-
}
115-
109+
// first try obtaining the uploader from the menu (will not work for MUSIC_PLAYLISTS though)
116110
final JsonArray items = albumOrPlaylistInfoItem.getObject("menu")
117111
.getObject("menuRenderer")
118112
.getArray("items");
@@ -127,7 +121,15 @@ public String getUploaderUrl() throws ParsingException {
127121
}
128122
}
129123

130-
throw new ParsingException("Could not get uploader URL");
124+
// then try obtaining it from the uploader description element
125+
if (descriptionElementUploader.has("navigationEndpoint")) {
126+
return getUrlFromNavigationEndpoint(
127+
descriptionElementUploader.getObject("navigationEndpoint"));
128+
} else {
129+
// if there is no navigationEndpoint for the uploader
130+
// then this playlist/album is likely autogenerated
131+
return null;
132+
}
131133
}
132134

133135
@Override
@@ -137,21 +139,7 @@ public boolean isUploaderVerified() throws ParsingException {
137139

138140
@Override
139141
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");
142+
// YouTube Music album and playlist info items don't expose the stream count anywhere...
143+
return ITEM_COUNT_UNKNOWN;
156144
}
157145
}

extractor/src/test/java/org/schabi/newpipe/extractor/services/youtube/search/YoutubeMusicSearchExtractorTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ protected SearchExtractor createExtractor() throws Exception {
5757
}
5858

5959
public static class MusicAlbums extends DefaultSearchExtractorTest implements InitYoutubeTest {
60-
private static final String QUERY = "johnny sellah";
60+
// searching for "scenography" on 28/07/2025 returns some autogenerated albums,
61+
// and we want to test the extraction of those, too
62+
private static final String QUERY = "scenography";
6163

6264
@Override
6365
protected SearchExtractor createExtractor() throws Exception {

extractor/src/test/resources/mocks/v1/org/schabi/newpipe/extractor/services/youtube/search/youtubemusicsearchextractor/musicalbums/generated_mock_0.json

Lines changed: 8 additions & 75 deletions
Large diffs are not rendered by default.

extractor/src/test/resources/mocks/v1/org/schabi/newpipe/extractor/services/youtube/search/youtubemusicsearchextractor/musicalbums/generated_mock_1.json

Lines changed: 80 additions & 4 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)