Skip to content

Commit 7c7096e

Browse files
committed
improve open spotify api, version 1.1.8
1 parent d275ad3 commit 7c7096e

File tree

4 files changed

+157
-64
lines changed

4 files changed

+157
-64
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ repositories {
2323
}
2424
2525
dependencies {
26-
implementation 'com.github.LabyStudio:java-spotify-api:1.1.7:all'
26+
implementation 'com.github.LabyStudio:java-spotify-api:1.1.8:all'
2727
}
2828
```
2929

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66
group 'de.labystudio'
7-
version '1.1.7'
7+
version '1.1.8'
88

99
compileJava {
1010
sourceCompatibility = '1.8'
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package de.labystudio.spotifyapi.open;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Map;
6+
import java.util.concurrent.ConcurrentHashMap;
7+
8+
/**
9+
* A cache for the open spotify api.
10+
*
11+
* @param <T> The type of the cached object
12+
* @author LabyStudio
13+
*/
14+
public class Cache<T> {
15+
16+
private final Map<String, T> cache = new ConcurrentHashMap<>();
17+
private final List<String> cacheQueue = new ArrayList<>();
18+
private int cacheSize;
19+
20+
/**
21+
* Create a new cache with a specific size
22+
*
23+
* @param cacheSize The size of the cache. The cache will remove the oldest entry if the size is reached.
24+
*/
25+
public Cache(int cacheSize) {
26+
this.cacheSize = cacheSize;
27+
}
28+
29+
/**
30+
* Set the maximal amount of entries to cache.
31+
*
32+
* @param cacheSize The maximal amount of entries to cache
33+
*/
34+
public void setCacheSize(int cacheSize) {
35+
this.cacheSize = cacheSize;
36+
}
37+
38+
/**
39+
* Store an entry in the cache
40+
* If the max cache size is reached, the oldest entry will be removed.
41+
*
42+
* @param key The key of the entry
43+
*/
44+
public void push(String key, T value) {
45+
// Remove entry from cache if cache is full
46+
if (this.cacheQueue.size() > this.cacheSize) {
47+
String urlToRemove = this.cacheQueue.remove(0);
48+
this.cache.remove(urlToRemove);
49+
}
50+
51+
// Add new entry to cache
52+
this.cache.put(key, value);
53+
this.cacheQueue.add(key);
54+
}
55+
56+
/**
57+
* Check if the cache contains the given key.
58+
*
59+
* @param key The key to check
60+
* @return True if the cache contains the key
61+
*/
62+
public boolean has(String key) {
63+
return this.cache.containsKey(key);
64+
}
65+
66+
/**
67+
* Get the cached entry by the given key.
68+
*
69+
* @param key The key of the entry
70+
* @return The cached entry or null if it doesn't exist
71+
*/
72+
public T get(String key) {
73+
return this.cache.get(key);
74+
}
75+
76+
/**
77+
* Clear the cache.
78+
*/
79+
public void clear() {
80+
this.cache.clear();
81+
this.cacheQueue.clear();
82+
}
83+
84+
}

src/main/java/de/labystudio/spotifyapi/open/OpenSpotifyAPI.java

Lines changed: 71 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@
1212
import java.io.IOException;
1313
import java.io.InputStreamReader;
1414
import java.net.URL;
15-
import java.util.ArrayList;
16-
import java.util.List;
17-
import java.util.Map;
18-
import java.util.concurrent.ConcurrentHashMap;
1915
import java.util.concurrent.Executor;
2016
import java.util.concurrent.Executors;
2117
import 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

Comments
 (0)