Abstract more stuff in the site packages
This commit is contained in:
parent
362d90b29b
commit
387661cfdf
|
@ -30,5 +30,6 @@ public interface Model {
|
|||
public void invalidateCacheEntries();
|
||||
public void receiveTip(int tokens) throws IOException;
|
||||
public int[] getStreamResolution(boolean failFast) throws ExecutionException;
|
||||
|
||||
public boolean follow() throws IOException;
|
||||
public boolean unfollow() throws IOException;
|
||||
}
|
|
@ -28,7 +28,6 @@ public class Settings {
|
|||
public boolean determineResolution = false;
|
||||
public boolean requireAuthentication = false;
|
||||
public boolean chooseStreamQuality = false;
|
||||
public boolean recordFollowed = false;
|
||||
public byte[] key = null;
|
||||
public ProxyType proxyType = ProxyType.DIRECT;
|
||||
public String proxyHost;
|
||||
|
|
|
@ -1,44 +1,24 @@
|
|||
package ctbrec.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Settings.ProxyType;
|
||||
import ctbrec.ui.CamrecApplication;
|
||||
import ctbrec.ui.CookieJarImpl;
|
||||
import ctbrec.ui.HtmlParser;
|
||||
import okhttp3.ConnectionPool;
|
||||
import okhttp3.Cookie;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class HttpClient {
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(HttpClient.class);
|
||||
private static HttpClient instance = new HttpClient();
|
||||
public abstract class HttpClient {
|
||||
protected OkHttpClient client;
|
||||
protected CookieJarImpl cookieJar = new CookieJarImpl();
|
||||
protected boolean loggedIn = false;
|
||||
protected int loginTries = 0;
|
||||
|
||||
private OkHttpClient client;
|
||||
private CookieJarImpl cookieJar = new CookieJarImpl();
|
||||
private boolean loggedIn = false;
|
||||
private int loginTries = 0;
|
||||
private String token;
|
||||
|
||||
private HttpClient() {
|
||||
loadProxySettings();
|
||||
client = new OkHttpClient.Builder()
|
||||
.cookieJar(cookieJar)
|
||||
.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();
|
||||
protected HttpClient() {
|
||||
reconfigure();
|
||||
}
|
||||
|
||||
private void loadProxySettings() {
|
||||
|
@ -89,24 +69,11 @@ public class HttpClient {
|
|||
}
|
||||
}
|
||||
|
||||
public static HttpClient getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Response execute(Request request) throws IOException {
|
||||
Response resp = execute(request, false);
|
||||
return resp;
|
||||
}
|
||||
|
||||
private void extractCsrfToken(Request request) {
|
||||
try {
|
||||
Cookie csrfToken = cookieJar.getCookie(request.url(), "csrftoken");
|
||||
token = csrfToken.value();
|
||||
} catch(NoSuchElementException e) {
|
||||
LOG.trace("CSRF token not found in cookies");
|
||||
}
|
||||
}
|
||||
|
||||
public Response execute(Request req, boolean requiresLogin) throws IOException {
|
||||
if(requiresLogin && !loggedIn) {
|
||||
boolean loginSuccessful = login();
|
||||
|
@ -115,64 +82,20 @@ public class HttpClient {
|
|||
}
|
||||
}
|
||||
Response resp = client.newCall(req).execute();
|
||||
extractCsrfToken(req);
|
||||
return resp;
|
||||
}
|
||||
|
||||
public boolean login() throws IOException {
|
||||
try {
|
||||
Request login = new Request.Builder()
|
||||
.url(CamrecApplication.BASE_URI + "/auth/login/")
|
||||
.build();
|
||||
Response response = client.newCall(login).execute();
|
||||
String content = response.body().string();
|
||||
token = HtmlParser.getTag(content, "input[name=csrfmiddlewaretoken]").attr("value");
|
||||
LOG.debug("csrf token is {}", token);
|
||||
|
||||
RequestBody body = new FormBody.Builder()
|
||||
.add("username", Config.getInstance().getSettings().username)
|
||||
.add("password", Config.getInstance().getSettings().password)
|
||||
.add("next", "")
|
||||
.add("csrfmiddlewaretoken", token)
|
||||
.build();
|
||||
login = new Request.Builder()
|
||||
.url(CamrecApplication.BASE_URI + "/auth/login/")
|
||||
.header("Referer", CamrecApplication.BASE_URI + "/auth/login/")
|
||||
.post(body)
|
||||
.build();
|
||||
|
||||
response = client.newCall(login).execute();
|
||||
if(response.isSuccessful()) {
|
||||
content = response.body().string();
|
||||
if(content.contains("Login, Chaturbate login")) {
|
||||
loggedIn = false;
|
||||
} else {
|
||||
loggedIn = true;
|
||||
extractCsrfToken(login);
|
||||
}
|
||||
} else {
|
||||
if(loginTries++ < 3) {
|
||||
login();
|
||||
} else {
|
||||
throw new IOException("Login failed: " + response.code() + " " + response.message());
|
||||
}
|
||||
}
|
||||
response.close();
|
||||
} finally {
|
||||
loginTries = 0;
|
||||
}
|
||||
return loggedIn;
|
||||
}
|
||||
public abstract boolean login() throws IOException;
|
||||
|
||||
public void reconfigure() {
|
||||
instance = new HttpClient();
|
||||
}
|
||||
|
||||
public String getToken() throws IOException {
|
||||
if(token == null) {
|
||||
login();
|
||||
}
|
||||
return token;
|
||||
loadProxySettings();
|
||||
client = new OkHttpClient.Builder()
|
||||
.cookieJar(cookieJar)
|
||||
.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();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package ctbrec.recorder;
|
||||
|
||||
import static ctbrec.Recording.STATUS.FINISHED;
|
||||
import static ctbrec.Recording.STATUS.GENERATING_PLAYLIST;
|
||||
import static ctbrec.Recording.STATUS.RECORDING;
|
||||
import static ctbrec.Recording.STATUS.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -30,32 +28,27 @@ import com.iheartradio.m3u8.PlaylistException;
|
|||
import ctbrec.Config;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.Recording;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.recorder.PlaylistGenerator.InvalidPlaylistException;
|
||||
import ctbrec.recorder.download.Download;
|
||||
import ctbrec.recorder.download.HlsDownload;
|
||||
import ctbrec.recorder.download.MergedHlsDownload;
|
||||
import ctbrec.sites.chaturbate.ChaturbateModelParser;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import ctbrec.recorder.server.RecorderHttpClient;
|
||||
|
||||
public class LocalRecorder implements Recorder {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(LocalRecorder.class);
|
||||
|
||||
private static final boolean IGNORE_CACHE = true;
|
||||
private List<Model> followedModels = Collections.synchronizedList(new ArrayList<>());
|
||||
private List<Model> models = Collections.synchronizedList(new ArrayList<>());
|
||||
private Map<Model, Download> recordingProcesses = Collections.synchronizedMap(new HashMap<>());
|
||||
private Map<File, PlaylistGenerator> playlistGenerators = new HashMap<>();
|
||||
private Config config;
|
||||
private ProcessMonitor processMonitor;
|
||||
private OnlineMonitor onlineMonitor;
|
||||
private FollowedMonitor followedMonitor;
|
||||
private PlaylistGeneratorTrigger playlistGenTrigger;
|
||||
private HttpClient client = HttpClient.getInstance();
|
||||
private volatile boolean recording = true;
|
||||
private List<File> deleteInProgress = Collections.synchronizedList(new ArrayList<>());
|
||||
private RecorderHttpClient client = new RecorderHttpClient();
|
||||
|
||||
public LocalRecorder(Config config) {
|
||||
this.config = config;
|
||||
|
@ -74,11 +67,6 @@ public class LocalRecorder implements Recorder {
|
|||
playlistGenTrigger.start();
|
||||
}
|
||||
|
||||
if (config.getSettings().recordFollowed) {
|
||||
followedMonitor = new FollowedMonitor();
|
||||
followedMonitor.start();
|
||||
}
|
||||
|
||||
LOG.debug("Recorder initialized");
|
||||
LOG.info("Models to record: {}", models);
|
||||
LOG.info("Saving recordings in {}", config.getSettings().recordingsDir);
|
||||
|
@ -88,9 +76,6 @@ public class LocalRecorder implements Recorder {
|
|||
public void startRecording(Model model) {
|
||||
if (!models.contains(model)) {
|
||||
LOG.info("Model {} added", model);
|
||||
if (followedModels.contains(model)) {
|
||||
followedModels.remove(model);
|
||||
}
|
||||
models.add(model);
|
||||
config.getSettings().models.add(model);
|
||||
}
|
||||
|
@ -98,9 +83,8 @@ public class LocalRecorder implements Recorder {
|
|||
|
||||
@Override
|
||||
public void stopRecording(Model model) throws IOException {
|
||||
if (models.contains(model) || followedModels.contains(model)) {
|
||||
if (models.contains(model)) {
|
||||
models.remove(model);
|
||||
followedModels.remove(model);
|
||||
config.getSettings().models.remove(model);
|
||||
if (recordingProcesses.containsKey(model)) {
|
||||
stopRecordingProcess(model);
|
||||
|
@ -118,7 +102,7 @@ public class LocalRecorder implements Recorder {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!models.contains(model) && !followedModels.contains(model)) {
|
||||
if (!models.contains(model)) {
|
||||
LOG.info("Model {} has been removed. Restarting of recording cancelled.", model);
|
||||
return;
|
||||
}
|
||||
|
@ -151,15 +135,12 @@ public class LocalRecorder implements Recorder {
|
|||
|
||||
@Override
|
||||
public boolean isRecording(Model model) {
|
||||
return models.contains(model) || followedModels.contains(model);
|
||||
return models.contains(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Model> getModelsRecording() {
|
||||
List<Model> union = new ArrayList<>();
|
||||
union.addAll(models);
|
||||
union.addAll(followedModels);
|
||||
return Collections.unmodifiableList(union);
|
||||
return Collections.unmodifiableList(models);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -170,11 +151,9 @@ public class LocalRecorder implements Recorder {
|
|||
onlineMonitor.running = false;
|
||||
processMonitor.running = false;
|
||||
playlistGenTrigger.running = false;
|
||||
if (followedMonitor != null) {
|
||||
followedMonitor.running = false;
|
||||
}
|
||||
LOG.debug("Stopping all recording processes");
|
||||
stopRecordingProcesses();
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
private void stopRecordingProcesses() {
|
||||
|
@ -251,54 +230,6 @@ public class LocalRecorder implements Recorder {
|
|||
}
|
||||
}
|
||||
|
||||
private class FollowedMonitor extends Thread {
|
||||
private volatile boolean running = false;
|
||||
|
||||
public FollowedMonitor() {
|
||||
setName("FollowedMonitor");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
running = true;
|
||||
while (running) {
|
||||
try {
|
||||
String url = "https://chaturbate.com/followed-cams/?page=1&keywords=&_=" + System.currentTimeMillis();
|
||||
LOG.debug("Fetching page {}", url);
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Response response = client.execute(request, true);
|
||||
if (response.isSuccessful()) {
|
||||
List<Model> followed = ChaturbateModelParser.parseModels(response.body().string());
|
||||
response.close();
|
||||
followedModels.clear();
|
||||
for (Model model : followed) {
|
||||
if (!followedModels.contains(model) && !models.contains(model)) {
|
||||
LOG.info("Model {} added", model);
|
||||
followedModels.add(model);
|
||||
}
|
||||
}
|
||||
onlineMonitor.interrupt();
|
||||
} else {
|
||||
int code = response.code();
|
||||
response.close();
|
||||
LOG.error("Couldn't retrieve followed models. HTTP status {}", code);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error("Couldn't retrieve followed models.", e);
|
||||
}
|
||||
|
||||
try {
|
||||
if (running)
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error("Couldn't sleep", e);
|
||||
}
|
||||
}
|
||||
LOG.debug(getName() + " terminated");
|
||||
}
|
||||
}
|
||||
|
||||
private void finishRecording(File directory) {
|
||||
Thread t = new Thread() {
|
||||
@Override
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package ctbrec.recorder.server;
|
||||
import static javax.servlet.http.HttpServletResponse.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -33,20 +36,20 @@ public class HlsServlet extends AbstractCtbrecServlet {
|
|||
File recordingsDir = new File(config.getSettings().recordingsDir);
|
||||
File requestedFile = new File(recordingsDir, request);
|
||||
|
||||
// try {
|
||||
// boolean isRequestAuthenticated = checkAuthentication(req, req.getRequestURI());
|
||||
// if (!isRequestAuthenticated) {
|
||||
// resp.setStatus(SC_UNAUTHORIZED);
|
||||
// String response = "{\"status\": \"error\", \"msg\": \"HMAC does not match\"}";
|
||||
// resp.getWriter().write(response);
|
||||
// return;
|
||||
// }
|
||||
// } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e1) {
|
||||
// resp.setStatus(SC_UNAUTHORIZED);
|
||||
// String response = "{\"status\": \"error\", \"msg\": \"Authentication failed\"}";
|
||||
// resp.getWriter().write(response);
|
||||
// return;
|
||||
// }
|
||||
try {
|
||||
boolean isRequestAuthenticated = checkAuthentication(req, req.getRequestURI());
|
||||
if (!isRequestAuthenticated) {
|
||||
resp.setStatus(SC_UNAUTHORIZED);
|
||||
String response = "{\"status\": \"error\", \"msg\": \"HMAC does not match\"}";
|
||||
resp.getWriter().write(response);
|
||||
return;
|
||||
}
|
||||
} catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e1) {
|
||||
resp.setStatus(SC_UNAUTHORIZED);
|
||||
String response = "{\"status\": \"error\", \"msg\": \"Authentication failed\"}";
|
||||
resp.getWriter().write(response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestedFile.getCanonicalPath().startsWith(config.getSettings().recordingsDir)) {
|
||||
if (requestedFile.getName().equals("playlist.m3u8")) {
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package ctbrec.recorder.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ctbrec.io.HttpClient;
|
||||
|
||||
public class RecorderHttpClient extends HttpClient {
|
||||
|
||||
@Override
|
||||
public boolean login() throws IOException {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
package ctbrec;
|
||||
package ctbrec.sites;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ctbrec.Model;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.ui.TabProvider;
|
||||
|
||||
|
@ -10,4 +14,9 @@ public interface Site {
|
|||
public void setRecorder(Recorder recorder);
|
||||
public TabProvider getTabProvider();
|
||||
public Model createModel(String name);
|
||||
public Integer getTokenBalance() throws IOException;
|
||||
public String getBuyTokensLink();
|
||||
public void login() throws IOException;
|
||||
public HttpClient getHttpClient();
|
||||
public void shutdown();
|
||||
}
|
|
@ -1,13 +1,49 @@
|
|||
package ctbrec.sites.chaturbate;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
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 com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.Moshi;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.Site;
|
||||
import ctbrec.Settings;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.sites.Site;
|
||||
import ctbrec.ui.HtmlParser;
|
||||
import ctbrec.ui.TabProvider;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class Chaturbate implements Site {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(Chaturbate.class);
|
||||
public static final String BASE_URI = "https://chaturbate.com";
|
||||
public static final String AFFILIATE_LINK = BASE_URI + "/in/?track=default&tour=LQps&campaign=55vTi&room=0xb00bface";
|
||||
private Recorder recorder;
|
||||
private ChaturbateHttpClient httpClient = new ChaturbateHttpClient();
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
@ -36,9 +72,214 @@ public class Chaturbate implements Site {
|
|||
|
||||
@Override
|
||||
public Model createModel(String name) {
|
||||
ChaturbateModel m = new ChaturbateModel();
|
||||
ChaturbateModel m = new ChaturbateModel(this);
|
||||
m.setName(name);
|
||||
m.setUrl(getBaseUrl() + '/' + name + '/');
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTokenBalance() throws IOException {
|
||||
String username = Config.getInstance().getSettings().username;
|
||||
if (username == null || username.trim().isEmpty()) {
|
||||
throw new IOException("Not logged in");
|
||||
}
|
||||
|
||||
String url = "https://chaturbate.com/p/" + username + "/";
|
||||
Request req = new Request.Builder().url(url).build();
|
||||
Response resp = httpClient.execute(req, true);
|
||||
if (resp.isSuccessful()) {
|
||||
String profilePage = resp.body().string();
|
||||
String tokenText = HtmlParser.getText(profilePage, "span.tokencount");
|
||||
int tokens = Integer.parseInt(tokenText);
|
||||
return tokens;
|
||||
} else {
|
||||
throw new IOException("HTTP response: " + resp.code() + " - " + resp.message());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuyTokensLink() {
|
||||
return AFFILIATE_LINK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void login() {
|
||||
Settings settings = Config.getInstance().getSettings();
|
||||
if (settings.username != null && !settings.username.isEmpty()) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
httpClient.login();
|
||||
} catch (IOException e1) {
|
||||
LOG.warn("Initial login failed", e1);
|
||||
}
|
||||
};
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClient getHttpClient() {
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
httpClient.shutdown();
|
||||
}
|
||||
|
||||
// #######################
|
||||
private long lastRequest = System.currentTimeMillis();
|
||||
|
||||
LoadingCache<String, StreamInfo> streamInfoCache = CacheBuilder.newBuilder()
|
||||
.initialCapacity(10_000)
|
||||
.maximumSize(10_000)
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.build(new CacheLoader<String, StreamInfo> () {
|
||||
@Override
|
||||
public StreamInfo load(String model) throws Exception {
|
||||
return loadStreamInfo(model);
|
||||
}
|
||||
});
|
||||
|
||||
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()
|
||||
.add("csrfmiddlewaretoken", httpClient.getToken())
|
||||
.add("tip_amount", Integer.toString(tokens))
|
||||
.add("tip_room_type", "public")
|
||||
.build();
|
||||
Request req = new Request.Builder()
|
||||
.url("https://chaturbate.com/tipping/send_tip/"+name+"/")
|
||||
.post(body)
|
||||
.addHeader("Referer", "https://chaturbate.com/"+name+"/")
|
||||
.addHeader("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
try(Response response = httpClient.execute(req, true)) {
|
||||
if(!response.isSuccessful()) {
|
||||
throw new IOException(response.code() + " " + response.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StreamInfo getStreamInfo(String modelName) throws IOException, ExecutionException {
|
||||
return streamInfoCache.get(modelName);
|
||||
}
|
||||
|
||||
StreamInfo loadStreamInfo(String modelName) throws IOException, InterruptedException {
|
||||
throttleRequests();
|
||||
RequestBody body = new FormBody.Builder()
|
||||
.add("room_slug", modelName)
|
||||
.add("bandwidth", "high")
|
||||
.build();
|
||||
Request req = new Request.Builder()
|
||||
.url("https://chaturbate.com/get_edge_hls_url_ajax/")
|
||||
.post(body)
|
||||
.addHeader("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
Response response = httpClient.execute(req);
|
||||
try {
|
||||
if(response.isSuccessful()) {
|
||||
String content = response.body().string();
|
||||
LOG.trace("Raw stream info: {}", content);
|
||||
Moshi moshi = new Moshi.Builder().build();
|
||||
JsonAdapter<StreamInfo> adapter = moshi.adapter(StreamInfo.class);
|
||||
StreamInfo streamInfo = adapter.fromJson(content);
|
||||
streamInfoCache.put(modelName, streamInfo);
|
||||
return streamInfo;
|
||||
} else {
|
||||
int code = response.code();
|
||||
String message = response.message();
|
||||
throw new IOException("Server responded with " + code + " - " + message + " headers: [" + response.headers() + "]");
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getResolution(String modelName) throws ExecutionException {
|
||||
return streamResolutionCache.get(modelName);
|
||||
}
|
||||
|
||||
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")) {
|
||||
return res;
|
||||
}
|
||||
|
||||
EOFException ex = null;
|
||||
for(int i=0; i<2; i++) {
|
||||
try {
|
||||
MasterPlaylist master = getMasterPlaylist(modelName);
|
||||
for (PlaylistData playlistData : master.getPlaylists()) {
|
||||
if(playlistData.hasStreamInfo() && playlistData.getStreamInfo().hasResolution()) {
|
||||
int h = playlistData.getStreamInfo().getResolution().height;
|
||||
int w = playlistData.getStreamInfo().getResolution().width;
|
||||
if(w > res[1]) {
|
||||
res[0] = w;
|
||||
res[1] = h;
|
||||
}
|
||||
}
|
||||
}
|
||||
ex = null;
|
||||
break; // this attempt worked, exit loop
|
||||
} catch(EOFException e) {
|
||||
// the cause might be, that the playlist url in streaminfo is outdated,
|
||||
// so let's remove it from cache and retry in the next iteration
|
||||
streamInfoCache.invalidate(modelName);
|
||||
ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
if(ex != null) {
|
||||
throw ex;
|
||||
}
|
||||
|
||||
streamResolutionCache.put(modelName, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
private void throttleRequests() throws InterruptedException {
|
||||
long now = System.currentTimeMillis();
|
||||
long diff = now - lastRequest;
|
||||
if(diff < 500) {
|
||||
Thread.sleep(diff);
|
||||
}
|
||||
lastRequest = now;
|
||||
}
|
||||
|
||||
public MasterPlaylist getMasterPlaylist(String modelName) throws IOException, ParseException, PlaylistException, ExecutionException {
|
||||
StreamInfo streamInfo = getStreamInfo(modelName);
|
||||
return getMasterPlaylist(streamInfo);
|
||||
}
|
||||
|
||||
public MasterPlaylist getMasterPlaylist(StreamInfo streamInfo) throws IOException, ParseException, PlaylistException {
|
||||
LOG.trace("Loading master playlist {}", streamInfo.url);
|
||||
Request req = new Request.Builder().url(streamInfo.url).build();
|
||||
Response response = httpClient.execute(req);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package ctbrec.sites.chaturbate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.ui.HtmlParser;
|
||||
import okhttp3.Cookie;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class ChaturbateHttpClient extends HttpClient {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(ChaturbateHttpClient.class);
|
||||
protected String token;
|
||||
|
||||
private void extractCsrfToken(Request request) {
|
||||
try {
|
||||
Cookie csrfToken = cookieJar.getCookie(request.url(), "csrftoken");
|
||||
token = csrfToken.value();
|
||||
} catch(NoSuchElementException e) {
|
||||
LOG.trace("CSRF token not found in cookies");
|
||||
}
|
||||
}
|
||||
|
||||
public String getToken() throws IOException {
|
||||
if(token == null) {
|
||||
login();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login() throws IOException {
|
||||
try {
|
||||
Request login = new Request.Builder()
|
||||
.url(Chaturbate.BASE_URI + "/auth/login/")
|
||||
.build();
|
||||
Response response = client.newCall(login).execute();
|
||||
String content = response.body().string();
|
||||
token = HtmlParser.getTag(content, "input[name=csrfmiddlewaretoken]").attr("value");
|
||||
LOG.debug("csrf token is {}", token);
|
||||
|
||||
RequestBody body = new FormBody.Builder()
|
||||
.add("username", Config.getInstance().getSettings().username)
|
||||
.add("password", Config.getInstance().getSettings().password)
|
||||
.add("next", "")
|
||||
.add("csrfmiddlewaretoken", token)
|
||||
.build();
|
||||
login = new Request.Builder()
|
||||
.url(Chaturbate.BASE_URI + "/auth/login/")
|
||||
.header("Referer", Chaturbate.BASE_URI + "/auth/login/")
|
||||
.post(body)
|
||||
.build();
|
||||
|
||||
response = client.newCall(login).execute();
|
||||
if(response.isSuccessful()) {
|
||||
content = response.body().string();
|
||||
if(content.contains("Login, Chaturbate login")) {
|
||||
loggedIn = false;
|
||||
} else {
|
||||
loggedIn = true;
|
||||
extractCsrfToken(login);
|
||||
}
|
||||
} else {
|
||||
if(loginTries++ < 3) {
|
||||
login();
|
||||
} else {
|
||||
throw new IOException("Login failed: " + response.code() + " " + response.message());
|
||||
}
|
||||
}
|
||||
response.close();
|
||||
} finally {
|
||||
loginTries = 0;
|
||||
}
|
||||
return loggedIn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response execute(Request req, boolean requiresLogin) throws IOException {
|
||||
Response resp = super.execute(req, requiresLogin);
|
||||
extractCsrfToken(req);
|
||||
return resp;
|
||||
}
|
||||
}
|
|
@ -1,35 +1,23 @@
|
|||
package ctbrec.sites.chaturbate;
|
||||
|
||||
import java.io.EOFException;
|
||||
import static ctbrec.sites.chaturbate.Chaturbate.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
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 com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.Moshi;
|
||||
|
||||
import ctbrec.AbstractModel;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.recorder.download.StreamSource;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
@ -37,6 +25,11 @@ import okhttp3.Response;
|
|||
public class ChaturbateModel extends AbstractModel {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(ChaturbateModel.class);
|
||||
private Chaturbate site;
|
||||
|
||||
ChaturbateModel(Chaturbate site) {
|
||||
this.site = site;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() throws IOException, ExecutionException, InterruptedException {
|
||||
|
@ -47,24 +40,24 @@ public class ChaturbateModel extends AbstractModel {
|
|||
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
|
||||
StreamInfo info;
|
||||
if(ignoreCache) {
|
||||
info = Chaturbate.INSTANCE.loadStreamInfo(getName());
|
||||
info = site.loadStreamInfo(getName());
|
||||
LOG.trace("Model {} room status: {}", getName(), info.room_status);
|
||||
} else {
|
||||
info = Chaturbate.INSTANCE.getStreamInfo(getName());
|
||||
info = site.getStreamInfo(getName());
|
||||
}
|
||||
return Objects.equals("public", info.room_status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
|
||||
int[] resolution = Chaturbate.INSTANCE.streamResolutionCache.getIfPresent(getName());
|
||||
int[] resolution = site.streamResolutionCache.getIfPresent(getName());
|
||||
if(resolution != null) {
|
||||
return Chaturbate.INSTANCE.getResolution(getName());
|
||||
return site.getResolution(getName());
|
||||
} else {
|
||||
if(failFast) {
|
||||
return new int[2];
|
||||
} else {
|
||||
return Chaturbate.INSTANCE.getResolution(getName());
|
||||
return site.getResolution(getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,8 +68,8 @@ public class ChaturbateModel extends AbstractModel {
|
|||
*/
|
||||
@Override
|
||||
public void invalidateCacheEntries() {
|
||||
Chaturbate.INSTANCE.streamInfoCache.invalidate(getName());
|
||||
Chaturbate.INSTANCE.streamResolutionCache.invalidate(getName());
|
||||
site.streamInfoCache.invalidate(getName());
|
||||
site.streamResolutionCache.invalidate(getName());
|
||||
}
|
||||
|
||||
public String getOnlineState() throws IOException, ExecutionException {
|
||||
|
@ -85,20 +78,20 @@ public class ChaturbateModel extends AbstractModel {
|
|||
|
||||
@Override
|
||||
public String getOnlineState(boolean failFast) throws IOException, ExecutionException {
|
||||
StreamInfo info = Chaturbate.INSTANCE.streamInfoCache.getIfPresent(getName());
|
||||
StreamInfo info = site.streamInfoCache.getIfPresent(getName());
|
||||
return info != null ? info.room_status : "n/a";
|
||||
}
|
||||
|
||||
public StreamInfo getStreamInfo() throws IOException, ExecutionException {
|
||||
return Chaturbate.INSTANCE.getStreamInfo(getName());
|
||||
return site.getStreamInfo(getName());
|
||||
}
|
||||
public MasterPlaylist getMasterPlaylist() throws IOException, ParseException, PlaylistException, ExecutionException {
|
||||
return Chaturbate.INSTANCE.getMasterPlaylist(getName());
|
||||
return site.getMasterPlaylist(getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveTip(int tokens) throws IOException {
|
||||
Chaturbate.INSTANCE.sendTip(getName(), tokens);
|
||||
site.sendTip(getName(), tokens);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -123,167 +116,52 @@ public class ChaturbateModel extends AbstractModel {
|
|||
return sources;
|
||||
}
|
||||
|
||||
private static class Chaturbate {
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(Chaturbate.class);
|
||||
@Override
|
||||
public boolean follow() throws IOException {
|
||||
return follow(true);
|
||||
}
|
||||
|
||||
public static final Chaturbate INSTANCE = new Chaturbate(HttpClient.getInstance());
|
||||
@Override
|
||||
public boolean unfollow() throws IOException {
|
||||
return follow(false);
|
||||
}
|
||||
|
||||
private HttpClient client;
|
||||
private boolean follow(boolean follow) throws IOException {
|
||||
Request req = new Request.Builder().url(getUrl()).build();
|
||||
Response resp = site.getHttpClient().execute(req);
|
||||
resp.close();
|
||||
|
||||
private static long lastRequest = System.currentTimeMillis();
|
||||
|
||||
private LoadingCache<String, StreamInfo> streamInfoCache = CacheBuilder.newBuilder()
|
||||
.initialCapacity(10_000)
|
||||
.maximumSize(10_000)
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.build(new CacheLoader<String, StreamInfo> () {
|
||||
@Override
|
||||
public StreamInfo load(String model) throws Exception {
|
||||
return loadStreamInfo(model);
|
||||
}
|
||||
});
|
||||
|
||||
private 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 Chaturbate(HttpClient client) {
|
||||
this.client = client;
|
||||
String url = null;
|
||||
if(follow) {
|
||||
url = BASE_URI + "/follow/follow/" + getName() + "/";
|
||||
} else {
|
||||
url = BASE_URI + "/follow/unfollow/" + getName() + "/";
|
||||
}
|
||||
|
||||
public void sendTip(String name, int tokens) throws IOException {
|
||||
if (!Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
|
||||
RequestBody body = new FormBody.Builder()
|
||||
.add("csrfmiddlewaretoken", client.getToken())
|
||||
.add("tip_amount", Integer.toString(tokens))
|
||||
.add("tip_room_type", "public")
|
||||
.build();
|
||||
Request req = new Request.Builder()
|
||||
.url("https://chaturbate.com/tipping/send_tip/"+name+"/")
|
||||
.post(body)
|
||||
.addHeader("Referer", "https://chaturbate.com/"+name+"/")
|
||||
.addHeader("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
try(Response response = client.execute(req, true)) {
|
||||
if(!response.isSuccessful()) {
|
||||
throw new IOException(response.code() + " " + response.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private StreamInfo getStreamInfo(String modelName) throws IOException, ExecutionException {
|
||||
return streamInfoCache.get(modelName);
|
||||
}
|
||||
|
||||
private StreamInfo loadStreamInfo(String modelName) throws IOException, InterruptedException {
|
||||
throttleRequests();
|
||||
RequestBody body = new FormBody.Builder()
|
||||
.add("room_slug", modelName)
|
||||
.add("bandwidth", "high")
|
||||
.build();
|
||||
Request req = new Request.Builder()
|
||||
.url("https://chaturbate.com/get_edge_hls_url_ajax/")
|
||||
.post(body)
|
||||
.addHeader("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
Response response = client.execute(req);
|
||||
try {
|
||||
if(response.isSuccessful()) {
|
||||
String content = response.body().string();
|
||||
LOG.trace("Raw stream info: {}", content);
|
||||
Moshi moshi = new Moshi.Builder().build();
|
||||
JsonAdapter<StreamInfo> adapter = moshi.adapter(StreamInfo.class);
|
||||
StreamInfo streamInfo = adapter.fromJson(content);
|
||||
streamInfoCache.put(modelName, streamInfo);
|
||||
return streamInfo;
|
||||
} else {
|
||||
int code = response.code();
|
||||
String message = response.message();
|
||||
throw new IOException("Server responded with " + code + " - " + message + " headers: [" + response.headers() + "]");
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getResolution(String modelName) throws ExecutionException {
|
||||
return streamResolutionCache.get(modelName);
|
||||
}
|
||||
|
||||
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")) {
|
||||
return res;
|
||||
}
|
||||
|
||||
EOFException ex = null;
|
||||
for(int i=0; i<2; i++) {
|
||||
try {
|
||||
MasterPlaylist master = getMasterPlaylist(modelName);
|
||||
for (PlaylistData playlistData : master.getPlaylists()) {
|
||||
if(playlistData.hasStreamInfo() && playlistData.getStreamInfo().hasResolution()) {
|
||||
int h = playlistData.getStreamInfo().getResolution().height;
|
||||
int w = playlistData.getStreamInfo().getResolution().width;
|
||||
if(w > res[1]) {
|
||||
res[0] = w;
|
||||
res[1] = h;
|
||||
}
|
||||
}
|
||||
}
|
||||
ex = null;
|
||||
break; // this attempt worked, exit loop
|
||||
} catch(EOFException e) {
|
||||
// the cause might be, that the playlist url in streaminfo is outdated,
|
||||
// so let's remove it from cache and retry in the next iteration
|
||||
streamInfoCache.invalidate(modelName);
|
||||
ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
if(ex != null) {
|
||||
throw ex;
|
||||
}
|
||||
|
||||
streamResolutionCache.put(modelName, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
private void throttleRequests() throws InterruptedException {
|
||||
long now = System.currentTimeMillis();
|
||||
long diff = now - lastRequest;
|
||||
if(diff < 500) {
|
||||
Thread.sleep(diff);
|
||||
}
|
||||
lastRequest = now;
|
||||
}
|
||||
|
||||
public MasterPlaylist getMasterPlaylist(String modelName) throws IOException, ParseException, PlaylistException, ExecutionException {
|
||||
StreamInfo streamInfo = getStreamInfo(modelName);
|
||||
return getMasterPlaylist(streamInfo);
|
||||
}
|
||||
|
||||
public MasterPlaylist getMasterPlaylist(StreamInfo streamInfo) throws IOException, ParseException, PlaylistException {
|
||||
LOG.trace("Loading master playlist {}", streamInfo.url);
|
||||
Request req = new Request.Builder().url(streamInfo.url).build();
|
||||
Response response = client.execute(req);
|
||||
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();
|
||||
RequestBody body = RequestBody.create(null, new byte[0]);
|
||||
req = new Request.Builder()
|
||||
.url(url)
|
||||
.method("POST", body)
|
||||
.header("Accept", "*/*")
|
||||
.header("Accept-Language", "en-US,en;q=0.5")
|
||||
.header("Referer", getUrl())
|
||||
.header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0")
|
||||
.header("X-CSRFToken", ((ChaturbateHttpClient)site.getHttpClient()).getToken())
|
||||
.header("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
resp = site.getHttpClient().execute(req, true);
|
||||
if(resp.isSuccessful()) {
|
||||
String msg = resp.body().string();
|
||||
if(!msg.equalsIgnoreCase("ok")) {
|
||||
LOG.debug(msg);
|
||||
throw new IOException("Response was " + msg.substring(0, Math.min(msg.length(), 500)));
|
||||
} else {
|
||||
LOG.debug("Follow/Unfollow -> {}", msg);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
resp.close();
|
||||
throw new IOException("HTTP status " + resp.code() + " " + resp.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package ctbrec.sites.chaturbate;
|
||||
|
||||
import static ctbrec.ui.CamrecApplication.BASE_URI;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -16,16 +14,15 @@ import ctbrec.ui.HtmlParser;
|
|||
public class ChaturbateModelParser {
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(ChaturbateModelParser.class);
|
||||
|
||||
public static List<Model> parseModels(String html) {
|
||||
public static List<Model> parseModels(Chaturbate chaturbate, String html) {
|
||||
List<Model> models = new ArrayList<>();
|
||||
Elements cells = HtmlParser.getTags(html, "ul.list > li");
|
||||
for (Element cell : cells) {
|
||||
String cellHtml = cell.html();
|
||||
try {
|
||||
Model model = new ChaturbateModel();
|
||||
Model model = chaturbate.createModel(HtmlParser.getText(cellHtml, "div.title > a").trim());
|
||||
model.setName(HtmlParser.getText(cellHtml, "div.title > a").trim());
|
||||
model.setPreview(HtmlParser.getTag(cellHtml, "a img").attr("src"));
|
||||
model.setUrl(BASE_URI + HtmlParser.getTag(cellHtml, "a").attr("href"));
|
||||
model.setDescription(HtmlParser.getText(cellHtml, "div.details ul.subject"));
|
||||
Elements tags = HtmlParser.getTags(cellHtml, "div.details ul.subject li a");
|
||||
if(tags != null) {
|
||||
|
|
|
@ -29,7 +29,7 @@ public class ChaturbateTabProvider extends TabProvider {
|
|||
tabs.add(createTab("Male", BASE_URI + "/male-cams/"));
|
||||
tabs.add(createTab("Couples", BASE_URI + "/couple-cams/"));
|
||||
tabs.add(createTab("Trans", BASE_URI + "/trans-cams/"));
|
||||
FollowedTab followedTab = new FollowedTab("Followed", BASE_URI + "/followed-cams/");
|
||||
FollowedTab followedTab = new FollowedTab("Followed", BASE_URI + "/followed-cams/", chaturbate);
|
||||
followedTab.setRecorder(recorder);
|
||||
followedTab.setScene(scene);
|
||||
tabs.add(followedTab);
|
||||
|
@ -37,8 +37,8 @@ public class ChaturbateTabProvider extends TabProvider {
|
|||
}
|
||||
|
||||
private Tab createTab(String title, String url) {
|
||||
ChaturbateUpdateService updateService = new ChaturbateUpdateService(url, false);
|
||||
ThumbOverviewTab tab = new ThumbOverviewTab(title, updateService);
|
||||
ChaturbateUpdateService updateService = new ChaturbateUpdateService(url, false, chaturbate);
|
||||
ThumbOverviewTab tab = new ThumbOverviewTab(title, updateService, chaturbate);
|
||||
tab.setRecorder(recorder);
|
||||
return tab;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Model;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.ui.PaginatedScheduledService;
|
||||
import javafx.concurrent.Task;
|
||||
import okhttp3.Request;
|
||||
|
@ -21,10 +20,12 @@ public class ChaturbateUpdateService extends PaginatedScheduledService {
|
|||
private static final transient Logger LOG = LoggerFactory.getLogger(ChaturbateUpdateService.class);
|
||||
private String url;
|
||||
private boolean loginRequired;
|
||||
private Chaturbate chaturbate;
|
||||
|
||||
public ChaturbateUpdateService(String url, boolean loginRequired) {
|
||||
public ChaturbateUpdateService(String url, boolean loginRequired, Chaturbate chaturbate) {
|
||||
this.url = url;
|
||||
this.loginRequired = loginRequired;
|
||||
this.chaturbate = chaturbate;
|
||||
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
|
||||
@Override
|
||||
|
@ -46,9 +47,9 @@ public class ChaturbateUpdateService extends PaginatedScheduledService {
|
|||
String url = ChaturbateUpdateService.this.url + "?page="+page+"&keywords=&_=" + System.currentTimeMillis();
|
||||
LOG.debug("Fetching page {}", url);
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Response response = HttpClient.getInstance().execute(request, loginRequired);
|
||||
Response response = chaturbate.getHttpClient().execute(request, loginRequired);
|
||||
if (response.isSuccessful()) {
|
||||
List<Model> models = ChaturbateModelParser.parseModels(response.body().string());
|
||||
List<Model> models = ChaturbateModelParser.parseModels(chaturbate, response.body().string());
|
||||
response.close();
|
||||
return models;
|
||||
} else {
|
||||
|
|
|
@ -37,7 +37,7 @@ public class FriendsUpdateService extends PaginatedScheduledService {
|
|||
.url(url)
|
||||
.header("Referer", myFreeCams.getBaseUrl())
|
||||
.build();
|
||||
Response resp = MyFreeCams.httpClient.newCall(req).execute();
|
||||
Response resp = myFreeCams.getHttpClient().execute(req, true);
|
||||
if(resp.isSuccessful()) {
|
||||
String json = resp.body().string().substring(4);
|
||||
JSONObject object = new JSONObject(json);
|
||||
|
|
|
@ -1,35 +1,18 @@
|
|||
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.sites.Site;
|
||||
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);
|
||||
public static final String BASE_URI = "https://www.myfreecams.com";
|
||||
|
||||
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();
|
||||
private MyFreeCamsHttpClient httpClient = new MyFreeCamsHttpClient();
|
||||
|
||||
public MyFreeCams() throws IOException {
|
||||
client = MyFreeCamsClient.getInstance();
|
||||
|
@ -39,25 +22,9 @@ public class MyFreeCams implements Site {
|
|||
login();
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
|
@ -67,7 +34,7 @@ public class MyFreeCams implements Site {
|
|||
|
||||
@Override
|
||||
public String getBaseUrl() {
|
||||
return "https://www.myfreecams.com";
|
||||
return BASE_URI;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,9 +54,29 @@ public class MyFreeCams implements Site {
|
|||
|
||||
@Override
|
||||
public MyFreeCamsModel createModel(String name) {
|
||||
MyFreeCamsModel model = new MyFreeCamsModel();
|
||||
MyFreeCamsModel model = new MyFreeCamsModel(this);
|
||||
model.setName(name);
|
||||
model.setUrl("https://profiles.myfreecams.com/" + name);
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTokenBalance() throws IOException {
|
||||
throw new RuntimeException("Not implemented for MFC");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuyTokensLink() {
|
||||
return "https://www.myfreecams.com/php/purchase.php?request=tokens";
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyFreeCamsHttpClient getHttpClient() {
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
httpClient.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public class MyFreeCamsClient {
|
|||
|
||||
public void start() throws IOException {
|
||||
running = true;
|
||||
ServerConfig serverConfig = new ServerConfig(MyFreeCams.httpClient);
|
||||
ServerConfig serverConfig = new ServerConfig(mfc.getHttpClient());
|
||||
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";
|
||||
|
@ -89,7 +89,7 @@ public class MyFreeCamsClient {
|
|||
}
|
||||
|
||||
private WebSocket createWebSocket(Request req) {
|
||||
WebSocket ws = MyFreeCams.httpClient.newWebSocket(req, new WebSocketListener() {
|
||||
WebSocket ws = mfc.getHttpClient().newWebSocket(req, new WebSocketListener() {
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket, Response response) {
|
||||
super.onOpen(webSocket, response);
|
||||
|
@ -111,7 +111,7 @@ public class MyFreeCamsClient {
|
|||
super.onClosed(webSocket, code, reason);
|
||||
LOG.trace("close: {} {}", code, reason);
|
||||
running = false;
|
||||
MyFreeCams.httpClient.dispatcher().executorService().shutdownNow();
|
||||
mfc.getHttpClient().shutdown();
|
||||
}
|
||||
|
||||
private StringBuilder msgBuffer = new StringBuilder();
|
||||
|
@ -190,7 +190,7 @@ public class MyFreeCamsClient {
|
|||
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();
|
||||
Response resp = mfc.getHttpClient().execute(req);
|
||||
|
||||
if(resp.isSuccessful()) {
|
||||
parseExtDataSessionStates(resp.body().string());
|
||||
|
@ -354,4 +354,14 @@ public class MyFreeCamsClient {
|
|||
public void execute(Runnable r) {
|
||||
executor.execute(r);
|
||||
}
|
||||
|
||||
public void getSessionState(ctbrec.Model model) {
|
||||
for (SessionState state : sessionStates.values()) {
|
||||
if(Objects.equals(state.getNm(), model.getName())) {
|
||||
JsonAdapter<SessionState> adapter = moshi.adapter(SessionState.class).indent(" ");
|
||||
System.out.println(adapter.toJson(state));
|
||||
System.out.println("#####################");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.io.HttpClient;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.WebSocket;
|
||||
import okhttp3.WebSocketListener;
|
||||
|
||||
public class MyFreeCamsHttpClient extends HttpClient {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(MyFreeCamsHttpClient.class);
|
||||
|
||||
@Override
|
||||
public boolean login() throws IOException {
|
||||
String username = Config.getInstance().getSettings().username;
|
||||
String password = Config.getInstance().getSettings().password;
|
||||
RequestBody body = new FormBody.Builder()
|
||||
.add("username", username)
|
||||
.add("password", password)
|
||||
.add("tz", "2")
|
||||
.add("ss", "1920x1080")
|
||||
.add("submit_login", "97")
|
||||
.build();
|
||||
Request req = new Request.Builder()
|
||||
.url(MyFreeCams.BASE_URI + "/php/login.php")
|
||||
.header("Referer", MyFreeCams.BASE_URI)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.post(body)
|
||||
.build();
|
||||
Response resp = execute(req);
|
||||
if(resp.isSuccessful()) {
|
||||
resp.close();
|
||||
return true;
|
||||
} else {
|
||||
resp.close();
|
||||
LOG.error("Login failed {} {}", resp.code(), resp.message());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public WebSocket newWebSocket(Request req, WebSocketListener webSocketListener) {
|
||||
return client.newWebSocket(req, webSocketListener);
|
||||
}
|
||||
}
|
|
@ -32,6 +32,11 @@ public class MyFreeCamsModel extends AbstractModel {
|
|||
private double camScore;
|
||||
private State state;
|
||||
private int resolution[];
|
||||
private MyFreeCams site;
|
||||
|
||||
MyFreeCamsModel(MyFreeCams site) {
|
||||
this.site = site;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() throws IOException, ExecutionException, InterruptedException {
|
||||
|
@ -78,7 +83,7 @@ public class MyFreeCamsModel extends AbstractModel {
|
|||
}
|
||||
LOG.debug("Loading master playlist {}", hlsUrl);
|
||||
Request req = new Request.Builder().url(hlsUrl).build();
|
||||
Response response = MyFreeCams.httpClient.newCall(req).execute();
|
||||
Response response = site.getHttpClient().execute(req);
|
||||
try {
|
||||
InputStream inputStream = response.body().byteStream();
|
||||
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8);
|
||||
|
@ -97,7 +102,7 @@ public class MyFreeCamsModel extends AbstractModel {
|
|||
|
||||
@Override
|
||||
public void receiveTip(int tokens) throws IOException {
|
||||
throw new RuntimeException("Not implemented");
|
||||
throw new RuntimeException("Not implemented for MFC");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -155,8 +160,29 @@ public class MyFreeCamsModel extends AbstractModel {
|
|||
// stream url
|
||||
Integer camserv = state.getU().getCamserv();
|
||||
if(camserv != null) {
|
||||
if(state.getM() != null) {
|
||||
if(state.getM().getFlags() != null) {
|
||||
int flags = state.getM().getFlags();
|
||||
int hd = flags >> 18 & 0x1;
|
||||
if(hd == 1) {
|
||||
String hlsUrl = "http://video" + (camserv - 500) + ".myfreecams.com:1935/NxServer/ngrp:mfc_a_" + (100000000 + state.getUid()) + ".f4v_mobile/playlist.m3u8";
|
||||
setStreamUrl(hlsUrl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
String hlsUrl = "http://video" + (camserv - 500) + ".myfreecams.com:1935/NxServer/ngrp:mfc_" + (100000000 + state.getUid()) + ".f4v_mobile/playlist.m3u8";
|
||||
setStreamUrl(hlsUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean follow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unfollow() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,18 +26,17 @@ public class MyFreeCamsTabProvider extends TabProvider {
|
|||
List<Tab> tabs = new ArrayList<>();
|
||||
|
||||
PaginatedScheduledService updateService = new OnlineCamsUpdateService();
|
||||
ThumbOverviewTab online = new ThumbOverviewTab("Online", updateService);
|
||||
ThumbOverviewTab online = new ThumbOverviewTab("Online", updateService, myFreeCams);
|
||||
online.setRecorder(recorder);
|
||||
updateService.setPeriod(new Duration(TimeUnit.SECONDS.toMillis(10)));
|
||||
tabs.add(online);
|
||||
|
||||
updateService = new FriendsUpdateService(myFreeCams);
|
||||
ThumbOverviewTab friends = new ThumbOverviewTab("Friends", updateService);
|
||||
ThumbOverviewTab friends = new ThumbOverviewTab("Friends", updateService, myFreeCams);
|
||||
friends.setRecorder(recorder);
|
||||
updateService.setPeriod(new Duration(TimeUnit.SECONDS.toMillis(10)));
|
||||
tabs.add(friends);
|
||||
|
||||
|
||||
return tabs;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package ctbrec.sites.mfc;
|
||||
|
||||
import ctbrec.sites.Site;
|
||||
import ctbrec.ui.PaginatedScheduledService;
|
||||
import ctbrec.ui.ThumbOverviewTab;
|
||||
|
||||
public class OnlineModelsTab extends ThumbOverviewTab {
|
||||
|
||||
public OnlineModelsTab(String title, PaginatedScheduledService updateService) {
|
||||
super(title, updateService);
|
||||
public OnlineModelsTab(String title, PaginatedScheduledService updateService, Site site) {
|
||||
super(title, updateService, site);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import java.util.Map;
|
|||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import ctbrec.io.HttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
|
@ -23,9 +23,9 @@ public class ServerConfig {
|
|||
Map<String, String> wzobsServers;
|
||||
Map<String, String> ngVideo;
|
||||
|
||||
public ServerConfig(OkHttpClient client) throws IOException {
|
||||
public ServerConfig(HttpClient client) throws IOException {
|
||||
Request req = new Request.Builder().url("http://www.myfreecams.com/_js/serverconfig.js").build();
|
||||
Response resp = client.newCall(req).execute();
|
||||
Response resp = client.execute(req);
|
||||
String json = resp.body().string();
|
||||
|
||||
JSONObject serverConfig = new JSONObject(json);
|
||||
|
|
|
@ -7,7 +7,6 @@ import java.io.InputStreamReader;
|
|||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
@ -20,26 +19,31 @@ import com.squareup.moshi.Moshi;
|
|||
import com.squareup.moshi.Types;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Site;
|
||||
import ctbrec.Version;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.recorder.LocalRecorder;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.recorder.RemoteRecorder;
|
||||
import ctbrec.sites.Site;
|
||||
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;
|
||||
|
@ -47,12 +51,9 @@ import okhttp3.Response;
|
|||
public class CamrecApplication extends Application {
|
||||
|
||||
static final transient Logger LOG = LoggerFactory.getLogger(CamrecApplication.class);
|
||||
public static final String BASE_URI = "https://chaturbate.com";
|
||||
public static final String AFFILIATE_LINK = BASE_URI + "/in/?track=default&tour=LQps&campaign=55vTi&room=0xb00bface";
|
||||
|
||||
private Config config;
|
||||
private Recorder recorder;
|
||||
private HttpClient client;
|
||||
static HostServices hostServices;
|
||||
private SettingsTab settingsTab;
|
||||
private TabPane tabPane = new TabPane();
|
||||
|
@ -65,12 +66,13 @@ public class CamrecApplication extends Application {
|
|||
loadConfig();
|
||||
bus = new AsyncEventBus(Executors.newSingleThreadExecutor());
|
||||
hostServices = getHostServices();
|
||||
client = HttpClient.getInstance();
|
||||
createRecorder();
|
||||
//site = new Chaturbate();
|
||||
// site = new Chaturbate();
|
||||
site = new MyFreeCams();
|
||||
site.setRecorder(recorder);
|
||||
// TODO move this to Chaturbate class doInitialLogin();
|
||||
if (!Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
|
||||
site.login();
|
||||
}
|
||||
createGui(primaryStage);
|
||||
checkForUpdates();
|
||||
}
|
||||
|
@ -132,7 +134,7 @@ public class CamrecApplication extends Application {
|
|||
public void run() {
|
||||
settingsTab.saveConfig();
|
||||
recorder.shutdown();
|
||||
client.shutdown();
|
||||
site.shutdown();
|
||||
try {
|
||||
Config.getInstance().save();
|
||||
LOG.info("Shutdown complete. Goodbye!");
|
||||
|
@ -152,103 +154,43 @@ public class CamrecApplication extends Application {
|
|||
}.start();
|
||||
});
|
||||
|
||||
// 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) {
|
||||
Task<Integer> task = new Task<Integer>() {
|
||||
@Override
|
||||
protected Integer call() throws Exception {
|
||||
if (!Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
|
||||
String username = Config.getInstance().getSettings().username;
|
||||
if (username == null || username.trim().isEmpty()) {
|
||||
throw new IOException("Not logged in");
|
||||
}
|
||||
|
||||
String url = "https://chaturbate.com/p/" + username + "/";
|
||||
HttpClient client = HttpClient.getInstance();
|
||||
Request req = new Request.Builder().url(url).build();
|
||||
Response resp = client.execute(req, true);
|
||||
if (resp.isSuccessful()) {
|
||||
String profilePage = resp.body().string();
|
||||
String tokenText = HtmlParser.getText(profilePage, "span.tokencount");
|
||||
int tokens = Integer.parseInt(tokenText);
|
||||
return tokens;
|
||||
} else {
|
||||
throw new IOException("HTTP response: " + resp.code() + " - " + resp.message());
|
||||
}
|
||||
} else {
|
||||
return 1_000_000;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
int tokens = get();
|
||||
label.update(tokens);
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
LOG.error("Couldn't retrieve account balance", e);
|
||||
Platform.runLater(() -> label.setText("Tokens: error"));
|
||||
}
|
||||
}
|
||||
};
|
||||
new Thread(task).start();
|
||||
}
|
||||
|
||||
private void doInitialLogin() {
|
||||
if(config.getSettings().username != null && !config.getSettings().username.isEmpty()) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
if(!Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
|
||||
try {
|
||||
client.login();
|
||||
} catch (IOException e1) {
|
||||
LOG.warn("Initial login failed" , e1);
|
||||
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(site.getBuyTokensLink()));
|
||||
buyTokens.setMaxHeight(tabPane.getTabMaxHeight());
|
||||
TokenLabel tokenBalance = new TokenLabel(site);
|
||||
tokenPanel = new HBox(5, tokenBalance, buyTokens);
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}.start();
|
||||
|
||||
}
|
||||
}
|
||||
tokenBalance.loadBalance();
|
||||
}
|
||||
}
|
||||
|
||||
private void createRecorder() {
|
||||
if(config.getSettings().localRecording) {
|
||||
if (config.getSettings().localRecording) {
|
||||
recorder = new LocalRecorder(config);
|
||||
} else {
|
||||
recorder = new RemoteRecorder(config, client);
|
||||
recorder = new RemoteRecorder(config, site.getHttpClient());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,8 +217,8 @@ public class CamrecApplication extends Application {
|
|||
try {
|
||||
String url = "https://api.github.com/repos/0xboobface/ctbrec/releases";
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Response response = client.execute(request);
|
||||
if(response.isSuccessful()) {
|
||||
Response response = site.getHttpClient().execute(request);
|
||||
if (response.isSuccessful()) {
|
||||
Moshi moshi = new Moshi.Builder().build();
|
||||
Type type = Types.newParameterizedType(List.class, Release.class);
|
||||
JsonAdapter<List<Release>> adapter = moshi.adapter(type);
|
||||
|
@ -284,7 +226,7 @@ public class CamrecApplication extends Application {
|
|||
Release latest = releases.get(0);
|
||||
Version latestVersion = latest.getVersion();
|
||||
Version ctbrecVersion = getVersion();
|
||||
if(latestVersion.compareTo(ctbrecVersion) > 0) {
|
||||
if (latestVersion.compareTo(ctbrecVersion) > 0) {
|
||||
LOG.debug("Update available {} < {}", ctbrecVersion, latestVersion);
|
||||
Platform.runLater(() -> tabPane.getTabs().add(new UpdateTab(latest)));
|
||||
} else {
|
||||
|
@ -303,10 +245,10 @@ public class CamrecApplication extends Application {
|
|||
}
|
||||
|
||||
private Version getVersion() throws IOException {
|
||||
if(Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
|
||||
if (Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
|
||||
return Version.of("0.0.0-DEV");
|
||||
} else {
|
||||
try(InputStream is = getClass().getClassLoader().getResourceAsStream("version")) {
|
||||
try (InputStream is = getClass().getClassLoader().getResourceAsStream("version")) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||
String versionString = reader.readLine();
|
||||
Version version = Version.of(versionString);
|
||||
|
|
|
@ -2,6 +2,7 @@ package ctbrec.ui;
|
|||
|
||||
|
||||
|
||||
import ctbrec.sites.chaturbate.Chaturbate;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
|
@ -44,7 +45,7 @@ public class DonateTabFx extends Tab {
|
|||
|
||||
ImageView tokenImage = new ImageView(getClass().getResource("/html/token.png").toString());
|
||||
Button tokenButton = new Button("Buy tokens");
|
||||
tokenButton.setOnAction((e) -> { DesktopIntergation.open(CamrecApplication.AFFILIATE_LINK); });
|
||||
tokenButton.setOnAction((e) -> { DesktopIntergation.open(Chaturbate.AFFILIATE_LINK); });
|
||||
VBox tokenBox = new VBox(5);
|
||||
tokenBox.setAlignment(Pos.TOP_CENTER);
|
||||
Label tokenDesc = new Label("If you buy tokens by using this button,\n"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ctbrec.ui;
|
||||
|
||||
import ctbrec.sites.chaturbate.Chaturbate;
|
||||
import ctbrec.sites.chaturbate.ChaturbateUpdateService;
|
||||
import javafx.concurrent.WorkerStateEvent;
|
||||
import javafx.geometry.Insets;
|
||||
|
@ -16,8 +17,8 @@ public class FollowedTab extends ThumbOverviewTab {
|
|||
private String onlineUrl;
|
||||
private String offlineUrl;
|
||||
|
||||
public FollowedTab(String title, String url) {
|
||||
super(title, new ChaturbateUpdateService(url, true));
|
||||
public FollowedTab(String title, String url, Chaturbate chaturbate) {
|
||||
super(title, new ChaturbateUpdateService(url, true, chaturbate), chaturbate);
|
||||
onlineUrl = url;
|
||||
offlineUrl = url + "offline/";
|
||||
|
||||
|
|
|
@ -126,4 +126,14 @@ public class JavaFxModel extends AbstractModel {
|
|||
public int[] getStreamResolution(boolean b) throws ExecutionException {
|
||||
return delegate.getStreamResolution(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean follow() throws IOException {
|
||||
return delegate.follow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unfollow() throws IOException {
|
||||
return delegate.unfollow();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,8 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Model;
|
||||
import ctbrec.Site;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.sites.Site;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
@ -266,7 +265,6 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
|
|||
return;
|
||||
}
|
||||
|
||||
HttpClient client = HttpClient.getInstance();
|
||||
Function<Model, Void> onSuccess = (m) -> {
|
||||
try {
|
||||
recorder.switchStreamSource(m);
|
||||
|
@ -281,7 +279,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
|
|||
showStreamSwitchErrorDialog(t);
|
||||
return null;
|
||||
};
|
||||
StreamSourceSelectionDialog.show(fxModel.getDelegate(), client, onSuccess, onFail);
|
||||
StreamSourceSelectionDialog.show(fxModel.getDelegate(), site.getHttpClient(), onSuccess, onFail);
|
||||
}
|
||||
|
||||
private void showStreamSwitchErrorDialog(Throwable throwable) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package ctbrec.ui;
|
||||
|
||||
import static javafx.scene.control.ButtonType.NO;
|
||||
import static javafx.scene.control.ButtonType.YES;
|
||||
import static javafx.scene.control.ButtonType.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
|
@ -32,10 +31,9 @@ import ctbrec.Config;
|
|||
import ctbrec.Model;
|
||||
import ctbrec.Recording;
|
||||
import ctbrec.Recording.STATUS;
|
||||
import ctbrec.Site;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.recorder.download.MergedHlsDownload;
|
||||
import ctbrec.sites.Site;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
|
@ -306,7 +304,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
|
|||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
MergedHlsDownload download = new MergedHlsDownload(HttpClient.getInstance());
|
||||
MergedHlsDownload download = new MergedHlsDownload(site.getHttpClient());
|
||||
download.start(url.toString(), target, (progress) -> {
|
||||
Platform.runLater(() -> {
|
||||
if (progress == 100) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.sun.javafx.collections.ObservableListWrapper;
|
|||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Hmac;
|
||||
import ctbrec.sites.chaturbate.Chaturbate;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Insets;
|
||||
|
@ -57,7 +58,6 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
private CheckBox loadResolution;
|
||||
private CheckBox secureCommunication = new CheckBox();
|
||||
private CheckBox chooseStreamQuality = new CheckBox();
|
||||
private CheckBox autoRecordFollowed = new CheckBox();
|
||||
private CheckBox multiplePlayers = new CheckBox();
|
||||
private PasswordField password;
|
||||
private RadioButton recordLocal;
|
||||
|
@ -188,25 +188,9 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
layout.add(password, 1, 1);
|
||||
|
||||
Button createAccount = new Button("Create new Account");
|
||||
createAccount.setOnAction((e) -> DesktopIntergation.open(CamrecApplication.AFFILIATE_LINK));
|
||||
createAccount.setOnAction((e) -> DesktopIntergation.open(Chaturbate.AFFILIATE_LINK));
|
||||
layout.add(createAccount, 1, 2);
|
||||
GridPane.setColumnSpan(createAccount, 2);
|
||||
|
||||
l = new Label("Record all followed models");
|
||||
layout.add(l, 0, 3);
|
||||
autoRecordFollowed = new CheckBox();
|
||||
autoRecordFollowed.setSelected(Config.getInstance().getSettings().recordFollowed);
|
||||
autoRecordFollowed.setOnAction((e) -> {
|
||||
Config.getInstance().getSettings().recordFollowed = autoRecordFollowed.isSelected();
|
||||
showRestartRequired();
|
||||
});
|
||||
layout.add(autoRecordFollowed, 1, 3);
|
||||
Label warning = new Label("Don't do this, if you follow many models. You have been warned ;) !");
|
||||
warning.setTextFill(Color.RED);
|
||||
layout.add(warning, 2, 3);
|
||||
GridPane.setMargin(l, new Insets(3, 0, 0, 0));
|
||||
GridPane.setMargin(warning, new Insets(3, 0, 0, 0));
|
||||
GridPane.setMargin(autoRecordFollowed, new Insets(3, 0, 0, CHECKBOX_MARGIN));
|
||||
GridPane.setMargin(username, new Insets(0, 0, 0, CHECKBOX_MARGIN));
|
||||
GridPane.setMargin(password, new Insets(0, 0, 0, CHECKBOX_MARGIN));
|
||||
GridPane.setMargin(createAccount, new Insets(0, 0, 0, CHECKBOX_MARGIN));
|
||||
|
|
|
@ -45,9 +45,6 @@ import javafx.scene.text.Font;
|
|||
import javafx.scene.text.Text;
|
||||
import javafx.scene.text.TextAlignment;
|
||||
import javafx.util.Duration;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class ThumbCell extends StackPane {
|
||||
|
||||
|
@ -347,91 +344,55 @@ public class ThumbCell extends StackPane {
|
|||
}
|
||||
|
||||
private void _startStopAction(Model model, boolean start) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if(start) {
|
||||
recorder.startRecording(model);
|
||||
setRecording(true);
|
||||
} else {
|
||||
recorder.stopRecording(model);
|
||||
setRecording(false);
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
LOG.error("Couldn't start/stop recording", e1);
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR);
|
||||
alert.setTitle("Error");
|
||||
alert.setHeaderText("Couldn't start/stop recording");
|
||||
alert.setContentText("I/O error while starting/stopping the recording: " + e1.getLocalizedMessage());
|
||||
alert.showAndWait();
|
||||
});
|
||||
} finally {
|
||||
setCursor(Cursor.DEFAULT);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
if(start) {
|
||||
recorder.startRecording(model);
|
||||
setRecording(true);
|
||||
} else {
|
||||
recorder.stopRecording(model);
|
||||
setRecording(false);
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
LOG.error("Couldn't start/stop recording", e1);
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR);
|
||||
alert.setTitle("Error");
|
||||
alert.setHeaderText("Couldn't start/stop recording");
|
||||
alert.setContentText("I/O error while starting/stopping the recording: " + e1.getLocalizedMessage());
|
||||
alert.showAndWait();
|
||||
});
|
||||
} finally {
|
||||
setCursor(Cursor.DEFAULT);
|
||||
}
|
||||
}.start();
|
||||
}).start();
|
||||
}
|
||||
|
||||
void follow(boolean follow) {
|
||||
setCursor(Cursor.WAIT);
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Request req = new Request.Builder().url(model.getUrl()).build();
|
||||
Response resp = HttpClient.getInstance().execute(req);
|
||||
resp.close();
|
||||
|
||||
String url = null;
|
||||
if(follow) {
|
||||
url = CamrecApplication.BASE_URI + "/follow/follow/" + model.getName() + "/";
|
||||
} else {
|
||||
url = CamrecApplication.BASE_URI + "/follow/unfollow/" + model.getName() + "/";
|
||||
new Thread(() -> {
|
||||
try {
|
||||
if(follow) {
|
||||
model.follow();
|
||||
} else {
|
||||
boolean unfollowed = model.unfollow();
|
||||
if(unfollowed) {
|
||||
Platform.runLater(() -> thumbCellList.remove(ThumbCell.this));
|
||||
}
|
||||
|
||||
RequestBody body = RequestBody.create(null, new byte[0]);
|
||||
req = new Request.Builder()
|
||||
.url(url)
|
||||
.method("POST", body)
|
||||
.header("Accept", "*/*")
|
||||
.header("Accept-Language", "en-US,en;q=0.5")
|
||||
.header("Referer", model.getUrl())
|
||||
.header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0")
|
||||
.header("X-CSRFToken", HttpClient.getInstance().getToken())
|
||||
.header("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
resp = HttpClient.getInstance().execute(req, true);
|
||||
if(resp.isSuccessful()) {
|
||||
String msg = resp.body().string();
|
||||
if(!msg.equalsIgnoreCase("ok")) {
|
||||
LOG.debug(msg);
|
||||
throw new IOException("Response was " + msg.substring(0, Math.min(msg.length(), 500)));
|
||||
} else {
|
||||
LOG.debug("Follow/Unfollow -> {}", msg);
|
||||
if(!follow) {
|
||||
Platform.runLater(() -> thumbCellList.remove(ThumbCell.this));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resp.close();
|
||||
throw new IOException("HTTP status " + resp.code() + " " + resp.message());
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
LOG.error("Couldn't follow/unfollow model {}", model.getName(), e1);
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR);
|
||||
alert.setTitle("Error");
|
||||
alert.setHeaderText("Couldn't follow/unfollow model");
|
||||
alert.setContentText("I/O error while following/unfollowing model " + model.getName() + ": " + e1.getLocalizedMessage());
|
||||
alert.showAndWait();
|
||||
});
|
||||
} finally {
|
||||
setCursor(Cursor.DEFAULT);
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
LOG.error("Couldn't follow/unfollow model {}", model.getName(), e1);
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR);
|
||||
alert.setTitle("Error");
|
||||
alert.setHeaderText("Couldn't follow/unfollow model");
|
||||
alert.setContentText("I/O error while following/unfollowing model " + model.getName() + ": " + e1.getLocalizedMessage());
|
||||
alert.showAndWait();
|
||||
});
|
||||
} finally {
|
||||
setCursor(Cursor.DEFAULT);
|
||||
}
|
||||
}.start();
|
||||
}).start();
|
||||
}
|
||||
|
||||
public Model getModel() {
|
||||
|
|
|
@ -29,6 +29,8 @@ import ctbrec.Config;
|
|||
import ctbrec.Model;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.sites.Site;
|
||||
import ctbrec.sites.mfc.MyFreeCamsClient;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.concurrent.Worker.State;
|
||||
import javafx.concurrent.WorkerStateEvent;
|
||||
|
@ -71,19 +73,20 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
ReentrantLock gridLock = new ReentrantLock();
|
||||
ScrollPane scrollPane = new ScrollPane();
|
||||
boolean loginRequired;
|
||||
HttpClient client = HttpClient.getInstance();
|
||||
HBox pagination;
|
||||
TextField pageInput = new TextField(Integer.toString(1));
|
||||
Button pagePrev = new Button("◀");
|
||||
Button pageNext = new Button("▶");
|
||||
private volatile boolean updatesSuspended = false;
|
||||
ContextMenu popup;
|
||||
Site site;
|
||||
|
||||
private ComboBox<Integer> thumbWidth;
|
||||
|
||||
public ThumbOverviewTab(String title, PaginatedScheduledService updateService) {
|
||||
public ThumbOverviewTab(String title, PaginatedScheduledService updateService, Site site) {
|
||||
super(title);
|
||||
this.updateService = updateService;
|
||||
this.site = site;
|
||||
setClosable(false);
|
||||
createGui();
|
||||
initializeUpdateService();
|
||||
|
@ -253,7 +256,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
}
|
||||
if(!found) {
|
||||
ThumbCell newCell = createThumbCell(this, model, recorder, client);
|
||||
ThumbCell newCell = createThumbCell(this, model, recorder, site.getHttpClient());
|
||||
newCell.setIndex(index);
|
||||
positionChangedOrNew.add(newCell);
|
||||
}
|
||||
|
@ -277,7 +280,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
|
||||
ThumbCell createThumbCell(ThumbOverviewTab thumbOverviewTab, Model model, Recorder recorder2, HttpClient client2) {
|
||||
ThumbCell newCell = new ThumbCell(this, model, recorder, client);
|
||||
ThumbCell newCell = new ThumbCell(this, model, recorder, site.getHttpClient());
|
||||
newCell.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> {
|
||||
suspendUpdates(true);
|
||||
popup = createContextMenu(newCell);
|
||||
|
@ -328,7 +331,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
|
||||
MenuItem sendTip = new MenuItem("Send Tip");
|
||||
sendTip.setOnAction((e) -> {
|
||||
TipDialog tipDialog = new TipDialog(cell.getModel());
|
||||
TipDialog tipDialog = new TipDialog(site, cell.getModel());
|
||||
tipDialog.showAndWait();
|
||||
String tipText = tipDialog.getResult();
|
||||
if(tipText != null) {
|
||||
|
@ -340,7 +343,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
event.put("event", "tokens.sent");
|
||||
event.put("amount", tokens);
|
||||
CamrecApplication.bus.post(event);
|
||||
} catch (IOException e1) {
|
||||
} catch (Exception e1) {
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR);
|
||||
alert.setTitle("Error");
|
||||
alert.setHeaderText("Couldn't send tip");
|
||||
|
@ -374,12 +377,17 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
}
|
||||
|
||||
MenuItem debug = new MenuItem("debug");
|
||||
debug.setOnAction((e) -> {
|
||||
MyFreeCamsClient.getInstance().getSessionState(cell.getModel());
|
||||
});
|
||||
|
||||
ContextMenu contextMenu = new ContextMenu();
|
||||
contextMenu.setAutoHide(true);
|
||||
contextMenu.setHideOnEscape(true);
|
||||
contextMenu.setAutoFix(true);
|
||||
MenuItem followOrUnFollow = this instanceof FollowedTab ? unfollow : follow;
|
||||
contextMenu.getItems().addAll(openInPlayer, startStop , followOrUnFollow, copyUrl, sendTip);
|
||||
contextMenu.getItems().addAll(openInPlayer, startStop , followOrUnFollow, copyUrl, sendTip, debug);
|
||||
return contextMenu;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,30 +1,27 @@
|
|||
package ctbrec.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.sites.Site;
|
||||
import ctbrec.sites.chaturbate.Chaturbate;
|
||||
import javafx.application.Platform;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.TextInputDialog;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class TipDialog extends TextInputDialog {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(TipDialog.class);
|
||||
private Site site;
|
||||
|
||||
public TipDialog(Model model) {
|
||||
public TipDialog(Site site, Model model) {
|
||||
this.site = site;
|
||||
setTitle("Send Tip");
|
||||
loadTokenBalance();
|
||||
setHeaderText("Loading token balance…");
|
||||
|
@ -38,27 +35,7 @@ public class TipDialog extends TextInputDialog {
|
|||
@Override
|
||||
protected Integer call() throws Exception {
|
||||
if (!Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
|
||||
String username = Config.getInstance().getSettings().username;
|
||||
if (username == null || username.trim().isEmpty()) {
|
||||
throw new IOException("Not logged in");
|
||||
}
|
||||
|
||||
String url = "https://chaturbate.com/p/" + username + "/";
|
||||
HttpClient client = HttpClient.getInstance();
|
||||
Request req = new Request.Builder().url(url).build();
|
||||
Response resp = client.execute(req, true);
|
||||
if (resp.isSuccessful()) {
|
||||
String profilePage = resp.body().string();
|
||||
String tokenText = HtmlParser.getText(profilePage, "span.tokencount");
|
||||
int tokens = Integer.parseInt(tokenText);
|
||||
Map<String, Object> event = new HashMap<>();
|
||||
event.put("event", "tokens");
|
||||
event.put("amount", tokens);
|
||||
CamrecApplication.bus.post(event);
|
||||
return tokens;
|
||||
} else {
|
||||
throw new IOException("HTTP response: " + resp.code() + " - " + resp.message());
|
||||
}
|
||||
return site.getTokenBalance();
|
||||
} else {
|
||||
return 1_000_000;
|
||||
}
|
||||
|
@ -78,7 +55,7 @@ public class TipDialog extends TextInputDialog {
|
|||
buyTokens.showAndWait();
|
||||
TipDialog.this.close();
|
||||
if(buyTokens.getResult() == ButtonType.YES) {
|
||||
DesktopIntergation.open(CamrecApplication.AFFILIATE_LINK);
|
||||
DesktopIntergation.open(Chaturbate.AFFILIATE_LINK);
|
||||
}
|
||||
} else {
|
||||
getEditor().setDisable(false);
|
||||
|
|
|
@ -2,17 +2,26 @@ package ctbrec.ui;
|
|||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import ctbrec.sites.Site;
|
||||
import javafx.application.Platform;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
public class TokenLabel extends Label {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(TokenLabel.class);
|
||||
private int tokens = -1;
|
||||
private Site site;
|
||||
|
||||
public TokenLabel() {
|
||||
public TokenLabel(Site site) {
|
||||
this.site = site;
|
||||
setText("Tokens: loading…");
|
||||
CamrecApplication.bus.register(new Object() {
|
||||
@Subscribe
|
||||
|
@ -42,4 +51,29 @@ public class TokenLabel extends Label {
|
|||
private void updateText() {
|
||||
Platform.runLater(() -> setText("Tokens: " + tokens));
|
||||
}
|
||||
|
||||
public void loadBalance() {
|
||||
Task<Integer> task = new Task<Integer>() {
|
||||
@Override
|
||||
protected Integer call() throws Exception {
|
||||
if (!Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
|
||||
return site.getTokenBalance();
|
||||
} else {
|
||||
return 1_000_000;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
int tokens = get();
|
||||
update(tokens);
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
LOG.error("Couldn't retrieve account balance", e);
|
||||
Platform.runLater(() -> setText("Tokens: error"));
|
||||
}
|
||||
}
|
||||
};
|
||||
new Thread(task).start();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue