Fix WinkTV browsing
This commit is contained in:
parent
03dd723fb6
commit
970b2ab14e
|
@ -2,7 +2,6 @@ package ctbrec.ui.sites.winktv;
|
||||||
|
|
||||||
import ctbrec.sites.winktv.WinkTv;
|
import ctbrec.sites.winktv.WinkTv;
|
||||||
import ctbrec.sites.winktv.WinkTvModel;
|
import ctbrec.sites.winktv.WinkTvModel;
|
||||||
|
|
||||||
import ctbrec.ui.sites.AbstractTabProvider;
|
import ctbrec.ui.sites.AbstractTabProvider;
|
||||||
import ctbrec.ui.tabs.ThumbOverviewTab;
|
import ctbrec.ui.tabs.ThumbOverviewTab;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
@ -10,7 +9,6 @@ import javafx.scene.control.Tab;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class WinkTvTabProvider extends AbstractTabProvider {
|
public class WinkTvTabProvider extends AbstractTabProvider {
|
||||||
|
@ -22,7 +20,9 @@ public class WinkTvTabProvider extends AbstractTabProvider {
|
||||||
@Override
|
@Override
|
||||||
protected List<Tab> getSiteTabs(Scene scene) {
|
protected List<Tab> getSiteTabs(Scene scene) {
|
||||||
List<Tab> tabs = new ArrayList<>();
|
List<Tab> tabs = new ArrayList<>();
|
||||||
tabs.add(createTab("Live", m -> !m.isAdult()));
|
tabs.add(createTab("Live", Predicate.not(WinkTvModel::isAdult)));
|
||||||
|
// adult streams are only available with korean age verification, you have to be logged in
|
||||||
|
//tabs.add(createTab("Adult", WinkTvModel::isAdult));
|
||||||
return tabs;
|
return tabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,32 @@
|
||||||
package ctbrec.ui.sites.winktv;
|
package ctbrec.ui.sites.winktv;
|
||||||
|
|
||||||
import static ctbrec.io.HttpConstants.*;
|
|
||||||
|
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
import ctbrec.Model;
|
import ctbrec.Model;
|
||||||
import ctbrec.io.HttpException;
|
import ctbrec.io.HttpException;
|
||||||
import ctbrec.sites.winktv.WinkTv;
|
import ctbrec.sites.winktv.WinkTv;
|
||||||
import ctbrec.sites.winktv.WinkTvModel;
|
import ctbrec.sites.winktv.WinkTvModel;
|
||||||
import ctbrec.ui.SiteUiFactory;
|
|
||||||
import ctbrec.ui.tabs.PaginatedScheduledService;
|
import ctbrec.ui.tabs.PaginatedScheduledService;
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.MessageFormat;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import okhttp3.FormBody;
|
import okhttp3.FormBody;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import static ctbrec.io.HttpConstants.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class WinkTvUpdateService extends PaginatedScheduledService {
|
public class WinkTvUpdateService extends PaginatedScheduledService {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(WinkTvUpdateService.class);
|
|
||||||
private static final String API_URL = "https://api.winktv.co.kr/v1/live";
|
private static final String API_URL = "https://api.winktv.co.kr/v1/live";
|
||||||
|
|
||||||
private WinkTv site;
|
private final WinkTv site;
|
||||||
private String url;
|
|
||||||
private static List<WinkTvModel> modelsList;
|
|
||||||
private static Instant lastListInfoRequest = Instant.EPOCH;
|
|
||||||
protected int modelsPerPage = 48;
|
protected int modelsPerPage = 48;
|
||||||
protected Predicate<WinkTvModel> filter;
|
protected Predicate<WinkTvModel> filter;
|
||||||
|
|
||||||
|
@ -48,54 +37,46 @@ public class WinkTvUpdateService extends PaginatedScheduledService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Task<List<Model>> createTask() {
|
protected Task<List<Model>> createTask() {
|
||||||
return new Task<List<Model>>() {
|
return new Task<>() {
|
||||||
@Override
|
@Override
|
||||||
public List<Model> call() throws IOException {
|
public List<Model> call() throws IOException {
|
||||||
return getModelList().stream()
|
return loadModelList()
|
||||||
|
.stream()
|
||||||
.filter(filter)
|
.filter(filter)
|
||||||
.skip((page - 1) * (long) modelsPerPage)
|
.map(Model.class::cast)
|
||||||
.limit(modelsPerPage)
|
.toList();
|
||||||
.collect(Collectors.toList()); // NOSONAR
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<WinkTvModel> getModelList() throws IOException {
|
|
||||||
if (Duration.between(lastListInfoRequest, Instant.now()).getSeconds() < 30) {
|
|
||||||
return Optional.ofNullable(modelsList).orElse(loadModelList());
|
|
||||||
}
|
|
||||||
modelsList = loadModelList();
|
|
||||||
return Optional.ofNullable(modelsList).orElse(Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<WinkTvModel> loadModelList() throws IOException {
|
private List<WinkTvModel> loadModelList() throws IOException {
|
||||||
LOG.debug("Fetching page {}", API_URL);
|
int offset = (page - 1) * modelsPerPage;
|
||||||
lastListInfoRequest = Instant.now();
|
log.debug("Fetching page {} offset:{}, limit:{}", API_URL, offset, modelsPerPage);
|
||||||
|
|
||||||
|
|
||||||
FormBody body = new FormBody.Builder()
|
FormBody body = new FormBody.Builder()
|
||||||
.add("offset", "0")
|
.add("offset", String.valueOf(offset))
|
||||||
.add("limit", "500")
|
.add("limit", String.valueOf(modelsPerPage))
|
||||||
.add("orderBy", "hot")
|
.add("orderBy", "hot")
|
||||||
|
.add("onlyNewBj", "N")
|
||||||
.build();
|
.build();
|
||||||
Request req = new Request.Builder()
|
Request req = new Request.Builder()
|
||||||
.url(API_URL)
|
.url(API_URL)
|
||||||
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||||
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
|
.header(ACCEPT, "*/*")
|
||||||
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
|
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
|
||||||
.header(X_REQUESTED_WITH, XML_HTTP_REQUEST)
|
|
||||||
.header(REFERER, site.getBaseUrl() + "/")
|
|
||||||
.header(ORIGIN, site.getBaseUrl())
|
|
||||||
.post(body)
|
.post(body)
|
||||||
.build();
|
.build();
|
||||||
try (var response = site.getHttpClient().execute(req)) {
|
try (var response = site.getHttpClient().execute(req)) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
List<WinkTvModel> models = new ArrayList<>();
|
List<WinkTvModel> result = new ArrayList<>();
|
||||||
var content = response.body().string();
|
var content = response.body().string();
|
||||||
var json = new JSONObject(content);
|
var json = new JSONObject(content);
|
||||||
if (json.optBoolean("result")) {
|
if (json.optBoolean("result")) {
|
||||||
var modelNodes = json.getJSONArray("list");
|
var modelNodes = json.getJSONArray("list");
|
||||||
parseModels(modelNodes, models);
|
parseModels(modelNodes, result);
|
||||||
}
|
}
|
||||||
return models;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
throw new HttpException(response.code(), response.message());
|
throw new HttpException(response.code(), response.message());
|
||||||
}
|
}
|
||||||
|
@ -106,7 +87,7 @@ public class WinkTvUpdateService extends PaginatedScheduledService {
|
||||||
for (var i = 0; i < jsonModels.length(); i++) {
|
for (var i = 0; i < jsonModels.length(); i++) {
|
||||||
var m = jsonModels.getJSONObject(i);
|
var m = jsonModels.getJSONObject(i);
|
||||||
String name = m.optString("userId");
|
String name = m.optString("userId");
|
||||||
WinkTvModel model = (WinkTvModel) site.createModel(name);
|
WinkTvModel model = site.createModel(name);
|
||||||
model.setDisplayName(m.getString("userNick"));
|
model.setDisplayName(m.getString("userNick"));
|
||||||
boolean isAdult = m.optBoolean("isAdult");
|
boolean isAdult = m.optBoolean("isAdult");
|
||||||
model.setAdult(isAdult);
|
model.setAdult(isAdult);
|
||||||
|
|
|
@ -10,12 +10,13 @@ import ctbrec.io.HttpException;
|
||||||
import ctbrec.recorder.download.RecordingProcess;
|
import ctbrec.recorder.download.RecordingProcess;
|
||||||
import ctbrec.recorder.download.StreamSource;
|
import ctbrec.recorder.download.StreamSource;
|
||||||
import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload;
|
import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import okhttp3.FormBody;
|
import okhttp3.FormBody;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -25,19 +26,21 @@ import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import static ctbrec.Model.State.*;
|
import static ctbrec.Model.State.*;
|
||||||
import static ctbrec.io.HttpConstants.*;
|
import static ctbrec.io.HttpConstants.*;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class WinkTvModel extends AbstractModel {
|
public class WinkTvModel extends AbstractModel {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(WinkTvModel.class);
|
|
||||||
private int[] resolution = new int[]{0, 0};
|
private int[] resolution = new int[]{0, 0};
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private boolean adult = false;
|
private boolean adult = false;
|
||||||
private JSONObject modelInfo;
|
private transient JSONObject modelInfo;
|
||||||
|
|
||||||
private transient Instant lastInfoRequest = Instant.EPOCH;
|
private transient Instant lastInfoRequest = Instant.EPOCH;
|
||||||
|
|
||||||
|
@ -98,7 +101,7 @@ public class WinkTvModel extends AbstractModel {
|
||||||
src.bandwidth = playlist.getStreamInfo().getBandwidth();
|
src.bandwidth = playlist.getStreamInfo().getBandwidth();
|
||||||
src.height = playlist.getStreamInfo().getResolution().height;
|
src.height = playlist.getStreamInfo().getResolution().height;
|
||||||
src.mediaPlaylistUrl = playlist.getUri();
|
src.mediaPlaylistUrl = playlist.getUri();
|
||||||
LOG.trace("Media playlist {}", src.mediaPlaylistUrl);
|
log.trace("Media playlist {}", src.mediaPlaylistUrl);
|
||||||
sources.add(src);
|
sources.add(src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +109,7 @@ public class WinkTvModel extends AbstractModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private MasterPlaylist getMasterPlaylist(String url) throws IOException, ParseException, PlaylistException {
|
private MasterPlaylist getMasterPlaylist(String url) throws IOException, ParseException, PlaylistException {
|
||||||
LOG.trace("Loading master playlist {}", url);
|
log.trace("Loading master playlist {}", url);
|
||||||
Request req = new Request.Builder()
|
Request req = new Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||||
|
@ -153,7 +156,7 @@ public class WinkTvModel extends AbstractModel {
|
||||||
String hlsUrl = hls.optString("url");
|
String hlsUrl = hls.optString("url");
|
||||||
return hlsUrl;
|
return hlsUrl;
|
||||||
} else {
|
} else {
|
||||||
LOG.debug("Error while get master playlist url for {}: {}", getName(), response.body().string());
|
log.debug("Error while get master playlist url for {}: {}", getName(), response.body().string());
|
||||||
throw new HttpException(response.code(), response.message());
|
throw new HttpException(response.code(), response.message());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,11 +179,10 @@ public class WinkTvModel extends AbstractModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private JSONObject getModelInfo() throws IOException {
|
private JSONObject getModelInfo() throws IOException {
|
||||||
if (Duration.between(lastInfoRequest, Instant.now()).getSeconds() < 5) {
|
if (modelInfo == null || Duration.between(lastInfoRequest, Instant.now()).getSeconds() < 5) {
|
||||||
return Optional.ofNullable(modelInfo).orElse(new JSONObject());
|
|
||||||
}
|
|
||||||
lastInfoRequest = Instant.now();
|
lastInfoRequest = Instant.now();
|
||||||
modelInfo = loadModelInfo();
|
modelInfo = loadModelInfo();
|
||||||
|
}
|
||||||
return modelInfo;
|
return modelInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,19 +212,6 @@ public class WinkTvModel extends AbstractModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPreviewURL() throws IOException {
|
|
||||||
JSONObject json = getModelInfo();
|
|
||||||
if (json.has("media")) {
|
|
||||||
JSONObject media = json.getJSONObject("media");
|
|
||||||
return media.optString("ivsThumbnail");
|
|
||||||
}
|
|
||||||
if (json.has("bjInfo")) {
|
|
||||||
JSONObject info = json.getJSONObject("bjInfo");
|
|
||||||
return info.optString("thumbUrl");
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean follow() throws IOException {
|
public boolean follow() throws IOException {
|
||||||
return false;
|
return false;
|
||||||
|
@ -233,14 +222,6 @@ public class WinkTvModel extends AbstractModel {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAdult() {
|
|
||||||
return adult;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAdult(boolean a) {
|
|
||||||
this.adult = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void receiveTip(Double tokens) throws IOException {
|
public void receiveTip(Double tokens) throws IOException {
|
||||||
// not implemented
|
// not implemented
|
||||||
|
|
Loading…
Reference in New Issue