@@ -35,8 +35,9 @@ public class OpenSpotifyAPI {
3535
3636 private final Executor executor = Executors .newSingleThreadExecutor ();
3737
38- private final Map <Track , BufferedImage > imageCache = new ConcurrentHashMap <>();
39- private final List <Track > cacheQueue = new ArrayList <>();
38+ private final Map <Track , String > urlCache = new ConcurrentHashMap <>();
39+ private final Map <String , BufferedImage > imageCache = new ConcurrentHashMap <>();
40+ private final List <String > cacheQueue = new ArrayList <>();
4041 private int cacheSize = 10 ;
4142
4243 private AccessTokenResponse accessTokenResponse ;
@@ -94,6 +95,26 @@ public void requestImageAsync(Track track, Consumer<BufferedImage> callback) {
9495 });
9596 }
9697
98+ /**
99+ * Request the cover image url of the given track asynchronously.
100+ * If the track is already in the cache, it will be returned.
101+ *
102+ * @param track The track to lookup
103+ * @param callback Response with the image url of the track. It won't be called on an error.
104+ */
105+ public void requestImageUrlAsync (Track track , Consumer <String > callback ) {
106+ this .executor .execute (() -> {
107+ try {
108+ String imageUrl = this .requestImageUrl (track );
109+ if (imageUrl != null ) {
110+ callback .accept (imageUrl );
111+ }
112+ } catch (Exception error ) {
113+ error .printStackTrace ();
114+ }
115+ });
116+ }
117+
97118 /**
98119 * Request the cover image of the given track synchronously.
99120 * If the track is already in the cache, it will be returned.
@@ -103,22 +124,61 @@ public void requestImageAsync(Track track, Consumer<BufferedImage> callback) {
103124 * @throws IOException if the request failed
104125 */
105126 public BufferedImage requestImage (Track track ) throws IOException {
106- return this .requestImage (track , true );
127+ String url = this .requestImageUrl (track );
128+ if (url == null ) {
129+ return null ;
130+ }
131+
132+ // Try to get image from cache by url
133+ BufferedImage cachedImage = this .imageCache .get (url );
134+ if (cachedImage != null ) {
135+ return cachedImage ;
136+ }
137+
138+ // Download the image
139+ BufferedImage image = ImageIO .read (new URL (url ));
140+ if (image == null ) {
141+ throw new IOException ("Could not load image: " + url );
142+ }
143+
144+ // Remove image from cache if cache is full
145+ if (this .cacheQueue .size () > this .cacheSize ) {
146+ String urlToRemove = this .cacheQueue .remove (0 );
147+ this .imageCache .remove (urlToRemove );
148+ }
149+
150+ // Add new image to cache
151+ this .imageCache .put (url , image );
152+ this .cacheQueue .add (url );
153+
154+ return image ;
155+ }
156+
157+ /**
158+ * Request the cover image url of the given track.
159+ * If the track is already in the cache, it will be returned.
160+ *
161+ * @param track The track to lookup
162+ * @return The url of the track or null if it failed
163+ * @throws IOException if the request failed
164+ */
165+ public String requestImageUrl (Track track ) throws IOException {
166+ return this .requestImageUrl (track , true );
107167 }
108168
109169 /**
110- * Request the cover image of the given track.
170+ * Request the cover image url of the given track.
111171 * If the track is already in the cache, it will be returned.
112172 *
113173 * @param track The track to lookup
114174 * @param canGenerateNewAccessToken It will try again once if it fails
115- * @return Buffered image track.
175+ * @return The url of the track or null if it failed
116176 * @throws IOException if the request failed
117177 */
118- private BufferedImage requestImage (Track track , boolean canGenerateNewAccessToken ) throws IOException {
119- BufferedImage cachedImage = this .imageCache .get (track );
120- if (cachedImage != null ) {
121- return cachedImage ;
178+ private String requestImageUrl (Track track , boolean canGenerateNewAccessToken ) throws IOException {
179+ String cachedUrl = this .urlCache .get (track );
180+ if (cachedUrl != null ) {
181+ return cachedUrl ;
122182 }
123183
124184 // Create REST API url
@@ -140,7 +200,7 @@ private BufferedImage requestImage(Track track, boolean canGenerateNewAccessToke
140200 this .accessTokenResponse = this .generateAccessToken ();
141201
142202 // Try again
143- return this .requestImage (track , false );
203+ return this .requestImageUrl (track , false );
144204 } else {
145205 // Request failed twice
146206 return null ;
@@ -154,24 +214,10 @@ private BufferedImage requestImage(Track track, boolean canGenerateNewAccessToke
154214 // Get largest image url
155215 String imageUrl = openTrack .album .images .get (0 ).url ;
156216
157- // Download cover image
217+ // Cache url and return it
158218 if (imageUrl != null ) {
159- BufferedImage image = ImageIO .read (new URL (imageUrl ));
160- if (image == null ) {
161- throw new IOException ("Could not load image: " + imageUrl );
162- }
163-
164- // Remove image from cache if cache is full
165- if (this .cacheQueue .size () > this .cacheSize ) {
166- Track element = this .cacheQueue .remove (0 );
167- this .imageCache .remove (element );
168- }
169-
170- // Add new image to cache
171- this .imageCache .put (track , image );
172- this .cacheQueue .add (track );
173-
174- return image ;
219+ this .urlCache .put (track , imageUrl );
220+ return imageUrl ;
175221 }
176222
177223 return null ;
0 commit comments