diff --git a/src/main/java/ctbrec/Config.java b/src/main/java/ctbrec/Config.java index 168613b4..65df09eb 100644 --- a/src/main/java/ctbrec/Config.java +++ b/src/main/java/ctbrec/Config.java @@ -1,14 +1,13 @@ package ctbrec; -import static java.nio.file.StandardOpenOption.CREATE; -import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; -import static java.nio.file.StandardOpenOption.WRITE; +import static java.nio.file.StandardOpenOption.*; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; +import java.util.List; import java.util.Objects; import org.slf4j.Logger; @@ -18,6 +17,7 @@ import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.Moshi; import ctbrec.io.ModelJsonAdapter; +import ctbrec.sites.Site; import okio.Buffer; import okio.BufferedSource; @@ -28,8 +28,10 @@ public class Config { private static Config instance; private Settings settings; private String filename; + private List sites; - private Config() throws FileNotFoundException, IOException { + private Config(List sites) throws FileNotFoundException, IOException { + this.sites = sites; if(System.getProperty("ctbrec.config") != null) { filename = System.getProperty("ctbrec.config"); } else { @@ -40,7 +42,7 @@ public class Config { private void load() throws FileNotFoundException, IOException { Moshi moshi = new Moshi.Builder() - .add(Model.class, new ModelJsonAdapter()) + .add(Model.class, new ModelJsonAdapter(sites)) .build(); JsonAdapter adapter = moshi.adapter(Settings.class); File configDir = OS.getConfigDir(); @@ -57,9 +59,9 @@ public class Config { } } - public static synchronized void init() throws FileNotFoundException, IOException { + public static synchronized void init(List sites) throws FileNotFoundException, IOException { if(instance == null) { - instance = new Config(); + instance = new Config(sites); } } diff --git a/src/main/java/ctbrec/Model.java b/src/main/java/ctbrec/Model.java index 4f3b4dbc..4663e86b 100644 --- a/src/main/java/ctbrec/Model.java +++ b/src/main/java/ctbrec/Model.java @@ -8,6 +8,7 @@ import com.iheartradio.m3u8.ParseException; import com.iheartradio.m3u8.PlaylistException; import ctbrec.recorder.download.StreamSource; +import ctbrec.sites.Site; public interface Model { public String getUrl(); @@ -32,4 +33,5 @@ public interface Model { public int[] getStreamResolution(boolean failFast) throws ExecutionException; public boolean follow() throws IOException; public boolean unfollow() throws IOException; + public void setSite(Site site); } \ No newline at end of file diff --git a/src/main/java/ctbrec/io/ModelJsonAdapter.java b/src/main/java/ctbrec/io/ModelJsonAdapter.java index 6ac86591..0c6d8129 100644 --- a/src/main/java/ctbrec/io/ModelJsonAdapter.java +++ b/src/main/java/ctbrec/io/ModelJsonAdapter.java @@ -1,6 +1,7 @@ package ctbrec.io; import java.io.IOException; +import java.util.List; import java.util.Optional; import com.squareup.moshi.JsonAdapter; @@ -9,10 +10,20 @@ import com.squareup.moshi.JsonReader.Token; import com.squareup.moshi.JsonWriter; import ctbrec.Model; +import ctbrec.sites.Site; import ctbrec.sites.chaturbate.ChaturbateModel; public class ModelJsonAdapter extends JsonAdapter { + private List sites; + + public ModelJsonAdapter() { + } + + public ModelJsonAdapter(List sites) { + this.sites = sites; + } + @Override public Model fromJson(JsonReader reader) throws IOException { reader.beginObject(); @@ -49,6 +60,13 @@ public class ModelJsonAdapter extends JsonAdapter { model.setDescription(description); model.setUrl(url); model.setStreamUrlIndex(streamUrlIndex); + if(sites != null) { + for (Site site : sites) { + if(site.isSiteForModel(model)) { + model.setSite(site); + } + } + } return model; } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new IOException("Couldn't instantiate mode class [" + type + "]", e); diff --git a/src/main/java/ctbrec/recorder/download/HlsDownload.java b/src/main/java/ctbrec/recorder/download/HlsDownload.java index c5902dcd..313b0c0e 100644 --- a/src/main/java/ctbrec/recorder/download/HlsDownload.java +++ b/src/main/java/ctbrec/recorder/download/HlsDownload.java @@ -13,7 +13,6 @@ import java.nio.file.LinkOption; import java.nio.file.Path; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.Objects; import java.util.concurrent.Callable; import org.slf4j.Logger; @@ -45,7 +44,7 @@ public class HlsDownload extends AbstractHlsDownload { Path modelDir = FileSystems.getDefault().getPath(config.getSettings().recordingsDir, model.getName()); downloadDir = FileSystems.getDefault().getPath(modelDir.toString(), startTime); - if(!Objects.equals(model.getOnlineState(false), "public")) { + if(!model.isOnline()) { throw new IOException(model.getName() +"'s room is not public"); } diff --git a/src/main/java/ctbrec/recorder/server/HttpServer.java b/src/main/java/ctbrec/recorder/server/HttpServer.java index c59f3327..ccf47cde 100644 --- a/src/main/java/ctbrec/recorder/server/HttpServer.java +++ b/src/main/java/ctbrec/recorder/server/HttpServer.java @@ -2,6 +2,8 @@ package ctbrec.recorder.server; import java.io.IOException; import java.net.BindException; +import java.util.ArrayList; +import java.util.List; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; @@ -17,6 +19,9 @@ import org.slf4j.LoggerFactory; import ctbrec.Config; import ctbrec.recorder.LocalRecorder; import ctbrec.recorder.Recorder; +import ctbrec.sites.Site; +import ctbrec.sites.chaturbate.Chaturbate; +import ctbrec.sites.mfc.MyFreeCams; public class HttpServer { @@ -24,14 +29,16 @@ public class HttpServer { private Recorder recorder; private Config config; private Server server = new Server(); + private List sites = new ArrayList<>(); public HttpServer() throws Exception { + createSites(); System.setProperty("ctbrec.server.mode", "1"); if(System.getProperty("ctbrec.config") == null) { System.setProperty("ctbrec.config", "server.json"); } try { - Config.init(); + Config.init(sites); } catch (Exception e) { LOG.error("Couldn't load config", e); System.exit(1); @@ -44,9 +51,17 @@ public class HttpServer { LOG.info("HMAC authentication is enabled"); } recorder = new LocalRecorder(config); + for (Site site : sites) { + site.init(); + } startHttpServer(); } + private void createSites() { + sites.add(new Chaturbate()); + sites.add(new MyFreeCams()); + } + private void addShutdownHook() { Runtime.getRuntime().addShutdownHook(new Thread() { @Override @@ -86,7 +101,7 @@ public class HttpServer { handlers.setHandlers(new Handler[] { handler }); server.setHandler(handlers); - RecorderServlet recorderServlet = new RecorderServlet(recorder); + RecorderServlet recorderServlet = new RecorderServlet(recorder, sites); ServletHolder holder = new ServletHolder(recorderServlet); handler.addServletWithMapping(holder, "/rec"); diff --git a/src/main/java/ctbrec/recorder/server/RecorderServlet.java b/src/main/java/ctbrec/recorder/server/RecorderServlet.java index bb491126..4d258906 100644 --- a/src/main/java/ctbrec/recorder/server/RecorderServlet.java +++ b/src/main/java/ctbrec/recorder/server/RecorderServlet.java @@ -1,9 +1,6 @@ package ctbrec.recorder.server; -import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; -import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; -import static javax.servlet.http.HttpServletResponse.SC_OK; -import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; +import static javax.servlet.http.HttpServletResponse.*; import java.io.IOException; import java.time.Instant; @@ -25,6 +22,7 @@ import ctbrec.Recording; import ctbrec.io.InstantJsonAdapter; import ctbrec.io.ModelJsonAdapter; import ctbrec.recorder.Recorder; +import ctbrec.sites.Site; public class RecorderServlet extends AbstractCtbrecServlet { @@ -32,8 +30,11 @@ public class RecorderServlet extends AbstractCtbrecServlet { private Recorder recorder; - public RecorderServlet(Recorder recorder) { + private List sites; + + public RecorderServlet(Recorder recorder, List sites) { this.recorder = recorder; + this.sites = sites; } @Override @@ -54,7 +55,7 @@ public class RecorderServlet extends AbstractCtbrecServlet { LOG.debug("Request: {}", json); Moshi moshi = new Moshi.Builder() .add(Instant.class, new InstantJsonAdapter()) - .add(Model.class, new ModelJsonAdapter()) + .add(Model.class, new ModelJsonAdapter(sites)) .build(); JsonAdapter requestAdapter = moshi.adapter(Request.class); Request request = requestAdapter.fromJson(json); diff --git a/src/main/java/ctbrec/sites/Site.java b/src/main/java/ctbrec/sites/Site.java index 6265c8f3..b27baa95 100644 --- a/src/main/java/ctbrec/sites/Site.java +++ b/src/main/java/ctbrec/sites/Site.java @@ -18,7 +18,9 @@ public interface Site { public String getBuyTokensLink(); public void login() throws IOException; public HttpClient getHttpClient(); + public void init() throws IOException; public void shutdown(); public boolean supportsTips(); public boolean supportsFollow(); + public boolean isSiteForModel(Model m); } diff --git a/src/main/java/ctbrec/sites/chaturbate/Chaturbate.java b/src/main/java/ctbrec/sites/chaturbate/Chaturbate.java index c2f5cf56..215d40d4 100644 --- a/src/main/java/ctbrec/sites/chaturbate/Chaturbate.java +++ b/src/main/java/ctbrec/sites/chaturbate/Chaturbate.java @@ -43,7 +43,12 @@ public class Chaturbate implements Site { 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(); + private ChaturbateHttpClient httpClient; + + @Override + public void init() throws IOException { + + } @Override public String getName() { @@ -87,7 +92,7 @@ public class Chaturbate implements Site { String url = "https://chaturbate.com/p/" + username + "/"; Request req = new Request.Builder().url(url).build(); - Response resp = httpClient.execute(req, true); + Response resp = getHttpClient().execute(req, true); if (resp.isSuccessful()) { String profilePage = resp.body().string(); String tokenText = HtmlParser.getText(profilePage, "span.tokencount"); @@ -111,7 +116,7 @@ public class Chaturbate implements Site { @Override public void run() { try { - httpClient.login(); + getHttpClient().login(); } catch (IOException e1) { LOG.warn("Initial login failed", e1); } @@ -122,12 +127,15 @@ public class Chaturbate implements Site { @Override public HttpClient getHttpClient() { + if(httpClient == null) { + httpClient = new ChaturbateHttpClient(); + } return httpClient; } @Override public void shutdown() { - httpClient.shutdown(); + getHttpClient().shutdown(); } @Override @@ -140,6 +148,11 @@ public class Chaturbate implements Site { return true; } + @Override + public boolean isSiteForModel(Model m) { + return m instanceof ChaturbateModel; + } + // ####################### private long lastRequest = System.currentTimeMillis(); @@ -168,7 +181,7 @@ public class Chaturbate implements Site { 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("csrfmiddlewaretoken", ((ChaturbateHttpClient)getHttpClient()).getToken()) .add("tip_amount", Integer.toString(tokens)) .add("tip_room_type", "public") .build(); @@ -178,7 +191,7 @@ public class Chaturbate implements Site { .addHeader("Referer", "https://chaturbate.com/"+name+"/") .addHeader("X-Requested-With", "XMLHttpRequest") .build(); - try(Response response = httpClient.execute(req, true)) { + try(Response response = getHttpClient().execute(req, true)) { if(!response.isSuccessful()) { throw new IOException(response.code() + " " + response.message()); } @@ -201,7 +214,7 @@ public class Chaturbate implements Site { .post(body) .addHeader("X-Requested-With", "XMLHttpRequest") .build(); - Response response = httpClient.execute(req); + Response response = getHttpClient().execute(req); try { if(response.isSuccessful()) { String content = response.body().string(); @@ -281,7 +294,7 @@ public class Chaturbate implements Site { 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); + Response response = getHttpClient().execute(req); try { InputStream inputStream = response.body().byteStream(); PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8); diff --git a/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java b/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java index febb5887..e47199cf 100644 --- a/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java +++ b/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java @@ -18,6 +18,7 @@ import com.iheartradio.m3u8.data.PlaylistData; import ctbrec.AbstractModel; import ctbrec.recorder.download.StreamSource; +import ctbrec.sites.Site; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; @@ -164,4 +165,13 @@ public class ChaturbateModel extends AbstractModel { throw new IOException("HTTP status " + resp.code() + " " + resp.message()); } } + + @Override + public void setSite(Site site) { + if(site instanceof Chaturbate) { + this.site = (Chaturbate) site; + } else { + throw new IllegalArgumentException("Site has to be an instance of Chaturbate"); + } + } } diff --git a/src/main/java/ctbrec/sites/mfc/MyFreeCams.java b/src/main/java/ctbrec/sites/mfc/MyFreeCams.java index 70414cbb..aaf99b19 100644 --- a/src/main/java/ctbrec/sites/mfc/MyFreeCams.java +++ b/src/main/java/ctbrec/sites/mfc/MyFreeCams.java @@ -4,6 +4,7 @@ import java.io.IOException; import org.jsoup.select.Elements; +import ctbrec.Model; import ctbrec.recorder.Recorder; import ctbrec.sites.Site; import ctbrec.ui.HtmlParser; @@ -17,9 +18,10 @@ public class MyFreeCams implements Site { private Recorder recorder; private MyFreeCamsClient client; - private MyFreeCamsHttpClient httpClient = new MyFreeCamsHttpClient(); + private MyFreeCamsHttpClient httpClient; - public MyFreeCams() throws IOException { + @Override + public void init() throws IOException { client = MyFreeCamsClient.getInstance(); client.setSite(this); client.start(); @@ -27,7 +29,7 @@ public class MyFreeCams implements Site { @Override public void login() throws IOException { - httpClient.login(); + getHttpClient().login(); } @Override @@ -66,7 +68,7 @@ public class MyFreeCams implements Site { @Override public Integer getTokenBalance() throws IOException { Request req = new Request.Builder().url(BASE_URI + "/php/account.php?request=status").build(); - Response resp = httpClient.execute(req, true); + Response resp = getHttpClient().execute(req, true); if(resp.isSuccessful()) { String content = resp.body().string(); Elements tags = HtmlParser.getTags(content, "div.content > p > b"); @@ -85,6 +87,9 @@ public class MyFreeCams implements Site { @Override public MyFreeCamsHttpClient getHttpClient() { + if(httpClient == null) { + httpClient = new MyFreeCamsHttpClient(); + } return httpClient; } @@ -100,6 +105,11 @@ public class MyFreeCams implements Site { @Override public boolean supportsTips() { - return true; + return false; + } + + @Override + public boolean isSiteForModel(Model m) { + return m instanceof MyFreeCamsModel; } } diff --git a/src/main/java/ctbrec/sites/mfc/MyFreeCamsHttpClient.java b/src/main/java/ctbrec/sites/mfc/MyFreeCamsHttpClient.java index 02fbc533..dfd1451e 100644 --- a/src/main/java/ctbrec/sites/mfc/MyFreeCamsHttpClient.java +++ b/src/main/java/ctbrec/sites/mfc/MyFreeCamsHttpClient.java @@ -48,7 +48,6 @@ public class MyFreeCamsHttpClient extends HttpClient { Response resp = execute(req); if(resp.isSuccessful()) { String page = resp.body().string(); - System.out.println(page); if(page.contains("Your username or password are incorrect")) { return false; } else { diff --git a/src/main/java/ctbrec/sites/mfc/MyFreeCamsModel.java b/src/main/java/ctbrec/sites/mfc/MyFreeCamsModel.java index 6cd0880d..f037e349 100644 --- a/src/main/java/ctbrec/sites/mfc/MyFreeCamsModel.java +++ b/src/main/java/ctbrec/sites/mfc/MyFreeCamsModel.java @@ -26,6 +26,7 @@ import com.iheartradio.m3u8.data.PlaylistData; import ctbrec.AbstractModel; import ctbrec.recorder.download.StreamSource; +import ctbrec.sites.Site; import ctbrec.ui.HtmlParser; import okhttp3.FormBody; import okhttp3.Request; @@ -43,6 +44,11 @@ public class MyFreeCamsModel extends AbstractModel { private int resolution[]; private MyFreeCams site; + /** + * This constructor exists only for deserialization. Please don't call it directly + */ + public MyFreeCamsModel() {} + MyFreeCamsModel(MyFreeCams site) { this.site = site; } @@ -255,4 +261,13 @@ public class MyFreeCamsModel extends AbstractModel { public void setUid(int uid) { this.uid = uid; } + + @Override + public void setSite(Site site) { + if(site instanceof MyFreeCams) { + this.site = (MyFreeCams) site; + } else { + throw new IllegalArgumentException("Site has to be an instance of MyFreeCams"); + } + } } diff --git a/src/main/java/ctbrec/ui/CamrecApplication.java b/src/main/java/ctbrec/ui/CamrecApplication.java index 5ff0bb9a..1a307dcf 100644 --- a/src/main/java/ctbrec/ui/CamrecApplication.java +++ b/src/main/java/ctbrec/ui/CamrecApplication.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Type; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.Executors; @@ -24,6 +25,7 @@ import ctbrec.recorder.LocalRecorder; import ctbrec.recorder.Recorder; import ctbrec.recorder.RemoteRecorder; import ctbrec.sites.Site; +import ctbrec.sites.chaturbate.Chaturbate; import ctbrec.sites.mfc.MyFreeCams; import javafx.application.Application; import javafx.application.HostServices; @@ -60,16 +62,22 @@ public class CamrecApplication extends Application { static EventBus bus; private HBox tokenPanel; private Site site; + private List sites = new ArrayList<>(); @Override public void start(Stage primaryStage) throws Exception { + site = new MyFreeCams(); + sites.add(site); + Chaturbate ctb = new Chaturbate(); + sites.add(ctb); loadConfig(); bus = new AsyncEventBus(Executors.newSingleThreadExecutor()); hostServices = getHostServices(); createRecorder(); - // site = new Chaturbate(); - site = new MyFreeCams(); - site.setRecorder(recorder); + for (Site site : sites) { + site.setRecorder(recorder); + site.init(); + } if (!Objects.equals(System.getenv("CTBREC_DEV"), "1")) { site.login(); } @@ -155,7 +163,7 @@ public class CamrecApplication extends Application { }); String username = Config.getInstance().getSettings().username; - if(username != null && !username.trim().isEmpty()) { + if(site.supportsTips() && username != null && !username.trim().isEmpty()) { double fontSize = tabPane.getTabMaxHeight() / 2 - 1; Button buyTokens = new Button("Buy Tokens"); buyTokens.setFont(Font.font(fontSize)); @@ -196,7 +204,7 @@ public class CamrecApplication extends Application { private void loadConfig() { try { - Config.init(); + Config.init(sites); } catch (Exception e) { LOG.error("Couldn't load settings", e); Alert alert = new AutosizeAlert(Alert.AlertType.ERROR); diff --git a/src/main/java/ctbrec/ui/JavaFxModel.java b/src/main/java/ctbrec/ui/JavaFxModel.java index ef6bda02..fbb6f2ff 100644 --- a/src/main/java/ctbrec/ui/JavaFxModel.java +++ b/src/main/java/ctbrec/ui/JavaFxModel.java @@ -11,6 +11,7 @@ import com.iheartradio.m3u8.PlaylistException; import ctbrec.AbstractModel; import ctbrec.Model; import ctbrec.recorder.download.StreamSource; +import ctbrec.sites.Site; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -136,4 +137,9 @@ public class JavaFxModel extends AbstractModel { public boolean unfollow() throws IOException { return delegate.unfollow(); } + + @Override + public void setSite(Site site) { + delegate.setSite(site); + } } diff --git a/src/main/java/ctbrec/ui/ThumbOverviewTab.java b/src/main/java/ctbrec/ui/ThumbOverviewTab.java index ce3d8164..3424651c 100644 --- a/src/main/java/ctbrec/ui/ThumbOverviewTab.java +++ b/src/main/java/ctbrec/ui/ThumbOverviewTab.java @@ -279,7 +279,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { } } - ThumbCell createThumbCell(ThumbOverviewTab thumbOverviewTab, Model model, Recorder recorder2, HttpClient client2) { + ThumbCell createThumbCell(ThumbOverviewTab thumbOverviewTab, Model model, Recorder recorder, HttpClient client2) { ThumbCell newCell = new ThumbCell(this, model, recorder, site.getHttpClient()); newCell.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> { suspendUpdates(true); @@ -391,7 +391,10 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { if(site.supportsFollow()) { contextMenu.getItems().add(followOrUnFollow); } - contextMenu.getItems().addAll(copyUrl, sendTip, debug); + if(site.supportsTips()) { + contextMenu.getItems().add(sendTip); + } + contextMenu.getItems().addAll(copyUrl, debug); return contextMenu; }