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.
* Add setting to disable tab dragging, because that might be the cause for tab
freezes
* Fix thumbnails for Stripchat
* Fix Stripchat recordings
* Fix Stripchat thumbsnails
4.7.5
========================

View File

@ -1,38 +1,42 @@
package ctbrec.sites.stripchat;
import static ctbrec.Model.State.*;
import static ctbrec.io.HttpConstants.*;
import static ctbrec.sites.stripchat.StripchatHttpClient.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import javax.xml.bind.JAXBException;
import com.iheartradio.m3u8.*;
import com.iheartradio.m3u8.data.MasterPlaylist;
import com.iheartradio.m3u8.data.Playlist;
import com.iheartradio.m3u8.data.PlaylistData;
import ctbrec.AbstractModel;
import ctbrec.Config;
import ctbrec.io.HttpException;
import ctbrec.recorder.download.StreamSource;
import ctbrec.sites.Site;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.PlaylistException;
import java.io.ByteArrayInputStream;
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 ctbrec.Config;
import ctbrec.io.HttpException;
import ctbrec.recorder.download.StreamSource;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import static ctbrec.Model.State.*;
import static ctbrec.io.HttpConstants.*;
import static ctbrec.sites.stripchat.StripchatHttpClient.JSON;
import static java.nio.charset.StandardCharsets.UTF_8;
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 int[] resolution = new int[] {0, 0};
private int[] resolution = new int[]{0, 0};
private static StripchatConfig stripchatConfig;
@Override
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
@ -49,25 +53,14 @@ public class StripchatModel extends AbstractModel {
private void mapOnlineState(String status) {
switch (status) {
case "public":
setOnlineState(ONLINE);
break;
case "idle":
setOnlineState(AWAY);
break;
case "private":
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;
case "public" -> setOnlineState(ONLINE);
case "idle" -> setOnlineState(AWAY);
case "private", "p2p", "groupShow", "virtualPrivate" -> setOnlineState(PRIVATE);
case "off" -> setOnlineState(OFFLINE);
default -> {
LOG.debug("Unknown online state {} for model {}", status, getName());
setOnlineState(OFFLINE);
}
}
}
@ -93,7 +86,49 @@ public class StripchatModel extends AbstractModel {
}
@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 url = getSite().getBaseUrl() + "/api/front/models/username/" + name + "/cam?triggerRequest=loadCam";
Request req = new Request.Builder()
@ -110,29 +145,35 @@ public class StripchatModel extends AbstractModel {
String streamName = jsonResponse.optString("streamName", jsonResponse.optString(""));
JSONObject viewServers = jsonResponse.getJSONObject("viewServers");
String serverName = viewServers.optString("flashphoner-hls");
JSONObject broadcastSettings = jsonResponse.getJSONObject("broadcastSettings");
List<StreamSource> sources = new ArrayList<>();
StreamSource best = new StreamSource();
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");
Object defaultObject = presets.get("testing");
if (defaultObject instanceof JSONObject) {
JSONObject defaults = (JSONObject) defaultObject;
JSONArray heights = defaults.names();
for (int i = 0; i < heights.length(); i++) {
String h = heights.getString(i);
StreamSource streamSource = new StreamSource();
streamSource.height = Integer.parseInt(h.replaceAll("[^\\d]", ""));
streamSource.width = streamSource.height * best.getWidth() / best.getHeight();
String source = streamName + '_' + streamSource.height + 'p';
streamSource.mediaPlaylistUrl = "https://b-" + serverName + ".stripst.com/hls/" + source + '/' + source + ".m3u8";
sources.add(streamSource);
}
}
return sources.stream().sorted((a, b) -> a.height - b.height).collect(Collectors.toList());
return "https://b-" + serverName + '.' + stripchatConfig.hlsStreamHost + "/hls/" + streamName + "/master/" + streamName + "_auto.m3u8";
} else {
throw new HttpException(response.code(), response.message());
}
}
}
private static synchronized void loadStripchatConfig(Site site) throws IOException {
if (stripchatConfig != null) {
return;
}
Request req = new Request.Builder()
.url(site.getBaseUrl() + "/api/front/v2/config?uniq=g8wizmarpvck4dj5")
.header(ACCEPT, "*/*")
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
.header(X_REQUESTED_WITH, XML_HTTP_REQUEST)
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.header(REFERER, site.getBaseUrl())
.build();
LOG.debug("Loading config from {}", req.url());
try (Response response = site.getHttpClient().execute(req)) {
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 {
throw new HttpException(response.code(), response.message());
}
@ -143,7 +184,7 @@ public class StripchatModel extends AbstractModel {
@Override
public void invalidateCacheEntries() {
status = null;
resolution = new int[] { 0, 0 };
resolution = new int[]{0, 0};
}
@Override
@ -158,9 +199,9 @@ public class StripchatModel extends AbstractModel {
List<StreamSource> sources = getStreamSources();
if (!sources.isEmpty()) {
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);
}
}
@ -233,4 +274,8 @@ public class StripchatModel extends AbstractModel {
}
}
}
private static class StripchatConfig {
String hlsStreamHost;
}
}