diff --git a/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/LastFMToSpotify.java b/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/LastFMToSpotify.java
index d13787b..aa23918 100644
--- a/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/LastFMToSpotify.java
+++ b/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/LastFMToSpotify.java
@@ -7,6 +7,7 @@ import de.b00tload.tools.lastfmtospotifyplaylist.arguments.Arguments;
import de.b00tload.tools.lastfmtospotifyplaylist.util.BrowserHelper;
import de.b00tload.tools.lastfmtospotifyplaylist.util.PeriodHelper;
+import de.b00tload.tools.lastfmtospotifyplaylist.util.SpotifyCredentials;
import de.b00tload.tools.lastfmtospotifyplaylist.util.TokenHelper;
import de.umass.lastfm.Caller;
import de.umass.lastfm.Track;
@@ -15,7 +16,6 @@ import io.javalin.Javalin;
import io.javalin.http.ContentType;
import io.javalin.http.HttpStatus;
import se.michaelthelin.spotify.SpotifyApi;
-import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials;
import se.michaelthelin.spotify.model_objects.specification.Playlist;
import java.net.URI;
@@ -83,11 +83,16 @@ public class LastFMToSpotify {
AtomicBoolean waiting = new AtomicBoolean(true);
if (configuration.containsKey("cache.crypto") && TokenHelper.existsTokens()) {
logLn("Cached credentials have been found.", 2);
- logLn("Fetching old credentials, refreshing them and saving to cache", 2);
- AuthorizationCodeCredentials oldcred = TokenHelper.fetchTokens();
- AuthorizationCodeCredentials newcred = api.authorizationCodeRefresh(api.getClientId(), api.getClientSecret(), oldcred.getRefreshToken()).setHeader("User-Agent", configuration.get("requests.useragent")).build().execute();
- TokenHelper.saveTokens(newcred);
- configuration.put("spotify.access", newcred.getAccessToken());
+ logLn("Fetching credentials from cache.", 2);
+ SpotifyCredentials cred = TokenHelper.fetchTokens();
+ api.setRefreshToken(cred.getRefreshToken());
+
+ if(!cred.isValid()){
+ logLn("Cached credentials are invalid due to age. Refreshing and saving to cache", 2);
+ cred.refreshCredentials(api.authorizationCodeRefresh().build().execute());
+ TokenHelper.saveTokens(cred);
+ }
+ configuration.put("spotify.access", cred.getAccessToken());
} else {
try (Javalin webserver = Javalin.create().start(9876)) {
if (configuration.containsKey("cache.crypto")) logLn("No cached credentials have been found.", 2);
@@ -99,7 +104,7 @@ public class LastFMToSpotify {
webserver.get("/callback/spotify", ctx -> {
if(ctx.queryParamMap().containsKey("code")) {
logLn("Received spotify authentication code. Requesting credentials.", 2);
- AuthorizationCodeCredentials cred = api.authorizationCode(ctx.queryParam("code")).setHeader("User-Agent", configuration.get("requests.useragent")).build().execute();
+ SpotifyCredentials cred = new SpotifyCredentials(api.authorizationCode(ctx.queryParam("code")).setHeader("User-Agent", configuration.get("requests.useragent")).build().execute());
configuration.put("spotify.access", cred.getAccessToken());
if(configuration.containsKey("cache.crypto")) {
logLn("Saving credentials to cache.", 2);
diff --git a/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/util/SpotifyCredentials.java b/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/util/SpotifyCredentials.java
new file mode 100644
index 0000000..5c5f4d2
--- /dev/null
+++ b/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/util/SpotifyCredentials.java
@@ -0,0 +1,70 @@
+package de.b00tload.tools.lastfmtospotifyplaylist.util;
+import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials;
+
+import java.io.Serializable;
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+/**
+ * A wrapper class for se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials. Implements checking validity of access token.
+ */
+public class SpotifyCredentials implements Serializable {
+
+ private String accessToken;
+ private String refreshToken;
+ private LocalDateTime validUntil;
+
+ /**
+ * Initializes the class
+ * @param cred The AuthorizationCodeCredentials to be saved. Recommended for use with recently (last few seconds) generated Credentials
+ */
+ public SpotifyCredentials(AuthorizationCodeCredentials cred){
+ this.accessToken = cred.getAccessToken();
+ this.refreshToken = cred.getRefreshToken();
+ this.validUntil = LocalDateTime.now(Clock.systemDefaultZone()).plusSeconds(cred.getExpiresIn());
+ }
+
+ /**
+ * Get the access token. It becomes invalid after a certain period of time. Check validity with isValid().
+ * @return An access token that can be provided in subsequent calls, for example to Spotify Web API services.
+ */
+ public String getAccessToken(){
+ return accessToken;
+ }
+
+ /**
+ * Get the refresh token. This token can be sent to the Spotify Accounts service in place of an authorization code to retrieve a new access token.
+ * @return A token that can be sent to the Spotify Accounts service in place of an access token.
+ */
+ public String getRefreshToken(){
+ return refreshToken;
+ }
+
+ /**
+ * Returns a LocalDateTime which represents the latest point in time when the access token is still valid.
+ * @return A LocalDateTime representing the latest point in time of access token validity
+ */
+ public LocalDateTime getValidUntil(){
+ return validUntil;
+ }
+
+ /**
+ * Checks whether the saved access token is still valid for use in calls, for example to the Spotify Web API services.
+ * @return true if the access token is still valid for use, false if not.
+ */
+ public boolean isValid(){
+ return LocalDateTime.now(Clock.systemDefaultZone()).isBefore(getValidUntil());
+ }
+
+ /**
+ * Refreshes the access token. If a new refresh token is provided it will be saved as well.
+ * @param cred The AuthorizationCodeCredentials to be saved. Recommended for use with recently (last few seconds) generated Credentials
+ */
+ public void refreshCredentials(AuthorizationCodeCredentials cred){
+ this.accessToken = cred.getAccessToken();
+ if(Objects.nonNull(cred.getRefreshToken())) this.refreshToken = cred.getRefreshToken();
+ this.validUntil = LocalDateTime.now(Clock.systemDefaultZone()).plusSeconds(cred.getExpiresIn());
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/util/TokenHelper.java b/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/util/TokenHelper.java
index b9e434a..ffe40c6 100644
--- a/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/util/TokenHelper.java
+++ b/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/util/TokenHelper.java
@@ -1,7 +1,5 @@
package de.b00tload.tools.lastfmtospotifyplaylist.util;
-import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials;
-
import java.nio.file.Path;
import static de.b00tload.tools.lastfmtospotifyplaylist.LastFMToSpotify.USER_HOME;
@@ -10,23 +8,23 @@ import static de.b00tload.tools.lastfmtospotifyplaylist.LastFMToSpotify.configur
public class TokenHelper {
/**
- * Manages saving a se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials into "~/.lfm2s/spotify.lfm2scred" using de.b00tload.tools.lastfmtospotifyplaylist.util.CryptoHelper.serializeEncrypted(...)
- * @param cred The se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials to be saved
+ * Manages saving a de.b00tload.tools.lastfmtospotifyplaylist.util.SpotifyCredentials into "~/.lfm2s/spotify.lfm2scred" using de.b00tload.tools.lastfmtospotifyplaylist.util.CryptoHelper.serializeEncrypted(...)
+ * @param cred The de.b00tload.tools.lastfmtospotifyplaylist.util.SpotifyCredentials to be saved
*/
- public static void saveTokens(AuthorizationCodeCredentials cred) {
+ public static void saveTokens(SpotifyCredentials cred) {
CryptoHelper.serializeEncrypted(cred, Path.of(USER_HOME, "/.lfm2s/spotify.lfm2scred"), CryptoHelper.createKeyFromPassword(configuration.get("cache.crypto")));
}
/**
- * Manages retrieving a se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials from "~/.lfm2s/spotify.lfm2scred" using de.b00tload.tools.lastfmtospotifyplaylist.util.CryptoHelper.deserializeEncrypted(...)
- * @return The retrieved se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials
+ * Manages retrieving a de.b00tload.tools.lastfmtospotifyplaylist.util.SpotifyCredentials from "~/.lfm2s/spotify.lfm2scred" using de.b00tload.tools.lastfmtospotifyplaylist.util.CryptoHelper.deserializeEncrypted(...)
+ * @return The retrieved de.b00tload.tools.lastfmtospotifyplaylist.util.SpotifyCredentials
*/
- public static AuthorizationCodeCredentials fetchTokens() {
- return (AuthorizationCodeCredentials) CryptoHelper.deserializeEncrypted(Path.of(USER_HOME, "/.lfm2s/spotify.lfm2scred"), CryptoHelper.createKeyFromPassword(configuration.get("cache.crypto")));
+ public static SpotifyCredentials fetchTokens() {
+ return (SpotifyCredentials) CryptoHelper.deserializeEncrypted(Path.of(USER_HOME, "/.lfm2s/spotify.lfm2scred"), CryptoHelper.createKeyFromPassword(configuration.get("cache.crypto")));
}
/**
- * Checks whether the saved spotify AuthorizationCodeCredentials at "~/.lfm2s/spotify.lfm2scred" exist
+ * Checks whether the saved SpotifyCredentials at "~/.lfm2s/spotify.lfm2scred" exist
* @return true if file exists, false if not
*/
public static boolean existsTokens(){