From 3c71624f38a74e005d06e5f0d3f11ec49dc730d8 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 29 May 2021 17:39:05 +0200 Subject: [PATCH] Add login, follow and unfollow and FollowedTab for Amateur.TV --- .../ui/sites/amateurtv/AmateurTvConfigUI.java | 99 ++++++++++++++++ .../AmateurTvElectronLoginDialog.java | 108 ++++++++++++++++++ .../sites/amateurtv/AmateurTvFollowedTab.java | 13 +++ .../ui/sites/amateurtv/AmateurTvSiteUi.java | 35 +++++- .../sites/amateurtv/AmateurTvTabProvider.java | 11 +- .../amateurtv/AmateurTvUpdateService.java | 15 ++- common/src/main/java/ctbrec/Settings.java | 2 + .../src/main/java/ctbrec/io/HttpClient.java | 12 +- .../main/java/ctbrec/io/HttpConstants.java | 1 + .../ctbrec/sites/amateurtv/AmateurTv.java | 10 +- .../sites/amateurtv/AmateurTvHttpClient.java | 38 +++++- .../sites/amateurtv/AmateurTvModel.java | 49 +++++++- 12 files changed, 368 insertions(+), 25 deletions(-) create mode 100644 client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvConfigUI.java create mode 100644 client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvElectronLoginDialog.java create mode 100644 client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvFollowedTab.java diff --git a/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvConfigUI.java b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvConfigUI.java new file mode 100644 index 00000000..e414c531 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvConfigUI.java @@ -0,0 +1,99 @@ +package ctbrec.ui.sites.amateurtv; + +import ctbrec.Config; +import ctbrec.sites.amateurtv.AmateurTv; +import ctbrec.ui.DesktopIntegration; +import ctbrec.ui.settings.SettingsTab; +import ctbrec.ui.sites.AbstractConfigUI; +import javafx.geometry.Insets; +import javafx.scene.Parent; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; + +public class AmateurTvConfigUI extends AbstractConfigUI { + private AmateurTv site; + + public AmateurTvConfigUI(AmateurTv site) { + this.site = site; + } + + @Override + public Parent createConfigPanel() { + GridPane layout = SettingsTab.createGridLayout(); + var settings = Config.getInstance().getSettings(); + + var row = 0; + var l = new Label("Active"); + layout.add(l, 0, row); + var enabled = new CheckBox(); + enabled.setSelected(!settings.disabledSites.contains(site.getName())); + enabled.setOnAction(e -> { + if(enabled.isSelected()) { + settings.disabledSites.remove(site.getName()); + } else { + settings.disabledSites.add(site.getName()); + } + save(); + }); + GridPane.setMargin(enabled, new Insets(0, 0, 0, SettingsTab.CHECKBOX_MARGIN)); + layout.add(enabled, 1, row++); + + layout.add(new Label("Amateur.TV User"), 0, row); + var username = new TextField(settings.amateurTvUsername); + username.setPrefWidth(300); + username.textProperty().addListener((ob, o, n) -> { + if(!n.equals(Config.getInstance().getSettings().amateurTvUsername)) { + Config.getInstance().getSettings().amateurTvUsername = username.getText(); + site.getHttpClient().logout(); + save(); + } + }); + GridPane.setFillWidth(username, true); + GridPane.setHgrow(username, Priority.ALWAYS); + GridPane.setColumnSpan(username, 2); + layout.add(username, 1, row++); + + layout.add(new Label("Amateur.TV Password"), 0, row); + var password = new PasswordField(); + password.setText(settings.amateurTvPassword); + password.textProperty().addListener((ob, o, n) -> { + if(!n.equals(Config.getInstance().getSettings().amateurTvPassword)) { + Config.getInstance().getSettings().amateurTvPassword = password.getText(); + site.getHttpClient().logout(); + save(); + } + }); + GridPane.setFillWidth(password, true); + GridPane.setHgrow(password, Priority.ALWAYS); + GridPane.setColumnSpan(password, 2); + layout.add(password, 1, row++); + + // layout.add(new Label("Bongacams Base URL"), 0, row); + // var baseUrl = new TextField(); + // baseUrl.setText(Config.getInstance().getSettings().bongacamsBaseUrl); + // baseUrl.textProperty().addListener((ob, o, n) -> { + // Config.getInstance().getSettings().bongacamsBaseUrl = baseUrl.getText(); + // save(); + // }); + // GridPane.setFillWidth(baseUrl, true); + // GridPane.setHgrow(baseUrl, Priority.ALWAYS); + // GridPane.setColumnSpan(baseUrl, 2); + // layout.add(baseUrl, 1, row++); + + var createAccount = new Button("Create new Account"); + createAccount.setOnAction(e -> DesktopIntegration.open(site.getAffiliateLink())); + layout.add(createAccount, 1, row); + GridPane.setColumnSpan(createAccount, 2); + GridPane.setMargin(username, new Insets(0, 0, 0, SettingsTab.CHECKBOX_MARGIN)); + GridPane.setMargin(password, new Insets(0, 0, 0, SettingsTab.CHECKBOX_MARGIN)); + // GridPane.setMargin(baseUrl, new Insets(0, 0, 0, SettingsTab.CHECKBOX_MARGIN)); + GridPane.setMargin(createAccount, new Insets(0, 0, 0, SettingsTab.CHECKBOX_MARGIN)); + return layout; + } + +} diff --git a/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvElectronLoginDialog.java b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvElectronLoginDialog.java new file mode 100644 index 00000000..c517ceeb --- /dev/null +++ b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvElectronLoginDialog.java @@ -0,0 +1,108 @@ +package ctbrec.ui.sites.amateurtv; + +import java.io.IOException; +import java.util.Collections; +import java.util.Objects; +import java.util.function.Consumer; + +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ctbrec.sites.amateurtv.AmateurTv; +import ctbrec.ui.ExternalBrowser; +import okhttp3.Cookie; +import okhttp3.Cookie.Builder; +import okhttp3.CookieJar; +import okhttp3.HttpUrl; + +public class AmateurTvElectronLoginDialog { + + private static final Logger LOG = LoggerFactory.getLogger(AmateurTvElectronLoginDialog.class); + public static final String DOMAIN = "amateur.tv"; + public static final String URL = AmateurTv.baseUrl; + private CookieJar cookieJar; + private ExternalBrowser browser; + + public AmateurTvElectronLoginDialog(CookieJar cookieJar) throws IOException { + this.cookieJar = cookieJar; + browser = ExternalBrowser.getInstance(); + try { + var config = new JSONObject(); + config.put("url", URL); + config.put("w", 640); + config.put("h", 480); + var msg = new JSONObject(); + msg.put("config", config); + browser.run(msg, msgHandler); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Couldn't wait for login dialog", e); + } finally { + browser.close(); + } + } + + private Consumer msgHandler = line -> { + if (!line.startsWith("{")) { + LOG.error("Didn't received a JSON object {}", line); + } else { + var json = new JSONObject(line); + try { + browser.executeJavaScript("let loginDialogVisible = document.querySelectorAll('div[class~=\"MuiDialog-container\"]').length > 1"); + browser.executeJavaScript("if (!loginDialogVisible) { document.querySelector('button').innerHTML.indexOf('I agree') >= 0 && document.querySelector('button').click(); }"); + browser.executeJavaScript("if (!loginDialogVisible) { document.querySelector('button[aria-label=\"open drawer\"]').click(); }"); // open the burger menu to get to the login button + browser.executeJavaScript("if (!loginDialogVisible) { document.querySelectorAll('button').forEach(function(b) { if (b.textContent === 'Log in') b.click(); }); }"); // click the login button to open the login dialog + browser.executeJavaScript("loginDialogVisible = document.querySelectorAll('div[class~=\"MuiDialog-container\"]').length > 1"); + browser.executeJavaScript("if (loginDialogVisible) throw new Error(\"Stop execution right here\")"); + // String username = Config.getInstance().getSettings().amateurTvUsername; + // String password = Config.getInstance().getSettings().amateurTvPassword; + // browser.executeJavaScript("if (loginDialogVisible) { document.querySelectorAll('div[class~=\"MuiDialog-container\"] input').item(0).value = '" + username + "' }"); // enter username + // browser.executeJavaScript("if (loginDialogVisible) { document.querySelectorAll('div[class~=\"MuiDialog-container\"] input').item(1).value = '" + password + "' }"); // enter password + //browser.executeJavaScript("console.log('submit')"); + // browser.executeJavaScript("if(loginDialogVisible) { document.querySelector('div[class~=\"MuiDialog-container\"] button[type=\"submit\"]').click() }"); // click the submit button + } catch(Exception e) { + LOG.warn("Couldn't auto fill username and password for Amateur.TV", e); + } + + var loginSuccessful = false; + if (json.has("cookies")) { + var cookiesFromBrowser = json.getJSONArray("cookies"); + for (var i = 0; i < cookiesFromBrowser.length(); i++) { + var cookie = cookiesFromBrowser.getJSONObject(i); + if (cookie.getString("domain").contains(DOMAIN)) { + Builder b = new Cookie.Builder() + .path(cookie.getString("path")) + .domain(DOMAIN) + .name(cookie.getString("name")) + .value(cookie.getString("value")) + .expiresAt((long) cookie.optDouble("expirationDate") * 1000l); + if (cookie.optBoolean("hostOnly")) { + b.hostOnlyDomain(DOMAIN); + } + if (cookie.optBoolean("httpOnly")) { + b.httpOnly(); + } + if (cookie.optBoolean("secure")) { + b.secure(); + } + Cookie c = b.build(); + cookieJar.saveFromResponse(HttpUrl.parse(AmateurTv.baseUrl), Collections.singletonList(c)); + LOG.debug("{}={}", c.name(), c.value()); + if (Objects.equals(c.name(), "userType") && Objects.equals(c.value(), "registered")) { + loginSuccessful = true; + } + } + } + } + + if (loginSuccessful) { + try { + browser.close(); + } catch (IOException e) { + LOG.error("Couldn't send shutdown request to external browser", e); + } + } + } + }; +} diff --git a/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvFollowedTab.java b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvFollowedTab.java new file mode 100644 index 00000000..c6f187a3 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvFollowedTab.java @@ -0,0 +1,13 @@ +package ctbrec.ui.sites.amateurtv; + +import ctbrec.sites.Site; +import ctbrec.ui.tabs.FollowedTab; +import ctbrec.ui.tabs.PaginatedScheduledService; +import ctbrec.ui.tabs.ThumbOverviewTab; + +public class AmateurTvFollowedTab extends ThumbOverviewTab implements FollowedTab { + + public AmateurTvFollowedTab(String title, PaginatedScheduledService updateService, Site site) { + super(title, updateService, site); + } +} diff --git a/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvSiteUi.java b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvSiteUi.java index ddd1c6fa..14af5419 100644 --- a/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvSiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvSiteUi.java @@ -2,19 +2,28 @@ package ctbrec.ui.sites.amateurtv; import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import ctbrec.sites.amateurtv.AmateurTv; +import ctbrec.sites.amateurtv.AmateurTvHttpClient; +import ctbrec.ui.controls.Dialogs; import ctbrec.ui.sites.AbstractSiteUi; import ctbrec.ui.sites.ConfigUI; import ctbrec.ui.tabs.TabProvider; public class AmateurTvSiteUi extends AbstractSiteUi { + private static final Logger LOG = LoggerFactory.getLogger(AmateurTvSiteUi.class); + private final AmateurTvTabProvider tabProvider; - private final AmateurTv amateurTv; + private final AmateurTv site; + private final AmateurTvConfigUI configUi; public AmateurTvSiteUi(AmateurTv amateurTv) { - this.amateurTv = amateurTv; + this.site = amateurTv; tabProvider = new AmateurTvTabProvider(amateurTv); + configUi = new AmateurTvConfigUI(amateurTv); } @Override @@ -24,11 +33,29 @@ public class AmateurTvSiteUi extends AbstractSiteUi { @Override public ConfigUI getConfigUI() { - return null; + return configUi; } @Override public synchronized boolean login() throws IOException { - return amateurTv.login(); + if (!site.credentialsAvailable()) { + return false; + } + + boolean automaticLogin = site.login(); + if (automaticLogin) { + return true; + } else { + // login with external browser window + try { + new AmateurTvElectronLoginDialog(site.getHttpClient().getCookieJar()); + } catch (Exception e1) { + LOG.error("Error logging in with external browser", e1); + Dialogs.showError("Login error", "Couldn't login to " + site.getName(), e1); + } + + AmateurTvHttpClient httpClient = (AmateurTvHttpClient) site.getHttpClient(); + return httpClient.checkLoginSuccess(); + } } } diff --git a/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvTabProvider.java b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvTabProvider.java index 44df2ac7..f906cc5e 100644 --- a/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvTabProvider.java +++ b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvTabProvider.java @@ -15,6 +15,7 @@ public class AmateurTvTabProvider implements TabProvider { private AmateurTv amateurTv; private Recorder recorder; + private AmateurTvFollowedTab followedTab; public AmateurTvTabProvider(AmateurTv amateurTv) { this.amateurTv = amateurTv; @@ -50,6 +51,14 @@ public class AmateurTvTabProvider implements TabProvider { updateService = new AmateurTvUpdateService(amateurTv, url); tabs.add(createTab("Trans", updateService)); + // followed + url = AmateurTv.baseUrl + "/v3/readmodel/cache/cams/F"; + updateService = new AmateurTvUpdateService(amateurTv, url); + updateService.requiresLogin(true); + followedTab = new AmateurTvFollowedTab("Followed", updateService, amateurTv); + followedTab.setRecorder(recorder); + tabs.add(followedTab); + return tabs; } @@ -61,7 +70,7 @@ public class AmateurTvTabProvider implements TabProvider { @Override public Tab getFollowedTab() { - return null; + return followedTab; } } diff --git a/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvUpdateService.java b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvUpdateService.java index 8c5fccca..411c06e8 100644 --- a/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/amateurtv/AmateurTvUpdateService.java @@ -16,6 +16,7 @@ import ctbrec.Config; import ctbrec.Model; import ctbrec.sites.amateurtv.AmateurTv; import ctbrec.sites.amateurtv.AmateurTvModel; +import ctbrec.ui.SiteUiFactory; import ctbrec.ui.tabs.PaginatedScheduledService; import javafx.concurrent.Task; import okhttp3.Request; @@ -27,6 +28,7 @@ public class AmateurTvUpdateService extends PaginatedScheduledService { private AmateurTv site; private String url; + private boolean requiresLogin = false; public AmateurTvUpdateService(AmateurTv site, String url) { this.site = site; @@ -38,26 +40,29 @@ public class AmateurTvUpdateService extends PaginatedScheduledService { return new Task>() { @Override public List call() throws IOException { + if (requiresLogin) { + SiteUiFactory.getUi(site).login(); + } return loadModelList(); } - }; } private List loadModelList() throws IOException { int offset = page - 1; - String pageUrl = url + '/' + offset * ITEMS_PER_PAGE + '/' + ITEMS_PER_PAGE + "/es"; + String pageUrl = url + '/' + offset * ITEMS_PER_PAGE + '/' + ITEMS_PER_PAGE + "/en"; LOG.debug("Fetching page {}", pageUrl); var request = new Request.Builder() .url(pageUrl) .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) .header(ACCEPT, MIMETYPE_APPLICATION_JSON) .header(ACCEPT, Locale.ENGLISH.getLanguage()) - .header(REFERER, site.getBaseUrl()) + .header(REFERER, site.getBaseUrl() + "/following") .build(); try (var response = site.getHttpClient().execute(request)) { if (response.isSuccessful()) { var content = response.body().string(); + LOG.debug(content); List models = new ArrayList<>(); var json = new JSONObject(content); var modelNodes = json.getJSONObject("cams").getJSONArray("nodes"); @@ -81,4 +86,8 @@ public class AmateurTvUpdateService extends PaginatedScheduledService { models.add(model); } } + + public void requiresLogin(boolean requiresLogin) { + this.requiresLogin = requiresLogin; + } } diff --git a/common/src/main/java/ctbrec/Settings.java b/common/src/main/java/ctbrec/Settings.java index 81de026f..e9877611 100644 --- a/common/src/main/java/ctbrec/Settings.java +++ b/common/src/main/java/ctbrec/Settings.java @@ -43,6 +43,8 @@ public class Settings { TIME_OR_SIZE } + public String amateurTvUsername = ""; + public String amateurTvPassword = ""; public String bongacamsBaseUrl = "https://bongacams.com"; public String bongaPassword = ""; public String bongaUsername = ""; diff --git a/common/src/main/java/ctbrec/io/HttpClient.java b/common/src/main/java/ctbrec/io/HttpClient.java index d906bdeb..1de8dd20 100644 --- a/common/src/main/java/ctbrec/io/HttpClient.java +++ b/common/src/main/java/ctbrec/io/HttpClient.java @@ -25,6 +25,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import java.util.zip.GZIPInputStream; import javax.net.ssl.KeyManager; @@ -217,7 +218,6 @@ public abstract class HttpClient { } } - @SuppressWarnings({ "unchecked", "rawtypes" }) private void loadCookies() { try { File cookieFile = new File(Config.getInstance().getConfigDir(), "cookies-" + name + ".json"); @@ -233,10 +233,12 @@ public abstract class HttpClient { .build(); JsonAdapter adapter = moshi.adapter(CookieContainer.class).indent(" "); CookieContainer fromJson = adapter.fromJson(json); - Set entries = fromJson.entrySet(); - for (Object _entry : entries) { - Entry entry = (Entry) _entry; - cookies.put((String)entry.getKey(), (List)entry.getValue()); + Set>> entries = fromJson.entrySet(); + for (Entry> entry : entries) { + List filteredCookies = entry.getValue().stream() + .filter(c -> !Objects.equals("deleted", c.value())) + .collect(Collectors.toList()); + cookies.put(entry.getKey(), filteredCookies); } } catch (Exception e) { diff --git a/common/src/main/java/ctbrec/io/HttpConstants.java b/common/src/main/java/ctbrec/io/HttpConstants.java index 4ae7202b..d99f6e69 100644 --- a/common/src/main/java/ctbrec/io/HttpConstants.java +++ b/common/src/main/java/ctbrec/io/HttpConstants.java @@ -13,6 +13,7 @@ public class HttpConstants { public static final String COOKIE = "Cookie"; public static final String KEEP_ALIVE = "keep-alive"; public static final String MIMETYPE_APPLICATION_JSON = "application/json"; + public static final String MIMETYPE_TEXT_HTML = "text/html"; public static final String NO_CACHE = "no-cache"; public static final String ORIGIN = "Origin"; public static final String PRAGMA = "Pragma"; diff --git a/common/src/main/java/ctbrec/sites/amateurtv/AmateurTv.java b/common/src/main/java/ctbrec/sites/amateurtv/AmateurTv.java index 16ae8612..b0a0bbde 100644 --- a/common/src/main/java/ctbrec/sites/amateurtv/AmateurTv.java +++ b/common/src/main/java/ctbrec/sites/amateurtv/AmateurTv.java @@ -6,13 +6,14 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import ctbrec.Config; import ctbrec.Model; import ctbrec.io.HttpClient; import ctbrec.sites.AbstractSite; public class AmateurTv extends AbstractSite { - public static String baseUrl = "https://www.amateur.tv"; + public static String baseUrl = "https://en.amateur.tv"; private AmateurTvHttpClient httpClient; @@ -79,7 +80,7 @@ public class AmateurTv extends AbstractSite { @Override public boolean supportsFollow() { - return false; + return true; } @Override @@ -104,9 +105,8 @@ public class AmateurTv extends AbstractSite { @Override public boolean credentialsAvailable() { - //String username = Config.getInstance().getSettings().bongaUsername; - //return username != null && !username.trim().isEmpty(); - return false; + String username = Config.getInstance().getSettings().amateurTvUsername; + return username != null && !username.trim().isEmpty(); } @Override diff --git a/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvHttpClient.java b/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvHttpClient.java index 74d23d4f..5b36a006 100644 --- a/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvHttpClient.java +++ b/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvHttpClient.java @@ -1,22 +1,50 @@ package ctbrec.sites.amateurtv; +import static ctbrec.io.HttpConstants.*; + import java.io.IOException; +import java.util.Locale; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.json.JSONObject; +import ctbrec.Config; import ctbrec.io.HttpClient; +import ctbrec.io.HttpException; +import okhttp3.Request; +import okhttp3.Response; public class AmateurTvHttpClient extends HttpClient { - private static final Logger LOG = LoggerFactory.getLogger(AmateurTvHttpClient.class); - public AmateurTvHttpClient() { super("amateurtv"); } @Override public boolean login() throws IOException { - return false; + return checkLoginSuccess(); + } + + /** + * Check, if the login worked by requesting the user profile + * + * @throws IOException + */ + public boolean checkLoginSuccess() throws IOException { + String url = AmateurTv.baseUrl + "/v3/readmodel/user/me"; + Request request = new Request.Builder() + .url(url) + .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) + .header(ACCEPT, MIMETYPE_APPLICATION_JSON) + .header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage()) + .header(REFERER, AmateurTv.baseUrl) + .build(); + try (Response response = execute(request)) { + if (response.isSuccessful()) { + JSONObject json = new JSONObject(response.body().string()); + return json.has("username") && !json.getString("username").equalsIgnoreCase("guest"); + } else { + throw new HttpException(response.code(), response.message()); + } + } } } diff --git a/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvModel.java b/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvModel.java index 7697516d..661e5372 100644 --- a/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvModel.java +++ b/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvModel.java @@ -7,12 +7,16 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.Locale; +import java.util.Objects; import java.util.concurrent.ExecutionException; import javax.xml.bind.JAXBException; import org.json.JSONObject; import org.jsoup.nodes.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.iheartradio.m3u8.Encoding; import com.iheartradio.m3u8.Format; @@ -30,11 +34,15 @@ import ctbrec.Config; import ctbrec.io.HttpClient; import ctbrec.io.HttpException; import ctbrec.recorder.download.StreamSource; +import okhttp3.FormBody; import okhttp3.Request; +import okhttp3.RequestBody; import okhttp3.Response; public class AmateurTvModel extends AbstractModel { + private static final Logger LOG = LoggerFactory.getLogger(AmateurTvModel.class); + private boolean online = false; @Override @@ -127,12 +135,49 @@ public class AmateurTvModel extends AbstractModel { @Override public boolean follow() throws IOException { - return false; + String url = getSite().getBaseUrl() + "/v3/user/follow"; + return followUnfollow(url); } @Override public boolean unfollow() throws IOException { - return false; + String url = getSite().getBaseUrl() + "/v3/user/unfollow"; + return followUnfollow(url); + } + + private boolean followUnfollow(String url) throws IOException { + if(!getSite().login()) { + throw new IOException("Not logged in"); + } + + LOG.debug("Calling {}", url); + RequestBody body = new FormBody.Builder() + .add("username", getName()) + .build(); + Request req = new Request.Builder() + .url(url) + .method("POST", body) + .header(ACCEPT, "*/*") + .header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage()) + .header(REFERER, getUrl()) + .header(ORIGIN, getSite().getBaseUrl()) + .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) + .build(); + try (Response resp = site.getHttpClient().execute(req)) { + if (resp.isSuccessful()) { + String msg = resp.body().string(); + JSONObject json = new JSONObject(msg); + if (Objects.equals(json.getString("result"), "OK")) { + LOG.debug("Follow/Unfollow -> {}", msg); + return true; + } else { + LOG.debug(msg); + throw new IOException("Response was " + msg); + } + } else { + throw new HttpException(resp.code(), resp.message()); + } + } } private JSONObject getModelInfo() throws IOException {