2020-04-10 08:51:05 +00:00
|
|
|
package org.schabi.newpipe.extractor.services.soundcloud.extractors;
|
2017-08-04 14:21:45 +00:00
|
|
|
|
2022-03-18 10:12:45 +00:00
|
|
|
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.SOUNDCLOUD_API_V2_URL;
|
2022-03-03 19:46:53 +00:00
|
|
|
import static org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper.clientId;
|
|
|
|
import static org.schabi.newpipe.extractor.stream.AudioStream.UNKNOWN_BITRATE;
|
|
|
|
import static org.schabi.newpipe.extractor.stream.Stream.ID_UNKNOWN;
|
2022-03-18 10:12:45 +00:00
|
|
|
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
|
|
|
|
2019-12-21 23:40:32 +00:00
|
|
|
import com.grack.nanojson.JsonArray;
|
2017-08-16 02:40:03 +00:00
|
|
|
import com.grack.nanojson.JsonObject;
|
|
|
|
import com.grack.nanojson.JsonParser;
|
|
|
|
import com.grack.nanojson.JsonParserException;
|
2022-03-18 10:12:45 +00:00
|
|
|
|
2020-02-08 22:58:46 +00:00
|
|
|
import org.schabi.newpipe.extractor.MediaFormat;
|
|
|
|
import org.schabi.newpipe.extractor.NewPipe;
|
|
|
|
import org.schabi.newpipe.extractor.StreamingService;
|
2019-04-28 20:03:16 +00:00
|
|
|
import org.schabi.newpipe.extractor.downloader.Downloader;
|
2017-08-04 14:21:45 +00:00
|
|
|
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
|
|
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
2021-01-10 18:03:20 +00:00
|
|
|
import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException;
|
2017-08-04 14:21:45 +00:00
|
|
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
2021-02-20 13:53:57 +00:00
|
|
|
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
|
2018-07-13 16:02:40 +00:00
|
|
|
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
|
2019-11-03 18:45:25 +00:00
|
|
|
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
2020-04-10 08:51:05 +00:00
|
|
|
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
|
2022-03-18 10:12:45 +00:00
|
|
|
import org.schabi.newpipe.extractor.stream.AudioStream;
|
2022-05-29 17:08:18 +00:00
|
|
|
import org.schabi.newpipe.extractor.stream.DeliveryMethod;
|
2022-03-18 10:12:45 +00:00
|
|
|
import org.schabi.newpipe.extractor.stream.Description;
|
2022-03-03 19:46:53 +00:00
|
|
|
import org.schabi.newpipe.extractor.stream.Stream;
|
2022-03-18 10:12:45 +00:00
|
|
|
import org.schabi.newpipe.extractor.stream.StreamExtractor;
|
|
|
|
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
|
|
|
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
|
|
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
2022-08-09 02:03:29 +00:00
|
|
|
import org.schabi.newpipe.extractor.utils.Utils;
|
2017-08-06 20:20:15 +00:00
|
|
|
|
|
|
|
import java.io.IOException;
|
2017-11-25 01:20:16 +00:00
|
|
|
import java.io.UnsupportedEncodingException;
|
2018-03-04 20:30:31 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.List;
|
|
|
|
|
2022-03-18 10:12:45 +00:00
|
|
|
import javax.annotation.Nonnull;
|
|
|
|
import javax.annotation.Nullable;
|
2020-04-16 14:08:14 +00:00
|
|
|
|
2017-08-04 14:21:45 +00:00
|
|
|
public class SoundcloudStreamExtractor extends StreamExtractor {
|
2017-08-16 02:40:03 +00:00
|
|
|
private JsonObject track;
|
2021-03-13 14:49:59 +00:00
|
|
|
private boolean isAvailable = true;
|
2017-08-04 14:21:45 +00:00
|
|
|
|
2021-05-15 15:51:43 +00:00
|
|
|
public SoundcloudStreamExtractor(final StreamingService service,
|
|
|
|
final LinkHandler linkHandler) {
|
2019-04-28 20:03:16 +00:00
|
|
|
super(service, linkHandler);
|
2017-08-06 20:20:15 +00:00
|
|
|
}
|
2017-08-04 14:21:45 +00:00
|
|
|
|
2017-08-06 20:20:15 +00:00
|
|
|
@Override
|
2021-05-15 15:51:43 +00:00
|
|
|
public void onFetchPage(@Nonnull final Downloader downloader) throws IOException,
|
|
|
|
ExtractionException {
|
2020-10-16 02:39:58 +00:00
|
|
|
track = SoundcloudParsingHelper.resolveFor(downloader, getUrl());
|
2017-08-04 14:21:45 +00:00
|
|
|
|
2022-08-15 03:49:40 +00:00
|
|
|
final String policy = track.getString("policy", "");
|
2017-08-10 17:50:59 +00:00
|
|
|
if (!policy.equals("ALLOW") && !policy.equals("MONETIZE")) {
|
2021-03-13 14:49:59 +00:00
|
|
|
isAvailable = false;
|
2022-03-03 19:46:53 +00:00
|
|
|
|
2021-01-10 18:03:20 +00:00
|
|
|
if (policy.equals("SNIP")) {
|
2021-02-20 13:53:57 +00:00
|
|
|
throw new SoundCloudGoPlusContentException();
|
2021-01-10 18:03:20 +00:00
|
|
|
}
|
2022-03-03 19:46:53 +00:00
|
|
|
|
2022-03-18 10:12:45 +00:00
|
|
|
if (policy.equals("BLOCK")) {
|
|
|
|
throw new GeographicRestrictionException(
|
2021-05-15 15:51:43 +00:00
|
|
|
"This track is not available in user's country");
|
2022-03-18 10:12:45 +00:00
|
|
|
}
|
2022-03-03 19:46:53 +00:00
|
|
|
|
2017-08-10 17:50:59 +00:00
|
|
|
throw new ContentNotAvailableException("Content not available: policy " + policy);
|
2017-08-04 14:21:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-25 00:10:04 +00:00
|
|
|
@Nonnull
|
2017-08-04 14:21:45 +00:00
|
|
|
@Override
|
|
|
|
public String getId() {
|
2022-03-03 19:46:53 +00:00
|
|
|
return String.valueOf(track.getInt("id"));
|
2017-08-04 14:21:45 +00:00
|
|
|
}
|
|
|
|
|
2017-11-25 00:10:04 +00:00
|
|
|
@Nonnull
|
2017-08-04 14:21:45 +00:00
|
|
|
@Override
|
2017-08-11 01:23:09 +00:00
|
|
|
public String getName() {
|
2017-08-16 02:40:03 +00:00
|
|
|
return track.getString("title");
|
2017-08-04 14:21:45 +00:00
|
|
|
}
|
|
|
|
|
2017-11-25 00:10:04 +00:00
|
|
|
@Nonnull
|
2017-08-10 17:50:59 +00:00
|
|
|
@Override
|
2020-04-11 15:18:05 +00:00
|
|
|
public String getTextualUploadDate() {
|
|
|
|
return track.getString("created_at")
|
2021-02-07 21:12:22 +00:00
|
|
|
.replace("T", " ")
|
2022-08-15 03:49:40 +00:00
|
|
|
.replace("Z", "");
|
2019-04-28 20:03:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Nonnull
|
|
|
|
@Override
|
2019-11-03 18:45:25 +00:00
|
|
|
public DateWrapper getUploadDate() throws ParsingException {
|
2021-05-15 15:51:43 +00:00
|
|
|
return new DateWrapper(SoundcloudParsingHelper.parseDateFrom(track.getString(
|
|
|
|
"created_at")));
|
2017-08-04 14:21:45 +00:00
|
|
|
}
|
|
|
|
|
2017-11-25 00:10:04 +00:00
|
|
|
@Nonnull
|
2017-08-04 14:21:45 +00:00
|
|
|
@Override
|
2017-08-11 01:23:09 +00:00
|
|
|
public String getThumbnailUrl() {
|
2022-08-15 03:49:40 +00:00
|
|
|
String artworkUrl = track.getString("artwork_url", "");
|
2019-04-25 21:29:15 +00:00
|
|
|
if (artworkUrl.isEmpty()) {
|
2022-08-15 03:49:40 +00:00
|
|
|
artworkUrl = track.getObject("user").getString("avatar_url", "");
|
2019-04-25 21:29:15 +00:00
|
|
|
}
|
2020-04-11 15:18:05 +00:00
|
|
|
return artworkUrl.replace("large.jpg", "crop.jpg");
|
2017-08-04 14:21:45 +00:00
|
|
|
}
|
|
|
|
|
2020-04-11 15:18:05 +00:00
|
|
|
@Nonnull
|
2017-08-04 14:21:45 +00:00
|
|
|
@Override
|
2020-02-06 22:35:46 +00:00
|
|
|
public Description getDescription() {
|
2020-02-07 12:28:27 +00:00
|
|
|
return new Description(track.getString("description"), Description.PLAIN_TEXT);
|
2017-08-04 14:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-08-11 01:23:09 +00:00
|
|
|
public long getLength() {
|
2020-06-13 18:25:38 +00:00
|
|
|
return track.getLong("duration") / 1000L;
|
2017-08-04 14:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-08-11 01:23:09 +00:00
|
|
|
public long getTimeStamp() throws ParsingException {
|
2017-11-22 17:45:49 +00:00
|
|
|
return getTimestampSeconds("(#t=\\d{0,3}h?\\d{0,3}m?\\d{1,3}s?)");
|
2017-08-04 14:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-08-11 01:23:09 +00:00
|
|
|
public long getViewCount() {
|
2020-06-13 18:25:38 +00:00
|
|
|
return track.getLong("playback_count");
|
2017-08-04 14:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-08-10 17:50:59 +00:00
|
|
|
public long getLikeCount() {
|
2023-01-29 00:00:49 +00:00
|
|
|
return track.getLong("likes_count", -1);
|
2017-08-04 14:21:45 +00:00
|
|
|
}
|
|
|
|
|
2017-11-25 00:10:04 +00:00
|
|
|
@Nonnull
|
2017-08-11 01:23:09 +00:00
|
|
|
@Override
|
|
|
|
public String getUploaderUrl() {
|
2018-02-21 08:23:57 +00:00
|
|
|
return SoundcloudParsingHelper.getUploaderUrl(track);
|
2017-08-11 01:23:09 +00:00
|
|
|
}
|
|
|
|
|
2017-11-25 00:10:04 +00:00
|
|
|
@Nonnull
|
2017-08-11 01:23:09 +00:00
|
|
|
@Override
|
|
|
|
public String getUploaderName() {
|
2018-02-21 08:23:57 +00:00
|
|
|
return SoundcloudParsingHelper.getUploaderName(track);
|
2017-08-11 01:23:09 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 00:44:58 +00:00
|
|
|
@Override
|
|
|
|
public boolean isUploaderVerified() throws ParsingException {
|
|
|
|
return track.getObject("user").getBoolean("verified");
|
|
|
|
}
|
|
|
|
|
2017-11-25 00:10:04 +00:00
|
|
|
@Nonnull
|
2017-08-11 01:23:09 +00:00
|
|
|
@Override
|
|
|
|
public String getUploaderAvatarUrl() {
|
2018-02-21 08:23:57 +00:00
|
|
|
return SoundcloudParsingHelper.getAvatarUrl(track);
|
2017-08-11 01:23:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2021-03-13 14:26:05 +00:00
|
|
|
public List<AudioStream> getAudioStreams() throws ExtractionException {
|
2021-01-23 17:17:35 +00:00
|
|
|
final List<AudioStream> audioStreams = new ArrayList<>();
|
2017-08-11 01:23:09 +00:00
|
|
|
|
2019-12-21 23:40:32 +00:00
|
|
|
// Streams can be streamable and downloadable - or explicitly not.
|
|
|
|
// For playing the track, it is only necessary to have a streamable track.
|
|
|
|
// If this is not the case, this track might not be published yet.
|
2022-03-18 10:12:45 +00:00
|
|
|
if (!track.getBoolean("streamable") || !isAvailable) {
|
|
|
|
return audioStreams;
|
|
|
|
}
|
2019-12-21 23:40:32 +00:00
|
|
|
|
|
|
|
try {
|
2020-04-11 15:18:05 +00:00
|
|
|
final JsonArray transcodings = track.getObject("media").getArray("transcodings");
|
2022-03-03 19:46:53 +00:00
|
|
|
if (!isNullOrEmpty(transcodings)) {
|
2021-03-13 14:26:05 +00:00
|
|
|
// Get information about what stream formats are available
|
2021-03-14 16:53:08 +00:00
|
|
|
extractAudioStreams(transcodings, checkMp3ProgressivePresence(transcodings),
|
2021-03-13 14:26:05 +00:00
|
|
|
audioStreams);
|
2021-01-25 19:42:08 +00:00
|
|
|
}
|
2022-05-29 17:08:18 +00:00
|
|
|
|
2022-03-03 19:46:53 +00:00
|
|
|
extractDownloadableFileIfAvailable(audioStreams);
|
2021-03-13 14:26:05 +00:00
|
|
|
} catch (final NullPointerException e) {
|
2022-05-29 17:08:18 +00:00
|
|
|
throw new ExtractionException("Could not get audio streams", e);
|
2021-03-13 14:26:05 +00:00
|
|
|
}
|
2021-01-25 19:42:08 +00:00
|
|
|
|
2021-03-13 14:26:05 +00:00
|
|
|
return audioStreams;
|
|
|
|
}
|
|
|
|
|
2022-03-03 19:46:53 +00:00
|
|
|
private static boolean checkMp3ProgressivePresence(@Nonnull final JsonArray transcodings) {
|
2022-05-29 17:08:18 +00:00
|
|
|
return transcodings.stream()
|
|
|
|
.filter(JsonObject.class::isInstance)
|
|
|
|
.map(JsonObject.class::cast)
|
|
|
|
.anyMatch(transcodingJsonObject -> transcodingJsonObject.getString("preset")
|
|
|
|
.contains("mp3") && transcodingJsonObject.getObject("format")
|
|
|
|
.getString("protocol").equals("progressive"));
|
2021-03-13 14:26:05 +00:00
|
|
|
}
|
2021-01-23 17:17:35 +00:00
|
|
|
|
2021-03-13 14:26:05 +00:00
|
|
|
@Nonnull
|
2022-05-29 17:08:18 +00:00
|
|
|
private String getTranscodingUrl(final String endpointUrl)
|
2021-05-15 15:51:43 +00:00
|
|
|
throws IOException, ExtractionException {
|
2022-05-29 17:08:18 +00:00
|
|
|
final String apiStreamUrl = endpointUrl + "?client_id=" + clientId();
|
|
|
|
final String response = NewPipe.getDownloader().get(apiStreamUrl).responseBody();
|
2021-03-13 14:26:05 +00:00
|
|
|
final JsonObject urlObject;
|
|
|
|
try {
|
|
|
|
urlObject = JsonParser.object().from(response);
|
|
|
|
} catch (final JsonParserException e) {
|
2022-03-03 19:46:53 +00:00
|
|
|
throw new ParsingException("Could not parse streamable URL", e);
|
2021-03-13 14:26:05 +00:00
|
|
|
}
|
2022-03-03 19:46:53 +00:00
|
|
|
|
2022-05-29 17:08:18 +00:00
|
|
|
return urlObject.getString("url");
|
2021-03-13 14:26:05 +00:00
|
|
|
}
|
|
|
|
|
2022-03-03 19:46:53 +00:00
|
|
|
@Nullable
|
|
|
|
private String getDownloadUrl(@Nonnull final String trackId)
|
|
|
|
throws IOException, ExtractionException {
|
2022-03-15 10:19:13 +00:00
|
|
|
final String response = NewPipe.getDownloader().get(SOUNDCLOUD_API_V2_URL + "tracks/"
|
|
|
|
+ trackId + "/download" + "?client_id=" + clientId()).responseBody();
|
2022-03-03 19:46:53 +00:00
|
|
|
|
2022-03-15 10:19:13 +00:00
|
|
|
final JsonObject downloadJsonObject;
|
2022-03-03 19:46:53 +00:00
|
|
|
try {
|
|
|
|
downloadJsonObject = JsonParser.object().from(response);
|
|
|
|
} catch (final JsonParserException e) {
|
|
|
|
throw new ParsingException("Could not parse download URL", e);
|
|
|
|
}
|
|
|
|
final String redirectUri = downloadJsonObject.getString("redirectUri");
|
|
|
|
if (!isNullOrEmpty(redirectUri)) {
|
|
|
|
return redirectUri;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void extractAudioStreams(@Nonnull final JsonArray transcodings,
|
|
|
|
final boolean mp3ProgressiveInStreams,
|
|
|
|
final List<AudioStream> audioStreams) {
|
2022-05-29 17:08:18 +00:00
|
|
|
transcodings.stream()
|
|
|
|
.filter(JsonObject.class::isInstance)
|
|
|
|
.map(JsonObject.class::cast)
|
|
|
|
.forEachOrdered(transcoding -> {
|
|
|
|
final String url = transcoding.getString("url");
|
|
|
|
if (isNullOrEmpty(url)) {
|
|
|
|
return;
|
|
|
|
}
|
2021-01-23 17:17:35 +00:00
|
|
|
|
2022-05-29 17:08:18 +00:00
|
|
|
try {
|
2022-06-16 09:04:56 +00:00
|
|
|
final String preset = transcoding.getString("preset", ID_UNKNOWN);
|
|
|
|
final String protocol = transcoding.getObject("format")
|
|
|
|
.getString("protocol");
|
|
|
|
final AudioStream.Builder builder = new AudioStream.Builder()
|
|
|
|
.setId(preset);
|
|
|
|
|
|
|
|
final boolean isHls = protocol.equals("hls");
|
|
|
|
if (isHls) {
|
|
|
|
builder.setDeliveryMethod(DeliveryMethod.HLS);
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.setContent(getTranscodingUrl(url), true);
|
2022-05-29 17:08:18 +00:00
|
|
|
|
|
|
|
if (preset.contains("mp3")) {
|
|
|
|
// Don't add the MP3 HLS stream if there is a progressive stream
|
2022-06-16 09:04:56 +00:00
|
|
|
// present because both have the same bitrate
|
2022-05-29 17:08:18 +00:00
|
|
|
if (mp3ProgressiveInStreams && isHls) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.setMediaFormat(MediaFormat.MP3);
|
|
|
|
builder.setAverageBitrate(128);
|
|
|
|
} else if (preset.contains("opus")) {
|
|
|
|
builder.setMediaFormat(MediaFormat.OPUS);
|
|
|
|
builder.setAverageBitrate(64);
|
|
|
|
builder.setDeliveryMethod(DeliveryMethod.HLS);
|
|
|
|
} else {
|
|
|
|
// Unknown format, skip to the next audio stream
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
final AudioStream audioStream = builder.build();
|
|
|
|
if (!Stream.containSimilarStream(audioStream, audioStreams)) {
|
|
|
|
audioStreams.add(audioStream);
|
|
|
|
}
|
|
|
|
} catch (final ExtractionException | IOException ignored) {
|
|
|
|
// Something went wrong when trying to get and add this audio stream,
|
|
|
|
// skip to the next one
|
2021-03-14 16:53:08 +00:00
|
|
|
}
|
2022-05-29 17:08:18 +00:00
|
|
|
});
|
2022-03-03 19:46:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the downloadable format if it is available.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* A track can have the {@code downloadable} boolean set to {@code true}, but it doesn't mean
|
2022-03-15 10:19:13 +00:00
|
|
|
* we can download it.
|
|
|
|
* </p>
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* If the value of the {@code has_download_left} boolean is {@code true}, the track can be
|
|
|
|
* downloaded, and not otherwise.
|
2022-03-03 19:46:53 +00:00
|
|
|
* </p>
|
|
|
|
*
|
2022-03-15 10:19:13 +00:00
|
|
|
* @param audioStreams the audio streams to which the downloadable file is added
|
2022-03-03 19:46:53 +00:00
|
|
|
*/
|
|
|
|
public void extractDownloadableFileIfAvailable(final List<AudioStream> audioStreams) {
|
|
|
|
if (track.getBoolean("downloadable") && track.getBoolean("has_downloads_left")) {
|
|
|
|
try {
|
|
|
|
final String downloadUrl = getDownloadUrl(getId());
|
|
|
|
if (!isNullOrEmpty(downloadUrl)) {
|
|
|
|
audioStreams.add(new AudioStream.Builder()
|
|
|
|
.setId("original-format")
|
|
|
|
.setContent(downloadUrl, true)
|
|
|
|
.setAverageBitrate(UNKNOWN_BITRATE)
|
|
|
|
.build());
|
|
|
|
}
|
|
|
|
} catch (final Exception ignored) {
|
|
|
|
// If something went wrong when trying to get the download URL, ignore the
|
|
|
|
// exception throw because this "stream" is not necessary to play the track
|
2019-12-21 23:40:32 +00:00
|
|
|
}
|
2017-08-16 02:40:03 +00:00
|
|
|
}
|
2017-08-11 01:23:09 +00:00
|
|
|
}
|
|
|
|
|
2021-02-20 14:59:05 +00:00
|
|
|
private static String urlEncode(final String value) {
|
2017-11-25 01:20:16 +00:00
|
|
|
try {
|
2022-08-09 02:03:29 +00:00
|
|
|
return Utils.encodeUrlUtf8(value);
|
2021-02-20 14:59:05 +00:00
|
|
|
} catch (final UnsupportedEncodingException e) {
|
2017-11-25 01:20:16 +00:00
|
|
|
throw new IllegalStateException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-11 01:23:09 +00:00
|
|
|
@Override
|
2020-04-11 15:18:05 +00:00
|
|
|
public List<VideoStream> getVideoStreams() {
|
2020-04-09 16:15:34 +00:00
|
|
|
return Collections.emptyList();
|
2017-08-11 01:23:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-04-11 15:18:05 +00:00
|
|
|
public List<VideoStream> getVideoOnlyStreams() {
|
2020-04-09 16:15:34 +00:00
|
|
|
return Collections.emptyList();
|
2017-08-11 01:23:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public StreamType getStreamType() {
|
|
|
|
return StreamType.AUDIO_STREAM;
|
|
|
|
}
|
|
|
|
|
2020-10-24 19:17:58 +00:00
|
|
|
@Nullable
|
2017-08-04 14:21:45 +00:00
|
|
|
@Override
|
2021-03-31 18:21:49 +00:00
|
|
|
public StreamInfoItemsCollector getRelatedItems() throws IOException, ExtractionException {
|
2020-04-11 15:18:05 +00:00
|
|
|
final StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
|
2017-08-04 14:21:45 +00:00
|
|
|
|
2021-05-15 17:42:29 +00:00
|
|
|
final String apiUrl = SOUNDCLOUD_API_V2_URL + "tracks/" + urlEncode(getId())
|
2022-03-03 19:46:53 +00:00
|
|
|
+ "/related?client_id=" + urlEncode(clientId());
|
2017-08-04 14:21:45 +00:00
|
|
|
|
2017-08-10 17:50:59 +00:00
|
|
|
SoundcloudParsingHelper.getStreamsFromApi(collector, apiUrl);
|
2017-08-04 14:21:45 +00:00
|
|
|
return collector;
|
|
|
|
}
|
|
|
|
|
added metadata, fix descriptions, fix thumbnail, update tests
thumbnail: quality before: https://peertube.cpy.re/static/thumbnails/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
quality after: https://peertube.cpy.re/static/previews/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
description: we were getting about the first 260 characters, we now get full description (with fallback to first 260 chars if the get request for full description fails)
test: updated tests to match description, also changed some test: it was assertEquals(extracted, expected), but the proper way to do it is assertEquals(expected, extracted)
metadata: got host, privacy (public, private, unlisted), licence, language, tags
2020-01-19 11:45:52 +00:00
|
|
|
@Override
|
2020-02-09 10:59:23 +00:00
|
|
|
public Privacy getPrivacy() {
|
|
|
|
return track.getString("sharing").equals("public") ? Privacy.PUBLIC : Privacy.PRIVATE;
|
added metadata, fix descriptions, fix thumbnail, update tests
thumbnail: quality before: https://peertube.cpy.re/static/thumbnails/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
quality after: https://peertube.cpy.re/static/previews/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
description: we were getting about the first 260 characters, we now get full description (with fallback to first 260 chars if the get request for full description fails)
test: updated tests to match description, also changed some test: it was assertEquals(extracted, expected), but the proper way to do it is assertEquals(expected, extracted)
metadata: got host, privacy (public, private, unlisted), licence, language, tags
2020-01-19 11:45:52 +00:00
|
|
|
}
|
|
|
|
|
2020-04-11 15:18:05 +00:00
|
|
|
@Nonnull
|
added metadata, fix descriptions, fix thumbnail, update tests
thumbnail: quality before: https://peertube.cpy.re/static/thumbnails/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
quality after: https://peertube.cpy.re/static/previews/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
description: we were getting about the first 260 characters, we now get full description (with fallback to first 260 chars if the get request for full description fails)
test: updated tests to match description, also changed some test: it was assertEquals(extracted, expected), but the proper way to do it is assertEquals(expected, extracted)
metadata: got host, privacy (public, private, unlisted), licence, language, tags
2020-01-19 11:45:52 +00:00
|
|
|
@Override
|
2020-04-11 15:18:05 +00:00
|
|
|
public String getCategory() {
|
2020-02-09 10:59:23 +00:00
|
|
|
return track.getString("genre");
|
added metadata, fix descriptions, fix thumbnail, update tests
thumbnail: quality before: https://peertube.cpy.re/static/thumbnails/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
quality after: https://peertube.cpy.re/static/previews/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
description: we were getting about the first 260 characters, we now get full description (with fallback to first 260 chars if the get request for full description fails)
test: updated tests to match description, also changed some test: it was assertEquals(extracted, expected), but the proper way to do it is assertEquals(expected, extracted)
metadata: got host, privacy (public, private, unlisted), licence, language, tags
2020-01-19 11:45:52 +00:00
|
|
|
}
|
|
|
|
|
2020-04-11 15:18:05 +00:00
|
|
|
@Nonnull
|
added metadata, fix descriptions, fix thumbnail, update tests
thumbnail: quality before: https://peertube.cpy.re/static/thumbnails/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
quality after: https://peertube.cpy.re/static/previews/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
description: we were getting about the first 260 characters, we now get full description (with fallback to first 260 chars if the get request for full description fails)
test: updated tests to match description, also changed some test: it was assertEquals(extracted, expected), but the proper way to do it is assertEquals(expected, extracted)
metadata: got host, privacy (public, private, unlisted), licence, language, tags
2020-01-19 11:45:52 +00:00
|
|
|
@Override
|
2020-04-11 15:18:05 +00:00
|
|
|
public String getLicence() {
|
2020-02-09 10:59:23 +00:00
|
|
|
return track.getString("license");
|
added metadata, fix descriptions, fix thumbnail, update tests
thumbnail: quality before: https://peertube.cpy.re/static/thumbnails/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
quality after: https://peertube.cpy.re/static/previews/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
description: we were getting about the first 260 characters, we now get full description (with fallback to first 260 chars if the get request for full description fails)
test: updated tests to match description, also changed some test: it was assertEquals(extracted, expected), but the proper way to do it is assertEquals(expected, extracted)
metadata: got host, privacy (public, private, unlisted), licence, language, tags
2020-01-19 11:45:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Nonnull
|
|
|
|
@Override
|
2020-04-11 15:18:05 +00:00
|
|
|
public List<String> getTags() {
|
2021-05-15 15:51:43 +00:00
|
|
|
// Tags are separated by spaces, but they can be multiple words escaped by quotes "
|
|
|
|
final String[] tagList = track.getString("tag_list").split(" ");
|
2020-02-09 10:59:23 +00:00
|
|
|
final List<String> tags = new ArrayList<>();
|
2022-03-03 19:46:53 +00:00
|
|
|
final StringBuilder escapedTag = new StringBuilder();
|
2020-02-09 10:59:23 +00:00
|
|
|
boolean isEscaped = false;
|
2022-03-18 10:12:45 +00:00
|
|
|
for (final String tag : tagList) {
|
2020-02-09 10:59:23 +00:00
|
|
|
if (tag.startsWith("\"")) {
|
2022-03-03 19:46:53 +00:00
|
|
|
escapedTag.append(tag.replace("\"", ""));
|
2020-02-09 10:59:23 +00:00
|
|
|
isEscaped = true;
|
|
|
|
} else if (isEscaped) {
|
|
|
|
if (tag.endsWith("\"")) {
|
2022-03-03 19:46:53 +00:00
|
|
|
escapedTag.append(" ").append(tag.replace("\"", ""));
|
2020-02-09 10:59:23 +00:00
|
|
|
isEscaped = false;
|
2022-03-03 19:46:53 +00:00
|
|
|
tags.add(escapedTag.toString());
|
2020-02-09 10:59:23 +00:00
|
|
|
} else {
|
2022-03-03 19:46:53 +00:00
|
|
|
escapedTag.append(" ").append(tag);
|
2020-02-09 10:59:23 +00:00
|
|
|
}
|
2022-03-18 10:12:45 +00:00
|
|
|
} else if (!tag.isEmpty()) {
|
2020-02-09 10:59:23 +00:00
|
|
|
tags.add(tag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tags;
|
added metadata, fix descriptions, fix thumbnail, update tests
thumbnail: quality before: https://peertube.cpy.re/static/thumbnails/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
quality after: https://peertube.cpy.re/static/previews/d2a5ec78-5f85-4090-8ec5-dc1102e022ea.jpg
description: we were getting about the first 260 characters, we now get full description (with fallback to first 260 chars if the get request for full description fails)
test: updated tests to match description, also changed some test: it was assertEquals(extracted, expected), but the proper way to do it is assertEquals(expected, extracted)
metadata: got host, privacy (public, private, unlisted), licence, language, tags
2020-01-19 11:45:52 +00:00
|
|
|
}
|
2017-08-04 14:21:45 +00:00
|
|
|
}
|