forked from j62/ctbrec
Improve caching of stream resolution info
This commit is contained in:
parent
a5732cb572
commit
dd47d121bc
|
@ -1,8 +1,8 @@
|
||||||
package ctbrec.ui.tabs;
|
package ctbrec.ui.tabs;
|
||||||
|
|
||||||
|
import static ctbrec.Model.State.*;
|
||||||
import static ctbrec.io.HttpConstants.*;
|
import static ctbrec.io.HttpConstants.*;
|
||||||
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
@ -15,12 +15,13 @@ import java.util.function.Function;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.google.common.cache.Cache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
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.Config;
|
||||||
import ctbrec.Model;
|
import ctbrec.Model;
|
||||||
|
import ctbrec.Model.State;
|
||||||
import ctbrec.io.HttpException;
|
import ctbrec.io.HttpException;
|
||||||
import ctbrec.recorder.Recorder;
|
import ctbrec.recorder.Recorder;
|
||||||
import ctbrec.ui.AutosizeAlert;
|
import ctbrec.ui.AutosizeAlert;
|
||||||
|
@ -64,7 +65,6 @@ import okhttp3.Response;
|
||||||
public class ThumbCell extends StackPane {
|
public class ThumbCell extends StackPane {
|
||||||
|
|
||||||
private static final String COULDNT_START_STOP_RECORDING = "Couldn't start/stop recording";
|
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 String ERROR = "Error";
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ThumbCell.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ThumbCell.class);
|
||||||
private static final Duration ANIMATION_DURATION = new Duration(250);
|
private static final Duration ANIMATION_DURATION = new Duration(250);
|
||||||
|
@ -96,7 +96,10 @@ public class ThumbCell extends StackPane {
|
||||||
private boolean mouseHovering = false;
|
private boolean mouseHovering = false;
|
||||||
private boolean recording = false;
|
private boolean recording = false;
|
||||||
private static ExecutorService imageLoadingThreadPool = Executors.newFixedThreadPool(30);
|
private static ExecutorService imageLoadingThreadPool = Executors.newFixedThreadPool(30);
|
||||||
private static Cache<String, int[]> resolutionCache = CacheBuilder.newBuilder().expireAfterAccess(4, TimeUnit.HOURS).maximumSize(1000).build();
|
private static LoadingCache<Model, int[]> resolutionCache = CacheBuilder.newBuilder()
|
||||||
|
.expireAfterAccess(5, TimeUnit.MINUTES)
|
||||||
|
.maximumSize(10000)
|
||||||
|
.build(CacheLoader.from(ThumbCell::getStreamResolution));
|
||||||
private ThumbOverviewTab parent;
|
private ThumbOverviewTab parent;
|
||||||
|
|
||||||
public ThumbCell(ThumbOverviewTab parent, Model model, Recorder recorder) {
|
public ThumbCell(ThumbOverviewTab parent, Model model, Recorder recorder) {
|
||||||
|
@ -272,81 +275,48 @@ public class ThumbCell extends StackPane {
|
||||||
return selectionProperty;
|
return selectionProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void determineResolution() {
|
private void updateResolutionTag() {
|
||||||
if (ThumbOverviewTab.resolutionProcessing.contains(model)) {
|
ThumbOverviewTab.threadPool.submit(() -> {
|
||||||
LOG.trace("Already fetching resolution for model {}. Queue size {}", model.getName(), ThumbOverviewTab.resolutionProcessing.size());
|
int[] resolution;
|
||||||
return;
|
String tagText;
|
||||||
}
|
Paint resolutionBackgroundColor;
|
||||||
|
try {
|
||||||
int[] resolution = resolutionCache.getIfPresent(model.getUrl());
|
resolution = resolutionCache.get(model);
|
||||||
if (resolution != null) {
|
resolutionBackgroundColor = resolutionOnlineColor;
|
||||||
ThumbOverviewTab.threadPool.submit(() -> {
|
LOG.trace("Model resolution {} {}x{}", model.getName(), resolution[0], resolution[1]);
|
||||||
try {
|
LOG.trace("Resolution queue size: {}", ThumbOverviewTab.queue.size());
|
||||||
updateResolutionTag(resolution);
|
final int w = resolution[1];
|
||||||
} catch (Exception e) {
|
String width = w != Integer.MAX_VALUE ? Integer.toString(w) : "HD";
|
||||||
LOG.debug(COULDNT_UPDATE_RESOLUTION_TAG_FOR_MODEL, model.getName(), e.getLocalizedMessage());
|
tagText = width;
|
||||||
}
|
if (w == 0) {
|
||||||
});
|
State state = model.getOnlineState(false);
|
||||||
} else {
|
tagText = state.name();
|
||||||
ThumbOverviewTab.threadPool.submit(() -> {
|
if (model.isOnline() && state == ONLINE) {
|
||||||
try {
|
resolutionCache.invalidate(model);
|
||||||
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());
|
|
||||||
} else {
|
} else {
|
||||||
LOG.debug(COULDNT_UPDATE_RESOLUTION_TAG_FOR_MODEL, model.getName(), e.getMessage());
|
resolutionBackgroundColor = resolutionOfflineColor;
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
ThumbOverviewTab.resolutionProcessing.remove(model);
|
|
||||||
}
|
}
|
||||||
});
|
} catch (InterruptedException e) {
|
||||||
}
|
Thread.currentThread().interrupt();
|
||||||
}
|
tagText = "error";
|
||||||
|
resolutionBackgroundColor = resolutionOfflineColor;
|
||||||
private void updateResolutionTag(int[] resolution) throws IOException, ExecutionException, InterruptedException {
|
} catch (ExecutionException | IOException e) {
|
||||||
Paint resolutionBackgroundColor = resolutionOnlineColor;
|
tagText = "error";
|
||||||
String state = model.getOnlineState(false).toString();
|
resolutionBackgroundColor = resolutionOfflineColor;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
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);
|
topic.setText(txt);
|
||||||
|
|
||||||
if (Config.getInstance().getSettings().determineResolution) {
|
if (Config.getInstance().getSettings().determineResolution) {
|
||||||
determineResolution();
|
updateResolutionTag();
|
||||||
} else {
|
} else {
|
||||||
resolutionBackground.setVisible(false);
|
resolutionBackground.setVisible(false);
|
||||||
resolutionTag.setVisible(false);
|
resolutionTag.setVisible(false);
|
||||||
|
@ -650,4 +620,13 @@ public class ThumbCell extends StackPane {
|
||||||
clip.arcHeightProperty().bind(clip.arcWidthProperty());
|
clip.arcHeightProperty().bind(clip.arcWidthProperty());
|
||||||
this.setClip(clip);
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,11 @@ import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
@ -95,7 +93,6 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
||||||
|
|
||||||
protected static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
protected static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
||||||
static ExecutorService threadPool = new ThreadPoolExecutor(2, 2, 10, TimeUnit.MINUTES, queue, createThreadFactory());
|
static ExecutorService threadPool = new ThreadPoolExecutor(2, 2, 10, TimeUnit.MINUTES, queue, createThreadFactory());
|
||||||
static Set<Model> resolutionProcessing = Collections.synchronizedSet(new HashSet<>());
|
|
||||||
|
|
||||||
protected FlowPane grid = new FlowPane();
|
protected FlowPane grid = new FlowPane();
|
||||||
protected PaginatedScheduledService updateService;
|
protected PaginatedScheduledService updateService;
|
||||||
|
|
Loading…
Reference in New Issue