diff --git a/main/src/main/java/com/github/topi314/lavasrc/deezer/DeezerAudioSourceManager.java b/main/src/main/java/com/github/topi314/lavasrc/deezer/DeezerAudioSourceManager.java index 5fe2b16a..034617f3 100644 --- a/main/src/main/java/com/github/topi314/lavasrc/deezer/DeezerAudioSourceManager.java +++ b/main/src/main/java/com/github/topi314/lavasrc/deezer/DeezerAudioSourceManager.java @@ -15,6 +15,7 @@ import com.sedmelluq.discord.lavaplayer.track.*; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.HttpClientBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,6 +26,8 @@ import java.io.IOException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -32,6 +35,7 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Pattern; +import java.util.stream.Collectors; public class DeezerAudioSourceManager extends ExtendedAudioSourceManager implements HttpConfigurable, AudioSearchManager { @@ -49,6 +53,7 @@ public class DeezerAudioSourceManager extends ExtendedAudioSourceManager impleme private final String masterDecryptionKey; private final HttpInterfaceManager httpInterfaceManager; + private Tokens tokens; public DeezerAudioSourceManager(String masterDecryptionKey) { if (masterDecryptionKey == null || masterDecryptionKey.isEmpty()) { @@ -58,6 +63,43 @@ public DeezerAudioSourceManager(String masterDecryptionKey) { this.httpInterfaceManager = HttpClientTools.createDefaultThreadLocalManager(); } + private void refreshSession() throws IOException{ + var getSessionID = new HttpPost(DeezerAudioSourceManager.PRIVATE_API_BASE + "?method=deezer.ping&input=3&api_version=1.0&api_token="); + var json = LavaSrcTools.fetchResponseAsJson(this.getHttpInterface(), getSessionID); + + checkResponse(json, "Failed to get session ID: "); + var sessionID = json.get("results").get("SESSION").text(); + + var getUserToken = new HttpPost(DeezerAudioSourceManager.PRIVATE_API_BASE + "?method=deezer.getUserData&input=3&api_version=1.0&api_token="); + getUserToken.setHeader("Cookie", "sid=" + sessionID); + json = LavaSrcTools.fetchResponseAsJson(this.getHttpInterface(), getUserToken); + + checkResponse(json, "Failed to get user token: "); + this.tokens = new Tokens( + json.get("results").get("USER").get("OPTIONS").get("license_token").text(), + json.get("results").get("checkForm").text(), + Instant.now().plus(3600, ChronoUnit.SECONDS) + ); + } + + public Tokens getTokens() throws IOException{ + if (this.tokens == null || Instant.now().isAfter(this.tokens.expireAt)) { + this.refreshSession(); + } + return this.tokens; + } + + static void checkResponse(JsonBrowser json, String message) throws IllegalStateException { + if (json == null) { + throw new IllegalStateException(message + "No response"); + } + var errors = json.get("data").index(0).get("errors").values(); + if (!errors.isEmpty()) { + var errorsStr = errors.stream().map(error -> error.get("code").text() + ": " + error.get("message").text()).collect(Collectors.joining(", ")); + throw new IllegalStateException(message + errorsStr); + } + } + @NotNull @Override public String getSourceName() { @@ -361,4 +403,16 @@ public HttpInterface getHttpInterface() { return this.httpInterfaceManager.getInterface(); } + public static class Tokens { + public String api; + public String license; + public Instant expireAt; + + public Tokens(String api,String license, Instant expireAt) { + this.api = api; + this.license = license; + this.expireAt = expireAt; + } + } + } diff --git a/main/src/main/java/com/github/topi314/lavasrc/deezer/DeezerAudioTrack.java b/main/src/main/java/com/github/topi314/lavasrc/deezer/DeezerAudioTrack.java index d1ddf4cf..a08451d3 100644 --- a/main/src/main/java/com/github/topi314/lavasrc/deezer/DeezerAudioTrack.java +++ b/main/src/main/java/com/github/topi314/lavasrc/deezer/DeezerAudioTrack.java @@ -36,46 +36,22 @@ public DeezerAudioTrack(AudioTrackInfo trackInfo, String albumName, String album } private URI getTrackMediaURI() throws IOException, URISyntaxException { - var getSessionID = new HttpPost(DeezerAudioSourceManager.PRIVATE_API_BASE + "?method=deezer.ping&input=3&api_version=1.0&api_token="); - var json = LavaSrcTools.fetchResponseAsJson(this.sourceManager.getHttpInterface(), getSessionID); - - this.checkResponse(json, "Failed to get session ID: "); - var sessionID = json.get("results").get("SESSION").text(); - - var getUserToken = new HttpPost(DeezerAudioSourceManager.PRIVATE_API_BASE + "?method=deezer.getUserData&input=3&api_version=1.0&api_token="); - getUserToken.setHeader("Cookie", "sid=" + sessionID); - json = LavaSrcTools.fetchResponseAsJson(this.sourceManager.getHttpInterface(), getUserToken); - - this.checkResponse(json, "Failed to get user token: "); - var userLicenseToken = json.get("results").get("USER").get("OPTIONS").get("license_token").text(); - var apiToken = json.get("results").get("checkForm").text(); - - var getTrackToken = new HttpPost(DeezerAudioSourceManager.PRIVATE_API_BASE + "?method=song.getData&input=3&api_version=1.0&api_token=" + apiToken); + var tokens = this.sourceManager.getTokens(); + var getTrackToken = new HttpPost(DeezerAudioSourceManager.PRIVATE_API_BASE + "?method=song.getData&input=3&api_version=1.0&api_token=" + tokens.api); getTrackToken.setEntity(new StringEntity("{\"sng_id\":\"" + this.trackInfo.identifier + "\"}", ContentType.APPLICATION_JSON)); - json = LavaSrcTools.fetchResponseAsJson(this.sourceManager.getHttpInterface(), getTrackToken); + var json = LavaSrcTools.fetchResponseAsJson(this.sourceManager.getHttpInterface(), getTrackToken); - this.checkResponse(json, "Failed to get track token: "); + DeezerAudioSourceManager.checkResponse(json, "Failed to get track token: "); var trackToken = json.get("results").get("TRACK_TOKEN").text(); var getMediaURL = new HttpPost(DeezerAudioSourceManager.MEDIA_BASE + "/get_url"); - getMediaURL.setEntity(new StringEntity("{\"license_token\":\"" + userLicenseToken + "\",\"media\": [{\"type\": \"FULL\",\"formats\": [{\"cipher\": \"BF_CBC_STRIPE\", \"format\": \"MP3_128\"}]}],\"track_tokens\": [\"" + trackToken + "\"]}", ContentType.APPLICATION_JSON)); + getMediaURL.setEntity(new StringEntity("{\"license_token\":\"" + tokens.license + "\",\"media\": [{\"type\": \"FULL\",\"formats\": [{\"cipher\": \"BF_CBC_STRIPE\", \"format\": \"MP3_128\"}]}],\"track_tokens\": [\"" + trackToken + "\"]}", ContentType.APPLICATION_JSON)); json = LavaSrcTools.fetchResponseAsJson(this.sourceManager.getHttpInterface(), getMediaURL); - this.checkResponse(json, "Failed to get media URL: "); + DeezerAudioSourceManager.checkResponse(json, "Failed to get media URL: "); return new URI(json.get("data").index(0).get("media").index(0).get("sources").index(0).get("url").text()); } - private void checkResponse(JsonBrowser json, String message) throws IllegalStateException { - if (json == null) { - throw new IllegalStateException(message + "No response"); - } - var errors = json.get("data").index(0).get("errors").values(); - if (!errors.isEmpty()) { - var errorsStr = errors.stream().map(error -> error.get("code").text() + ": " + error.get("message").text()).collect(Collectors.joining(", ")); - throw new IllegalStateException(message + errorsStr); - } - } - private byte[] getTrackDecryptionKey() throws NoSuchAlgorithmException { var md5 = Hex.encodeHex(MessageDigest.getInstance("MD5").digest(this.trackInfo.identifier.getBytes()), true); var master_key = this.sourceManager.getMasterDecryptionKey().getBytes();