forked from j62/ctbrec
1
0
Fork 0

Fix construction of Stripchat streaming URLs

This commit is contained in:
0xb00bface 2022-05-07 17:33:54 +02:00
parent 14f2626492
commit 0c4cdfb795
2 changed files with 117 additions and 71 deletions

View File

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

View File

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