Skip to content

Commit a5fcc7d

Browse files
authored
Merge pull request #1351 from Stypox/fix-ytm-albums-playlists
[YouTube Music] Fix uploader and stream count for album/playlist info items
2 parents 260ba47 + bd6e5d3 commit a5fcc7d

5 files changed

Lines changed: 414 additions & 114 deletions

File tree

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

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,32 @@
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+
this.descriptionElementUploader = descriptionElements.getObject(
30+
// For albums: "Album/Single/EP", " • ", uploader, " • ", year -> uploader is at 2
31+
// For playlists: uploader, " • ", view count -> uploader is at 0
32+
MUSIC_ALBUMS.equals(searchType) ? 2 : 0
33+
);
3434
}
3535

3636
@Nonnull
@@ -92,12 +92,7 @@ public String getUrl() throws ParsingException {
9292

9393
@Override
9494
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-
}
95+
final String name = descriptionElementUploader.getString("text");
10196

10297
if (!isNullOrEmpty(name)) {
10398
return name;
@@ -109,10 +104,7 @@ public String getUploaderName() throws ParsingException {
109104
@Nullable
110105
@Override
111106
public String getUploaderUrl() throws ParsingException {
112-
if (searchType.equals(MUSIC_PLAYLISTS)) {
113-
return null;
114-
}
115-
107+
// first try obtaining the uploader from the menu (will not work for MUSIC_PLAYLISTS though)
116108
final JsonArray items = albumOrPlaylistInfoItem.getObject("menu")
117109
.getObject("menuRenderer")
118110
.getArray("items");
@@ -127,7 +119,14 @@ public String getUploaderUrl() throws ParsingException {
127119
}
128120
}
129121

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

133132
@Override
@@ -137,21 +136,7 @@ public boolean isUploaderVerified() throws ParsingException {
137136

138137
@Override
139138
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");
139+
// YouTube Music album and playlist info items don't expose the stream count anywhere...
140+
return ITEM_COUNT_UNKNOWN;
156141
}
157142
}

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)