1212import java .io .IOException ;
1313import java .io .InputStreamReader ;
1414import java .net .URL ;
15- import java .util .ArrayList ;
16- import java .util .List ;
17- import java .util .Map ;
18- import java .util .concurrent .ConcurrentHashMap ;
1915import java .util .concurrent .Executor ;
2016import java .util .concurrent .Executors ;
2117import java .util .function .Consumer ;
@@ -35,10 +31,8 @@ public class OpenSpotifyAPI {
3531
3632 private final Executor executor = Executors .newSingleThreadExecutor ();
3733
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 <>();
41- private int cacheSize = 10 ;
34+ private final Cache <BufferedImage > imageCache = new Cache <>(10 );
35+ private final Cache <OpenTrack > openTrackCache = new Cache <>(100 );
4236
4337 private AccessTokenResponse accessTokenResponse ;
4438
@@ -115,6 +109,26 @@ public void requestImageUrlAsync(Track track, Consumer<String> callback) {
115109 });
116110 }
117111
112+ /**
113+ * Request the track information of the given track asynchronously.
114+ * If the open track is already in the cache, it will be returned.
115+ *
116+ * @param track The track to lookup
117+ * @param callback Response with the open track. It won't be called on an error.
118+ */
119+ public void requestOpenTrackAsync (Track track , Consumer <OpenTrack > callback ) {
120+ this .executor .execute (() -> {
121+ try {
122+ OpenTrack openTrack = this .requestOpenTrack (track );
123+ if (openTrack != null ) {
124+ callback .accept (openTrack );
125+ }
126+ } catch (Exception error ) {
127+ error .printStackTrace ();
128+ }
129+ });
130+ }
131+
118132 /**
119133 * Request the cover image of the given track synchronously.
120134 * If the track is already in the cache, it will be returned.
@@ -124,33 +138,26 @@ public void requestImageUrlAsync(Track track, Consumer<String> callback) {
124138 * @throws IOException if the request failed
125139 */
126140 public BufferedImage requestImage (Track track ) throws IOException {
141+ // Try to get image from cache by track id
142+ BufferedImage cachedImage = this .imageCache .get (track .getId ());
143+ if (cachedImage != null ) {
144+ return cachedImage ;
145+ }
146+
147+ // Request the image url
127148 String url = this .requestImageUrl (track );
128149 if (url == null ) {
129150 return null ;
130151 }
131152
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-
138153 // Download the image
139154 BufferedImage image = ImageIO .read (new URL (url ));
140155 if (image == null ) {
141156 throw new IOException ("Could not load image: " + url );
142157 }
143158
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-
159+ // Cache the image and return it
160+ this .imageCache .push (track .getId (), image );
154161 return image ;
155162 }
156163
@@ -162,28 +169,51 @@ public BufferedImage requestImage(Track track) throws IOException {
162169 * @return The url of the track or null if it failed
163170 * @throws IOException if the request failed
164171 */
165- public String requestImageUrl (Track track ) throws IOException {
166- return this .requestImageUrl (track , true );
172+ private String requestImageUrl (Track track ) throws IOException {
173+ // Request track information
174+ OpenTrack openTrack = this .requestOpenTrack (track );
175+ if (openTrack == null ) {
176+ return null ;
177+ }
178+
179+ // Get largest image url
180+ return openTrack .album .images .get (0 ).url ;
167181 }
168182
169183 /**
170- * Request the cover image url of the given track.
171- * If the track is already in the cache, it will be returned.
184+ * Request the track information of the given track.
185+ * If the open track is already in the cache, it will be returned.
172186 *
173- * @param track The track to lookup
174- * @param canGenerateNewAccessToken It will try again once if it fails
175- * @return The url of the track or null if it failed
187+ * @param track The track to lookup
176188 * @throws IOException if the request failed
177189 */
178- private String requestImageUrl (Track track , boolean canGenerateNewAccessToken ) throws IOException {
179- String cachedUrl = this .urlCache .get (track );
180- if (cachedUrl != null ) {
181- return cachedUrl ;
190+ public OpenTrack requestOpenTrack (Track track ) throws IOException {
191+ OpenTrack cachedOpenTrack = this .openTrackCache .get (track . getId () );
192+ if (cachedOpenTrack != null ) {
193+ return cachedOpenTrack ;
182194 }
183195
184196 // Create REST API url
185197 String url = String .format (URL_API_TRACKS , track .getId ());
198+ OpenTrack openTrack = this .request (url , OpenTrack .class , true );
199+
200+ // Cache the open track and return it
201+ this .openTrackCache .push (track .getId (), openTrack );
202+ return openTrack ;
203+ }
186204
205+ /**
206+ * Request the open spotify api with the given url
207+ * It will try again once if it fails
208+ *
209+ * @param url The url to request
210+ * @param clazz The class to parse the response to
211+ * @param canGenerateNewAccessToken It will try again once if it fails
212+ * @param <T> The type of the response
213+ * @return The parsed response
214+ * @throws IOException if the request failed
215+ */
216+ public <T > T request (String url , Class <?> clazz , boolean canGenerateNewAccessToken ) throws IOException {
187217 // Connect
188218 HttpsURLConnection connection = (HttpsURLConnection ) new URL (url ).openConnection ();
189219 connection .addRequestProperty ("User-Agent" , USER_AGENT );
@@ -203,7 +233,7 @@ private String requestImageUrl(Track track, boolean canGenerateNewAccessToken) t
203233 this .accessTokenResponse = this .generateAccessToken ();
204234
205235 // Try again
206- return this .requestImageUrl ( track , false );
236+ return this .request ( url , clazz , false );
207237 } else {
208238 // Request failed twice
209239 return null ;
@@ -212,35 +242,14 @@ private String requestImageUrl(Track track, boolean canGenerateNewAccessToken) t
212242
213243 // Read response
214244 JsonReader reader = new JsonReader (new InputStreamReader (connection .getInputStream ()));
215- OpenTrack openTrack = new Gson ().fromJson (reader , OpenTrack .class );
216-
217- // Get largest image url
218- String imageUrl = openTrack .album .images .get (0 ).url ;
219-
220- // Cache url and return it
221- if (imageUrl != null ) {
222- this .urlCache .put (track , imageUrl );
223- return imageUrl ;
224- }
225-
226- return null ;
245+ return new Gson ().fromJson (reader , clazz );
227246 }
228247
229- /**
230- * Set the maximal amount of images to cache.
231- * Default is 10.
232- *
233- * @param cacheSize The maximal amount of images to cache
234- */
235- public void setCacheSize (int cacheSize ) {
236- this .cacheSize = cacheSize ;
248+ public Cache <BufferedImage > getImageCache () {
249+ return this .imageCache ;
237250 }
238251
239- /**
240- * Clear the cache of images.
241- */
242- public void clearCache () {
243- this .imageCache .clear ();
244- this .cacheQueue .clear ();
252+ public Cache <OpenTrack > getOpenTrackCache () {
253+ return this .openTrackCache ;
245254 }
246255}
0 commit comments