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<String> 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<List<Model>>() {
             @Override
             public List<Model> call() throws IOException {
+                if (requiresLogin) {
+                    SiteUiFactory.getUi(site).login();
+                }
                 return loadModelList();
             }
-
         };
     }
 
     private List<Model> 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<Model> 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<CookieContainer> 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<Cookie>)entry.getValue());
+            Set<Entry<String, List<Cookie>>> entries = fromJson.entrySet();
+            for (Entry<String, List<Cookie>> entry : entries) {
+                List<Cookie> 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 {