First implementation for MFC
Implemented Tabs are Online and Friends
This commit is contained in:
parent
54de1339fb
commit
362d90b29b
5
pom.xml
5
pom.xml
|
@ -140,6 +140,11 @@
|
|||
<artifactId>moshi</artifactId>
|
||||
<version>1.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20180130</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
|
|
|
@ -9,8 +9,8 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Settings.ProxyType;
|
||||
import ctbrec.ui.CookieJarImpl;
|
||||
import ctbrec.ui.CamrecApplication;
|
||||
import ctbrec.ui.CookieJarImpl;
|
||||
import ctbrec.ui.HtmlParser;
|
||||
import okhttp3.ConnectionPool;
|
||||
import okhttp3.Cookie;
|
||||
|
@ -34,8 +34,8 @@ public class HttpClient {
|
|||
loadProxySettings();
|
||||
client = new OkHttpClient.Builder()
|
||||
.cookieJar(cookieJar)
|
||||
.connectTimeout(Config.getInstance().getSettings().httpTimeout, TimeUnit.SECONDS)
|
||||
.readTimeout(Config.getInstance().getSettings().httpTimeout, TimeUnit.SECONDS)
|
||||
.connectTimeout(Config.getInstance().getSettings().httpTimeout, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(Config.getInstance().getSettings().httpTimeout, TimeUnit.MILLISECONDS)
|
||||
.connectionPool(new ConnectionPool(50, 10, TimeUnit.MINUTES))
|
||||
//.addInterceptor(new LoggingInterceptor())
|
||||
.build();
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.text.DecimalFormat;
|
|||
|
||||
public class StreamSource implements Comparable<StreamSource> {
|
||||
public int bandwidth;
|
||||
public int width;
|
||||
public int height;
|
||||
public String mediaPlaylistUrl;
|
||||
|
||||
|
@ -15,6 +16,14 @@ public class StreamSource implements Comparable<StreamSource> {
|
|||
this.bandwidth = bandwidth;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Fcext {
|
||||
|
||||
private String sm;
|
||||
private Integer sfw;
|
||||
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
|
||||
|
||||
public String getSm() {
|
||||
return sm;
|
||||
}
|
||||
|
||||
public void setSm(String sm) {
|
||||
this.sm = sm;
|
||||
}
|
||||
|
||||
public Integer getSfw() {
|
||||
return sfw;
|
||||
}
|
||||
|
||||
public void setSfw(Integer sfw) {
|
||||
this.sfw = sfw;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAdditionalProperties() {
|
||||
return this.additionalProperties;
|
||||
}
|
||||
|
||||
public void setAdditionalProperty(String name, Object value) {
|
||||
this.additionalProperties.put(name, value);
|
||||
}
|
||||
|
||||
public void merge(Fcext fcext) {
|
||||
if(fcext == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
sm = fcext.sm != null ? fcext.sm : sm;
|
||||
sfw = fcext.sfw != null ? fcext.sfw : sfw;
|
||||
additionalProperties.putAll(fcext.additionalProperties);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Model;
|
||||
import ctbrec.ui.PaginatedScheduledService;
|
||||
import javafx.concurrent.Task;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class FriendsUpdateService extends PaginatedScheduledService {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(FriendsUpdateService.class);
|
||||
private MyFreeCams myFreeCams;
|
||||
|
||||
public FriendsUpdateService(MyFreeCams myFreeCams) {
|
||||
this.myFreeCams = myFreeCams;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task<List<Model>> createTask() {
|
||||
return new Task<List<Model>>() {
|
||||
@Override
|
||||
public List<Model> call() throws IOException {
|
||||
List<Model> models = new ArrayList<>();
|
||||
String url = myFreeCams.getBaseUrl() + "/php/manage_lists2.php?passcode=&list_type=friends&data_mode=online&get_user_list=1";
|
||||
Request req = new Request.Builder()
|
||||
.url(url)
|
||||
.header("Referer", myFreeCams.getBaseUrl())
|
||||
.build();
|
||||
Response resp = MyFreeCams.httpClient.newCall(req).execute();
|
||||
if(resp.isSuccessful()) {
|
||||
String json = resp.body().string().substring(4);
|
||||
JSONObject object = new JSONObject(json);
|
||||
for (String key : object.keySet()) {
|
||||
int uid = Integer.parseInt(key);
|
||||
MyFreeCamsModel model = MyFreeCamsClient.getInstance().getModel(uid);
|
||||
if(model == null) {
|
||||
JSONObject modelObject = object.getJSONObject(key);
|
||||
String name = modelObject.getString("u");
|
||||
model = myFreeCams.createModel(name);
|
||||
SessionState st = new SessionState();
|
||||
st.setM(new ctbrec.sites.mfc.Model());
|
||||
st.getM().setCamscore(0.0);
|
||||
st.setU(new User());
|
||||
st.setUid(uid);
|
||||
st.setLv(modelObject.getInt("lv"));
|
||||
st.setVs(127);
|
||||
model.update(st);
|
||||
}
|
||||
models.add(model);
|
||||
}
|
||||
} else {
|
||||
LOG.error("Couldn't load friends list {} {}", resp.code(), resp.message());
|
||||
resp.close();
|
||||
}
|
||||
return models.stream()
|
||||
.sorted((a, b) -> {
|
||||
try {
|
||||
if(a.isOnline() && b.isOnline() || !a.isOnline() && !b.isOnline()) {
|
||||
return a.getName().compareTo(b.getName());
|
||||
} else {
|
||||
if(a.isOnline()) {
|
||||
return -1;
|
||||
}
|
||||
if(b.isOnline()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} catch (IOException | ExecutionException | InterruptedException e) {
|
||||
LOG.warn("Couldn't sort friends list", e);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
.skip((page-1) * 50)
|
||||
.limit(50)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
public class Message {
|
||||
private int type;
|
||||
private int sender;
|
||||
private int receiver;
|
||||
private int arg1;
|
||||
private int arg2;
|
||||
private String message;
|
||||
|
||||
public Message(int type, int sender, int receiver, int arg1, int arg2, String message) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.sender = sender;
|
||||
this.receiver = receiver;
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public void setSender(int sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
public int getReceiver() {
|
||||
return receiver;
|
||||
}
|
||||
|
||||
public void setReceiver(int receiver) {
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
public int getArg1() {
|
||||
return arg1;
|
||||
}
|
||||
|
||||
public void setArg1(int arg1) {
|
||||
this.arg1 = arg1;
|
||||
}
|
||||
|
||||
public int getArg2() {
|
||||
return arg2;
|
||||
}
|
||||
|
||||
public void setArg2(int arg2) {
|
||||
this.arg2 = arg2;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type + " " + sender + " " + receiver + " " + arg1 + " " + arg2 + " " + message;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
public class MessageTypes {
|
||||
public static final int CLIENT_DISCONNECTED = -5;
|
||||
public static final int CLIENT_MODELSLOADED = -4;
|
||||
public static final int CLIENT_CONNECTED = -3;
|
||||
public static final int ANY = -2;
|
||||
public static final int UNKNOWN = -1;
|
||||
public static final int NULL = 0;
|
||||
public static final int LOGIN = 1;
|
||||
public static final int ADDFRIEND = 2;
|
||||
public static final int PMESG = 3;
|
||||
public static final int STATUS = 4;
|
||||
public static final int DETAILS = 5;
|
||||
public static final int TOKENINC = 6;
|
||||
public static final int ADDIGNORE = 7;
|
||||
public static final int PRIVACY = 8;
|
||||
public static final int ADDFRIENDREQ = 9;
|
||||
public static final int USERNAMELOOKUP = 10;
|
||||
public static final int ZBAN = 11;
|
||||
public static final int BROADCASTNEWS = 12;
|
||||
public static final int ANNOUNCE = 13;
|
||||
public static final int MANAGELIST = 14;
|
||||
public static final int INBOX = 15;
|
||||
public static final int GWCONNECT = 16;
|
||||
public static final int RELOADSETTINGS = 17;
|
||||
public static final int HIDEUSERS = 18;
|
||||
public static final int RULEVIOLATION = 19;
|
||||
public static final int SESSIONSTATE = 20;
|
||||
public static final int REQUESTPVT = 21;
|
||||
public static final int ACCEPTPVT = 22;
|
||||
public static final int REJECTPVT = 23;
|
||||
public static final int ENDSESSION = 24;
|
||||
public static final int TXPROFILE = 25;
|
||||
public static final int STARTVOYEUR = 26;
|
||||
public static final int SERVERREFRESH = 27;
|
||||
public static final int SETTING = 28;
|
||||
public static final int BWSTATS = 29;
|
||||
public static final int SETGUESTNAME = 30;
|
||||
public static final int TKX = 30;
|
||||
public static final int SETTEXTOPT = 31;
|
||||
public static final int SERVERCONFIG = 32;
|
||||
public static final int MODELGROUP = 33;
|
||||
public static final int REQUESTGRP = 34;
|
||||
public static final int STATUSGRP = 35;
|
||||
public static final int GROUPCHAT = 36;
|
||||
public static final int CLOSEGRP = 37;
|
||||
public static final int UCR = 38;
|
||||
public static final int MYUCR = 39;
|
||||
public static final int SLAVECON = 40;
|
||||
public static final int SLAVECMD = 41;
|
||||
public static final int SLAVEFRIEND = 42;
|
||||
public static final int SLAVEVSHARE = 43;
|
||||
public static final int ROOMDATA = 44;
|
||||
public static final int NEWSITEM = 45;
|
||||
public static final int GUESTCOUNT = 46;
|
||||
public static final int PRELOGINQ = 47;
|
||||
public static final int MODELGROUPSZ = 48;
|
||||
public static final int ROOMHELPER = 49;
|
||||
public static final int CMESG = 50;
|
||||
public static final int JOINCHAN = 51;
|
||||
public static final int CREATECHAN = 52;
|
||||
public static final int INVITECHAN = 53;
|
||||
public static final int KICKCHAN = 54;
|
||||
public static final int QUIETCHAN = 55;
|
||||
public static final int BANCHAN = 56;
|
||||
public static final int PREVIEWCHAN = 57;
|
||||
public static final int SHUTDOWN = 58;
|
||||
public static final int LISTBANS = 59;
|
||||
public static final int UNBAN = 60;
|
||||
public static final int SETWELCOME = 61;
|
||||
public static final int CHANOP = 62;
|
||||
public static final int LISTCHAN = 63;
|
||||
public static final int TAGS = 64;
|
||||
public static final int SETPCODE = 65;
|
||||
public static final int SETMINTIP = 66;
|
||||
public static final int UEOPT = 67;
|
||||
public static final int HDVIDEO = 68;
|
||||
public static final int METRICS = 69;
|
||||
public static final int OFFERCAM = 70;
|
||||
public static final int REQUESTCAM = 71;
|
||||
public static final int MYWEBCAM = 72;
|
||||
public static final int MYCAMSTATE = 73;
|
||||
public static final int PMHISTORY = 74;
|
||||
public static final int CHATFLASH = 75;
|
||||
public static final int TRUEPVT = 76;
|
||||
public static final int BOOKMARKS = 77;
|
||||
public static final int EVENT = 78;
|
||||
public static final int STATEDUMP = 79;
|
||||
public static final int RECOMMEND = 80;
|
||||
public static final int EXTDATA = 81;
|
||||
public static final int NOTIFY = 84;
|
||||
public static final int PUBLISH = 85;
|
||||
public static final int ZGWINVALID = 95;
|
||||
public static final int CONNECTING = 96;
|
||||
public static final int CONNECTED = 97;
|
||||
public static final int DISCONNECTED = 98;
|
||||
public static final int LOGOUT = 99;
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class Model {
|
||||
|
||||
private Double camscore;
|
||||
private String continent;
|
||||
private Integer flags;
|
||||
private Boolean hidecs;
|
||||
private Integer kbit;
|
||||
private Integer lastnews;
|
||||
private Integer mg;
|
||||
private Integer missmfc;
|
||||
private Integer newModel;
|
||||
private Integer rank;
|
||||
private Integer rc;
|
||||
private Integer sfw;
|
||||
private String topic;
|
||||
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
|
||||
private Set<String> tags = new HashSet<>();
|
||||
|
||||
public Double getCamscore() {
|
||||
return camscore;
|
||||
}
|
||||
|
||||
public void setCamscore(Double camscore) {
|
||||
this.camscore = camscore;
|
||||
}
|
||||
|
||||
public String getContinent() {
|
||||
return continent;
|
||||
}
|
||||
|
||||
public void setContinent(String continent) {
|
||||
this.continent = continent;
|
||||
}
|
||||
|
||||
public Integer getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
public void setFlags(Integer flags) {
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
public Boolean getHidecs() {
|
||||
return hidecs;
|
||||
}
|
||||
|
||||
public void setHidecs(Boolean hidecs) {
|
||||
this.hidecs = hidecs;
|
||||
}
|
||||
|
||||
public Integer getKbit() {
|
||||
return kbit;
|
||||
}
|
||||
|
||||
public void setKbit(Integer kbit) {
|
||||
this.kbit = kbit;
|
||||
}
|
||||
|
||||
public Integer getLastnews() {
|
||||
return lastnews;
|
||||
}
|
||||
|
||||
public void setLastnews(Integer lastnews) {
|
||||
this.lastnews = lastnews;
|
||||
}
|
||||
|
||||
public Integer getMg() {
|
||||
return mg;
|
||||
}
|
||||
|
||||
public void setMg(Integer mg) {
|
||||
this.mg = mg;
|
||||
}
|
||||
|
||||
public Integer getMissmfc() {
|
||||
return missmfc;
|
||||
}
|
||||
|
||||
public void setMissmfc(Integer missmfc) {
|
||||
this.missmfc = missmfc;
|
||||
}
|
||||
|
||||
public Integer getNewModel() {
|
||||
return newModel;
|
||||
}
|
||||
|
||||
public void setNewModel(Integer newModel) {
|
||||
this.newModel = newModel;
|
||||
}
|
||||
|
||||
public Integer getRank() {
|
||||
return rank;
|
||||
}
|
||||
|
||||
public void setRank(Integer rank) {
|
||||
this.rank = rank;
|
||||
}
|
||||
|
||||
public Integer getRc() {
|
||||
return rc;
|
||||
}
|
||||
|
||||
public void setRc(Integer rc) {
|
||||
this.rc = rc;
|
||||
}
|
||||
|
||||
public Integer getSfw() {
|
||||
return sfw;
|
||||
}
|
||||
|
||||
public void setSfw(Integer sfw) {
|
||||
this.sfw = sfw;
|
||||
}
|
||||
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAdditionalProperties() {
|
||||
return this.additionalProperties;
|
||||
}
|
||||
|
||||
public void setAdditionalProperty(String name, Object value) {
|
||||
this.additionalProperties.put(name, value);
|
||||
}
|
||||
|
||||
public Set<String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(Set<String> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public void merge(Model m) {
|
||||
if(m == null) {
|
||||
return;
|
||||
}
|
||||
camscore = m.camscore != null ? m.camscore : camscore;
|
||||
continent = m.continent != null ? m.continent : continent;
|
||||
flags = m.flags != null ? m.flags : flags;
|
||||
hidecs = m.hidecs != null ? m.hidecs : hidecs;
|
||||
kbit = m.kbit != null ? m.kbit : kbit;
|
||||
lastnews = m.lastnews != null ? m.lastnews : lastnews;
|
||||
mg = m.mg != null ? m.mg : mg;
|
||||
missmfc = m.missmfc != null ? m.missmfc : missmfc;
|
||||
newModel = m.newModel != null ? m.newModel : newModel;
|
||||
rank = m.rank != null ? m.rank : rank;
|
||||
rc = m.rc != null ? m.rc : rc;
|
||||
sfw = m.sfw != null ? m.sfw : sfw;
|
||||
topic = m.topic != null ? m.topic : topic;
|
||||
additionalProperties.putAll(m.additionalProperties);
|
||||
tags.addAll(m.tags);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Site;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.ui.CookieJarImpl;
|
||||
import ctbrec.ui.TabProvider;
|
||||
import okhttp3.ConnectionPool;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class MyFreeCams implements Site {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(MyFreeCams.class);
|
||||
|
||||
private Recorder recorder;
|
||||
private MyFreeCamsClient client;
|
||||
public static OkHttpClient httpClient = new OkHttpClient.Builder()
|
||||
.connectTimeout(Config.getInstance().getSettings().httpTimeout, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(Config.getInstance().getSettings().httpTimeout, TimeUnit.MILLISECONDS)
|
||||
.connectionPool(new ConnectionPool(50, 10, TimeUnit.MINUTES))
|
||||
.cookieJar(new CookieJarImpl())
|
||||
.build();
|
||||
|
||||
public MyFreeCams() throws IOException {
|
||||
client = MyFreeCamsClient.getInstance();
|
||||
client.setSite(this);
|
||||
client.start();
|
||||
|
||||
login();
|
||||
}
|
||||
|
||||
public void login() throws IOException {
|
||||
RequestBody body = new FormBody.Builder()
|
||||
.add("username", "affenhubert")
|
||||
.add("password", "hampel81")
|
||||
.add("tz", "2")
|
||||
.add("ss", "1920x1080")
|
||||
.add("submit_login", "97")
|
||||
.build();
|
||||
Request req = new Request.Builder()
|
||||
.url(getBaseUrl() + "/php/login.php")
|
||||
.header("Referer", getBaseUrl())
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.post(body)
|
||||
.build();
|
||||
Response resp = httpClient.newCall(req).execute();
|
||||
if(!resp.isSuccessful()) {
|
||||
LOG.error("Login failed {} {}", resp.code(), resp.message());
|
||||
}
|
||||
resp.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "MyFreeCams";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseUrl() {
|
||||
return "https://www.myfreecams.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAffiliateLink() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecorder(Recorder recorder) {
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabProvider getTabProvider() {
|
||||
return new MyFreeCamsTabProvider(client, recorder, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyFreeCamsModel createModel(String name) {
|
||||
MyFreeCamsModel model = new MyFreeCamsModel();
|
||||
model.setName(name);
|
||||
model.setUrl("https://profiles.myfreecams.com/" + name);
|
||||
return model;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,357 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import static ctbrec.sites.mfc.MessageTypes.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
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;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.Moshi;
|
||||
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.WebSocket;
|
||||
import okhttp3.WebSocketListener;
|
||||
import okio.ByteString;
|
||||
|
||||
public class MyFreeCamsClient {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(MyFreeCamsClient.class);
|
||||
|
||||
private static MyFreeCamsClient instance;
|
||||
private MyFreeCams mfc;
|
||||
private WebSocket ws;
|
||||
private Moshi moshi;
|
||||
private volatile boolean running = false;
|
||||
|
||||
private Map<Integer, SessionState> sessionStates = new HashMap<>();
|
||||
private Map<Integer, MyFreeCamsModel> models = new HashMap<>();
|
||||
private Lock lock = new ReentrantLock();
|
||||
private ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private MyFreeCamsClient() {
|
||||
moshi = new Moshi.Builder().build();
|
||||
}
|
||||
|
||||
public static synchronized MyFreeCamsClient getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new MyFreeCamsClient();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void setSite(MyFreeCams mfc) {
|
||||
this.mfc = mfc;
|
||||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
running = true;
|
||||
ServerConfig serverConfig = new ServerConfig(MyFreeCams.httpClient);
|
||||
List<String> websocketServers = new ArrayList<String>(serverConfig.wsServers.keySet());
|
||||
String server = websocketServers.get((int) (Math.random()*websocketServers.size()));
|
||||
String wsUrl = "ws://" + server + ".myfreecams.com:8080/fcsl";
|
||||
Request req = new Request.Builder()
|
||||
.url(wsUrl)
|
||||
.addHeader("Origin", "http://m.myfreecams.com")
|
||||
.build();
|
||||
ws = createWebSocket(req);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
ws.close(1000, "Good Bye"); // terminate normally (1000)
|
||||
running = false;
|
||||
}
|
||||
|
||||
public List<MyFreeCamsModel> getModels() {
|
||||
lock.lock();
|
||||
try {
|
||||
LOG.trace("Models: {}", models.size());
|
||||
return new ArrayList<>(this.models.values());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private WebSocket createWebSocket(Request req) {
|
||||
WebSocket ws = MyFreeCams.httpClient.newWebSocket(req, new WebSocketListener() {
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket, Response response) {
|
||||
super.onOpen(webSocket, response);
|
||||
try {
|
||||
LOG.trace("open: [{}]", response.body().string());
|
||||
webSocket.send("hello fcserver\n");
|
||||
// TxCmd Sending - nType: 1, nTo: 0, nArg1: 20080909, nArg2: 0, sMsg:guest:guest
|
||||
webSocket.send("1 0 0 20080909 0 guest:guest\n");
|
||||
startKeepAlive(webSocket);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(WebSocket webSocket, int code, String reason) {
|
||||
// TODO decide what todo: is this the end of the session
|
||||
// or do we have to reconnect to keep things running?
|
||||
super.onClosed(webSocket, code, reason);
|
||||
LOG.trace("close: {} {}", code, reason);
|
||||
running = false;
|
||||
MyFreeCams.httpClient.dispatcher().executorService().shutdownNow();
|
||||
}
|
||||
|
||||
private StringBuilder msgBuffer = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket webSocket, String text) {
|
||||
super.onMessage(webSocket, text);
|
||||
msgBuffer.append(text);
|
||||
Message message;
|
||||
try {
|
||||
message = parseMessage(msgBuffer);
|
||||
if (message != null) {
|
||||
msgBuffer.setLength(0);
|
||||
}
|
||||
|
||||
switch (message.getType()) {
|
||||
case LOGIN:
|
||||
LOG.trace("login");
|
||||
break;
|
||||
case DETAILS:
|
||||
case ROOMHELPER:
|
||||
case ADDFRIEND:
|
||||
case ADDIGNORE:
|
||||
case CMESG:
|
||||
case PMESG:
|
||||
case TXPROFILE:
|
||||
case USERNAMELOOKUP:
|
||||
case MYCAMSTATE:
|
||||
case MYWEBCAM:
|
||||
case JOINCHAN:
|
||||
case SESSIONSTATE:
|
||||
if(!message.getMessage().isEmpty()) {
|
||||
JsonAdapter<SessionState> adapter = moshi.adapter(SessionState.class);
|
||||
try {
|
||||
SessionState sessionState = adapter.fromJson(message.getMessage());
|
||||
updateSessionState(sessionState);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Couldn't parse session state message", e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TAGS:
|
||||
JSONObject json = new JSONObject(message.getMessage());
|
||||
String[] names = JSONObject.getNames(json);
|
||||
Integer uid = Integer.parseInt(names[0]);
|
||||
SessionState sessionState = sessionStates.get(uid);
|
||||
if (sessionState != null) {
|
||||
JSONArray tags = json.getJSONArray(names[0]);
|
||||
for (Object obj : tags) {
|
||||
sessionState.getM().getTags().add((String) obj);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXTDATA:
|
||||
requestExtData(message.getMessage());
|
||||
break;
|
||||
default:
|
||||
LOG.trace("Unknown message {}", message);
|
||||
break;
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void requestExtData(String message) {
|
||||
try {
|
||||
JSONObject json = new JSONObject(message);
|
||||
long respkey = json.getInt("respkey");
|
||||
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 url = base + "?respkey="+respkey+"&opts="+opts+"&serv="+serv+"&type="+type;
|
||||
Request req = new Request.Builder().url(url).build();
|
||||
LOG.debug("Requesting EXTDATA {}", url);
|
||||
Response resp = MyFreeCams.httpClient.newCall(req).execute();
|
||||
|
||||
if(resp.isSuccessful()) {
|
||||
parseExtDataSessionStates(resp.body().string());
|
||||
}
|
||||
} catch(Exception e) {
|
||||
LOG.warn("Couldn't request EXTDATA", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseExtDataSessionStates(String json) {
|
||||
JSONObject object = new JSONObject(json);
|
||||
if(object.has("type") && object.getInt("type") == 21) {
|
||||
JSONArray outer = object.getJSONArray("rdata");
|
||||
for (int i = 1; i < outer.length(); i++) {
|
||||
JSONArray inner = outer.getJSONArray(i);
|
||||
try {
|
||||
SessionState state = new SessionState();
|
||||
int idx = 0;
|
||||
state.setNm(inner.getString(idx++));
|
||||
state.setSid(inner.getInt(idx++));
|
||||
state.setUid(inner.getInt(idx++));
|
||||
state.setVs(inner.getInt(idx++));
|
||||
state.setPid(inner.getInt(idx++));
|
||||
state.setLv(inner.getInt(idx++));
|
||||
state.setU(new User());
|
||||
state.getU().setCamserv(inner.getInt(idx++));
|
||||
idx++;
|
||||
state.getU().setChatColor(inner.getString(idx++));
|
||||
state.getU().setChatFont(inner.getInt(idx++));
|
||||
state.getU().setChatOpt(inner.getInt(idx++));
|
||||
state.getU().setCreation(inner.getInt(idx++));
|
||||
state.getU().setAvatar(inner.getInt(idx++));
|
||||
state.getU().setProfile(inner.getInt(idx++));
|
||||
state.getU().setPhotos(inner.getInt(idx++));
|
||||
state.getU().setBlurb(inner.getString(idx++));
|
||||
state.setM(new Model());
|
||||
state.getM().setNewModel(inner.getInt(idx++));
|
||||
state.getM().setMissmfc(inner.getInt(idx++));
|
||||
state.getM().setCamscore(inner.getDouble(idx++));
|
||||
state.getM().setContinent(inner.getString(idx++));
|
||||
state.getM().setFlags(inner.getInt(idx++));
|
||||
state.getM().setRank(inner.getInt(idx++));
|
||||
state.getM().setRc(inner.getInt(idx++));
|
||||
state.getM().setTopic(inner.getString(idx++));
|
||||
state.getM().setHidecs(inner.getInt(idx++) == 1);
|
||||
updateSessionState(state);
|
||||
} catch(Exception e) {
|
||||
LOG.warn("Couldn't parse session state {}", inner.toString());
|
||||
}
|
||||
}
|
||||
} else if(object.has("type") && object.getInt("type") == 20) {
|
||||
// TODO parseTags();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSessionState(SessionState newState) {
|
||||
if (newState.getUid() <= 0) {
|
||||
return;
|
||||
}
|
||||
SessionState storedState = sessionStates.get(newState.getUid());
|
||||
if (storedState != null) {
|
||||
storedState.merge(newState);
|
||||
updateModel(storedState);
|
||||
} else {
|
||||
lock.lock();
|
||||
try {
|
||||
sessionStates.put(newState.getUid(), newState);
|
||||
updateModel(newState);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateModel(SessionState state) {
|
||||
// essential data not yet available
|
||||
if(state.getNm() == null || state.getM() == null || state.getU() == null || state.getU().getCamserv() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
MyFreeCamsModel model = models.get(state.getUid());
|
||||
if(model == null) {
|
||||
model = mfc.createModel(state.getNm());
|
||||
models.put(state.getUid(), model);
|
||||
}
|
||||
model.update(state);
|
||||
}
|
||||
|
||||
private Message parseMessage(StringBuilder msg) throws UnsupportedEncodingException {
|
||||
if (msg.length() < 4) {
|
||||
// packet size not transmitted completely
|
||||
return null;
|
||||
} else {
|
||||
int packetLength = Integer.parseInt(msg.substring(0, 4));
|
||||
if (packetLength > msg.length() - 4) {
|
||||
// packet not complete
|
||||
return null;
|
||||
} else {
|
||||
msg.delete(0, 4);
|
||||
int type = parseNextInt(msg);
|
||||
int sender = parseNextInt(msg);
|
||||
int receiver = parseNextInt(msg);
|
||||
int arg1 = parseNextInt(msg);
|
||||
int arg2 = parseNextInt(msg);
|
||||
return new Message(type, sender, receiver, arg1, arg2, URLDecoder.decode(msg.toString(), "utf-8"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int parseNextInt(StringBuilder s) {
|
||||
int nextSpace = s.indexOf(" ");
|
||||
int i = Integer.parseInt(s.substring(0, nextSpace));
|
||||
s.delete(0, nextSpace + 1);
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket webSocket, ByteString bytes) {
|
||||
super.onMessage(webSocket, bytes);
|
||||
LOG.debug("msgb: {}", bytes.hex());
|
||||
}
|
||||
});
|
||||
return ws;
|
||||
}
|
||||
|
||||
private void startKeepAlive(WebSocket ws) {
|
||||
Thread keepAlive = new Thread(() -> {
|
||||
while(running) {
|
||||
LOG.trace("--> NULL to keep the connection alive");
|
||||
try {
|
||||
ws.send("0 0 0 0 0 -\n");
|
||||
Thread.sleep(TimeUnit.SECONDS.toMillis(15));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
keepAlive.setName("KeepAlive");
|
||||
keepAlive.setDaemon(true);
|
||||
keepAlive.start();
|
||||
}
|
||||
|
||||
public void update(MyFreeCamsModel model) {
|
||||
lock.lock();
|
||||
try {
|
||||
for (SessionState state : sessionStates.values()) {
|
||||
if(Objects.equals(state.getNm(), model.getName())) {
|
||||
model.update(state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public MyFreeCamsModel getModel(int uid) {
|
||||
return models.get(uid);
|
||||
}
|
||||
|
||||
public void execute(Runnable r) {
|
||||
executor.execute(r);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.iheartradio.m3u8.Encoding;
|
||||
import com.iheartradio.m3u8.Format;
|
||||
import com.iheartradio.m3u8.ParseException;
|
||||
import com.iheartradio.m3u8.PlaylistException;
|
||||
import com.iheartradio.m3u8.PlaylistParser;
|
||||
import com.iheartradio.m3u8.data.MasterPlaylist;
|
||||
import com.iheartradio.m3u8.data.Playlist;
|
||||
import com.iheartradio.m3u8.data.PlaylistData;
|
||||
|
||||
import ctbrec.AbstractModel;
|
||||
import ctbrec.recorder.download.StreamSource;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class MyFreeCamsModel extends AbstractModel {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(MyFreeCamsModel.class);
|
||||
|
||||
private String hlsUrl;
|
||||
private double camScore;
|
||||
private State state;
|
||||
private int resolution[];
|
||||
|
||||
@Override
|
||||
public boolean isOnline() throws IOException, ExecutionException, InterruptedException {
|
||||
MyFreeCamsClient.getInstance().update(this);
|
||||
return state == State.ONLINE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
|
||||
return isOnline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOnlineState(boolean failFast) throws IOException, ExecutionException {
|
||||
return state != null ? state.toString() : "offline";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException {
|
||||
MasterPlaylist masterPlaylist = getMasterPlaylist();
|
||||
List<StreamSource> sources = new ArrayList<>();
|
||||
for (PlaylistData playlist : masterPlaylist.getPlaylists()) {
|
||||
if (playlist.hasStreamInfo()) {
|
||||
StreamSource src = new StreamSource();
|
||||
src.bandwidth = playlist.getStreamInfo().getBandwidth();
|
||||
if(playlist.getStreamInfo().getResolution() != null) {
|
||||
src.width = playlist.getStreamInfo().getResolution().width;
|
||||
src.height = playlist.getStreamInfo().getResolution().height;
|
||||
String masterUrl = hlsUrl;
|
||||
String baseUrl = masterUrl.substring(0, masterUrl.lastIndexOf('/') + 1);
|
||||
String segmentUri = baseUrl + playlist.getUri();
|
||||
src.mediaPlaylistUrl = segmentUri;
|
||||
LOG.trace("Media playlist {}", src.mediaPlaylistUrl);
|
||||
sources.add(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
private MasterPlaylist getMasterPlaylist() throws IOException, ParseException, PlaylistException {
|
||||
if(hlsUrl == null) {
|
||||
throw new IllegalStateException("Stream url unknown");
|
||||
}
|
||||
LOG.debug("Loading master playlist {}", hlsUrl);
|
||||
Request req = new Request.Builder().url(hlsUrl).build();
|
||||
Response response = MyFreeCams.httpClient.newCall(req).execute();
|
||||
try {
|
||||
InputStream inputStream = response.body().byteStream();
|
||||
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8);
|
||||
Playlist playlist = parser.parse();
|
||||
MasterPlaylist master = playlist.getMasterPlaylist();
|
||||
return master;
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCacheEntries() {
|
||||
resolution = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveTip(int tokens) throws IOException {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
|
||||
if(resolution == null) {
|
||||
if(failFast || hlsUrl == null) {
|
||||
return new int[2];
|
||||
}
|
||||
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 (ExecutionException | IOException | ParseException | PlaylistException e) {
|
||||
LOG.error("Couldn't determine stream resolution", e);
|
||||
}
|
||||
});
|
||||
return new int[2];
|
||||
} else {
|
||||
return resolution;
|
||||
}
|
||||
}
|
||||
|
||||
public void setStreamUrl(String hlsUrl) {
|
||||
this.hlsUrl = hlsUrl;
|
||||
}
|
||||
|
||||
public String getStreamUrl() {
|
||||
return hlsUrl;
|
||||
}
|
||||
|
||||
public double getCamScore() {
|
||||
return camScore;
|
||||
}
|
||||
|
||||
public void setCamScore(double camScore) {
|
||||
this.camScore = camScore;
|
||||
}
|
||||
|
||||
public void setState(State state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void update(SessionState state) {
|
||||
setCamScore(state.getM().getCamscore());
|
||||
setState(State.of(state.getVs()));
|
||||
|
||||
// preview
|
||||
String uid = state.getUid().toString();
|
||||
String uidStart = uid.substring(0, 3);
|
||||
String previewUrl = "https://img.mfcimg.com/photos2/"+uidStart+'/'+uid+"/avatar.300x300.jpg";
|
||||
setPreview(previewUrl);
|
||||
|
||||
// stream url
|
||||
Integer camserv = state.getU().getCamserv();
|
||||
if(camserv != null) {
|
||||
String hlsUrl = "http://video" + (camserv - 500) + ".myfreecams.com:1935/NxServer/ngrp:mfc_" + (100000000 + state.getUid()) + ".f4v_mobile/playlist.m3u8";
|
||||
setStreamUrl(hlsUrl);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.ui.PaginatedScheduledService;
|
||||
import ctbrec.ui.TabProvider;
|
||||
import ctbrec.ui.ThumbOverviewTab;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.util.Duration;
|
||||
|
||||
public class MyFreeCamsTabProvider extends TabProvider {
|
||||
private Recorder recorder;
|
||||
private MyFreeCams myFreeCams;
|
||||
|
||||
public MyFreeCamsTabProvider(MyFreeCamsClient client, Recorder recorder, MyFreeCams myFreeCams) {
|
||||
this.recorder = recorder;
|
||||
this.myFreeCams = myFreeCams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tab> getTabs(Scene scene) {
|
||||
List<Tab> tabs = new ArrayList<>();
|
||||
|
||||
PaginatedScheduledService updateService = new OnlineCamsUpdateService();
|
||||
ThumbOverviewTab online = new ThumbOverviewTab("Online", updateService);
|
||||
online.setRecorder(recorder);
|
||||
updateService.setPeriod(new Duration(TimeUnit.SECONDS.toMillis(10)));
|
||||
tabs.add(online);
|
||||
|
||||
updateService = new FriendsUpdateService(myFreeCams);
|
||||
ThumbOverviewTab friends = new ThumbOverviewTab("Friends", updateService);
|
||||
friends.setRecorder(recorder);
|
||||
updateService.setPeriod(new Duration(TimeUnit.SECONDS.toMillis(10)));
|
||||
tabs.add(friends);
|
||||
|
||||
|
||||
return tabs;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ctbrec.Model;
|
||||
import ctbrec.ui.PaginatedScheduledService;
|
||||
import javafx.concurrent.Task;
|
||||
|
||||
public class OnlineCamsUpdateService extends PaginatedScheduledService {
|
||||
|
||||
@Override
|
||||
protected Task<List<Model>> createTask() {
|
||||
return new Task<List<Model>>() {
|
||||
@Override
|
||||
public List<Model> call() throws IOException {
|
||||
MyFreeCamsClient client = MyFreeCamsClient.getInstance();
|
||||
int modelsPerPage = 50;
|
||||
return client.getModels().stream()
|
||||
.filter((m) -> m.getPreview() != null)
|
||||
.filter((m) -> m.getStreamUrl() != null)
|
||||
.filter((m) -> {
|
||||
try {
|
||||
return m.isOnline();
|
||||
} catch(Exception e) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.sorted((m1,m2) -> (int)(m2.getCamScore() - m1.getCamScore()))
|
||||
.skip( (page-1) * modelsPerPage)
|
||||
.limit(modelsPerPage)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import ctbrec.ui.PaginatedScheduledService;
|
||||
import ctbrec.ui.ThumbOverviewTab;
|
||||
|
||||
public class OnlineModelsTab extends ThumbOverviewTab {
|
||||
|
||||
public OnlineModelsTab(String title, PaginatedScheduledService updateService) {
|
||||
super(title, updateService);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class ServerConfig {
|
||||
|
||||
List<String> ajaxServers;
|
||||
List<String> videoServers;
|
||||
List<String> chatServers;
|
||||
Map<String, String> h5Servers;
|
||||
Map<String, String> wsServers;
|
||||
Map<String, String> wzobsServers;
|
||||
Map<String, String> ngVideo;
|
||||
|
||||
public ServerConfig(OkHttpClient client) throws IOException {
|
||||
Request req = new Request.Builder().url("http://www.myfreecams.com/_js/serverconfig.js").build();
|
||||
Response resp = client.newCall(req).execute();
|
||||
String json = resp.body().string();
|
||||
|
||||
JSONObject serverConfig = new JSONObject(json);
|
||||
ajaxServers = parseList(serverConfig, "ajax_servers");
|
||||
videoServers = parseList(serverConfig, "video_servers");
|
||||
chatServers = parseList(serverConfig, "chat_servers");
|
||||
h5Servers = parseMap(serverConfig, "h5video_servers");
|
||||
wsServers = parseMap(serverConfig, "websocket_servers");
|
||||
wzobsServers = parseMap(serverConfig, "wzobs_servers");
|
||||
ngVideo = parseMap(serverConfig, "ngvideo_servers");
|
||||
}
|
||||
|
||||
private static Map<String, String> parseMap(JSONObject serverConfig, String name) {
|
||||
JSONObject servers = serverConfig.getJSONObject(name);
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (String key : servers.keySet()) {
|
||||
result.put(key, servers.getString(key));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<String> parseList(JSONObject serverConfig, String name) {
|
||||
JSONArray servers = serverConfig.getJSONArray(name);
|
||||
List<String> result = new ArrayList<>(servers.length());
|
||||
for (Object server : servers) {
|
||||
result.add((String) server);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SessionState {
|
||||
|
||||
private Integer lv;
|
||||
private String nm;
|
||||
private Integer pid;
|
||||
private Integer sid;
|
||||
private Integer uid;
|
||||
private Integer vs;
|
||||
private User u;
|
||||
private Model m;
|
||||
private X x;
|
||||
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
|
||||
|
||||
public Integer getLv() {
|
||||
return lv;
|
||||
}
|
||||
|
||||
public void setLv(Integer lv) {
|
||||
this.lv = lv;
|
||||
}
|
||||
|
||||
public String getNm() {
|
||||
return nm;
|
||||
}
|
||||
|
||||
public void setNm(String nm) {
|
||||
this.nm = nm;
|
||||
}
|
||||
|
||||
public Integer getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
public void setPid(Integer pid) {
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
public Integer getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
public void setSid(Integer sid) {
|
||||
this.sid = sid;
|
||||
}
|
||||
|
||||
public Integer getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public void setUid(Integer uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public Integer getVs() {
|
||||
return vs;
|
||||
}
|
||||
|
||||
public void setVs(Integer vs) {
|
||||
this.vs = vs;
|
||||
}
|
||||
|
||||
public User getU() {
|
||||
return u;
|
||||
}
|
||||
|
||||
public void setU(User u) {
|
||||
this.u = u;
|
||||
}
|
||||
|
||||
public Model getM() {
|
||||
return m;
|
||||
}
|
||||
|
||||
public void setM(Model m) {
|
||||
this.m = m;
|
||||
}
|
||||
|
||||
public X getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(X x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAdditionalProperties() {
|
||||
return this.additionalProperties;
|
||||
}
|
||||
|
||||
public void setAdditionalProperty(String name, Object value) {
|
||||
this.additionalProperties.put(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Integer.toString(uid) + " u:" + u + " m:" + m + " x:" + x + " " + nm;
|
||||
}
|
||||
|
||||
public void merge(SessionState newState) {
|
||||
lv = newState.lv != null ? newState.lv : lv;
|
||||
nm = newState.nm != null ? newState.nm : nm;
|
||||
pid = newState.pid != null ? newState.pid : pid;
|
||||
sid = newState.sid != null ? newState.sid : sid;
|
||||
vs = newState.vs != null ? newState.vs : vs;
|
||||
additionalProperties.putAll(newState.additionalProperties);
|
||||
|
||||
if (u != null) {
|
||||
u.merge(newState.u);
|
||||
} else {
|
||||
u = newState.u;
|
||||
}
|
||||
if (m != null) {
|
||||
m.merge(newState.m);
|
||||
} else {
|
||||
m = newState.m;
|
||||
}
|
||||
if (x != null) {
|
||||
x.merge(newState.x);
|
||||
} else {
|
||||
x = newState.x;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Share {
|
||||
|
||||
private Integer albums;
|
||||
private Integer follows;
|
||||
private Integer tmAlbum;
|
||||
private Integer things;
|
||||
private Integer clubs;
|
||||
private Integer collections;
|
||||
private Integer stores;
|
||||
private Integer goals;
|
||||
private Integer polls;
|
||||
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
|
||||
|
||||
public Integer getAlbums() {
|
||||
return albums;
|
||||
}
|
||||
|
||||
public void setAlbums(Integer albums) {
|
||||
this.albums = albums;
|
||||
}
|
||||
|
||||
public Integer getFollows() {
|
||||
return follows;
|
||||
}
|
||||
|
||||
public void setFollows(Integer follows) {
|
||||
this.follows = follows;
|
||||
}
|
||||
|
||||
public Integer getTmAlbum() {
|
||||
return tmAlbum;
|
||||
}
|
||||
|
||||
public void setTmAlbum(Integer tmAlbum) {
|
||||
this.tmAlbum = tmAlbum;
|
||||
}
|
||||
|
||||
public Integer getThings() {
|
||||
return things;
|
||||
}
|
||||
|
||||
public void setThings(Integer things) {
|
||||
this.things = things;
|
||||
}
|
||||
|
||||
public Integer getClubs() {
|
||||
return clubs;
|
||||
}
|
||||
|
||||
public void setClubs(Integer clubs) {
|
||||
this.clubs = clubs;
|
||||
}
|
||||
|
||||
public Integer getCollections() {
|
||||
return collections;
|
||||
}
|
||||
|
||||
public void setCollections(Integer collections) {
|
||||
this.collections = collections;
|
||||
}
|
||||
|
||||
public Integer getStores() {
|
||||
return stores;
|
||||
}
|
||||
|
||||
public void setStores(Integer stores) {
|
||||
this.stores = stores;
|
||||
}
|
||||
|
||||
public Integer getGoals() {
|
||||
return goals;
|
||||
}
|
||||
|
||||
public void setGoals(Integer goals) {
|
||||
this.goals = goals;
|
||||
}
|
||||
|
||||
public Integer getPolls() {
|
||||
return polls;
|
||||
}
|
||||
|
||||
public void setPolls(Integer polls) {
|
||||
this.polls = polls;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAdditionalProperties() {
|
||||
return this.additionalProperties;
|
||||
}
|
||||
|
||||
public void setAdditionalProperty(String name, Object value) {
|
||||
this.additionalProperties.put(name, value);
|
||||
}
|
||||
|
||||
public void merge(Share share) {
|
||||
if (share == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
albums = share.albums != null ? share.albums : albums;
|
||||
follows = share.follows != null ? share.follows : follows;
|
||||
tmAlbum = share.tmAlbum != null ? share.tmAlbum : tmAlbum;
|
||||
things = share.things != null ? share.things : things;
|
||||
clubs = share.clubs != null ? share.clubs : clubs;
|
||||
collections = share.collections != null ? share.collections : collections;
|
||||
stores = share.stores != null ? share.stores : stores;
|
||||
goals = share.goals != null ? share.goals : goals;
|
||||
polls = share.polls != null ? share.polls : polls;
|
||||
additionalProperties.putAll(share.additionalProperties);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public enum State {
|
||||
ONLINE("online"),
|
||||
CAMOFF("online - cam off"),
|
||||
RECORDING("recording"),
|
||||
INCLUDE("include"),
|
||||
EXCLUDE("exclude"),
|
||||
DELETE("delete"),
|
||||
AWAY("away"),
|
||||
PRIVATE("private"),
|
||||
GROUP_SHOW("group_show"),
|
||||
OFFLINE("offline"),
|
||||
UNKNOWN("unknown");
|
||||
|
||||
String literal;
|
||||
State(String literal) {
|
||||
this.literal = literal;
|
||||
}
|
||||
|
||||
public static State of(Integer vs) {
|
||||
Integer s = Optional.ofNullable(vs).orElse(Integer.MAX_VALUE);
|
||||
switch (s) {
|
||||
case 0:
|
||||
return ONLINE;
|
||||
case 90:
|
||||
return CAMOFF;
|
||||
case -4:
|
||||
return RECORDING;
|
||||
case -3:
|
||||
return INCLUDE;
|
||||
case -2:
|
||||
return EXCLUDE;
|
||||
case -1:
|
||||
return DELETE;
|
||||
case 2:
|
||||
return AWAY;
|
||||
case 12:
|
||||
case 91:
|
||||
return PRIVATE;
|
||||
case 13:
|
||||
return GROUP_SHOW;
|
||||
case 127:
|
||||
return OFFLINE;
|
||||
default:
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return literal;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class User {
|
||||
|
||||
private Integer avatar;
|
||||
private String blurb;
|
||||
private Integer camserv;
|
||||
private String chatColor;
|
||||
private Integer chatFont;
|
||||
private Integer chatOpt;
|
||||
private String country;
|
||||
private Integer creation;
|
||||
private String ethnic;
|
||||
private String occupation;
|
||||
private Integer photos;
|
||||
private Integer profile;
|
||||
private String status;
|
||||
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
|
||||
|
||||
public Integer getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(Integer avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public String getBlurb() {
|
||||
return blurb;
|
||||
}
|
||||
|
||||
public void setBlurb(String blurb) {
|
||||
this.blurb = blurb;
|
||||
}
|
||||
|
||||
public Integer getCamserv() {
|
||||
return camserv;
|
||||
}
|
||||
|
||||
public void setCamserv(Integer camserv) {
|
||||
this.camserv = camserv;
|
||||
}
|
||||
|
||||
public String getChatColor() {
|
||||
return chatColor;
|
||||
}
|
||||
|
||||
public void setChatColor(String chatColor) {
|
||||
this.chatColor = chatColor;
|
||||
}
|
||||
|
||||
public Integer getChatFont() {
|
||||
return chatFont;
|
||||
}
|
||||
|
||||
public void setChatFont(Integer chatFont) {
|
||||
this.chatFont = chatFont;
|
||||
}
|
||||
|
||||
public Integer getChatOpt() {
|
||||
return chatOpt;
|
||||
}
|
||||
|
||||
public void setChatOpt(Integer chatOpt) {
|
||||
this.chatOpt = chatOpt;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public Integer getCreation() {
|
||||
return creation;
|
||||
}
|
||||
|
||||
public void setCreation(Integer creation) {
|
||||
this.creation = creation;
|
||||
}
|
||||
|
||||
public String getEthnic() {
|
||||
return ethnic;
|
||||
}
|
||||
|
||||
public void setEthnic(String ethnic) {
|
||||
this.ethnic = ethnic;
|
||||
}
|
||||
|
||||
public String getOccupation() {
|
||||
return occupation;
|
||||
}
|
||||
|
||||
public void setOccupation(String occupation) {
|
||||
this.occupation = occupation;
|
||||
}
|
||||
|
||||
public Integer getPhotos() {
|
||||
return photos;
|
||||
}
|
||||
|
||||
public void setPhotos(Integer photos) {
|
||||
this.photos = photos;
|
||||
}
|
||||
|
||||
public Integer getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
public void setProfile(Integer profile) {
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAdditionalProperties() {
|
||||
return this.additionalProperties;
|
||||
}
|
||||
|
||||
public void setAdditionalProperty(String name, Object value) {
|
||||
this.additionalProperties.put(name, value);
|
||||
}
|
||||
|
||||
public void merge(User u) {
|
||||
if (u == null) {
|
||||
return;
|
||||
}
|
||||
avatar = u.avatar != null ? u.avatar : avatar;
|
||||
blurb = u.blurb != null ? u.blurb : blurb;
|
||||
camserv = u.camserv != null ? u.camserv : camserv;
|
||||
chatColor = u.chatColor != null ? u.chatColor : chatColor;
|
||||
chatFont = u.chatFont != null ? u.chatFont : chatFont;
|
||||
chatOpt = u.chatOpt != null ? u.chatOpt : chatOpt;
|
||||
country = u.country != null ? u.country : country;
|
||||
creation = u.creation != null ? u.creation : creation;
|
||||
ethnic = u.ethnic != null ? u.ethnic : ethnic;
|
||||
occupation = u.occupation != null ? u.occupation : occupation;
|
||||
photos = u.photos != null ? u.photos : photos;
|
||||
profile = u.profile != null ? u.profile : profile;
|
||||
status = u.status != null ? u.status : status;
|
||||
additionalProperties.putAll(u.additionalProperties);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class X {
|
||||
|
||||
private Fcext fcext;
|
||||
private Share share;
|
||||
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
|
||||
|
||||
public Fcext getFcext() {
|
||||
return fcext;
|
||||
}
|
||||
|
||||
public void setFcext(Fcext fcext) {
|
||||
this.fcext = fcext;
|
||||
}
|
||||
|
||||
public Share getShare() {
|
||||
return share;
|
||||
}
|
||||
|
||||
public void setShare(Share share) {
|
||||
this.share = share;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAdditionalProperties() {
|
||||
return this.additionalProperties;
|
||||
}
|
||||
|
||||
public void setAdditionalProperty(String name, Object value) {
|
||||
this.additionalProperties.put(name, value);
|
||||
}
|
||||
|
||||
public void merge(X x) {
|
||||
if(x == null) {
|
||||
return;
|
||||
}
|
||||
fcext.merge(x.fcext);
|
||||
share.merge(x.share);
|
||||
additionalProperties.putAll(x.additionalProperties);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -26,27 +26,20 @@ import ctbrec.io.HttpClient;
|
|||
import ctbrec.recorder.LocalRecorder;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.recorder.RemoteRecorder;
|
||||
import ctbrec.sites.chaturbate.Chaturbate;
|
||||
import ctbrec.sites.mfc.MyFreeCams;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.HostServices;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.control.TabPane;
|
||||
import javafx.scene.control.TabPane.TabClosingPolicy;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.stage.Stage;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
@ -74,9 +67,10 @@ public class CamrecApplication extends Application {
|
|||
hostServices = getHostServices();
|
||||
client = HttpClient.getInstance();
|
||||
createRecorder();
|
||||
site = new Chaturbate();
|
||||
//site = new Chaturbate();
|
||||
site = new MyFreeCams();
|
||||
site.setRecorder(recorder);
|
||||
doInitialLogin();
|
||||
// TODO move this to Chaturbate class doInitialLogin();
|
||||
createGui(primaryStage);
|
||||
checkForUpdates();
|
||||
}
|
||||
|
@ -158,37 +152,38 @@ public class CamrecApplication extends Application {
|
|||
}.start();
|
||||
});
|
||||
|
||||
String username = Config.getInstance().getSettings().username;
|
||||
if(username != null && !username.trim().isEmpty()) {
|
||||
double fontSize = tabPane.getTabMaxHeight() / 2 - 1;
|
||||
Button buyTokens = new Button("Buy Tokens");
|
||||
buyTokens.setFont(Font.font(fontSize));
|
||||
buyTokens.setOnAction((e) -> DesktopIntergation.open(AFFILIATE_LINK));
|
||||
buyTokens.setMaxHeight(tabPane.getTabMaxHeight());
|
||||
TokenLabel tokenBalance = new TokenLabel();
|
||||
tokenPanel = new HBox(5, tokenBalance, buyTokens);
|
||||
//tokenPanel.setBackground(new Background(new BackgroundFill(Color.GREEN, CornerRadii.EMPTY, new Insets(0))));
|
||||
tokenPanel.setAlignment(Pos.BASELINE_RIGHT);
|
||||
tokenPanel.setMaxHeight(tabPane.getTabMaxHeight());
|
||||
tokenPanel.setMaxWidth(200);
|
||||
tokenBalance.setFont(Font.font(fontSize));
|
||||
HBox.setMargin(tokenBalance, new Insets(0, 5, 0, 0));
|
||||
HBox.setMargin(buyTokens, new Insets(0, 5, 0, 0));
|
||||
for (Node node : tabPane.getChildrenUnmodifiable()) {
|
||||
if(node.getStyleClass().contains("tab-header-area")) {
|
||||
Parent header = (Parent) node;
|
||||
for (Node nd : header.getChildrenUnmodifiable()) {
|
||||
if(nd.getStyleClass().contains("tab-header-background")) {
|
||||
StackPane pane = (StackPane) nd;
|
||||
StackPane.setAlignment(tokenPanel, Pos.CENTER_RIGHT);
|
||||
pane.getChildren().add(tokenPanel);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
loadTokenBalance(tokenBalance);
|
||||
}
|
||||
// TODO think about a solution, which works for all sites
|
||||
// String username = Config.getInstance().getSettings().username;
|
||||
// if(username != null && !username.trim().isEmpty()) {
|
||||
// double fontSize = tabPane.getTabMaxHeight() / 2 - 1;
|
||||
// Button buyTokens = new Button("Buy Tokens");
|
||||
// buyTokens.setFont(Font.font(fontSize));
|
||||
// buyTokens.setOnAction((e) -> DesktopIntergation.open(AFFILIATE_LINK));
|
||||
// buyTokens.setMaxHeight(tabPane.getTabMaxHeight());
|
||||
// TokenLabel tokenBalance = new TokenLabel();
|
||||
// tokenPanel = new HBox(5, tokenBalance, buyTokens);
|
||||
// //tokenPanel.setBackground(new Background(new BackgroundFill(Color.GREEN, CornerRadii.EMPTY, new Insets(0))));
|
||||
// tokenPanel.setAlignment(Pos.BASELINE_RIGHT);
|
||||
// tokenPanel.setMaxHeight(tabPane.getTabMaxHeight());
|
||||
// tokenPanel.setMaxWidth(200);
|
||||
// tokenBalance.setFont(Font.font(fontSize));
|
||||
// HBox.setMargin(tokenBalance, new Insets(0, 5, 0, 0));
|
||||
// HBox.setMargin(buyTokens, new Insets(0, 5, 0, 0));
|
||||
// for (Node node : tabPane.getChildrenUnmodifiable()) {
|
||||
// if(node.getStyleClass().contains("tab-header-area")) {
|
||||
// Parent header = (Parent) node;
|
||||
// for (Node nd : header.getChildrenUnmodifiable()) {
|
||||
// if(nd.getStyleClass().contains("tab-header-background")) {
|
||||
// StackPane pane = (StackPane) nd;
|
||||
// StackPane.setAlignment(tokenPanel, Pos.CENTER_RIGHT);
|
||||
// pane.getChildren().add(tokenPanel);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// loadTokenBalance(tokenBalance);
|
||||
// }
|
||||
}
|
||||
|
||||
private void loadTokenBalance(TokenLabel label) {
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
package ctbrec.ui;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Tab;
|
||||
|
||||
public class TabProvider {
|
||||
public abstract class TabProvider {
|
||||
|
||||
public List<Tab> getTabs(Scene scene) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
public abstract List<Tab> getTabs(Scene scene);
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ public class ThumbCell extends StackPane {
|
|||
private final Color colorHighlight = Color.WHITE;
|
||||
private final Color colorRecording = new Color(0.8, 0.28, 0.28, 1);
|
||||
private SimpleBooleanProperty selectionProperty = new SimpleBooleanProperty(false);
|
||||
private double imgAspectRatio = 3.0 / 4.0;
|
||||
|
||||
private HttpClient client;
|
||||
|
||||
|
@ -91,6 +92,8 @@ public class ThumbCell extends StackPane {
|
|||
iv = new ImageView();
|
||||
setImage(model.getPreview());
|
||||
iv.setSmooth(true);
|
||||
iv.setPreserveRatio(true);
|
||||
iv.setStyle("-fx-background-color: #000");
|
||||
getChildren().add(iv);
|
||||
|
||||
nameBackground = new Rectangle();
|
||||
|
@ -208,17 +211,11 @@ public class ThumbCell extends StackPane {
|
|||
// 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
|
||||
try {
|
||||
if (model.isOnline() && resolution[1] == 0) {
|
||||
LOG.debug("Removing invalid resolution value for {}", model.getName());
|
||||
model.invalidateCacheEntries();
|
||||
}
|
||||
} catch (IOException | ExecutionException | InterruptedException e) {
|
||||
LOG.error("Coulnd't get resolution for model {}", model, e);
|
||||
if (model.isOnline() && resolution[1] == 0) {
|
||||
LOG.debug("Removing invalid resolution value for {}", model.getName());
|
||||
model.invalidateCacheEntries();
|
||||
}
|
||||
} catch (ExecutionException e1) {
|
||||
LOG.warn("Couldn't update resolution tag for model {}", model.getName(), e1);
|
||||
} catch (IOException e1) {
|
||||
} catch (ExecutionException | IOException | InterruptedException e1) {
|
||||
LOG.warn("Couldn't update resolution tag for model {}", model.getName(), e1);
|
||||
} finally {
|
||||
ThumbOverviewTab.resolutionProcessing.remove(model);
|
||||
|
@ -226,11 +223,11 @@ public class ThumbCell extends StackPane {
|
|||
});
|
||||
}
|
||||
|
||||
private void updateResolutionTag(int[] resolution) throws IOException, ExecutionException {
|
||||
private void updateResolutionTag(int[] resolution) throws IOException, ExecutionException, InterruptedException {
|
||||
String _res = "n/a";
|
||||
Paint resolutionBackgroundColor = resolutionOnlineColor;
|
||||
String state = model.getOnlineState(false);
|
||||
if ("public".equals(state)) {
|
||||
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];
|
||||
|
@ -262,6 +259,8 @@ public class ThumbCell extends StackPane {
|
|||
@Override
|
||||
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
|
||||
if(newValue.doubleValue() == 1.0) {
|
||||
imgAspectRatio = img.getHeight() / img.getWidth();
|
||||
setThumbWidth(width);
|
||||
iv.setImage(img);
|
||||
}
|
||||
}
|
||||
|
@ -283,26 +282,33 @@ public class ThumbCell extends StackPane {
|
|||
}
|
||||
|
||||
void startPlayer() {
|
||||
try {
|
||||
if(model.isOnline(true)) {
|
||||
List<StreamSource> sources = model.getStreamSources();
|
||||
Collections.sort(sources);
|
||||
StreamSource best = sources.get(sources.size()-1);
|
||||
LOG.debug("Playing {}", best.getMediaPlaylistUrl());
|
||||
Player.play(best.getMediaPlaylistUrl());
|
||||
} else {
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle("Room not public");
|
||||
alert.setHeaderText("Room is currently not public");
|
||||
alert.showAndWait();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
if(model.isOnline(true)) {
|
||||
List<StreamSource> sources = model.getStreamSources();
|
||||
Collections.sort(sources);
|
||||
StreamSource best = sources.get(sources.size()-1);
|
||||
LOG.debug("Playing {}", best.getMediaPlaylistUrl());
|
||||
Player.play(best.getMediaPlaylistUrl());
|
||||
} else {
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle("Room not public");
|
||||
alert.setHeaderText("Room is currently not public");
|
||||
alert.showAndWait();
|
||||
});
|
||||
}
|
||||
} catch (IOException | ExecutionException | ParseException | PlaylistException | InterruptedException e1) {
|
||||
LOG.error("Couldn't get stream information for model {}", model, e1);
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR);
|
||||
alert.setTitle("Error");
|
||||
alert.setHeaderText("Couldn't determine stream URL");
|
||||
alert.setContentText(e1.getLocalizedMessage());
|
||||
alert.showAndWait();
|
||||
});
|
||||
}
|
||||
} catch (IOException | ExecutionException | InterruptedException | ParseException | PlaylistException e1) {
|
||||
LOG.error("Couldn't get stream information for model {}", model, e1);
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR);
|
||||
alert.setTitle("Error");
|
||||
alert.setHeaderText("Couldn't determine stream URL");
|
||||
alert.showAndWait();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void setRecording(boolean recording) {
|
||||
|
@ -491,7 +497,7 @@ public class ThumbCell extends StackPane {
|
|||
}
|
||||
|
||||
public void setThumbWidth(int width) {
|
||||
int height = width * 3 / 4;
|
||||
int height = (int) (width * imgAspectRatio);
|
||||
setSize(width, height);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
||||
static ExecutorService threadPool = new ThreadPoolExecutor(2, 2, 10, TimeUnit.MINUTES, queue);
|
||||
|
||||
PaginatedScheduledService updateService;
|
||||
protected PaginatedScheduledService updateService;
|
||||
Recorder recorder;
|
||||
List<ThumbCell> filteredThumbCells = Collections.synchronizedList(new ArrayList<>());
|
||||
List<ThumbCell> selectedThumbCells = Collections.synchronizedList(new ArrayList<>());
|
||||
|
@ -215,9 +215,14 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
if(updatesSuspended) {
|
||||
return;
|
||||
}
|
||||
List<Model> models = updateService.getValue();
|
||||
updateGrid(models);
|
||||
|
||||
}
|
||||
|
||||
protected void updateGrid(List<? extends Model> models) {
|
||||
gridLock.lock();
|
||||
try {
|
||||
List<Model> models = updateService.getValue();
|
||||
ObservableList<Node> nodes = grid.getChildren();
|
||||
|
||||
// first remove models, which are not in the updated list
|
||||
|
@ -269,7 +274,6 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
} finally {
|
||||
gridLock.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ThumbCell createThumbCell(ThumbOverviewTab thumbOverviewTab, Model model, Recorder recorder2, HttpClient client2) {
|
||||
|
@ -439,6 +443,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
} else {
|
||||
alert.setContentText(event.getSource().getException().getLocalizedMessage());
|
||||
}
|
||||
LOG.error("Couldn't update model list", event.getSource().getException());
|
||||
} else {
|
||||
alert.setContentText(event.getEventType().toString());
|
||||
}
|
||||
|
|
|
@ -190,7 +190,7 @@ public class Streamer {
|
|||
Long sleepNanosPrevious = null;
|
||||
if (lastPcrValue != null && lastPcrTime != null) {
|
||||
if (pcrValue <= lastPcrValue) {
|
||||
log.error("PCR discontinuity ! " + packet.getPid());
|
||||
log.trace("PCR discontinuity ! " + packet.getPid());
|
||||
resetState = true;
|
||||
} else {
|
||||
sleepNanosPrevious = ((pcrValue - lastPcrValue) / 27 * 1000) - (pcrTime - lastPcrTime);
|
||||
|
|
Loading…
Reference in New Issue