Fix WinkTV browsing

This commit is contained in:
0xb00bface 2023-11-05 13:53:30 +01:00
parent 03dd723fb6
commit 970b2ab14e
3 changed files with 48 additions and 86 deletions

View File

@ -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;
} }

View File

@ -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);

View File

@ -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