@@ -29,6 +29,7 @@ import static de.b00tload.tools.lastfmtospotifyplaylist.util.Logger.logLn;
|
||||
public class LastFMToSpotify {
|
||||
|
||||
public static final String LINE_SEPERATOR = System.getProperty("line.separator");
|
||||
public static final String USER_HOME = System.getProperty("user.home");
|
||||
public static HashMap<String, String> configuration;
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -67,7 +68,6 @@ public class LastFMToSpotify {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
logLn("Authenticating with Spotify...", 1);
|
||||
SpotifyApi.Builder build = SpotifyApi.builder();
|
||||
@@ -76,13 +76,22 @@ public class LastFMToSpotify {
|
||||
build.setRedirectUri(URI.create("http://localhost:9876/callback/spotify/"));
|
||||
SpotifyApi api = build.build();
|
||||
AtomicBoolean waiting = new AtomicBoolean(true);
|
||||
if (configuration.containsKey("cache.crypto") && TokenHelper.existsTokens()) {
|
||||
AuthorizationCodeCredentials oldcred = TokenHelper.fetchTokens();
|
||||
AuthorizationCodeCredentials newcred = api.authorizationCodeRefresh(api.getClientId(), api.getClientSecret(), oldcred.getRefreshToken()).build().execute();
|
||||
TokenHelper.saveTokens(newcred);
|
||||
configuration.put("spotify.access", newcred.getAccessToken());
|
||||
} else {
|
||||
try (Javalin webserver = Javalin.create().start(9876)) {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(webserver::stop));
|
||||
// webserver.exception(Exception.class, (exception, ctx) -> {
|
||||
// ctx.result(exception.getMessage());
|
||||
// });
|
||||
webserver.get("/callback/spotify", ctx -> {
|
||||
if(ctx.queryParamMap().containsKey("code")) {
|
||||
AuthorizationCodeCredentials cred = api.authorizationCode(ctx.queryParam("code")).build().execute();
|
||||
configuration.put("spotify.access", cred.getAccessToken());
|
||||
if(configuration.containsKey("spotify.saveaccess")) TokenHelper.saveTokens(cred);
|
||||
if(configuration.containsKey("cache.crypto")) TokenHelper.saveTokens(cred);
|
||||
ctx.result("success. <script>let win = window.open(null, '_self');win.close();</script>").contentType(ContentType.TEXT_HTML).status(HttpStatus.OK);
|
||||
waiting.set(false);
|
||||
} else {
|
||||
@@ -94,6 +103,7 @@ public class LastFMToSpotify {
|
||||
//TODO: Open auth page in Browser
|
||||
while (waiting.get());
|
||||
}
|
||||
}
|
||||
logLn("Authenticating with LastFM...", 1);
|
||||
Caller.getInstance().setApiRootUrl("https://ws.audioscrobbler.com/2.0/");
|
||||
Caller.getInstance().setUserAgent(configuration.get("requests.useragent"));
|
||||
|
||||
@@ -41,6 +41,7 @@ public class ArgumentHandler {
|
||||
case NAME -> name(value);
|
||||
case PUBLIC -> access("public");
|
||||
case COLLABORATIVE -> access("collaborative");
|
||||
case SPOTIFY_CACHING -> cache(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,4 +230,12 @@ public class ArgumentHandler {
|
||||
|
||||
configuration.put("playlist.name", name);
|
||||
}
|
||||
|
||||
public static void cache(String value){
|
||||
if (value == null || value.isEmpty()) {
|
||||
logLn("--spotifycache must be provided with a password. Check usage: " + Arguments.SPOTIFY_CACHING.getUsage(), 1);
|
||||
System.exit(500);
|
||||
}
|
||||
configuration.put("cache.crypto", value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ public enum Arguments {
|
||||
PUBLIC("public", "[Optional] [EXCLUSIVE: public, collaborative]" + LINE_SEPERATOR
|
||||
+ "Makes the playlist public.", "--public", "pP"),
|
||||
COLLABORATIVE("collaborative", "[Optional] [EXCLUSIVE: public, collaborative]" + LINE_SEPERATOR
|
||||
+ "Makes the playlist collaborative.", "--collaborative", "pC");
|
||||
+ "Makes the playlist collaborative.", "--collaborative", "pC"),
|
||||
SPOTIFY_CACHING("spotifycache", "[Optional]" + LINE_SEPERATOR
|
||||
+ "Saves and (if possible) retrieves auth tokens from a locally saved file.", "--spotifycache <password>", "sTk");
|
||||
|
||||
private final String name;
|
||||
private final String description;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package de.b00tload.tools.lastfmtospotifyplaylist.util;
|
||||
|
||||
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.*;
|
||||
import java.nio.file.Path;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.KeySpec;
|
||||
|
||||
public class CryptoHelper {
|
||||
|
||||
public static SecretKey createKeyFromPassword(String pass){
|
||||
try {
|
||||
KeySpec spec = new PBEKeySpec(pass.toCharArray(), "abcdefghijklmnop".getBytes(), 65536, 256); // AES-256
|
||||
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
|
||||
byte[] key = new byte[0];
|
||||
key = f.generateSecret(spec).getEncoded();
|
||||
return new SecretKeySpec(key, "AES");
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void serializeEncrypted(Serializable obj, Path file, SecretKey key){
|
||||
try {
|
||||
if(file.toFile().exists()) file.toFile().delete();
|
||||
if(!file.getParent().toFile().exists()) file.getParent().toFile().mkdirs();
|
||||
file.toFile().createNewFile();
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
IvParameterSpec iv = new IvParameterSpec("abcdefghijklmnop".getBytes());
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
SealedObject sealedObject = new SealedObject( obj, cipher);
|
||||
CipherOutputStream cipherOutputStream = new CipherOutputStream( new BufferedOutputStream( new FileOutputStream( file.toFile() ) ), cipher );
|
||||
ObjectOutputStream outputStream = new ObjectOutputStream( cipherOutputStream );
|
||||
outputStream.writeObject( sealedObject );
|
||||
outputStream.close();
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IOException | IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Serializable deserializeEncrypted(Path file, SecretKey key) {
|
||||
Serializable ret = null;
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
IvParameterSpec iv = new IvParameterSpec("abcdefghijklmnop".getBytes());
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, iv);
|
||||
CipherInputStream cipherInputStream = new CipherInputStream( new BufferedInputStream( new FileInputStream( file.toFile() ) ), cipher );
|
||||
ObjectInputStream inputStream = new ObjectInputStream( cipherInputStream );
|
||||
SealedObject sealedObject = (SealedObject) inputStream.readObject();
|
||||
ret = (Serializable) sealedObject.getObject(cipher);
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IOException | IllegalBlockSizeException | ClassNotFoundException | BadPaddingException | InvalidAlgorithmParameterException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,18 +2,21 @@ 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;
|
||||
import static de.b00tload.tools.lastfmtospotifyplaylist.LastFMToSpotify.configuration;
|
||||
|
||||
public class TokenHelper {
|
||||
public static void saveTokens(AuthorizationCodeCredentials cred) {
|
||||
//TODO: Save tokens
|
||||
CryptoHelper.serializeEncrypted(cred, Path.of(USER_HOME, "/.lfm2s/spotify.lfm2scred"), CryptoHelper.createKeyFromPassword(configuration.get("cache.crypto")));
|
||||
}
|
||||
|
||||
public static String getAccessToken(){
|
||||
//TODO: Read AccessToken
|
||||
return "";
|
||||
public static AuthorizationCodeCredentials fetchTokens() {
|
||||
return (AuthorizationCodeCredentials) CryptoHelper.deserializeEncrypted(Path.of(USER_HOME, "/.lfm2s/spotify.lfm2scred"), CryptoHelper.createKeyFromPassword(configuration.get("cache.crypto")));
|
||||
}
|
||||
|
||||
public static String getRefreshToken(){
|
||||
//TODO: Read RefreshToken
|
||||
return "";
|
||||
public static boolean existsTokens(){
|
||||
return Path.of(USER_HOME, "/.lfm2s/spotify.lfm2scred").toFile().exists();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user