From 011cdc1481455e7d928e813a951e1ebae1e4a0e2 Mon Sep 17 00:00:00 2001 From: Alix von Schirp Date: Sat, 21 Jan 2023 02:24:52 +0100 Subject: [PATCH] Implemented adding songs to playlist \+ Creation of playlist with default name \+ implemented track search (opens #2) \+ Implemented adding tracks to list \+ Implemented setting custom playlist cover - Missing ability to select file to upload as cover (opens #3) - Missing saving and retrieving auth tokens for use in chron jobs + flag to enable (opens #4) - Known issue: some tracks cannot be found, although they were played on spotify to be scrobbled onto lastfm (see #2) - Missing flag to set a custom playlist name (opens #5) - missing flags to make playlists public and/or collaborative on creation. (opens #6) --- .../LastFMToSpotify.java | 70 +++++++++++++++---- .../util/TokenHelper.java | 19 +++++ 2 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/util/TokenHelper.java diff --git a/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/LastFMToSpotify.java b/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/LastFMToSpotify.java index 47b087d..48eacde 100644 --- a/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/LastFMToSpotify.java +++ b/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/LastFMToSpotify.java @@ -1,15 +1,26 @@ package de.b00tload.tools.lastfmtospotifyplaylist; +import com.neovisionaries.i18n.CountryCode; import de.b00tload.tools.lastfmtospotifyplaylist.arguments.ArgumentHandler; import de.b00tload.tools.lastfmtospotifyplaylist.arguments.Arguments; import de.b00tload.tools.lastfmtospotifyplaylist.util.PeriodHelper; +import de.b00tload.tools.lastfmtospotifyplaylist.util.TokenHelper; import de.umass.lastfm.Caller; import de.umass.lastfm.Track; import de.umass.lastfm.User; +import io.javalin.Javalin; +import io.javalin.http.ContentType; +import se.michaelthelin.spotify.SpotifyApi; +import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials; +import se.michaelthelin.spotify.model_objects.specification.Playlist; -import java.util.Collection; -import java.util.HashMap; +import java.net.URI; +import java.time.Clock; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; import static de.b00tload.tools.lastfmtospotifyplaylist.util.Logger.logLn; @@ -22,6 +33,7 @@ public class LastFMToSpotify { // create hash map with user agent configuration = new HashMap<>(); configuration.put("requests.useragent", "LastFMToSpotify/1.0-Snapshot (" + System.getProperty("os.name") + "; " + System.getProperty("os.arch") + ") Java/" + System.getProperty("java.version")); + configuration.put("playlist.name", "LastFMToSpotify@" + LocalDateTime.now(Clock.systemDefaultZone()).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); // parse arguments for (int a = 0; a < args.length; a++) { Arguments arg; @@ -49,28 +61,60 @@ public class LastFMToSpotify { // Start Progress Bar try { logLn("Authenticating with Spotify...", 1); + SpotifyApi.Builder build = SpotifyApi.builder(); + build.setClientId(configuration.get("spotify.clientid")); + build.setClientSecret(configuration.get("spotify.secret")); + build.setRedirectUri(URI.create("http://localhost:9876/callback/spotify/")); + SpotifyApi api = build.build(); + AtomicBoolean waiting = new AtomicBoolean(true); + try (Javalin webserver = Javalin.create().start(9876)) { + 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); + ctx.result("success. ").contentType(ContentType.TEXT_HTML); + waiting.set(false); + } else { + logLn("Error: Spotify authorization failed."+LINE_SEPERATOR+ctx.queryParam("error"), 1); + System.exit(500); + } + }); + logLn("Waiting for Spotify authorization.", 1); + //TODO: Open auth page in Browser + while (waiting.get()); + webserver.stop(); + } logLn("Authenticating with LastFM...", 1); Caller.getInstance().setUserAgent(configuration.get("requests.useragent")); logLn(User.getInfo(configuration.get("lastfm.user"), configuration.get("lastfm.apikey")).getName(), 1); logLn("Reading from LastFM...", 1); Collection tracks = User.getTopTracks(configuration.get("lastfm.user"), PeriodHelper.getPeriodByString(configuration.get("lastfm.period")), configuration.get("lastfm.apikey")); - for (Track track : tracks) { - logLn(track.getName() + " by " + track.getArtist(), 3); - } logLn("Creating Playlist...", 1); - //SpotifyApi.Builder build = SpotifyApi.builder(); - //build.setClientId(configuration.get("spotify.clientid")); - //build.setClientSecret(configuration.get("spotify.secret")); - //build.setRedirectUri(URI.create("http://localhost:9876/callback/spotify/")); - //SpotifyApi api = build.build(); - //api.setAccessToken(configuration.get("spotify.access")); - //api.createPlaylist(api.getCurrentUsersProfile().build().execute().getId(), configuration.get("playlist.name")).setHeader("User-Agent", configuration.get("requests.useragent")); + api.setAccessToken(configuration.get("spotify.access")); + Playlist list = api.createPlaylist(api.getCurrentUsersProfile().build().execute().getId(), configuration.get("playlist.name")).public_(configuration.containsKey("playlist.public")||configuration.containsKey("playlist.collab")).collaborative(configuration.containsKey("playlist.collab")).setHeader("User-Agent", configuration.get("requests.useragent")).build().execute(); + List adders = new LinkedList<>(); + for (Track track : tracks) { + logLn("Adding " + track.getName() + " by " + track.getArtist(), 3); + StringBuilder searchQuery = new StringBuilder(); + searchQuery.append("track:").append(track.getName()); + searchQuery.append(" artist:").append(track.getArtist()); + if(track.getAlbum()!=null&&!track.getAlbum().equalsIgnoreCase("null")&&!track.getAlbum().isEmpty()) + searchQuery.append(" album:").append(track.getAlbum()); + logLn("Search query: " + searchQuery.toString(), 3); + se.michaelthelin.spotify.model_objects.specification.Track[] add = api.searchTracks(searchQuery.toString()).market(CountryCode.DE).setHeader("User-Agent", configuration.get("requests.useragent")).build().execute().getItems(); + if(add.length!=0) { + adders.add(add[0].getUri()); + logLn("Added " + add[0].getName() + " to " + configuration.get("playlist.name"), 3); + } + } + api.addItemsToPlaylist(list.getId(), adders.toArray(String[]::new)).build().execute(); + if(configuration.containsKey(configuration.get("playlist.cover"))) api.uploadCustomPlaylistCoverImage(list.getId()).image_data(configuration.get("playlist.cover")).build().execute(); logLn("Done.", 1); // } catch (IOException | ParseException | SpotifyWebApiException e) { } catch (Exception e) { throw new RuntimeException(e); } - //TODO: Implement } } diff --git a/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/util/TokenHelper.java b/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/util/TokenHelper.java new file mode 100644 index 0000000..8dfd3d2 --- /dev/null +++ b/src/main/java/de/b00tload/tools/lastfmtospotifyplaylist/util/TokenHelper.java @@ -0,0 +1,19 @@ +package de.b00tload.tools.lastfmtospotifyplaylist.util; + +import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials; + +public class TokenHelper { + public static void saveTokens(AuthorizationCodeCredentials cred) { + //TODO: Save tokens + } + + public static String getAccessToken(){ + //TODO: Read AccessToken + return ""; + } + + public static String getRefreshToken(){ + //TODO: Read RefreshToken + return ""; + } +}