diff --git a/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java b/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java index 7c540aa..a6d2394 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java +++ b/src/main/java/dev/kruhlmann/imgfloat/service/ChannelDirectoryService.java @@ -1476,7 +1476,7 @@ public class ChannelDirectoryService { } private String topicFor(String broadcaster) { - return "/topic/channel/" + broadcaster.toLowerCase(Locale.ROOT); + return "/topic/channel/" + normalize(broadcaster); } private List sortAndMapAssets(String broadcaster, Collection assets) { diff --git a/src/main/java/dev/kruhlmann/imgfloat/service/MarketplaceScriptSeedLoader.java b/src/main/java/dev/kruhlmann/imgfloat/service/MarketplaceScriptSeedLoader.java index 578cd9a..51a6c63 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/service/MarketplaceScriptSeedLoader.java +++ b/src/main/java/dev/kruhlmann/imgfloat/service/MarketplaceScriptSeedLoader.java @@ -22,8 +22,6 @@ import org.springframework.stereotype.Component; @Component public class MarketplaceScriptSeedLoader { - // TODO: Code smell Large parser/loader with many branching paths; consider decomposing into smaller collaborators. - private static final Logger LOG = LoggerFactory.getLogger(MarketplaceScriptSeedLoader.class); private static final String METADATA_FILENAME = "metadata.json"; private static final String SOURCE_FILENAME = "source.js"; @@ -311,7 +309,7 @@ public class MarketplaceScriptSeedLoader { } try { String content = Files.readString(path); - return JsonSupport.read(content); + return readMetadata(content); } catch (IOException ex) { LOG.warn("Failed to read marketplace metadata {}", path, ex); return null; @@ -319,24 +317,11 @@ public class MarketplaceScriptSeedLoader { } } - private static final class JsonSupport { - private static final AtomicReference OBJECT_MAPPER = - new AtomicReference<>(); + private static final com.fasterxml.jackson.databind.ObjectMapper OBJECT_MAPPER = + new com.fasterxml.jackson.databind.ObjectMapper(); - private JsonSupport() {} - - static T read(String payload) throws IOException { - return mapper().readValue(payload, (Class) ScriptSeedMetadata.class); - } - - private static com.fasterxml.jackson.databind.ObjectMapper mapper() { - com.fasterxml.jackson.databind.ObjectMapper mapper = OBJECT_MAPPER.get(); - if (mapper == null) { - mapper = new com.fasterxml.jackson.databind.ObjectMapper(); - OBJECT_MAPPER.set(mapper); - } - return mapper; - } + private static ScriptSeedMetadata readMetadata(String content) throws IOException { + return OBJECT_MAPPER.readValue(content, ScriptSeedMetadata.class); } private String detectMediaType(Path path, String fallback) { diff --git a/src/main/java/dev/kruhlmann/imgfloat/service/SevenTvEmoteService.java b/src/main/java/dev/kruhlmann/imgfloat/service/SevenTvEmoteService.java index 380fdf9..681a352 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/service/SevenTvEmoteService.java +++ b/src/main/java/dev/kruhlmann/imgfloat/service/SevenTvEmoteService.java @@ -27,11 +27,15 @@ import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +/** + * Fetches, caches, and persists 7TV global and channel emote data. + * Emote JSON files are written to disk so they survive restarts without requiring + * an immediate API call. The cache is refreshed by {@link EmoteSyncScheduler} on + * a configurable interval. + */ @Service public class SevenTvEmoteService { - // TODO: Code smell Service handles transport, parsing, and storage concerns together instead of focused components. - private static final Logger LOG = LoggerFactory.getLogger(SevenTvEmoteService.class); private static final String USERS_URL = "https://api.twitch.tv/helix/users"; private static final String USER_EMOTE_URL = "https://7tv.io/v3/users/twitch/"; diff --git a/src/main/java/dev/kruhlmann/imgfloat/service/TwitchEmoteService.java b/src/main/java/dev/kruhlmann/imgfloat/service/TwitchEmoteService.java index c96090e..7d30d6e 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/service/TwitchEmoteService.java +++ b/src/main/java/dev/kruhlmann/imgfloat/service/TwitchEmoteService.java @@ -31,11 +31,15 @@ import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +/** + * Fetches, caches, and persists Twitch global and channel emote data. + * Emote JSON files are written to disk so they survive restarts without requiring + * an immediate API call. The cache is refreshed by {@link EmoteSyncScheduler} on + * a configurable interval. + */ @Service public class TwitchEmoteService { - // TODO: Code smell Service bundles API client calls, caching, disk persistence, and async scheduling in one class. - private static final Logger LOG = LoggerFactory.getLogger(TwitchEmoteService.class); private static final String GLOBAL_EMOTE_URL = "https://api.twitch.tv/helix/chat/emotes/global"; private static final String CHANNEL_EMOTE_URL = "https://api.twitch.tv/helix/chat/emotes";