forked from j62/ctbrec
Fix construction of Stripchat streaming URLs
This commit is contained in:
parent
14f2626492
commit
0c4cdfb795
|
@ -4,7 +4,8 @@
|
||||||
* Save coonfig in a sub-directory for each version.
|
* Save coonfig in a sub-directory for each version.
|
||||||
* Add setting to disable tab dragging, because that might be the cause for tab
|
* Add setting to disable tab dragging, because that might be the cause for tab
|
||||||
freezes
|
freezes
|
||||||
* Fix thumbnails for Stripchat
|
* Fix Stripchat recordings
|
||||||
|
* Fix Stripchat thumbsnails
|
||||||
|
|
||||||
4.7.5
|
4.7.5
|
||||||
========================
|
========================
|
||||||
|
|
|
@ -1,38 +1,42 @@
|
||||||
package ctbrec.sites.stripchat;
|
package ctbrec.sites.stripchat;
|
||||||
|
|
||||||
import static ctbrec.Model.State.*;
|
import com.iheartradio.m3u8.*;
|
||||||
import static ctbrec.io.HttpConstants.*;
|
import com.iheartradio.m3u8.data.MasterPlaylist;
|
||||||
import static ctbrec.sites.stripchat.StripchatHttpClient.*;
|
import com.iheartradio.m3u8.data.Playlist;
|
||||||
|
import com.iheartradio.m3u8.data.PlaylistData;
|
||||||
import java.io.IOException;
|
import ctbrec.AbstractModel;
|
||||||
import java.util.ArrayList;
|
import ctbrec.Config;
|
||||||
import java.util.List;
|
import ctbrec.io.HttpException;
|
||||||
import java.util.Locale;
|
import ctbrec.recorder.download.StreamSource;
|
||||||
import java.util.concurrent.ExecutionException;
|
import ctbrec.sites.Site;
|
||||||
import java.util.stream.Collectors;
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
import javax.xml.bind.JAXBException;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.iheartradio.m3u8.ParseException;
|
import java.io.ByteArrayInputStream;
|
||||||
import com.iheartradio.m3u8.PlaylistException;
|
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 ctbrec.AbstractModel;
|
import static ctbrec.Model.State.*;
|
||||||
import ctbrec.Config;
|
import static ctbrec.io.HttpConstants.*;
|
||||||
import ctbrec.io.HttpException;
|
import static ctbrec.sites.stripchat.StripchatHttpClient.JSON;
|
||||||
import ctbrec.recorder.download.StreamSource;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
import okhttp3.Response;
|
|
||||||
|
|
||||||
public class StripchatModel extends AbstractModel {
|
public class StripchatModel extends AbstractModel {
|
||||||
private static final transient Logger LOG = LoggerFactory.getLogger(StripchatModel.class);
|
private static final Logger LOG = LoggerFactory.getLogger(StripchatModel.class);
|
||||||
private String status = null;
|
private String status = null;
|
||||||
private int[] resolution = new int[] {0, 0};
|
private int[] resolution = new int[]{0, 0};
|
||||||
|
|
||||||
|
private static StripchatConfig stripchatConfig;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
|
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
|
||||||
|
@ -49,25 +53,14 @@ public class StripchatModel extends AbstractModel {
|
||||||
|
|
||||||
private void mapOnlineState(String status) {
|
private void mapOnlineState(String status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case "public":
|
case "public" -> setOnlineState(ONLINE);
|
||||||
setOnlineState(ONLINE);
|
case "idle" -> setOnlineState(AWAY);
|
||||||
break;
|
case "private", "p2p", "groupShow", "virtualPrivate" -> setOnlineState(PRIVATE);
|
||||||
case "idle":
|
case "off" -> setOnlineState(OFFLINE);
|
||||||
setOnlineState(AWAY);
|
default -> {
|
||||||
break;
|
LOG.debug("Unknown online state {} for model {}", status, getName());
|
||||||
case "private":
|
setOnlineState(OFFLINE);
|
||||||
case "p2p":
|
}
|
||||||
case "groupShow":
|
|
||||||
case "virtualPrivate":
|
|
||||||
setOnlineState(PRIVATE);
|
|
||||||
break;
|
|
||||||
case "off":
|
|
||||||
setOnlineState(OFFLINE);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG.debug("Unknown online state {} for model {}", status, getName());
|
|
||||||
setOnlineState(OFFLINE);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +86,49 @@ public class StripchatModel extends AbstractModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException, JAXBException {
|
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException {
|
||||||
|
MasterPlaylist masterPlaylist = getMasterPlaylist();
|
||||||
|
List<StreamSource> sources = new ArrayList<>();
|
||||||
|
for (PlaylistData playlist : masterPlaylist.getPlaylists()) {
|
||||||
|
if (playlist.hasStreamInfo()) {
|
||||||
|
StreamSource src = new StreamSource();
|
||||||
|
src.bandwidth = playlist.getStreamInfo().getBandwidth();
|
||||||
|
src.height = playlist.getStreamInfo().getResolution().height;
|
||||||
|
src.mediaPlaylistUrl = playlist.getUri();
|
||||||
|
if (src.mediaPlaylistUrl.contains("?")) {
|
||||||
|
src.mediaPlaylistUrl = src.mediaPlaylistUrl.substring(0, src.mediaPlaylistUrl.lastIndexOf('?'));
|
||||||
|
}
|
||||||
|
LOG.trace("Media playlist {}", src.mediaPlaylistUrl);
|
||||||
|
sources.add(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MasterPlaylist getMasterPlaylist() throws IOException, ParseException, PlaylistException {
|
||||||
|
String url = getMasterPlaylistUrl();
|
||||||
|
LOG.trace("Loading master playlist {}", url);
|
||||||
|
Request req = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||||
|
.build();
|
||||||
|
try (Response response = getSite().getHttpClient().execute(req)) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
String body = response.body().string();
|
||||||
|
LOG.trace(body);
|
||||||
|
InputStream inputStream = new ByteArrayInputStream(body.getBytes(UTF_8));
|
||||||
|
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8, ParsingMode.LENIENT);
|
||||||
|
Playlist playlist = parser.parse();
|
||||||
|
MasterPlaylist master = playlist.getMasterPlaylist();
|
||||||
|
return master;
|
||||||
|
} else {
|
||||||
|
throw new HttpException(response.code(), response.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMasterPlaylistUrl() throws IOException {
|
||||||
|
loadStripchatConfig(getSite());
|
||||||
String name = getName();
|
String name = getName();
|
||||||
String url = getSite().getBaseUrl() + "/api/front/models/username/" + name + "/cam?triggerRequest=loadCam";
|
String url = getSite().getBaseUrl() + "/api/front/models/username/" + name + "/cam?triggerRequest=loadCam";
|
||||||
Request req = new Request.Builder()
|
Request req = new Request.Builder()
|
||||||
|
@ -110,29 +145,35 @@ public class StripchatModel extends AbstractModel {
|
||||||
String streamName = jsonResponse.optString("streamName", jsonResponse.optString(""));
|
String streamName = jsonResponse.optString("streamName", jsonResponse.optString(""));
|
||||||
JSONObject viewServers = jsonResponse.getJSONObject("viewServers");
|
JSONObject viewServers = jsonResponse.getJSONObject("viewServers");
|
||||||
String serverName = viewServers.optString("flashphoner-hls");
|
String serverName = viewServers.optString("flashphoner-hls");
|
||||||
JSONObject broadcastSettings = jsonResponse.getJSONObject("broadcastSettings");
|
return "https://b-" + serverName + '.' + stripchatConfig.hlsStreamHost + "/hls/" + streamName + "/master/" + streamName + "_auto.m3u8";
|
||||||
List<StreamSource> sources = new ArrayList<>();
|
} else {
|
||||||
StreamSource best = new StreamSource();
|
throw new HttpException(response.code(), response.message());
|
||||||
best.height = broadcastSettings.optInt("height");
|
}
|
||||||
best.width = broadcastSettings.optInt("width");
|
}
|
||||||
best.mediaPlaylistUrl = "https://b-" + serverName + ".stripst.com/hls/" + streamName + '/' + streamName + ".m3u8";
|
}
|
||||||
sources.add(best);
|
|
||||||
JSONObject presets = broadcastSettings.optJSONObject("presets");
|
private static synchronized void loadStripchatConfig(Site site) throws IOException {
|
||||||
Object defaultObject = presets.get("testing");
|
if (stripchatConfig != null) {
|
||||||
if (defaultObject instanceof JSONObject) {
|
return;
|
||||||
JSONObject defaults = (JSONObject) defaultObject;
|
}
|
||||||
JSONArray heights = defaults.names();
|
Request req = new Request.Builder()
|
||||||
for (int i = 0; i < heights.length(); i++) {
|
.url(site.getBaseUrl() + "/api/front/v2/config?uniq=g8wizmarpvck4dj5")
|
||||||
String h = heights.getString(i);
|
.header(ACCEPT, "*/*")
|
||||||
StreamSource streamSource = new StreamSource();
|
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
|
||||||
streamSource.height = Integer.parseInt(h.replaceAll("[^\\d]", ""));
|
.header(X_REQUESTED_WITH, XML_HTTP_REQUEST)
|
||||||
streamSource.width = streamSource.height * best.getWidth() / best.getHeight();
|
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||||
String source = streamName + '_' + streamSource.height + 'p';
|
.header(REFERER, site.getBaseUrl())
|
||||||
streamSource.mediaPlaylistUrl = "https://b-" + serverName + ".stripst.com/hls/" + source + '/' + source + ".m3u8";
|
.build();
|
||||||
sources.add(streamSource);
|
|
||||||
}
|
LOG.debug("Loading config from {}", req.url());
|
||||||
}
|
try (Response response = site.getHttpClient().execute(req)) {
|
||||||
return sources.stream().sorted((a, b) -> a.height - b.height).collect(Collectors.toList());
|
if (response.isSuccessful()) {
|
||||||
|
JSONObject json = new JSONObject(Objects.requireNonNull(response.body(), "HTTP response body is null").string());
|
||||||
|
LOG.trace(json.toString(2));
|
||||||
|
JSONObject config = json.getJSONObject("config");
|
||||||
|
String hlsStreamHost = config.getString("hlsStreamHost");
|
||||||
|
stripchatConfig = new StripchatConfig();
|
||||||
|
stripchatConfig.hlsStreamHost = hlsStreamHost;
|
||||||
} else {
|
} else {
|
||||||
throw new HttpException(response.code(), response.message());
|
throw new HttpException(response.code(), response.message());
|
||||||
}
|
}
|
||||||
|
@ -143,7 +184,7 @@ public class StripchatModel extends AbstractModel {
|
||||||
@Override
|
@Override
|
||||||
public void invalidateCacheEntries() {
|
public void invalidateCacheEntries() {
|
||||||
status = null;
|
status = null;
|
||||||
resolution = new int[] { 0, 0 };
|
resolution = new int[]{0, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -158,9 +199,9 @@ public class StripchatModel extends AbstractModel {
|
||||||
List<StreamSource> sources = getStreamSources();
|
List<StreamSource> sources = getStreamSources();
|
||||||
if (!sources.isEmpty()) {
|
if (!sources.isEmpty()) {
|
||||||
StreamSource best = sources.get(sources.size() - 1);
|
StreamSource best = sources.get(sources.size() - 1);
|
||||||
resolution = new int[] { best.getWidth(), best.getHeight() };
|
resolution = new int[]{best.getWidth(), best.getHeight()};
|
||||||
}
|
}
|
||||||
} catch (IOException | ParseException | PlaylistException | JAXBException e) {
|
} catch (IOException | ParseException | PlaylistException e) {
|
||||||
throw new ExecutionException(e);
|
throw new ExecutionException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,4 +274,8 @@ public class StripchatModel extends AbstractModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class StripchatConfig {
|
||||||
|
String hlsStreamHost;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue