Start implementation for FC2Live
This commit is contained in:
parent
8fb5eac435
commit
7133283032
|
@ -31,6 +31,7 @@ import ctbrec.sites.bonga.BongaCams;
|
|||
import ctbrec.sites.cam4.Cam4;
|
||||
import ctbrec.sites.camsoda.Camsoda;
|
||||
import ctbrec.sites.chaturbate.Chaturbate;
|
||||
import ctbrec.sites.fc2live.Fc2Live;
|
||||
import ctbrec.sites.mfc.MyFreeCams;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.HostServices;
|
||||
|
@ -66,6 +67,7 @@ public class CamrecApplication extends Application {
|
|||
sites.add(new Cam4());
|
||||
sites.add(new Camsoda());
|
||||
sites.add(new Chaturbate());
|
||||
sites.add(new Fc2Live());
|
||||
sites.add(new MyFreeCams());
|
||||
loadConfig();
|
||||
createHttpClient();
|
||||
|
|
|
@ -5,11 +5,13 @@ import ctbrec.sites.bonga.BongaCams;
|
|||
import ctbrec.sites.cam4.Cam4;
|
||||
import ctbrec.sites.camsoda.Camsoda;
|
||||
import ctbrec.sites.chaturbate.Chaturbate;
|
||||
import ctbrec.sites.fc2live.Fc2Live;
|
||||
import ctbrec.sites.mfc.MyFreeCams;
|
||||
import ctbrec.ui.sites.bonga.BongaCamsSiteUi;
|
||||
import ctbrec.ui.sites.cam4.Cam4SiteUi;
|
||||
import ctbrec.ui.sites.camsoda.CamsodaSiteUi;
|
||||
import ctbrec.ui.sites.chaturbate.ChaturbateSiteUi;
|
||||
import ctbrec.ui.sites.fc2live.Fc2LiveUi;
|
||||
import ctbrec.ui.sites.myfreecams.MyFreeCamsSiteUi;
|
||||
|
||||
public class SiteUiFactory {
|
||||
|
@ -18,6 +20,7 @@ public class SiteUiFactory {
|
|||
private static Cam4SiteUi cam4SiteUi;
|
||||
private static CamsodaSiteUi camsodaSiteUi;
|
||||
private static ChaturbateSiteUi ctbSiteUi;
|
||||
private static Fc2LiveUi fc2SiteUi;
|
||||
private static MyFreeCamsSiteUi mfcSiteUi;
|
||||
|
||||
public static SiteUI getUi(Site site) {
|
||||
|
@ -41,6 +44,11 @@ public class SiteUiFactory {
|
|||
ctbSiteUi = new ChaturbateSiteUi((Chaturbate) site);
|
||||
}
|
||||
return ctbSiteUi;
|
||||
} else if (site instanceof Fc2Live) {
|
||||
if (fc2SiteUi == null) {
|
||||
fc2SiteUi = new Fc2LiveUi((Fc2Live) site);
|
||||
}
|
||||
return fc2SiteUi;
|
||||
} else if (site instanceof MyFreeCams) {
|
||||
if (mfcSiteUi == null) {
|
||||
mfcSiteUi = new MyFreeCamsSiteUi((MyFreeCams) site);
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package ctbrec.ui.sites.fc2live;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ctbrec.sites.ConfigUI;
|
||||
import ctbrec.sites.fc2live.Fc2Live;
|
||||
import ctbrec.ui.SiteUI;
|
||||
import ctbrec.ui.TabProvider;
|
||||
|
||||
public class Fc2LiveUi implements SiteUI {
|
||||
|
||||
private Fc2Live fc2live;
|
||||
private Fc2TabProvider tabProvider;
|
||||
|
||||
public Fc2LiveUi(Fc2Live fc2live) {
|
||||
this.fc2live = fc2live;
|
||||
this.tabProvider = new Fc2TabProvider(fc2live);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabProvider getTabProvider() {
|
||||
return tabProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigUI getConfigUI() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package ctbrec.ui.sites.fc2live;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ctbrec.sites.fc2live.Fc2Live;
|
||||
import ctbrec.ui.TabProvider;
|
||||
import ctbrec.ui.ThumbOverviewTab;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Tab;
|
||||
|
||||
public class Fc2TabProvider extends TabProvider {
|
||||
|
||||
private Fc2Live fc2live;
|
||||
|
||||
public Fc2TabProvider(Fc2Live fc2live) {
|
||||
this.fc2live = fc2live;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tab> getTabs(Scene scene) {
|
||||
List<Tab> tabs = new ArrayList<>();
|
||||
tabs.add(createTab("Online", Fc2Live.BASE_URL + "/adult/contents/allchannellist.php"));
|
||||
return tabs;
|
||||
}
|
||||
|
||||
private Tab createTab(String title, String url) {
|
||||
Fc2UpdateService updateService = new Fc2UpdateService(url, fc2live);
|
||||
ThumbOverviewTab tab = new ThumbOverviewTab(title, updateService, fc2live);
|
||||
tab.setRecorder(fc2live.getRecorder());
|
||||
return tab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tab getFollowedTab() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package ctbrec.ui.sites.fc2live;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.sites.fc2live.Fc2Live;
|
||||
import ctbrec.sites.fc2live.Fc2Model;
|
||||
import ctbrec.ui.PaginatedScheduledService;
|
||||
import javafx.concurrent.Task;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class Fc2UpdateService extends PaginatedScheduledService {
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(Fc2UpdateService.class);
|
||||
|
||||
private String url;
|
||||
private Fc2Live fc2live;
|
||||
private int modelsPerPage = 30;
|
||||
|
||||
public Fc2UpdateService(String url, Fc2Live fc2live) {
|
||||
this.url = url;
|
||||
this.fc2live = fc2live;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task<List<Model>> createTask() {
|
||||
return new Task<List<Model>>() {
|
||||
@Override
|
||||
public List<Model> call() throws IOException {
|
||||
RequestBody body = RequestBody.create(null, new byte[0]);
|
||||
Request req = new Request.Builder()
|
||||
.url(url)
|
||||
.method("POST", body)
|
||||
.header("Accept", "*/*")
|
||||
.header("Accept-Language", "en-US,en;q=0.5")
|
||||
.header("Referer", Fc2Live.BASE_URL)
|
||||
.header("User-Agent", Config.getInstance().getSettings().httpUserAgent)
|
||||
.header("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
LOG.debug("Fetching page {}", url);
|
||||
try(Response resp = fc2live.getHttpClient().execute(req)) {
|
||||
if(resp.isSuccessful()) {
|
||||
List<Fc2Model> models = new ArrayList<>();
|
||||
String msg = resp.body().string();
|
||||
JSONObject json = new JSONObject(msg);
|
||||
JSONArray channels = json.getJSONArray("channel");
|
||||
for (int i = 0; i < channels.length(); i++) {
|
||||
JSONObject channel = channels.getJSONObject(i);
|
||||
Fc2Model model = (Fc2Model) fc2live.createModel(channel.getString("name"));
|
||||
model.setId(channel.getString("id"));
|
||||
model.setUrl(Fc2Live.BASE_URL + '/' + model.getId());
|
||||
String previewUrl = channel.getString("image");
|
||||
if(previewUrl == null || previewUrl.trim().isEmpty()) {
|
||||
previewUrl = getClass().getResource("/image_not_found.png").toString();
|
||||
}
|
||||
model.setPreview(previewUrl);
|
||||
model.setDescription(channel.optString("title"));
|
||||
model.setViewerCount(channel.optInt("count"));
|
||||
if(channel.getInt("login") == 0) {
|
||||
models.add(model);
|
||||
}
|
||||
}
|
||||
return models.stream()
|
||||
.sorted((m1, m2) -> m2.getViewerCount() - m1.getViewerCount())
|
||||
.skip( (page - 1) * modelsPerPage)
|
||||
.limit(modelsPerPage)
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
resp.close();
|
||||
throw new IOException("HTTP status " + resp.code() + " " + resp.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -30,6 +30,8 @@ import okhttp3.OkHttpClient.Builder;
|
|||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.Route;
|
||||
import okhttp3.WebSocket;
|
||||
import okhttp3.WebSocketListener;
|
||||
|
||||
public abstract class HttpClient {
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(HttpClient.class);
|
||||
|
@ -215,4 +217,9 @@ public abstract class HttpClient {
|
|||
public CookieJar getCookieJar() {
|
||||
return cookieJar;
|
||||
}
|
||||
|
||||
public WebSocket newWebSocket(String url, WebSocketListener l) {
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
return client.newWebSocket(request, l);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package ctbrec.sites.fc2live;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jsoup.select.Elements;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.io.HtmlParser;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.io.HttpException;
|
||||
import ctbrec.sites.mfc.MyFreeCams;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.WebSocket;
|
||||
import okhttp3.WebSocketListener;
|
||||
|
||||
public class Fc2HttpClient extends HttpClient {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(Fc2HttpClient.class);
|
||||
|
||||
public Fc2HttpClient() {
|
||||
super("fc2live");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login() throws IOException {
|
||||
if(loggedIn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(checkLogin()) {
|
||||
loggedIn = true;
|
||||
LOG.debug("Logged in with cookies");
|
||||
return true;
|
||||
}
|
||||
|
||||
String username = Config.getInstance().getSettings().mfcUsername;
|
||||
String password = Config.getInstance().getSettings().mfcPassword;
|
||||
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()) {
|
||||
String page = resp.body().string();
|
||||
if(page.contains("Your username or password are incorrect")) {
|
||||
return false;
|
||||
} else {
|
||||
loggedIn = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
resp.close();
|
||||
LOG.error("Login failed {} {}", resp.code(), resp.message());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkLogin() throws IOException {
|
||||
Request req = new Request.Builder().url(MyFreeCams.BASE_URI + "/php/account.php?request=status").build();
|
||||
try(Response response = execute(req)) {
|
||||
if(response.isSuccessful()) {
|
||||
String content = response.body().string();
|
||||
try {
|
||||
Elements tags = HtmlParser.getTags(content, "div.content > p > b");
|
||||
tags.get(2).text();
|
||||
return true;
|
||||
} catch(Exception e) {
|
||||
LOG.debug("Token tag not found. Login failed");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
throw new HttpException(response.code(), response.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WebSocket newWebSocket(Request req, WebSocketListener webSocketListener) {
|
||||
return client.newWebSocket(req, webSocketListener);
|
||||
}
|
||||
|
||||
// public Cookie getCookie(String name) {
|
||||
// CookieJar jar = client.cookieJar();
|
||||
// HttpUrl url = HttpUrl.parse(MyFreeCams.BASE_URI);
|
||||
// List<Cookie> cookies = jar.loadForRequest(url);
|
||||
// for (Cookie cookie : cookies) {
|
||||
// if(Objects.equals(cookie.name(), name)) {
|
||||
// return cookie;
|
||||
// }
|
||||
// }
|
||||
// throw new NoSuchElementException("No cookie with name " + name);
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package ctbrec.sites.fc2live;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ctbrec.Model;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.sites.AbstractSite;
|
||||
|
||||
public class Fc2Live extends AbstractSite {
|
||||
|
||||
public static final String BASE_URL = "https://live.fc2.com";
|
||||
private Fc2HttpClient httpClient;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "FC2Live";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseUrl() {
|
||||
return BASE_URL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAffiliateLink() {
|
||||
return BASE_URL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Model createModel(String name) {
|
||||
Fc2Model model = new Fc2Model();
|
||||
model.setSite(this);
|
||||
model.setName(name);
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTokenBalance() throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuyTokensLink() {
|
||||
return BASE_URL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClient getHttpClient() {
|
||||
if(httpClient == null) {
|
||||
httpClient = new Fc2HttpClient();
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if(httpClient != null) {
|
||||
httpClient.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsTips() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFollow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSiteForModel(Model m) {
|
||||
return m instanceof Fc2Model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean credentialsAvailable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
package ctbrec.sites.fc2live;
|
||||
|
||||
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.function.BiConsumer;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.iheartradio.m3u8.Encoding;
|
||||
import com.iheartradio.m3u8.Format;
|
||||
import com.iheartradio.m3u8.ParseException;
|
||||
import com.iheartradio.m3u8.PlaylistException;
|
||||
import com.iheartradio.m3u8.PlaylistParser;
|
||||
import com.iheartradio.m3u8.data.MasterPlaylist;
|
||||
import com.iheartradio.m3u8.data.Playlist;
|
||||
import com.iheartradio.m3u8.data.PlaylistData;
|
||||
import com.iheartradio.m3u8.data.StreamInfo;
|
||||
|
||||
import ctbrec.AbstractModel;
|
||||
import ctbrec.Config;
|
||||
import ctbrec.io.HttpException;
|
||||
import ctbrec.recorder.download.StreamSource;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class Fc2Model extends AbstractModel {
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(Fc2Model.class);
|
||||
private String id;
|
||||
private int viewerCount;
|
||||
private boolean online;
|
||||
private String onlineState = "n/a";
|
||||
private String version;
|
||||
|
||||
@Override
|
||||
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
|
||||
if(ignoreCache) {
|
||||
loadModelInfo();
|
||||
}
|
||||
return online;
|
||||
}
|
||||
|
||||
private void loadModelInfo() throws IOException {
|
||||
String url = Fc2Live.BASE_URL + "/api/memberApi.php";
|
||||
RequestBody body = new FormBody.Builder()
|
||||
.add("channel", "1")
|
||||
.add("profile", "1")
|
||||
.add("streamid", id)
|
||||
.build();
|
||||
Request req = new Request.Builder()
|
||||
.url(url)
|
||||
.method("POST", body)
|
||||
.header("Accept", "*/*")
|
||||
.header("Accept-Language", "en-US,en;q=0.5")
|
||||
.header("Referer", Fc2Live.BASE_URL)
|
||||
.header("User-Agent", Config.getInstance().getSettings().httpUserAgent)
|
||||
.header("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
LOG.debug("Fetching page {}", url);
|
||||
try(Response resp = getSite().getHttpClient().execute(req)) {
|
||||
if(resp.isSuccessful()) {
|
||||
String msg = resp.body().string();
|
||||
JSONObject json = new JSONObject(msg);
|
||||
JSONObject data = json.getJSONObject("data");
|
||||
JSONObject channelData = data.getJSONObject("channel_data");
|
||||
online = channelData.optInt("is_publish") == 1;
|
||||
onlineState = online ? "online" : "offline";
|
||||
version = channelData.optString("version");
|
||||
} else {
|
||||
resp.close();
|
||||
throw new IOException("HTTP status " + resp.code() + " " + resp.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOnlineState(boolean failFast) throws IOException, ExecutionException {
|
||||
if(failFast) {
|
||||
return onlineState;
|
||||
} else if(Objects.equals(onlineState, "n/a")){
|
||||
loadModelInfo();
|
||||
}
|
||||
return onlineState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException {
|
||||
loadModelInfo();
|
||||
List<StreamSource> sources = new ArrayList<>();
|
||||
getControlToken((token, url) -> {
|
||||
url = url + "?control_token=" + token;
|
||||
LOG.debug("Session token: {}", token);
|
||||
LOG.debug("Getting playlist token over websocket {}", url);
|
||||
Fc2WebSocketClient wsClient = new Fc2WebSocketClient(url, getSite().getHttpClient());
|
||||
try {
|
||||
String playlistUrl = wsClient.getPlaylistUrl();
|
||||
LOG.debug("Paylist url {}", playlistUrl);
|
||||
sources.addAll(parseMasterPlaylist(playlistUrl));
|
||||
} catch (InterruptedException | IOException | ParseException | PlaylistException e) {
|
||||
LOG.error("Couldn't fetch stream information", e);
|
||||
}
|
||||
});
|
||||
return sources;
|
||||
}
|
||||
|
||||
private List<StreamSource> parseMasterPlaylist(String playlistUrl) throws IOException, ParseException, PlaylistException {
|
||||
List<StreamSource> sources = new ArrayList<>();
|
||||
Request req = new Request.Builder()
|
||||
.url(playlistUrl)
|
||||
.header("Accept", "*/*")
|
||||
.header("Accept-Language", "en-US,en;q=0.5")
|
||||
.header("User-Agent", Config.getInstance().getSettings().httpUserAgent)
|
||||
.header("Origin", Fc2Live.BASE_URL)
|
||||
.header("Referer", getUrl())
|
||||
.build();
|
||||
try(Response response = site.getHttpClient().execute(req)) {
|
||||
if(response.isSuccessful()) {
|
||||
InputStream inputStream = response.body().byteStream();
|
||||
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8);
|
||||
Playlist playlist = parser.parse();
|
||||
MasterPlaylist master = playlist.getMasterPlaylist();
|
||||
sources.clear();
|
||||
for (PlaylistData playlistData : master.getPlaylists()) {
|
||||
StreamSource streamsource = new StreamSource();
|
||||
streamsource.mediaPlaylistUrl = playlistData.getUri();
|
||||
if (playlistData.hasStreamInfo()) {
|
||||
StreamInfo info = playlistData.getStreamInfo();
|
||||
streamsource.bandwidth = info.getBandwidth();
|
||||
streamsource.width = info.hasResolution() ? info.getResolution().width : 0;
|
||||
streamsource.height = info.hasResolution() ? info.getResolution().height : 0;
|
||||
} else {
|
||||
streamsource.bandwidth = 0;
|
||||
streamsource.width = 0;
|
||||
streamsource.height = 0;
|
||||
}
|
||||
sources.add(streamsource);
|
||||
}
|
||||
LOG.debug(sources.toString());
|
||||
return sources;
|
||||
} else {
|
||||
throw new HttpException(response.code(), response.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void getControlToken(BiConsumer<String, String> callback) throws IOException {
|
||||
String url = Fc2Live.BASE_URL + "/api/getControlServer.php";
|
||||
RequestBody body = new FormBody.Builder()
|
||||
.add("channel_id", id)
|
||||
.add("channel_version", version)
|
||||
.add("client_app", "browser_hls")
|
||||
.add("client_type", "pc")
|
||||
.add("client_version", "1.6.0 [1]")
|
||||
.add("mode", "play")
|
||||
.build();
|
||||
Request req = new Request.Builder()
|
||||
.url(url)
|
||||
.method("POST", body)
|
||||
.header("Accept", "*/*")
|
||||
.header("Accept-Language", "en-US,en;q=0.5")
|
||||
.header("Referer", Fc2Live.BASE_URL)
|
||||
.header("User-Agent", Config.getInstance().getSettings().httpUserAgent)
|
||||
.header("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
LOG.debug("Fetching page {}", url);
|
||||
try(Response resp = getSite().getHttpClient().execute(req)) {
|
||||
if(resp.isSuccessful()) {
|
||||
String msg = resp.body().string();
|
||||
JSONObject json = new JSONObject(msg);
|
||||
String wssurl = json.getString("url");
|
||||
String token = json.getString("control_token");
|
||||
callback.accept(token, wssurl);
|
||||
} else {
|
||||
resp.close();
|
||||
throw new IOException("HTTP status " + resp.code() + " " + resp.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCacheEntries() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveTip(int tokens) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
|
||||
return new int[2];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean follow() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unfollow() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getViewerCount() {
|
||||
return viewerCount;
|
||||
}
|
||||
|
||||
public void setViewerCount(int viewerCount) {
|
||||
this.viewerCount = viewerCount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package ctbrec.sites.fc2live;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.io.HttpClient;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.WebSocket;
|
||||
import okhttp3.WebSocketListener;
|
||||
import okio.ByteString;
|
||||
|
||||
public class Fc2WebSocketClient {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(Fc2WebSocketClient.class);
|
||||
private String url;
|
||||
private HttpClient client;
|
||||
|
||||
public Fc2WebSocketClient(String url, HttpClient client) {
|
||||
this.url = url;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
String playlistUrl = "";
|
||||
public String getPlaylistUrl() throws InterruptedException {
|
||||
LOG.debug("Connecting to {}", url);
|
||||
Object monitor = new Object();
|
||||
client.newWebSocket(url, new WebSocketListener() {
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket, Response response) {
|
||||
response.close();
|
||||
webSocket.send("{\"name\":\"get_hls_information\",\"arguments\":{},\"id\":1}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket webSocket, String text) {
|
||||
JSONObject json = new JSONObject(text);
|
||||
if(json.optString("name").equals("_response_") && json.optInt("id") == 1) {
|
||||
LOG.debug(json.toString(2));
|
||||
JSONObject args = json.getJSONObject("arguments");
|
||||
JSONArray playlists = args.getJSONArray("playlists_high_latency");
|
||||
JSONObject playlist = playlists.getJSONObject(0);
|
||||
playlistUrl = playlist.getString("url");
|
||||
webSocket.close(1000, "");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket webSocket, ByteString bytes) {
|
||||
LOG.debug("ws btxt {}", bytes.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(WebSocket webSocket, int code, String reason) {
|
||||
synchronized (monitor) {
|
||||
monitor.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
|
||||
LOG.debug("ws failure", t);
|
||||
response.close();
|
||||
synchronized (monitor) {
|
||||
monitor.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
synchronized (monitor) {
|
||||
monitor.wait();
|
||||
}
|
||||
return playlistUrl;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue