mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Deferred emote sync
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
package dev.kruhlmann.imgfloat.config;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ApplicationLifecycleLogger {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ApplicationLifecycleLogger.class);
|
||||||
|
|
||||||
|
private final String serverPort;
|
||||||
|
|
||||||
|
public ApplicationLifecycleLogger(@Value("${server.port:8080}") String serverPort) {
|
||||||
|
this.serverPort = serverPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
|
public void logReady() {
|
||||||
|
LOG.info("Imgfloat ready to accept connections on port {}", serverPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,9 @@ import java.util.List;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@@ -22,6 +24,8 @@ import org.springframework.http.HttpHeaders;
|
|||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.client.RestClientException;
|
import org.springframework.web.client.RestClientException;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
@@ -41,6 +45,7 @@ public class TwitchEmoteService {
|
|||||||
private final Map<String, CachedEmote> emoteCache = new ConcurrentHashMap<>();
|
private final Map<String, CachedEmote> emoteCache = new ConcurrentHashMap<>();
|
||||||
private final Map<String, List<CachedEmote>> channelEmoteCache = new ConcurrentHashMap<>();
|
private final Map<String, List<CachedEmote>> channelEmoteCache = new ConcurrentHashMap<>();
|
||||||
private volatile List<CachedEmote> globalEmotes = List.of();
|
private volatile List<CachedEmote> globalEmotes = List.of();
|
||||||
|
private final AtomicBoolean initialGlobalSyncScheduled = new AtomicBoolean();
|
||||||
|
|
||||||
public TwitchEmoteService(
|
public TwitchEmoteService(
|
||||||
RestTemplateBuilder builder,
|
RestTemplateBuilder builder,
|
||||||
@@ -61,12 +66,11 @@ public class TwitchEmoteService {
|
|||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new IllegalStateException("Failed to create Twitch emote cache directory", ex);
|
throw new IllegalStateException("Failed to create Twitch emote cache directory", ex);
|
||||||
}
|
}
|
||||||
warmGlobalEmotes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EmoteDescriptor> getGlobalEmotes() {
|
public List<EmoteDescriptor> getGlobalEmotes() {
|
||||||
if (globalEmotes.isEmpty()) {
|
if (globalEmotes.isEmpty()) {
|
||||||
warmGlobalEmotes();
|
ensureInitialGlobalSyncScheduled();
|
||||||
}
|
}
|
||||||
return globalEmotes.stream().map(CachedEmote::descriptor).toList();
|
return globalEmotes.stream().map(CachedEmote::descriptor).toList();
|
||||||
}
|
}
|
||||||
@@ -127,6 +131,31 @@ public class TwitchEmoteService {
|
|||||||
LOG.info("Loaded {} global Twitch emotes", cached.size());
|
LOG.info("Loaded {} global Twitch emotes", cached.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureInitialGlobalSyncScheduled() {
|
||||||
|
if (initialGlobalSyncScheduled.compareAndSet(false, true)) {
|
||||||
|
LOG.info("Scheduling initial global Twitch emote sync in the background");
|
||||||
|
CompletableFuture.runAsync(this::safeWarmGlobalEmotes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void safeWarmGlobalEmotes() {
|
||||||
|
LOG.info("Initial global Twitch emote sync started");
|
||||||
|
try {
|
||||||
|
warmGlobalEmotes();
|
||||||
|
LOG.info(
|
||||||
|
"Initial global Twitch emote sync completed (cached {} emotes)",
|
||||||
|
globalEmotes.size()
|
||||||
|
);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LOG.warn("Initial global Twitch emote sync failed", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
|
public void startInitialGlobalEmoteSync() {
|
||||||
|
ensureInitialGlobalSyncScheduled();
|
||||||
|
}
|
||||||
|
|
||||||
private List<CachedEmote> fetchChannelEmotes(String channelLogin) {
|
private List<CachedEmote> fetchChannelEmotes(String channelLogin) {
|
||||||
String broadcasterId = fetchBroadcasterId(channelLogin).orElse(null);
|
String broadcasterId = fetchBroadcasterId(channelLogin).orElse(null);
|
||||||
if (broadcasterId == null) {
|
if (broadcasterId == null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user