forked from j62/ctbrec
Merge branch 'dev' into notify
This commit is contained in:
commit
65e6c5b76e
|
@ -1,5 +1,8 @@
|
|||
package ctbrec.ui;
|
||||
|
||||
import static ctbrec.EventBusHolder.*;
|
||||
import static ctbrec.EventBusHolder.EVENT_TYPE.*;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
@ -69,6 +72,7 @@ public class CamrecApplication extends Application {
|
|||
public void start(Stage primaryStage) throws Exception {
|
||||
this.primaryStage = primaryStage;
|
||||
logEnvironment();
|
||||
registerAlertSystem();
|
||||
sites.add(new BongaCams());
|
||||
sites.add(new Cam4());
|
||||
sites.add(new Camsoda());
|
||||
|
@ -91,7 +95,6 @@ public class CamrecApplication extends Application {
|
|||
createGui(primaryStage);
|
||||
checkForUpdates();
|
||||
|
||||
registerAlertSystem();
|
||||
}
|
||||
|
||||
private void logEnvironment() {
|
||||
|
@ -203,15 +206,14 @@ public class CamrecApplication extends Application {
|
|||
|
||||
private void registerAlertSystem() {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
// don't register before 1 minute has passed, because directly after
|
||||
// the start of ctbrec, an event for every online model would be fired,
|
||||
// which is annoying as f
|
||||
//Thread.sleep(TimeUnit.MINUTES.toMillis(1));
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// try {
|
||||
// // don't register before 1 minute has passed, because directly after
|
||||
// // the start of ctbrec, an event for every online model would be fired,
|
||||
// // which is annoying as f
|
||||
// Thread.sleep(TimeUnit.MINUTES.toMillis(1));
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
LOG.debug("Alert System registered");
|
||||
Platform.runLater(() -> {
|
||||
EventBusHolder.BUS.register(new Object() {
|
||||
|
@ -219,10 +221,10 @@ public class CamrecApplication extends Application {
|
|||
public void modelEvent(Map<String, Object> e) {
|
||||
LOG.debug("Alert: {}", e);
|
||||
try {
|
||||
if (Objects.equals("model.status", e.get("event"))) {
|
||||
String status = (String) e.get("status");
|
||||
Model model = (Model) e.get("model");
|
||||
if (Objects.equals("online", status)) {
|
||||
if (Objects.equals(e.get(EVENT), MODEL_STATUS_CHANGED)) {
|
||||
Model.STATUS status = (Model.STATUS) e.get(STATUS);
|
||||
Model model = (Model) e.get(MODEL);
|
||||
if (Objects.equals(Model.STATUS.ONLINE, status)) {
|
||||
Platform.runLater(() -> {
|
||||
String header = "Model Online";
|
||||
String msg = model.getDisplayName() + " is now online";
|
||||
|
|
|
@ -67,8 +67,14 @@ public class JavaFxRecording extends Recording {
|
|||
case DOWNLOADING:
|
||||
statusProperty.set("downloading");
|
||||
break;
|
||||
case MERGING:
|
||||
statusProperty.set("merging");
|
||||
case POST_PROCESSING:
|
||||
statusProperty.set("post-processing");
|
||||
break;
|
||||
case STOPPED:
|
||||
statusProperty.set("stopped");
|
||||
break;
|
||||
case UNKNOWN:
|
||||
statusProperty.set("unknown");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package ctbrec.ui;
|
|||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -296,13 +297,13 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
|
|||
massEdit(models, action);
|
||||
}
|
||||
|
||||
private void massEdit(List<Model> models, Consumer<Model> action) {
|
||||
getTabPane().setCursor(Cursor.WAIT);
|
||||
private void massEdit(List<? extends Model> models, Consumer<Model> action) {
|
||||
table.setCursor(Cursor.WAIT);
|
||||
threadPool.submit(() -> {
|
||||
for (Model model : models) {
|
||||
action.accept(model);
|
||||
}
|
||||
Platform.runLater(() -> getTabPane().setCursor(Cursor.DEFAULT));
|
||||
Platform.runLater(() -> table.setCursor(Cursor.DEFAULT));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -443,6 +444,8 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
|
|||
openInPlayer.setOnAction((e) -> openInPlayer(selectedModels.get(0)));
|
||||
MenuItem switchStreamSource = new MenuItem("Switch resolution");
|
||||
switchStreamSource.setOnAction((e) -> switchStreamSource(selectedModels.get(0)));
|
||||
MenuItem follow = new MenuItem("Follow");
|
||||
follow.setOnAction((e) -> follow(selectedModels));
|
||||
|
||||
ContextMenu menu = new ContextMenu(stop);
|
||||
if (selectedModels.size() == 1) {
|
||||
|
@ -450,7 +453,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
|
|||
} else {
|
||||
menu.getItems().addAll(resumeRecording, pauseRecording);
|
||||
}
|
||||
menu.getItems().addAll(copyUrl, openInPlayer, openInBrowser, switchStreamSource);
|
||||
menu.getItems().addAll(copyUrl, openInPlayer, openInBrowser, switchStreamSource, follow);
|
||||
|
||||
if (selectedModels.size() > 1) {
|
||||
copyUrl.setDisable(true);
|
||||
|
@ -462,6 +465,19 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
|
|||
return menu;
|
||||
}
|
||||
|
||||
private void follow(ObservableList<JavaFxModel> selectedModels) {
|
||||
Consumer<Model> action = (m) -> {
|
||||
try {
|
||||
m.follow();
|
||||
} catch(Throwable e) {
|
||||
LOG.error("Couldn't follow model {}", m, e);
|
||||
Platform.runLater(() ->
|
||||
showErrorDialog(e, "Couldn't follow model", "Following " + m.getName() + " failed: " + e.getMessage()));
|
||||
}
|
||||
};
|
||||
massEdit(new ArrayList<JavaFxModel>(selectedModels), action);
|
||||
}
|
||||
|
||||
private void openInPlayer(JavaFxModel selectedModel) {
|
||||
table.setCursor(Cursor.WAIT);
|
||||
new Thread(() -> {
|
||||
|
|
|
@ -25,7 +25,8 @@ public class StreamSourceSelectionDialog {
|
|||
List<StreamSource> sources;
|
||||
try {
|
||||
sources = selectStreamSource.get();
|
||||
ChoiceDialog<StreamSource> choiceDialog = new ChoiceDialog<StreamSource>(sources.get(sources.size()-1), sources);
|
||||
int selectedIndex = model.getStreamUrlIndex() > -1 ? Math.min(model.getStreamUrlIndex(), sources.size()-1) : sources.size()-1;
|
||||
ChoiceDialog<StreamSource> choiceDialog = new ChoiceDialog<StreamSource>(sources.get(selectedIndex), sources);
|
||||
choiceDialog.setTitle("Stream Quality");
|
||||
choiceDialog.setHeaderText("Select your preferred stream quality");
|
||||
choiceDialog.setResizable(true);
|
||||
|
|
|
@ -7,11 +7,14 @@ import java.util.concurrent.CompletableFuture;
|
|||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
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 ctbrec.Config;
|
||||
|
@ -80,6 +83,10 @@ public class ThumbCell extends StackPane {
|
|||
private boolean mouseHovering = false;
|
||||
private boolean recording = false;
|
||||
private static ExecutorService imageLoadingThreadPool = Executors.newFixedThreadPool(30);
|
||||
private static Cache<Model, int[]> resolutionCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(4, TimeUnit.HOURS)
|
||||
.maximumSize(1000)
|
||||
.build();
|
||||
|
||||
public ThumbCell(ThumbOverviewTab parent, Model model, Recorder recorder) {
|
||||
this.thumbCellList = parent.grid.getChildren();
|
||||
|
@ -212,35 +219,45 @@ public class ThumbCell extends StackPane {
|
|||
return;
|
||||
}
|
||||
|
||||
ThumbOverviewTab.threadPool.submit(() -> {
|
||||
int[] resolution = resolutionCache.getIfPresent(model);
|
||||
if(resolution != null) {
|
||||
try {
|
||||
ThumbOverviewTab.resolutionProcessing.add(model);
|
||||
int[] resolution = model.getStreamResolution(false);
|
||||
updateResolutionTag(resolution);
|
||||
|
||||
// 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() && resolution[1] == 0) {
|
||||
LOG.trace("Removing invalid resolution value for {}", model.getName());
|
||||
model.invalidateCacheEntries();
|
||||
}
|
||||
|
||||
Thread.sleep(500);
|
||||
} catch (IOException | InterruptedException e1) {
|
||||
LOG.warn("Couldn't update resolution tag for model {}", model.getName(), e1);
|
||||
} catch(ExecutionException e) {
|
||||
if(e.getCause() instanceof EOFException) {
|
||||
LOG.warn("Couldn't update resolution tag for model {}. Playlist empty", model.getName());
|
||||
} else if(e.getCause() instanceof ParseException) {
|
||||
LOG.warn("Couldn't update resolution tag for model {} - {}", model.getName(), e.getMessage());
|
||||
} else {
|
||||
LOG.warn("Couldn't update resolution tag for model {}", model.getName(), e);
|
||||
}
|
||||
} finally {
|
||||
ThumbOverviewTab.resolutionProcessing.remove(model);
|
||||
} catch(Exception e) {
|
||||
LOG.warn("Couldn't update resolution tag for model {}", model.getName(), e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ThumbOverviewTab.threadPool.submit(() -> {
|
||||
try {
|
||||
ThumbOverviewTab.resolutionProcessing.add(model);
|
||||
int[] _resolution = model.getStreamResolution(false);
|
||||
resolutionCache.put(model, _resolution);
|
||||
updateResolutionTag(_resolution);
|
||||
|
||||
// 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() && _resolution[1] == 0) {
|
||||
LOG.trace("Removing invalid resolution value for {}", model.getName());
|
||||
model.invalidateCacheEntries();
|
||||
}
|
||||
|
||||
Thread.sleep(100);
|
||||
} catch (IOException | InterruptedException e1) {
|
||||
LOG.warn("Couldn't update resolution tag for model {}", model.getName(), e1);
|
||||
} catch(ExecutionException e) {
|
||||
if(e.getCause() instanceof EOFException) {
|
||||
LOG.warn("Couldn't update resolution tag for model {}. Playlist empty", model.getName());
|
||||
} else if(e.getCause() instanceof ParseException) {
|
||||
LOG.warn("Couldn't update resolution tag for model {} - {}", model.getName(), e.getMessage());
|
||||
} else {
|
||||
LOG.warn("Couldn't update resolution tag for model {}", model.getName(), e);
|
||||
}
|
||||
} finally {
|
||||
ThumbOverviewTab.resolutionProcessing.remove(model);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateResolutionTag(int[] resolution) throws IOException, ExecutionException, InterruptedException {
|
||||
|
@ -492,7 +509,7 @@ public class ThumbCell extends StackPane {
|
|||
this.model.setPreview(model.getPreview());
|
||||
this.model.setTags(model.getTags());
|
||||
this.model.setUrl(model.getUrl());
|
||||
this.model.setSuspended(model.isSuspended());
|
||||
this.model.setSuspended(recorder.isSuspended(model));
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,5 +6,15 @@ import com.google.common.eventbus.AsyncEventBus;
|
|||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
public class EventBusHolder {
|
||||
|
||||
public static final String EVENT = "event";
|
||||
public static final String STATUS = "status";
|
||||
public static final String MODEL = "model";
|
||||
|
||||
public static enum EVENT_TYPE {
|
||||
MODEL_STATUS_CHANGED,
|
||||
RECORDING_STATUS_CHANGED
|
||||
}
|
||||
|
||||
public static final EventBus BUS = new AsyncEventBus(Executors.newSingleThreadExecutor());
|
||||
}
|
||||
|
|
|
@ -13,34 +13,92 @@ import ctbrec.recorder.download.StreamSource;
|
|||
import ctbrec.sites.Site;
|
||||
|
||||
public interface Model {
|
||||
|
||||
public static enum STATUS {
|
||||
ONLINE("online"),
|
||||
OFFLINE("offline"),
|
||||
AWAY("away"),
|
||||
PRIVATE("private"),
|
||||
GROUP("group"),
|
||||
UNKNOWN("unknown");
|
||||
|
||||
String display;
|
||||
STATUS(String display) {
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return display;
|
||||
}
|
||||
}
|
||||
|
||||
public String getUrl();
|
||||
|
||||
public void setUrl(String url);
|
||||
|
||||
public String getDisplayName();
|
||||
|
||||
public void setDisplayName(String name);
|
||||
|
||||
public String getName();
|
||||
|
||||
public void setName(String name);
|
||||
|
||||
public String getPreview();
|
||||
|
||||
public void setPreview(String preview);
|
||||
|
||||
public List<String> getTags();
|
||||
|
||||
public void setTags(List<String> tags);
|
||||
|
||||
public String getDescription();
|
||||
|
||||
public void setDescription(String description);
|
||||
|
||||
public int getStreamUrlIndex();
|
||||
|
||||
public void setStreamUrlIndex(int streamUrlIndex);
|
||||
|
||||
public boolean isOnline() throws IOException, ExecutionException, InterruptedException;
|
||||
|
||||
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException;
|
||||
|
||||
public String getOnlineState(boolean failFast) throws IOException, ExecutionException;
|
||||
|
||||
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException;
|
||||
|
||||
public void invalidateCacheEntries();
|
||||
|
||||
public void receiveTip(int tokens) throws IOException;
|
||||
|
||||
/**
|
||||
* Determines the stream resolution for this model
|
||||
*
|
||||
* @param failFast
|
||||
* If set to true, the method returns emmediately, even if the resolution is unknown. If
|
||||
* the resolution is unknown, the array contains 0,0
|
||||
*
|
||||
* @return a tupel of width and height represented by an int[2]
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
public int[] getStreamResolution(boolean failFast) throws ExecutionException;
|
||||
|
||||
public boolean follow() throws IOException;
|
||||
|
||||
public boolean unfollow() throws IOException;
|
||||
|
||||
public void setSite(Site site);
|
||||
|
||||
public Site getSite();
|
||||
|
||||
public void writeSiteSpecificData(JsonWriter writer) throws IOException;
|
||||
|
||||
public void readSiteSpecificData(JsonReader reader) throws IOException;
|
||||
|
||||
public boolean isSuspended();
|
||||
|
||||
public void setSuspended(boolean suspended);
|
||||
|
||||
}
|
|
@ -10,16 +10,18 @@ public class Recording {
|
|||
private Instant startDate;
|
||||
private String path;
|
||||
private boolean hasPlaylist;
|
||||
private STATUS status;
|
||||
private STATUS status = STATUS.UNKNOWN;
|
||||
private int progress = -1;
|
||||
private long sizeInByte;
|
||||
|
||||
public static enum STATUS {
|
||||
RECORDING,
|
||||
GENERATING_PLAYLIST,
|
||||
STOPPED,
|
||||
FINISHED,
|
||||
DOWNLOADING,
|
||||
MERGING
|
||||
POST_PROCESSING,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
public Recording() {}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ctbrec.recorder;
|
||||
|
||||
import static ctbrec.EventBusHolder.*;
|
||||
import static ctbrec.EventBusHolder.EVENT_TYPE.*;
|
||||
import static ctbrec.Recording.STATUS.*;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -180,7 +182,7 @@ public class LocalRecorder implements Recorder {
|
|||
}
|
||||
}
|
||||
}.start();
|
||||
fireRecordingStateChanged(model, true);
|
||||
fireRecordingStateChanged(model, RECORDING);
|
||||
}
|
||||
|
||||
private void stopRecordingProcess(Model model) {
|
||||
|
@ -190,7 +192,7 @@ public class LocalRecorder implements Recorder {
|
|||
if(!Config.isServerMode()) {
|
||||
postprocess(download);
|
||||
}
|
||||
fireRecordingStateChanged(model, false);
|
||||
fireRecordingStateChanged(model, FINISHED);
|
||||
}
|
||||
|
||||
private void postprocess(Download download) {
|
||||
|
@ -371,7 +373,7 @@ public class LocalRecorder implements Recorder {
|
|||
} else {
|
||||
postprocess(d);
|
||||
}
|
||||
fireRecordingStateChanged(m, false);
|
||||
fireRecordingStateChanged(m, FINISHED); // TODO fire all the events
|
||||
}
|
||||
}
|
||||
for (Model m : restart) {
|
||||
|
@ -438,11 +440,7 @@ public class LocalRecorder implements Recorder {
|
|||
List<Model> models = getModelsRecording();
|
||||
for (Model model : models) {
|
||||
try {
|
||||
boolean wasOnline = model.isOnline();
|
||||
boolean isOnline = model.isOnline(IGNORE_CACHE);
|
||||
if(wasOnline != isOnline) {
|
||||
fireModelOnlineStateChanged(model, isOnline);
|
||||
}
|
||||
LOG.trace("Checking online state for {}: {}", model, (isOnline ? "online" : "offline"));
|
||||
if (isOnline && !isSuspended(model) && !recordingProcesses.containsKey(model)) {
|
||||
LOG.info("Model {}'s room back to public", model);
|
||||
|
@ -476,23 +474,22 @@ public class LocalRecorder implements Recorder {
|
|||
}
|
||||
LOG.debug(getName() + " terminated");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void fireModelOnlineStateChanged(Model model, boolean online) {
|
||||
private void fireModelOnlineStateChanged(Model model, Model.STATUS status) {
|
||||
Map<String, Object> evt = new HashMap<>();
|
||||
evt.put("event", "model.status");
|
||||
evt.put("status", online ? "online" : "offline");
|
||||
evt.put("model", model);
|
||||
evt.put(EVENT, MODEL_STATUS_CHANGED);
|
||||
evt.put(STATUS, status);
|
||||
evt.put(MODEL, model);
|
||||
EventBusHolder.BUS.post(evt);
|
||||
LOG.debug("Event fired {}", evt);
|
||||
}
|
||||
|
||||
private void fireRecordingStateChanged(Model model, boolean recording) {
|
||||
private void fireRecordingStateChanged(Model model, Recording.STATUS status) {
|
||||
Map<String, Object> evt = new HashMap<>();
|
||||
evt.put("event", "recording.status");
|
||||
evt.put("status", recording ? "started" : "stopped");
|
||||
evt.put("model", model);
|
||||
evt.put(EVENT, RECORDING_STATUS_CHANGED);
|
||||
evt.put(STATUS, status);
|
||||
evt.put(MODEL, model);
|
||||
EventBusHolder.BUS.post(evt);
|
||||
LOG.debug("Event fired {}", evt);
|
||||
}
|
||||
|
|
|
@ -44,7 +44,11 @@ public class StreamSource implements Comparable<StreamSource> {
|
|||
public String toString() {
|
||||
DecimalFormat df = new DecimalFormat("0.00");
|
||||
float mbit = bandwidth / 1024.0f / 1024.0f;
|
||||
return height + "p (" + df.format(mbit) + " Mbit/s)";
|
||||
if(height == Integer.MAX_VALUE) {
|
||||
return "unknown resolution (" + df.format(mbit) + " Mbit/s)";
|
||||
} else {
|
||||
return height + "p (" + df.format(mbit) + " Mbit/s)";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -43,11 +43,6 @@ public class Cam4Model extends AbstractModel {
|
|||
private int[] resolution = null;
|
||||
private boolean privateRoom = false;
|
||||
|
||||
@Override
|
||||
public boolean isOnline() throws IOException, ExecutionException, InterruptedException {
|
||||
return isOnline(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
|
||||
if(ignoreCache || onlineState == null) {
|
||||
|
|
|
@ -6,14 +6,11 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.iheartradio.m3u8.Encoding;
|
||||
import com.iheartradio.m3u8.Format;
|
||||
import com.iheartradio.m3u8.ParseException;
|
||||
|
@ -41,12 +38,7 @@ public class CamsodaModel extends AbstractModel {
|
|||
private List<StreamSource> streamSources = null;
|
||||
private String status = "n/a";
|
||||
private float sortOrder = 0;
|
||||
|
||||
private static Cache<String, int[]> streamResolutionCache = CacheBuilder.newBuilder()
|
||||
.initialCapacity(10_000)
|
||||
.maximumSize(10_000)
|
||||
.expireAfterWrite(30, TimeUnit.MINUTES)
|
||||
.build();
|
||||
int[] resolution = new int[2];
|
||||
|
||||
public String getStreamUrl() throws IOException {
|
||||
if(streamUrl == null) {
|
||||
|
@ -139,13 +131,11 @@ public class CamsodaModel extends AbstractModel {
|
|||
@Override
|
||||
public void invalidateCacheEntries() {
|
||||
streamSources = null;
|
||||
streamResolutionCache.invalidate(getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
|
||||
int[] resolution = streamResolutionCache.getIfPresent(getName());
|
||||
if(resolution != null) {
|
||||
if(failFast) {
|
||||
return resolution;
|
||||
} else {
|
||||
if(failFast) {
|
||||
|
@ -158,7 +148,6 @@ public class CamsodaModel extends AbstractModel {
|
|||
} else {
|
||||
StreamSource src = streamSources.get(0);
|
||||
resolution = new int[] {src.width, src.height};
|
||||
streamResolutionCache.put(getName(), resolution);
|
||||
return resolution;
|
||||
}
|
||||
} catch (IOException | ParseException | PlaylistException e) {
|
||||
|
|
|
@ -189,17 +189,6 @@ public class Chaturbate extends AbstractSite {
|
|||
}
|
||||
});
|
||||
|
||||
LoadingCache<String, int[]> streamResolutionCache = CacheBuilder.newBuilder()
|
||||
.initialCapacity(10_000)
|
||||
.maximumSize(10_000)
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.build(new CacheLoader<String, int[]> () {
|
||||
@Override
|
||||
public int[] load(String model) throws Exception {
|
||||
return loadResolution(model);
|
||||
}
|
||||
});
|
||||
|
||||
public void sendTip(String name, int tokens) throws IOException {
|
||||
if (!Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
|
||||
RequestBody body = new FormBody.Builder()
|
||||
|
@ -264,11 +253,9 @@ public class Chaturbate extends AbstractSite {
|
|||
}
|
||||
}
|
||||
|
||||
public int[] getResolution(String modelName) throws ExecutionException {
|
||||
return streamResolutionCache.get(modelName);
|
||||
}
|
||||
public int[] getResolution(String modelName) throws ExecutionException, IOException, ParseException, PlaylistException, InterruptedException {
|
||||
throttleRequests();
|
||||
|
||||
private int[] loadResolution(String modelName) throws IOException, ParseException, PlaylistException, ExecutionException, InterruptedException {
|
||||
int[] res = new int[2];
|
||||
StreamInfo streamInfo = getStreamInfo(modelName);
|
||||
if(!streamInfo.url.startsWith("http")) {
|
||||
|
@ -303,7 +290,6 @@ public class Chaturbate extends AbstractSite {
|
|||
throw ex;
|
||||
}
|
||||
|
||||
streamResolutionCache.put(modelName, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import okhttp3.Response;
|
|||
public class ChaturbateModel extends AbstractModel {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(ChaturbateModel.class);
|
||||
private int[] resolution = new int[2];
|
||||
|
||||
/**
|
||||
* This constructor exists only for deserialization. Please don't call it directly
|
||||
|
@ -52,16 +53,16 @@ public class ChaturbateModel extends AbstractModel {
|
|||
|
||||
@Override
|
||||
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
|
||||
int[] resolution = getChaturbate().streamResolutionCache.getIfPresent(getName());
|
||||
if(resolution != null) {
|
||||
return getChaturbate().getResolution(getName());
|
||||
} else {
|
||||
if(failFast) {
|
||||
return new int[2];
|
||||
} else {
|
||||
return getChaturbate().getResolution(getName());
|
||||
}
|
||||
if(failFast) {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
try {
|
||||
resolution = getChaturbate().getResolution(getName());
|
||||
} catch(Exception e) {
|
||||
throw new ExecutionException(e);
|
||||
}
|
||||
return resolution;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,7 +72,6 @@ public class ChaturbateModel extends AbstractModel {
|
|||
@Override
|
||||
public void invalidateCacheEntries() {
|
||||
getChaturbate().streamInfoCache.invalidate(getName());
|
||||
getChaturbate().streamResolutionCache.invalidate(getName());
|
||||
}
|
||||
|
||||
public String getOnlineState() throws IOException, ExecutionException {
|
||||
|
|
|
@ -10,10 +10,9 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
@ -52,7 +51,6 @@ public class MyFreeCamsClient {
|
|||
private Cache<Integer, SessionState> sessionStates = CacheBuilder.newBuilder().maximumSize(4000).build();
|
||||
private Cache<Integer, MyFreeCamsModel> models = CacheBuilder.newBuilder().maximumSize(4000).build();
|
||||
private Lock lock = new ReentrantLock();
|
||||
private ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
private ServerConfig serverConfig;
|
||||
@SuppressWarnings("unused")
|
||||
private String tkx;
|
||||
|
@ -86,8 +84,13 @@ public class MyFreeCamsClient {
|
|||
public void start() throws IOException {
|
||||
running = true;
|
||||
serverConfig = new ServerConfig(mfc);
|
||||
List<String> websocketServers = new ArrayList<String>(serverConfig.wsServers.keySet());
|
||||
String server = websocketServers.get((int) (Math.random()*websocketServers.size()));
|
||||
List<String> websocketServers = new ArrayList<String>(serverConfig.wsServers.size());
|
||||
for (Entry<String, String> entry : serverConfig.wsServers.entrySet()) {
|
||||
if (entry.getValue().equals("rfc6455")) {
|
||||
websocketServers.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
String server = websocketServers.get((int) (Math.random() * websocketServers.size() - 1));
|
||||
String wsUrl = "ws://" + server + ".myfreecams.com:8080/fcsl";
|
||||
LOG.debug("Connecting to random websocket server {}", wsUrl);
|
||||
|
||||
|
@ -308,7 +311,7 @@ public class MyFreeCamsClient {
|
|||
long opts = json.getInt("opts");
|
||||
long serv = json.getInt("serv");
|
||||
long type = json.getInt("type");
|
||||
String base = "http://www.myfreecams.com/php/FcwExtResp.php";
|
||||
String base = mfc.getBaseUrl() + "/php/FcwExtResp.php";
|
||||
String url = base + "?respkey="+respkey+"&opts="+opts+"&serv="+serv+"&type="+type;
|
||||
Request req = new Request.Builder().url(url).build();
|
||||
LOG.trace("Requesting EXTDATA {}", url);
|
||||
|
@ -568,10 +571,6 @@ public class MyFreeCamsClient {
|
|||
return models.getIfPresent(uid);
|
||||
}
|
||||
|
||||
public void execute(Runnable r) {
|
||||
executor.execute(r);
|
||||
}
|
||||
|
||||
public void getSessionState(ctbrec.Model model) {
|
||||
for (SessionState state : sessionStates.asMap().values()) {
|
||||
if(Objects.equals(state.getNm(), model.getName())) {
|
||||
|
|
|
@ -45,7 +45,7 @@ public class MyFreeCamsModel extends AbstractModel {
|
|||
private double camScore;
|
||||
private int viewerCount;
|
||||
private State state;
|
||||
private int resolution[];
|
||||
private int resolution[] = new int[2];
|
||||
|
||||
/**
|
||||
* This constructor exists only for deserialization. Please don't call it directly
|
||||
|
@ -72,6 +72,24 @@ public class MyFreeCamsModel extends AbstractModel {
|
|||
return state != null ? state.toString() : "offline";
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public STATUS getOnlineState() {
|
||||
// switch(this.state) {
|
||||
// case ONLINE:
|
||||
// case RECORDING:
|
||||
// return ctbrec.Model.STATUS.ONLINE;
|
||||
// case AWAY:
|
||||
// return ctbrec.Model.STATUS.AWAY;
|
||||
// case PRIVATE:
|
||||
// return ctbrec.Model.STATUS.PRIVATE;
|
||||
// case GROUP_SHOW:
|
||||
// return ctbrec.Model.STATUS.GROUP;
|
||||
// default:
|
||||
// LOG.debug("State {} is not mapped", this.state);
|
||||
// return ctbrec.Model.STATUS.UNKNOWN;
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException {
|
||||
MasterPlaylist masterPlaylist = getMasterPlaylist();
|
||||
|
@ -174,26 +192,19 @@ public class MyFreeCamsModel extends AbstractModel {
|
|||
|
||||
@Override
|
||||
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
|
||||
if(resolution == null) {
|
||||
if(failFast || hlsUrl == null) {
|
||||
return new int[2];
|
||||
if (!failFast && hlsUrl != null) {
|
||||
try {
|
||||
List<StreamSource> streamSources = getStreamSources();
|
||||
Collections.sort(streamSources);
|
||||
StreamSource best = streamSources.get(streamSources.size() - 1);
|
||||
resolution = new int[] { best.width, best.height };
|
||||
} catch (ParseException | PlaylistException e) {
|
||||
LOG.warn("Couldn't determine stream resolution - {}", e.getMessage());
|
||||
} catch (ExecutionException | IOException e) {
|
||||
LOG.error("Couldn't determine stream resolution", e);
|
||||
}
|
||||
MyFreeCamsClient.getInstance().execute(()->{
|
||||
try {
|
||||
List<StreamSource> streamSources = getStreamSources();
|
||||
Collections.sort(streamSources);
|
||||
StreamSource best = streamSources.get(streamSources.size()-1);
|
||||
resolution = new int[] {best.width, best.height};
|
||||
} catch (ParseException | PlaylistException e) {
|
||||
LOG.warn("Couldn't determine stream resolution - {}", e.getMessage());
|
||||
} catch (ExecutionException | IOException e) {
|
||||
LOG.error("Couldn't determine stream resolution", e);
|
||||
}
|
||||
});
|
||||
return new int[2];
|
||||
} else {
|
||||
return resolution;
|
||||
}
|
||||
return resolution;
|
||||
}
|
||||
|
||||
public void setStreamUrl(String hlsUrl) {
|
||||
|
|
Loading…
Reference in New Issue