Skip to content

Commit 7f81821

Browse files
committed
[SoundCloud] Add utility methods to get images from track JSON objects and image URLs
These new public and static methods, added in SoundcloudParsingHelper, getAllImagesFromArtworkOrAvatarUrl(String) and getAllImagesFromVisualUrl(String) (which call a common private method, getAllImagesFromImageUrlReturned(String, List<ImageSuffix>, List<Image>)), return an unmodifiable list of JPEG images containing almost every image resolution provided by SoundCloud except the original size and the tiny resolution (for artworks and avatars, as the image size is 20x20 for artworks and 18x18 for avatars, so very close to or equal to the t20x20 resolution): - for artworks and avatars: - mini: 16x16; - t20x20: 20x20; - small: 32x32; - badge: 47x47; - t50x50: 50x50; - t60x60: 60x60; - t67x67: 67x67; - large: 100x100; - t120x120: 120x120; - t200x200: 200x200; - t240x240: 240x240; - t250x250: 250x250; - t300x300: 300x300; - t500x500: 500x500. - for visuals/user banners: - t1240x260: 1240x260; - t2480x520: 2480x520. Duplicated code in two methods of SoundcloudParsingHelper (getUsersFromApi(ChannelInfoItemsCollector, String) and getStreamsFromApi(StreamInfoItemsCollector, String, boolean)) has been merged into one common private method, getNextPageUrlFromResponseObject(JsonObject).
1 parent 266cd1f commit 7f81821

1 file changed

Lines changed: 101 additions & 4 deletions

File tree

extractor/src/main/java/org/schabi/newpipe/extractor/services/soundcloud/SoundcloudParsingHelper.java

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package org.schabi.newpipe.extractor.services.soundcloud;
22

3+
import static org.schabi.newpipe.extractor.Image.ResolutionLevel.LOW;
4+
import static org.schabi.newpipe.extractor.Image.ResolutionLevel.MEDIUM;
5+
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
6+
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
7+
import static org.schabi.newpipe.extractor.utils.Utils.replaceHttpWithHttps;
8+
39
import com.grack.nanojson.JsonArray;
410
import com.grack.nanojson.JsonObject;
511
import com.grack.nanojson.JsonParser;
@@ -9,6 +15,7 @@
915
import org.jsoup.nodes.Element;
1016
import org.jsoup.select.Elements;
1117
import org.schabi.newpipe.extractor.MultiInfoItemsCollector;
18+
import org.schabi.newpipe.extractor.Image;
1219
import org.schabi.newpipe.extractor.NewPipe;
1320
import org.schabi.newpipe.extractor.channel.ChannelInfoItemsCollector;
1421
import org.schabi.newpipe.extractor.downloader.Downloader;
@@ -20,12 +27,14 @@
2027
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudPlaylistInfoItemExtractor;
2128
import org.schabi.newpipe.extractor.services.soundcloud.extractors.SoundcloudStreamInfoItemExtractor;
2229
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
30+
import org.schabi.newpipe.extractor.utils.ImageSuffix;
2331
import org.schabi.newpipe.extractor.utils.JsonUtils;
2432
import org.schabi.newpipe.extractor.utils.Parser;
2533
import org.schabi.newpipe.extractor.utils.Parser.RegexException;
2634
import org.schabi.newpipe.extractor.utils.Utils;
2735

2836
import javax.annotation.Nonnull;
37+
import javax.annotation.Nullable;
2938
import java.io.IOException;
3039
import java.net.MalformedURLException;
3140
import java.net.URL;
@@ -35,12 +44,47 @@
3544
import java.util.Collections;
3645
import java.util.List;
3746
import java.util.Map;
38-
39-
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
40-
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
41-
import static org.schabi.newpipe.extractor.utils.Utils.replaceHttpWithHttps;
47+
import java.util.stream.Collectors;
4248

4349
public final class SoundcloudParsingHelper {
50+
// CHECKSTYLE:OFF
51+
// From https://web.archive.org/web/20210214185000/https://developers.soundcloud.com/docs/api/reference#tracks
52+
// and researches on images used by the websites
53+
// CHECKSTYLE:ON
54+
/*
55+
SoundCloud avatars and artworks are almost squares
56+
57+
When we get non-square pictures, all these images variants are still squares, except the
58+
original and the crop versions provides images which are respecting aspect ratios.
59+
The websites only use the square variants.
60+
61+
t2400x2400 and t3000x3000 variants also exists, but are not returned as several images are
62+
uploaded with a lower size than these variants: in this case, these variants return an upscaled
63+
version of the original image.
64+
*/
65+
private static final List<ImageSuffix> ALBUMS_AND_ARTWORKS_URL_SUFFIXES_AND_RESOLUTIONS =
66+
List.of(new ImageSuffix("mini.jpg", 16, 16, LOW),
67+
new ImageSuffix("t20x20.jpg", 20, 20, LOW),
68+
new ImageSuffix("small.jpg", 32, 32, LOW),
69+
new ImageSuffix("badge.jpg", 47, 47, LOW),
70+
new ImageSuffix("t50x50.jpg", 50, 50, LOW),
71+
new ImageSuffix("t60x60.jpg", 60, 60, LOW),
72+
// Seems to work also on avatars, even if it is written to be not the case in
73+
// the old API docs
74+
new ImageSuffix("t67x67.jpg", 67, 67, LOW),
75+
new ImageSuffix("t80x80.jpg", 80, 80, LOW),
76+
new ImageSuffix("large.jpg", 100, 100, LOW),
77+
new ImageSuffix("t120x120.jpg", 120, 120, LOW),
78+
new ImageSuffix("t200x200.jpg", 200, 200, MEDIUM),
79+
new ImageSuffix("t240x240.jpg", 240, 240, MEDIUM),
80+
new ImageSuffix("t250x250.jpg", 250, 250, MEDIUM),
81+
new ImageSuffix("t300x300.jpg", 300, 300, MEDIUM),
82+
new ImageSuffix("t500x500.jpg", 500, 500, MEDIUM));
83+
84+
private static final List<ImageSuffix> VISUALS_URL_SUFFIXES_AND_RESOLUTIONS =
85+
List.of(new ImageSuffix("t1240x260.jpg", 1240, 260, MEDIUM),
86+
new ImageSuffix("t2480x520.jpg", 2480, 520, MEDIUM));
87+
4488
private static String clientId;
4589
public static final String SOUNDCLOUD_API_V2_URL = "https://api-v2.soundcloud.com/";
4690

@@ -366,4 +410,57 @@ public static String getAvatarUrl(final JsonObject object) {
366410
public static String getUploaderName(final JsonObject object) {
367411
return object.getObject("user").getString("username", "");
368412
}
413+
414+
@Nonnull
415+
public static List<Image> getAllImagesFromTrackObject(@Nonnull final JsonObject trackObject)
416+
throws ParsingException {
417+
final String artworkUrl = trackObject.getString("artwork_url");
418+
if (artworkUrl != null) {
419+
return getAllImagesFromArtworkOrAvatarUrl(artworkUrl);
420+
}
421+
final String avatarUrl = trackObject.getObject("user").getString("avatar_url");
422+
if (avatarUrl != null) {
423+
return getAllImagesFromArtworkOrAvatarUrl(avatarUrl);
424+
}
425+
426+
throw new ParsingException("Could not get track or track user's thumbnails");
427+
}
428+
429+
@Nonnull
430+
public static List<Image> getAllImagesFromArtworkOrAvatarUrl(
431+
@Nullable final String originalArtworkOrAvatarUrl) {
432+
if (isNullOrEmpty(originalArtworkOrAvatarUrl)) {
433+
return List.of();
434+
}
435+
436+
return getAllImagesFromImageUrlReturned(
437+
// Artwork and avatars are originally returned with the "large" resolution, which
438+
// is 100x100
439+
originalArtworkOrAvatarUrl.replace("large.jpg", ""),
440+
ALBUMS_AND_ARTWORKS_URL_SUFFIXES_AND_RESOLUTIONS);
441+
}
442+
443+
@Nonnull
444+
public static List<Image> getAllImagesFromVisualUrl(
445+
@Nullable final String originalVisualUrl) {
446+
if (isNullOrEmpty(originalVisualUrl)) {
447+
return List.of();
448+
}
449+
450+
return getAllImagesFromImageUrlReturned(
451+
// Images are originally returned with the "original" resolution, which may be
452+
// huge so don't include it for size purposes
453+
originalVisualUrl.replace("original.jpg", ""),
454+
VISUALS_URL_SUFFIXES_AND_RESOLUTIONS);
455+
}
456+
457+
private static List<Image> getAllImagesFromImageUrlReturned(
458+
@Nonnull final String baseImageUrl,
459+
@Nonnull final List<ImageSuffix> imageSuffixes) {
460+
return imageSuffixes.stream()
461+
.map(imageSuffix -> new Image(baseImageUrl + imageSuffix.getSuffix(),
462+
imageSuffix.getHeight(), imageSuffix.getWidth(),
463+
imageSuffix.getResolutionLevel()))
464+
.collect(Collectors.toUnmodifiableList());
465+
}
369466
}

0 commit comments

Comments
 (0)