package me.moodcat.soundcloud;
import java.net.URLEncoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
/**
* An extractor that can connect to SoundCloud using a {@link HttpClientInvoker}.
*/
public class SoundCloudExtract extends SoundCloudAPIConnector {
/**
* The pattern of an API call to get data about a track.
*/
protected static final Pattern SONG_PATTERN = Pattern
.compile(SOUNDCLOUD_HOST + "/(?<artist>.*?)/(?<permalink>.*?)$");
/**
* {@link HttpClientInvoker} used to connect to the internet.
*
* @param urlFactory
* The new factory to connect to the internet.
* @return The factory that is used to connect to the internet.
*/
@Setter
@Getter
private HttpClientInvoker urlFactory;
/**
* Create an extractor.
*/
public SoundCloudExtract() {
this.urlFactory = new HttpClientInvoker();
}
@SneakyThrows
protected static String getUrlFromArtistAndPermalink(final String artist,
final String permalink) {
return String.format(SOUNDCLOUD_HOST + "/%s/%s",
URLEncoder.encode(artist, URI_CHARSET),
URLEncoder.encode(permalink, URI_CHARSET));
}
/**
* Retrieve a SoundCloudTrack given a SoundCloud URL.
*
* @param soundCloudUrl
* the given SoundCloud URL
* @return the parsed {@link SoundCloudTrack}
* @throws SoundCloudException
* if the URL is malformed
*/
public SoundCloudTrack extract(final String soundCloudUrl) throws SoundCloudException {
final Matcher matcher = SONG_PATTERN.matcher(soundCloudUrl);
if (matcher.find()) {
final String permalink = matcher.group("permalink");
final String artist = matcher.group("artist");
return this.extract(artist, permalink);
}
throw new SoundCloudException("Wrong URL supplied");
}
/**
* Retrieve a SoundCloudTrack given a SoundCloud id.
*
* @param id
* the given SoundCloud id
* @return the parsed {@link SoundCloudTrack}
* @throws SoundCloudException
* if the URL is malformed
*/
public SoundCloudTrack extract(final int id) throws SoundCloudException {
return this.getUrlFactory().retrieve(id, SoundCloudTrack.class);
}
/**
* Retrieve a SoundCloudTrack given the artist and permalink.
*
* @param artist
* the track's artist
* @param permalink
* the track's permalink
* @return the parsed {@link SoundCloudTrack}
* @throws SoundCloudException
* when the download has failed
* @throws SoundCloudException
* When a malformed title has been supplied.
*/
public SoundCloudTrack extract(final String artist, final String permalink)
throws SoundCloudException {
final String url = getUrlFromArtistAndPermalink(artist, permalink);
return this.getUrlFactory().resolve(url, SoundCloudTrack.class);
}
/**
* Mockable HttpClientInvoker that takes care of network connection.
*/
protected class HttpClientInvoker {
/**
* The resolve resource allows you to lookup and access API resources
* when you only know the SoundCloud.com URL.
*
* @param url
* The url to retrieve.
* @param targetEntity
* What entity type should be returned.
* @param <T>
* The type of the entity.
* @return The entity that corresponds to the request.
* @throws SoundCloudException
* Thrown if the resource could not be accessed.
*/
protected <T> T resolve(final String url, final Class<T> targetEntity)
throws SoundCloudException {
return perform(url, target -> target
.request()
.get(targetEntity));
}
/**
* The retrieve resource allows you to retrieve access API resources given a SoundCloud id,
* this method avoids the need of resolving the the URL through an extra request.
*
* @param id
* the url to retrieve
* @param <T>
* The type of the entity.
* @param targetEntity
* The entity to retrieve from the Soundcloud API.
* @return
* The object of type T from the API
* @throws SoundCloudException
* if the resource could not be accessed.
*/
protected <T> T retrieve(final int id, final Class<T> targetEntity)
throws SoundCloudException {
return perform(target -> target.path("tracks")
.path(id + ".json")
.queryParam("client_id", CLIENT_ID)
.request()
.get(targetEntity));
}
}
}