diff --git a/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java b/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java index 04530e63..98a1818b 100644 --- a/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java +++ b/client/src/main/java/ctbrec/ui/tabs/ThumbCell.java @@ -1,8 +1,8 @@ package ctbrec.ui.tabs; +import static ctbrec.Model.State.*; import static ctbrec.io.HttpConstants.*; -import java.io.EOFException; import java.io.IOException; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -15,12 +15,13 @@ import java.util.function.Function; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import com.iheartradio.m3u8.ParseException; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import ctbrec.Config; import ctbrec.Model; +import ctbrec.Model.State; import ctbrec.io.HttpException; import ctbrec.recorder.Recorder; import ctbrec.ui.AutosizeAlert; @@ -64,7 +65,6 @@ import okhttp3.Response; public class ThumbCell extends StackPane { private static final String COULDNT_START_STOP_RECORDING = "Couldn't start/stop recording"; - private static final String COULDNT_UPDATE_RESOLUTION_TAG_FOR_MODEL = "Couldn't update resolution tag for model {} - {}"; private static final String ERROR = "Error"; private static final Logger LOG = LoggerFactory.getLogger(ThumbCell.class); private static final Duration ANIMATION_DURATION = new Duration(250); @@ -96,7 +96,10 @@ public class ThumbCell extends StackPane { private boolean mouseHovering = false; private boolean recording = false; private static ExecutorService imageLoadingThreadPool = Executors.newFixedThreadPool(30); - private static Cache resolutionCache = CacheBuilder.newBuilder().expireAfterAccess(4, TimeUnit.HOURS).maximumSize(1000).build(); + private static LoadingCache resolutionCache = CacheBuilder.newBuilder() + .expireAfterAccess(5, TimeUnit.MINUTES) + .maximumSize(10000) + .build(CacheLoader.from(ThumbCell::getStreamResolution)); private ThumbOverviewTab parent; public ThumbCell(ThumbOverviewTab parent, Model model, Recorder recorder) { @@ -272,81 +275,48 @@ public class ThumbCell extends StackPane { return selectionProperty; } - private void determineResolution() { - if (ThumbOverviewTab.resolutionProcessing.contains(model)) { - LOG.trace("Already fetching resolution for model {}. Queue size {}", model.getName(), ThumbOverviewTab.resolutionProcessing.size()); - return; - } - - int[] resolution = resolutionCache.getIfPresent(model.getUrl()); - if (resolution != null) { - ThumbOverviewTab.threadPool.submit(() -> { - try { - updateResolutionTag(resolution); - } catch (Exception e) { - LOG.debug(COULDNT_UPDATE_RESOLUTION_TAG_FOR_MODEL, model.getName(), e.getLocalizedMessage()); - } - }); - } else { - ThumbOverviewTab.threadPool.submit(() -> { - try { - ThumbOverviewTab.resolutionProcessing.add(model); - int[] res = model.getStreamResolution(false); - resolutionCache.put(model.getUrl(), res); - updateResolutionTag(res); - - // the model is online, but the resolution is 0. probably something went wrong - // when we first requested the stream info, so we remove this invalid value from the "cache" - // so that it is requested again - if (model.isOnline() && res[1] == 0) { - LOG.trace("Removing invalid resolution value for {}", model.getName()); - model.invalidateCacheEntries(); - } - - Thread.sleep(100); - } catch (IOException e1) { - LOG.debug(COULDNT_UPDATE_RESOLUTION_TAG_FOR_MODEL, model.getName(), e1.getLocalizedMessage()); - } catch (InterruptedException e1) { - Thread.currentThread().interrupt(); - LOG.debug(COULDNT_UPDATE_RESOLUTION_TAG_FOR_MODEL, model.getName(), e1.getLocalizedMessage()); - } catch (ExecutionException e) { - if (e.getCause() instanceof EOFException) { - LOG.debug("Couldn't update resolution tag for model {}. Playlist empty", model.getName()); - } else if (e.getCause() instanceof ParseException) { - LOG.debug(COULDNT_UPDATE_RESOLUTION_TAG_FOR_MODEL, model.getName(), e.getMessage()); + private void updateResolutionTag() { + ThumbOverviewTab.threadPool.submit(() -> { + int[] resolution; + String tagText; + Paint resolutionBackgroundColor; + try { + resolution = resolutionCache.get(model); + resolutionBackgroundColor = resolutionOnlineColor; + LOG.trace("Model resolution {} {}x{}", model.getName(), resolution[0], resolution[1]); + LOG.trace("Resolution queue size: {}", ThumbOverviewTab.queue.size()); + final int w = resolution[1]; + String width = w != Integer.MAX_VALUE ? Integer.toString(w) : "HD"; + tagText = width; + if (w == 0) { + State state = model.getOnlineState(false); + tagText = state.name(); + if (model.isOnline() && state == ONLINE) { + resolutionCache.invalidate(model); } else { - LOG.debug(COULDNT_UPDATE_RESOLUTION_TAG_FOR_MODEL, model.getName(), e.getMessage()); + resolutionBackgroundColor = resolutionOfflineColor; } - } finally { - ThumbOverviewTab.resolutionProcessing.remove(model); } - }); - } - } - - private void updateResolutionTag(int[] resolution) throws IOException, ExecutionException, InterruptedException { - Paint resolutionBackgroundColor = resolutionOnlineColor; - String state = model.getOnlineState(false).toString(); - final String tagText; - if (model.isOnline()) { - LOG.trace("Model resolution {} {}x{}", model.getName(), resolution[0], resolution[1]); - LOG.trace("Resolution queue size: {}", ThumbOverviewTab.queue.size()); - final int w = resolution[1]; - String width = w != Integer.MAX_VALUE ? Integer.toString(w) : "HD"; - tagText = w > 0 ? width : state; - } else { - tagText = state; - resolutionBackgroundColor = resolutionOfflineColor; - } - final Paint c = resolutionBackgroundColor; - Platform.runLater(() -> { - resolutionTag.setText(tagText); - if (!mouseHovering) { - resolutionTag.setVisible(true); - resolutionBackground.setVisible(true); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + tagText = "error"; + resolutionBackgroundColor = resolutionOfflineColor; + } catch (ExecutionException | IOException e) { + tagText = "error"; + resolutionBackgroundColor = resolutionOfflineColor; } - resolutionBackground.setWidth(resolutionTag.getBoundsInLocal().getWidth() + 4); - resolutionBackground.setFill(c); + + final String text = tagText; + final Paint c = resolutionBackgroundColor; + Platform.runLater(() -> { + resolutionTag.setText(text); + if (!mouseHovering) { + resolutionTag.setVisible(true); + resolutionBackground.setVisible(true); + } + resolutionBackground.setWidth(resolutionTag.getBoundsInLocal().getWidth() + 4); + resolutionBackground.setFill(c); + }); }); } @@ -583,7 +553,7 @@ public class ThumbCell extends StackPane { topic.setText(txt); if (Config.getInstance().getSettings().determineResolution) { - determineResolution(); + updateResolutionTag(); } else { resolutionBackground.setVisible(false); resolutionTag.setVisible(false); @@ -650,4 +620,13 @@ public class ThumbCell extends StackPane { clip.arcHeightProperty().bind(clip.arcWidthProperty()); this.setClip(clip); } + + private static int[] getStreamResolution(Model model) { + try { + return model.getStreamResolution(false); + } catch (ExecutionException e) { + LOG.warn("Error loading stream resolution for model {}", model, e); + return new int[2]; + } + } } diff --git a/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java b/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java index db05f24e..da1cfbd9 100644 --- a/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java @@ -8,13 +8,11 @@ import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -95,7 +93,6 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { protected static BlockingQueue queue = new LinkedBlockingQueue<>(); static ExecutorService threadPool = new ThreadPoolExecutor(2, 2, 10, TimeUnit.MINUTES, queue, createThreadFactory()); - static Set resolutionProcessing = Collections.synchronizedSet(new HashSet<>()); protected FlowPane grid = new FlowPane(); protected PaginatedScheduledService updateService;