forked from j62/ctbrec
First working version of Showup.tv
This commit is contained in:
parent
57125b4820
commit
6c85a2a493
|
@ -46,6 +46,7 @@ import ctbrec.sites.fc2live.Fc2Live;
|
|||
import ctbrec.sites.flirt4free.Flirt4Free;
|
||||
import ctbrec.sites.jasmin.LiveJasmin;
|
||||
import ctbrec.sites.mfc.MyFreeCams;
|
||||
import ctbrec.sites.showup.Showup;
|
||||
import ctbrec.sites.streamate.Streamate;
|
||||
import ctbrec.sites.stripchat.Stripchat;
|
||||
import ctbrec.ui.news.NewsTab;
|
||||
|
@ -98,6 +99,7 @@ public class CamrecApplication extends Application {
|
|||
sites.add(new Flirt4Free());
|
||||
sites.add(new LiveJasmin());
|
||||
sites.add(new MyFreeCams());
|
||||
sites.add(new Showup());
|
||||
sites.add(new Streamate());
|
||||
sites.add(new Stripchat());
|
||||
loadConfig();
|
||||
|
|
|
@ -9,6 +9,7 @@ import ctbrec.sites.fc2live.Fc2Live;
|
|||
import ctbrec.sites.flirt4free.Flirt4Free;
|
||||
import ctbrec.sites.jasmin.LiveJasmin;
|
||||
import ctbrec.sites.mfc.MyFreeCams;
|
||||
import ctbrec.sites.showup.Showup;
|
||||
import ctbrec.sites.streamate.Streamate;
|
||||
import ctbrec.sites.stripchat.Stripchat;
|
||||
import ctbrec.ui.sites.bonga.BongaCamsSiteUi;
|
||||
|
@ -19,6 +20,7 @@ import ctbrec.ui.sites.fc2live.Fc2LiveSiteUi;
|
|||
import ctbrec.ui.sites.flirt4free.Flirt4FreeSiteUi;
|
||||
import ctbrec.ui.sites.jasmin.LiveJasminSiteUi;
|
||||
import ctbrec.ui.sites.myfreecams.MyFreeCamsSiteUi;
|
||||
import ctbrec.ui.sites.showup.ShowupSiteUi;
|
||||
import ctbrec.ui.sites.streamate.StreamateSiteUi;
|
||||
import ctbrec.ui.sites.stripchat.StripchatSiteUi;
|
||||
|
||||
|
@ -32,6 +34,7 @@ public class SiteUiFactory {
|
|||
private static Flirt4FreeSiteUi flirt4FreeSiteUi;
|
||||
private static LiveJasminSiteUi jasminSiteUi;
|
||||
private static MyFreeCamsSiteUi mfcSiteUi;
|
||||
private static ShowupSiteUi showupSiteUi;
|
||||
private static StreamateSiteUi streamateSiteUi;
|
||||
private static StripchatSiteUi stripchatSiteUi;
|
||||
|
||||
|
@ -73,6 +76,11 @@ public class SiteUiFactory {
|
|||
mfcSiteUi = new MyFreeCamsSiteUi((MyFreeCams) site);
|
||||
}
|
||||
return mfcSiteUi;
|
||||
} else if (site instanceof Showup) {
|
||||
if (showupSiteUi == null) {
|
||||
showupSiteUi = new ShowupSiteUi((Showup) site);
|
||||
}
|
||||
return showupSiteUi;
|
||||
} else if (site instanceof Streamate) {
|
||||
if (streamateSiteUi == null) {
|
||||
streamateSiteUi = new StreamateSiteUi((Streamate) site);
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package ctbrec.ui.sites.showup;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ctbrec.sites.ConfigUI;
|
||||
import ctbrec.sites.showup.Showup;
|
||||
import ctbrec.ui.sites.AbstractSiteUi;
|
||||
import ctbrec.ui.tabs.TabProvider;
|
||||
|
||||
public class ShowupSiteUi extends AbstractSiteUi {
|
||||
|
||||
private Showup site;
|
||||
|
||||
public ShowupSiteUi(Showup site) {
|
||||
this.site = site;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabProvider getTabProvider() {
|
||||
return new ShowupTabProvider(site);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigUI getConfigUI() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package ctbrec.ui.sites.showup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ctbrec.sites.showup.Showup;
|
||||
import ctbrec.ui.tabs.TabProvider;
|
||||
import ctbrec.ui.tabs.ThumbOverviewTab;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Tab;
|
||||
|
||||
public class ShowupTabProvider extends TabProvider {
|
||||
|
||||
private Showup site;
|
||||
|
||||
public ShowupTabProvider(Showup site) {
|
||||
this.site = site;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tab> getTabs(Scene scene) {
|
||||
List<Tab> tabs = new ArrayList<>();
|
||||
tabs.add(createTab("Women", "female"));
|
||||
tabs.add(createTab("Men", "male"));
|
||||
tabs.add(createTab("All", "all"));
|
||||
return tabs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tab getFollowedTab() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Tab createTab(String title, String category) {
|
||||
ShowupUpdateService updateService = new ShowupUpdateService(site, category);
|
||||
ThumbOverviewTab tab = new ThumbOverviewTab(title, updateService, site);
|
||||
tab.setRecorder(site.getRecorder());
|
||||
return tab;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package ctbrec.ui.sites.showup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ctbrec.Model;
|
||||
import ctbrec.sites.showup.Showup;
|
||||
import ctbrec.sites.showup.ShowupHttpClient;
|
||||
import ctbrec.ui.tabs.PaginatedScheduledService;
|
||||
import javafx.concurrent.Task;
|
||||
|
||||
public class ShowupUpdateService extends PaginatedScheduledService {
|
||||
|
||||
private Showup showup;
|
||||
private String category;
|
||||
|
||||
public ShowupUpdateService(Showup showup, String category) {
|
||||
this.showup = showup;
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task<List<Model>> createTask() {
|
||||
return new Task<List<Model>>() {
|
||||
@Override
|
||||
public List<Model> call() throws IOException {
|
||||
ShowupHttpClient httpClient = (ShowupHttpClient) showup.getHttpClient();
|
||||
httpClient.setCookie("category", category);
|
||||
return showup.getModelList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package ctbrec;
|
||||
|
||||
public class ModelNotFoundException extends RuntimeException {
|
||||
|
||||
public ModelNotFoundException(String modelName) {
|
||||
super(modelName);
|
||||
}
|
||||
|
||||
public ModelNotFoundException(String modelName, Exception e) {
|
||||
super(modelName, e);
|
||||
}
|
||||
}
|
|
@ -37,9 +37,13 @@ public class PlaylistGenerator {
|
|||
private List<ProgressListener> listeners = new ArrayList<>();
|
||||
|
||||
public File generate(File directory) throws IOException, ParseException, PlaylistException {
|
||||
return generate(directory, "ts");
|
||||
}
|
||||
|
||||
public File generate(File directory, String fileSuffix) throws IOException, ParseException, PlaylistException {
|
||||
LOG.info("Starting playlist generation for {}", directory);
|
||||
// get a list of all ts files and sort them by sequence
|
||||
File[] files = directory.listFiles(f -> f.getName().endsWith(".ts"));
|
||||
File[] files = directory.listFiles(f -> f.getName().endsWith('.' + fileSuffix));
|
||||
if (files == null || files.length == 0) {
|
||||
LOG.debug("{} is empty. Not going to generate a playlist", directory);
|
||||
throw new InvalidPlaylistException("Directory is empty");
|
||||
|
@ -57,15 +61,18 @@ public class PlaylistGenerator {
|
|||
int done = 0;
|
||||
for (File file : files) {
|
||||
try {
|
||||
float duration = (float) MpegUtil.getFileDuration(file);
|
||||
if (duration <= 0) {
|
||||
throw new InvalidTrackLengthException("Track has negative duration: " + file.getName());
|
||||
} else {
|
||||
track.add(new TrackData.Builder()
|
||||
.withUri(file.getName())
|
||||
.withTrackInfo(new TrackInfo(duration, file.getName()))
|
||||
.build());
|
||||
float duration = 0;
|
||||
if (file.getName().toLowerCase().endsWith(".ts")) {
|
||||
duration = (float) MpegUtil.getFileDuration(file);
|
||||
if (duration <= 0) {
|
||||
throw new InvalidTrackLengthException("Track has negative duration: " + file.getName());
|
||||
}
|
||||
}
|
||||
|
||||
track.add(new TrackData.Builder()
|
||||
.withUri(file.getName())
|
||||
.withTrackInfo(new TrackInfo(duration, file.getName()))
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Couldn't determine duration for {}. Skipping this file.", file.getName());
|
||||
File corruptedFile = new File(directory, file.getName() + ".corrupt");
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.io.FileNotFoundException;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
|
@ -60,7 +61,7 @@ public class HlsDownload extends AbstractHlsDownload {
|
|||
private NumberFormat nf = new DecimalFormat("000000");
|
||||
private transient AtomicBoolean downloadFinished = new AtomicBoolean(false);
|
||||
private ZonedDateTime splitRecStartTime;
|
||||
private transient Config config;
|
||||
protected transient Config config;
|
||||
|
||||
public HlsDownload(HttpClient client) {
|
||||
super(client);
|
||||
|
@ -270,6 +271,7 @@ public class HlsDownload extends AbstractHlsDownload {
|
|||
@Override
|
||||
void internalStop() {
|
||||
running = false;
|
||||
downloadThreadPool.shutdownNow();
|
||||
}
|
||||
|
||||
private static class SegmentDownload implements Callable<Boolean> {
|
||||
|
@ -289,7 +291,7 @@ public class HlsDownload extends AbstractHlsDownload {
|
|||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
LOG.trace("Downloading segment {} to {}", url, file);
|
||||
for (int tries = 1; tries <= 3; tries++) {
|
||||
for (int tries = 1; tries <= 3 && !Thread.currentThread().isInterrupted(); tries++) {
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||
|
@ -304,7 +306,7 @@ public class HlsDownload extends AbstractHlsDownload {
|
|||
}
|
||||
byte[] b = new byte[1024 * 100];
|
||||
int length = -1;
|
||||
while( (length = in.read(b)) >= 0 ) {
|
||||
while( (length = in.read(b)) >= 0 && !Thread.currentThread().isInterrupted()) {
|
||||
fos.write(b, 0, length);
|
||||
}
|
||||
return true;
|
||||
|
@ -312,7 +314,10 @@ public class HlsDownload extends AbstractHlsDownload {
|
|||
} catch(FileNotFoundException e) {
|
||||
LOG.debug("Segment does not exist {}", url.getFile());
|
||||
break;
|
||||
} catch (InterruptedIOException e) {
|
||||
break;
|
||||
} catch(Exception e) {
|
||||
LOG.error("Error", e);
|
||||
if (tries == 3) {
|
||||
LOG.warn("Error while downloading segment. Segment {} finally failed: {}", file.toFile().getName(), e.getMessage());
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ctbrec.recorder.download.hls;
|
||||
|
||||
import static ctbrec.io.HttpConstants.*;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
@ -50,7 +52,7 @@ public class MergedFfmpegHlsDownload extends AbstractHlsDownload {
|
|||
private transient Config config;
|
||||
private transient Process ffmpeg;
|
||||
private transient OutputStream ffmpegStdIn;
|
||||
private transient Thread ffmpegThread;
|
||||
protected transient Thread ffmpegThread;
|
||||
private transient Object ffmpegStartMonitor = new Object();
|
||||
|
||||
public MergedFfmpegHlsDownload(HttpClient client) {
|
||||
|
@ -178,7 +180,7 @@ public class MergedFfmpegHlsDownload extends AbstractHlsDownload {
|
|||
ffmpegThread.start();
|
||||
}
|
||||
|
||||
private void downloadSegments(String segmentPlaylistUri, boolean livestreamDownload) throws IOException, ParseException, PlaylistException {
|
||||
protected void downloadSegments(String segmentPlaylistUri, boolean livestreamDownload) throws IOException, ParseException, PlaylistException {
|
||||
int lastSegment = 0;
|
||||
int nextSegment = 0;
|
||||
while (running) {
|
||||
|
@ -307,17 +309,21 @@ public class MergedFfmpegHlsDownload extends AbstractHlsDownload {
|
|||
}
|
||||
}
|
||||
|
||||
private void writeSegment(byte[] segmentData) throws IOException {
|
||||
protected void writeSegment(byte[] segmentData, int offset, int length) throws IOException {
|
||||
if (running) {
|
||||
if (ffmpegStdIn != null) {
|
||||
ffmpegStdIn.write(segmentData);
|
||||
ffmpegStdIn.write(segmentData, offset, length);
|
||||
} else {
|
||||
LOG.error("FFmpeg stdin stream is null - skipping writing of segment");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean splitRecording() {
|
||||
private void writeSegment(byte[] segmentData) throws IOException {
|
||||
writeSegment(segmentData, 0, segmentData.length);
|
||||
}
|
||||
|
||||
protected boolean splitRecording() {
|
||||
if (config.getSettings().splitRecordings > 0) {
|
||||
Duration recordingDuration = Duration.between(splitRecStartTime, ZonedDateTime.now());
|
||||
long seconds = recordingDuration.getSeconds();
|
||||
|
@ -406,7 +412,10 @@ public class MergedFfmpegHlsDownload extends AbstractHlsDownload {
|
|||
LOG.trace("Downloading segment {}", url.getFile());
|
||||
int maxTries = 3;
|
||||
for (int i = 1; i <= maxTries && running; i++) {
|
||||
Request request = new Request.Builder().url(url).addHeader("connection", "keep-alive").build();
|
||||
Request request = new Request.Builder().url(url)
|
||||
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||
.header(CONNECTION, KEEP_ALIVE)
|
||||
.build();
|
||||
try (Response response = client.execute(request)) {
|
||||
if (response.isSuccessful()) {
|
||||
byte[] segment = response.body().bytes();
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
package ctbrec.sites.showup;
|
||||
|
||||
import static ctbrec.Model.State.*;
|
||||
import static ctbrec.io.HttpConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.ModelNotFoundException;
|
||||
import ctbrec.UnknownModel;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.io.HttpException;
|
||||
import ctbrec.sites.AbstractSite;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class Showup extends AbstractSite {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(Showup.class);
|
||||
private ShowupHttpClient httpClient;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Showup.tv";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseUrl() {
|
||||
return "https://showup.tv";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAffiliateLink() {
|
||||
return getBaseUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Model createModel(String name) {
|
||||
try {
|
||||
for (Model m : getModelList()) {
|
||||
if (Objects.equal(m.getName(), name)) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ModelNotFoundException(name, e);
|
||||
}
|
||||
throw new ModelNotFoundException(name);
|
||||
}
|
||||
|
||||
public List<Model> getModelList() throws IOException {
|
||||
String url = getBaseUrl() + "/site/get_stream_list/big";
|
||||
Request req = new Request.Builder()
|
||||
.url(url)
|
||||
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||
.build();
|
||||
|
||||
try (Response response = getHttpClient().execute(req)) {
|
||||
if (response.isSuccessful()) {
|
||||
String body = response.body().string();
|
||||
LOG.trace(body);
|
||||
JSONObject json = new JSONObject(body);
|
||||
List<Model> models = new ArrayList<>();
|
||||
JSONArray list = json.getJSONArray("list");
|
||||
for (int i = 0; i < list.length(); i++) {
|
||||
JSONObject entry = list.getJSONObject(i);
|
||||
ShowupModel model = new ShowupModel();
|
||||
model.setUid(entry.getLong("uid"));
|
||||
model.setPreview(getBaseUrl() + "/files/" + entry.optString("big_img") + ".jpg");
|
||||
model.setDescription(entry.optString("description"));
|
||||
model.setName(entry.optString("username"));
|
||||
model.setUrl(getBaseUrl() + '/' + model.getName());
|
||||
if(entry.optInt("is_group") == 1) {
|
||||
model.setOnlineState(GROUP);
|
||||
} else if(entry.optInt("is_prv") == 1) {
|
||||
model.setOnlineState(PRIVATE);
|
||||
} else {
|
||||
model.setOnlineState(ONLINE);
|
||||
}
|
||||
model.setStreamId(entry.optString("stream_id"));
|
||||
model.setStreamTranscoderAddr(entry.optString("stream_transcoder_addr"));
|
||||
model.setSite(this);
|
||||
models.add(model);
|
||||
}
|
||||
return models;
|
||||
} else {
|
||||
throw new HttpException(response.code(), response.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getTokenBalance() throws IOException {
|
||||
return 0d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuyTokensLink() {
|
||||
return getBaseUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClient getHttpClient() {
|
||||
if (httpClient == null) {
|
||||
httpClient = new ShowupHttpClient();
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws IOException {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (httpClient != null) {
|
||||
httpClient.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsTips() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFollow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSiteForModel(Model m) {
|
||||
return m instanceof ShowupModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean credentialsAvailable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Model createModelFromUrl(String url) {
|
||||
Matcher matcher = Pattern.compile(getBaseUrl() + "(?:/profile)?/(.*)").matcher(url);
|
||||
if (matcher.find()) {
|
||||
return createModel(matcher.group(1));
|
||||
} else {
|
||||
return new UnknownModel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package ctbrec.sites.showup;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import com.iheartradio.m3u8.ParseException;
|
||||
import com.iheartradio.m3u8.PlaylistException;
|
||||
|
||||
import ctbrec.Recording;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.recorder.PlaylistGenerator;
|
||||
import ctbrec.recorder.download.hls.HlsDownload;
|
||||
|
||||
public class ShowupDownload extends HlsDownload {
|
||||
|
||||
public ShowupDownload(HttpClient client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File generatePlaylist(Recording recording) throws IOException, ParseException, PlaylistException {
|
||||
File recDir = recording.getAbsoluteFile();
|
||||
if (!config.getSettings().generatePlaylist) {
|
||||
return null;
|
||||
}
|
||||
PlaylistGenerator playlistGenerator = new PlaylistGenerator();
|
||||
playlistGenerator.addProgressListener(recording::setProgress);
|
||||
File playlist = playlistGenerator.generate(recDir, "mp4");
|
||||
recording.setProgress(-1);
|
||||
return playlist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getLength() {
|
||||
return Duration.between(getStartTime(), Instant.now());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package ctbrec.sites.showup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import ctbrec.io.HttpClient;
|
||||
import okhttp3.Cookie;
|
||||
|
||||
public class ShowupHttpClient extends HttpClient {
|
||||
|
||||
protected ShowupHttpClient() {
|
||||
super("showup");
|
||||
setCookie("accept_rules", "true");
|
||||
setCookie("category", "all");
|
||||
}
|
||||
|
||||
public void setCookie(String name, String value) {
|
||||
Cookie cookie = new Cookie.Builder().domain("showup.tv").name(name).value(value).build();
|
||||
|
||||
Map<String, List<Cookie>> cookies = cookieJar.getCookies();
|
||||
List<Cookie> cookiesForDomain = cookies.computeIfAbsent(cookie.domain(), k -> new ArrayList<Cookie>());
|
||||
for (Iterator<Cookie> iterator = cookiesForDomain.iterator(); iterator.hasNext();) {
|
||||
Cookie existingCookie = iterator.next();
|
||||
if (Objects.equal(existingCookie.name(), cookie.name())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
cookiesForDomain.add(cookie);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package ctbrec.sites.showup;
|
||||
|
||||
import static ctbrec.io.HttpConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.iheartradio.m3u8.ParseException;
|
||||
import com.iheartradio.m3u8.PlaylistException;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.io.HttpClient;
|
||||
import ctbrec.io.HttpException;
|
||||
import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class ShowupMergedDownload extends MergedFfmpegHlsDownload {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ShowupMergedDownload.class);
|
||||
|
||||
public ShowupMergedDownload(HttpClient client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void downloadSegments(String segmentPlaylistUri, boolean livestreamDownload) throws IOException, ParseException, PlaylistException {
|
||||
try {
|
||||
SegmentPlaylist lsp = getNextSegments(segmentPlaylistUri);
|
||||
emptyPlaylistCheck(lsp);
|
||||
|
||||
for (String segment : lsp.segments) {
|
||||
Request request = new Request.Builder().url(segment)
|
||||
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||
.header(CONNECTION, KEEP_ALIVE)
|
||||
.build();
|
||||
try (Response response = client.execute(request)) {
|
||||
if (response.isSuccessful()) {
|
||||
InputStream in = response.body().byteStream();
|
||||
byte[] buffer = new byte[10240];
|
||||
int length = -1;
|
||||
while ((length = in.read(buffer)) >= 0 && running && !Thread.interrupted()) {
|
||||
writeSegment(buffer, 0, length);
|
||||
if (livestreamDownload && splitRecording()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new HttpException(response.code(), response.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (HttpException e) {
|
||||
if (e.getResponseCode() == 404) {
|
||||
LOG.debug("Playlist not found (404). Model {} probably went offline", model);
|
||||
} else if (e.getResponseCode() == 403) {
|
||||
LOG.debug("Playlist access forbidden (403). Model {} probably went private or offline", model);
|
||||
} else {
|
||||
LOG.info("Unexpected error while downloading {}", model, e);
|
||||
}
|
||||
running = false;
|
||||
} catch (Exception e) {
|
||||
LOG.info("Unexpected error while downloading {}", model, e);
|
||||
running = false;
|
||||
}
|
||||
|
||||
ffmpegThread.interrupt();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package ctbrec.sites.showup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import com.iheartradio.m3u8.ParseException;
|
||||
import com.iheartradio.m3u8.PlaylistException;
|
||||
import com.squareup.moshi.JsonReader;
|
||||
import com.squareup.moshi.JsonWriter;
|
||||
|
||||
import ctbrec.AbstractModel;
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.recorder.download.Download;
|
||||
import ctbrec.recorder.download.StreamSource;
|
||||
|
||||
public class ShowupModel extends AbstractModel {
|
||||
|
||||
private long uid;
|
||||
private String streamId;
|
||||
private String streamTranscoderAddr;
|
||||
|
||||
@Override
|
||||
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
|
||||
List<Model> modelList = getShowupSite().getModelList();
|
||||
for (Model model : modelList) {
|
||||
ShowupModel m = (ShowupModel) model;
|
||||
if (m.getUid() == uid) {
|
||||
return m.getOnlineState(false) != State.ONLINE;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException, JAXBException {
|
||||
StreamSource src = new StreamSource();
|
||||
src.width = 480;
|
||||
src.height = 360;
|
||||
|
||||
if(streamId == null || streamTranscoderAddr == null) {
|
||||
List<Model> modelList = getShowupSite().getModelList();
|
||||
for (Model model : modelList) {
|
||||
ShowupModel m = (ShowupModel) model;
|
||||
if (m.getUid() == uid) {
|
||||
streamId = m.getStreamId();
|
||||
streamTranscoderAddr = m.getStreamTranscoderAddr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int cdnHost = 1 + new Random().nextInt(5);
|
||||
src.mediaPlaylistUrl = MessageFormat.format("https://cdn-e0{0}.showup.tv/h5live/http/playlist.m3u8?url=rtmp%3A%2F%2F{1}%3A1935%2Fwebrtc&stream={2}_aac", cdnHost, streamTranscoderAddr, streamId);
|
||||
return Collections.singletonList(src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCacheEntries() {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveTip(Double tokens) throws IOException {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
|
||||
return new int[] { 480, 360 };
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean follow() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unfollow() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public long getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public void setUid(long uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSiteSpecificData(JsonReader reader) throws IOException {
|
||||
reader.nextName();
|
||||
uid = reader.nextLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSiteSpecificData(JsonWriter writer) throws IOException {
|
||||
writer.name("uid").value(uid);
|
||||
}
|
||||
|
||||
private Showup getShowupSite() {
|
||||
return (Showup) getSite();
|
||||
}
|
||||
|
||||
public String getStreamId() {
|
||||
return streamId;
|
||||
}
|
||||
|
||||
public void setStreamId(String streamId) {
|
||||
this.streamId = streamId;
|
||||
}
|
||||
|
||||
public String getStreamTranscoderAddr() {
|
||||
return streamTranscoderAddr;
|
||||
}
|
||||
|
||||
public void setStreamTranscoderAddr(String streamTranscoderAddr) {
|
||||
this.streamTranscoderAddr = streamTranscoderAddr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Download createDownload() {
|
||||
if (Config.isServerMode() && !Config.getInstance().getSettings().recordSingleFile) {
|
||||
return new ShowupDownload(getSite().getHttpClient());
|
||||
} else {
|
||||
return new ShowupMergedDownload(getSite().getHttpClient());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,6 +57,7 @@ import ctbrec.sites.fc2live.Fc2Live;
|
|||
import ctbrec.sites.flirt4free.Flirt4Free;
|
||||
import ctbrec.sites.jasmin.LiveJasmin;
|
||||
import ctbrec.sites.mfc.MyFreeCams;
|
||||
import ctbrec.sites.showup.Showup;
|
||||
import ctbrec.sites.streamate.Streamate;
|
||||
import ctbrec.sites.stripchat.Stripchat;
|
||||
|
||||
|
@ -110,6 +111,7 @@ public class HttpServer {
|
|||
sites.add(new Flirt4Free());
|
||||
sites.add(new LiveJasmin());
|
||||
sites.add(new MyFreeCams());
|
||||
sites.add(new Showup());
|
||||
sites.add(new Streamate());
|
||||
sites.add(new Stripchat());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue