Compare commits
No commits in common. "reusedname" and "main" have entirely different histories.
reusedname
...
main
|
@ -7,3 +7,4 @@
|
|||
.project
|
||||
*/.factorypath
|
||||
**/.antlr/
|
||||
*.code-workspace
|
17
CHANGELOG.md
|
@ -1,3 +1,20 @@
|
|||
5.3.4-J62
|
||||
========================
|
||||
Current Good working copy after chaturbate reverting their changes. Chaturbate Video and audio currently working.
|
||||
* jre bundled (in releases) (fixed permissions) (linux and macos)
|
||||
* updated ffmpeg (in releases) (fixed permissions) (windows, linux and macos)
|
||||
* fixed run script permissions (linux and macos)
|
||||
* bump ver
|
||||
* all builds checked
|
||||
|
||||
5.3.3-J62
|
||||
========================
|
||||
Reverted for chaturbate reverting their changes. Chaturbate Video and audio currently working.
|
||||
* jre bundled (in releases)
|
||||
* flirt4free fix - "topic" no longer sent, replaced with empty string
|
||||
* bump ver
|
||||
* all builds checked
|
||||
|
||||
5.3.0
|
||||
========================
|
||||
* Added menu entry to force recording of models without changing the prio
|
||||
|
|
30
README.md
|
@ -1,17 +1,39 @@
|
|||
# CTB Recorder
|
||||

|
||||
## Current State Version 5.3.3
|
||||
Reverted for chaturbate reverting their changes. Chaturbate Video and audio currently working.<br>
|
||||
|
||||
A free recording software for different camsites. Currently supported: BongaCams, Cam4, CamSoda, Chaturbate, FC2Live, LiveJasmin, MyFreeCams, Streamate
|
||||
- jre bundled<br>
|
||||
- f4f fix<br>
|
||||
- bump ver<br>
|
||||
- all builds checked<br>
|
||||
|
||||

|
||||
### ABOUT This Repo, server and domain hosted and maintained by J62
|
||||
I (j62) now have a fully set up development environment again(have been here and assisted in different ways since 2020), allowing me to handle all builds with ease. I’ve also set up a domain, Git server, and hosting—at my own expense—to provide a stable and maintained platform for the foreseeable future.<br>
|
||||
|
||||
I took this step after witnessing the chaotic distribution of zip files in Discord, where multiple developers were scrambling to fix the Chaturbate issue. It became clear that there was a need (once again) for a centralized collaboration and download hub.<br>
|
||||
|
||||
Ideally, everyone should utilize the Git server and create their own forks(of this repo). This way, we can manage changes efficiently through pull requests between forks.<br>
|
||||
|
||||
Support me:<br>
|
||||
|
||||
Bitcoin: bc1q7fvtkx8wklvd4zttsec7sfgxqh9zadk0x236lt <br>
|
||||
Ether: 0x2e687A5628ff16c8f9624A914C1f727000089C3A <br>
|
||||
Solana: Z5YwNPkLheSHuaSJjyHhg3L8UxjpJPt5WU6vu4hFsNR <br>
|
||||
Monero: 47tjD1z63wu3FEnDCvWnFaRAZbpDKc3Ys1WCbgzvB2Gg8XbqU8bARpcCC37mWzuWBAeZPu2UGY4TAcYGhb6fptoTR8X9vjc
|
||||
|
||||
## A free recording software for different camsites. Currently supported: BongaCams, Cam4, CamSoda, Chaturbate, FC2Live, LiveJasmin, MyFreeCams, Streamate
|
||||
|
||||

|
||||
|
||||
If you ever wanted to record a cam girl show to watch it later or if your favorite model lives in another timezone and is never online when you are, CTB Recorder is the solution for you.
|
||||
|
||||
CTB Recorder allows you to record any public show on different cam sites. It is very easy to use and set up in minutes. You can even run the recorder on a server and control it with the graphical user interface, so that you never miss a show again.
|
||||
|
||||
For more visit the [homepage](https://0xboobface.github.io/ctbrec)
|
||||
For more visit the [homepage](https://git.ctbrec.com/j62/ctbrec)
|
||||
|
||||
## Installation
|
||||
[Download](https://github.com/0xboobface/ctbrec/releases) the bundle ending in -jre for your platform and follow the instructions in the README.md contained in the zip.
|
||||
[Download](https://git.ctbrec.com/j62/ctbrec/releases) the bundle ending in -jre for your platform and follow the instructions in the README.md contained in the zip.
|
||||
|
||||
## License
|
||||
CTB Recorder is licensed under the GPLv3. See [LICENSE.txt](https://raw.githubusercontent.com/0xboobface/ctbrec/master/LICENSE.txt).
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<parent>
|
||||
<groupId>ctbrec</groupId>
|
||||
<artifactId>master</artifactId>
|
||||
<version>5.3.2</version>
|
||||
<version>5.3.4</version>
|
||||
<relativePath>../master</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -268,7 +268,8 @@ public class CamrecApplication extends Application {
|
|||
tabPane.getTabs().add(new RecentlyWatchedTab(recorder, sites));
|
||||
}
|
||||
tabPane.getTabs().add(new SettingsTab(sites, recorder));
|
||||
tabPane.getTabs().add(new NewsTab(config));
|
||||
//tabPane.getTabs().add(new NewsTab(config));
|
||||
tabPane.getTabs().add(new NewsTab(config, getHostServices()));
|
||||
tabPane.getTabs().add(new DonateTabFx());
|
||||
tabPane.getTabs().add(new HelpTab());
|
||||
tabPane.getTabs().add(new LoggingTab());
|
||||
|
|
|
@ -6,17 +6,22 @@ import ctbrec.GlobalThreadPool;
|
|||
import ctbrec.Version;
|
||||
import ctbrec.io.HttpException;
|
||||
import ctbrec.io.json.ObjectMapperFactory;
|
||||
import ctbrec.ui.CamrecApplication;
|
||||
import ctbrec.ui.controls.Dialogs;
|
||||
import ctbrec.ui.tabs.TabSelectionListener;
|
||||
import javafx.application.HostServices;
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.layout.VBox;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -27,16 +32,17 @@ import static ctbrec.io.HttpConstants.USER_AGENT;
|
|||
|
||||
@Slf4j
|
||||
public class NewsTab extends Tab implements TabSelectionListener {
|
||||
private static final String ACCESS_TOKEN = "a2804d73a89951a22e0f8483a6fcec8943afd88b7ba17c459c095aa9e6f94fd0";
|
||||
private static final String URL = "https://mastodon.cloud/api/v1/accounts/480960/statuses?limit=20&exclude_replies=true";
|
||||
private static final String URL = "https://git.ctbrec.com/api/v1/repos/j62/ctbrec/releases";
|
||||
private final Config config;
|
||||
private final HostServices hostServices;
|
||||
private final VBox layout = new VBox();
|
||||
|
||||
private final ObjectMapper mapper = ObjectMapperFactory.getMapper();
|
||||
private final OkHttpClient httpClient = new OkHttpClient();
|
||||
|
||||
public NewsTab(Config config) {
|
||||
public NewsTab(Config config, HostServices hostServices) {
|
||||
this.config = config;
|
||||
setText("News");
|
||||
this.hostServices = hostServices;
|
||||
setText("Releases");
|
||||
layout.setMaxWidth(800);
|
||||
layout.setAlignment(Pos.CENTER);
|
||||
setContent(new ScrollPane(layout));
|
||||
|
@ -44,24 +50,21 @@ public class NewsTab extends Tab implements TabSelectionListener {
|
|||
|
||||
@Override
|
||||
public void selected() {
|
||||
GlobalThreadPool.submit(this::loadToots);
|
||||
GlobalThreadPool.submit(this::loadReleases);
|
||||
}
|
||||
|
||||
private void loadToots() {
|
||||
private void loadReleases() {
|
||||
try {
|
||||
var request = new Request.Builder()
|
||||
.url(URL)
|
||||
.header("Authorization", "Bearer " + ACCESS_TOKEN)
|
||||
.header(USER_AGENT, "ctbrec " + Version.getVersion())
|
||||
.build();
|
||||
try (var response = CamrecApplication.httpClient.execute(request)) {
|
||||
try (Response response = httpClient.newCall(request).execute()) {
|
||||
if (response.isSuccessful()) {
|
||||
var body = Objects.requireNonNull(response.body(), HTTP_RESPONSE_BODY_IS_NULL).string();
|
||||
log.debug(body);
|
||||
if (body.startsWith("[")) {
|
||||
onSuccess(body);
|
||||
} else if (body.startsWith("{")) {
|
||||
onError(body);
|
||||
} else {
|
||||
throw new IOException("Unexpected response: " + body);
|
||||
}
|
||||
|
@ -70,30 +73,53 @@ public class NewsTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.info("Error while loading news", e);
|
||||
Dialogs.showError(getTabPane().getScene(), "News", "Couldn't load news from mastodon", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onError(String body) throws IOException {
|
||||
var json = new JSONObject(body);
|
||||
if (json.has("error")) {
|
||||
throw new IOException("Request not successful: " + json.getString("error"));
|
||||
} else {
|
||||
throw new IOException("Unexpected response: " + body);
|
||||
log.info("Error while loading releases", e);
|
||||
Dialogs.showError(getTabPane().getScene(), "Releases", "Couldn't load release information", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onSuccess(String body) throws IOException {
|
||||
Status[] statusArray = mapper.readValue(body, Status[].class);
|
||||
JSONArray releases = new JSONArray(body);
|
||||
Platform.runLater(() -> {
|
||||
layout.getChildren().clear();
|
||||
for (Status status : statusArray) {
|
||||
if (status.getInReplyToId() == null && !Objects.equals("direct", status.getVisibility())) {
|
||||
var stp = new StatusPane(status, config.getDateTimeFormatter());
|
||||
layout.getChildren().add(stp);
|
||||
VBox.setMargin(stp, new Insets(10));
|
||||
for (int i = 0; i < releases.length(); i++) {
|
||||
JSONObject release = releases.getJSONObject(i);
|
||||
String tagName = release.optString("tag_name", "Unknown Version");
|
||||
String releaseName = release.optString("name", "No Name");
|
||||
String description = release.optString("body", "No description available.");
|
||||
JSONArray assets = release.optJSONArray("assets");
|
||||
|
||||
var releasePane = new VBox();
|
||||
releasePane.setPadding(new Insets(10));
|
||||
releasePane.setSpacing(5);
|
||||
|
||||
Label versionLabel = new Label("Version: " + tagName);
|
||||
versionLabel.setStyle("-fx-font-weight: bold;");
|
||||
|
||||
Label nameLabel = new Label("Release: " + releaseName);
|
||||
Label descLabel = new Label(description);
|
||||
|
||||
releasePane.getChildren().addAll(versionLabel, nameLabel, descLabel);
|
||||
|
||||
if (assets != null) {
|
||||
Label assetsLabel = new Label("Assets:");
|
||||
releasePane.getChildren().add(assetsLabel);
|
||||
for (int j = 0; j < assets.length(); j++) {
|
||||
JSONObject asset = assets.getJSONObject(j);
|
||||
String assetName = asset.optString("name", "Unknown File");
|
||||
String downloadUrl = asset.optString("browser_download_url", "#");
|
||||
int size = asset.optInt("size", 0);
|
||||
int downloads = asset.optInt("download_count", 0);
|
||||
|
||||
Hyperlink assetLink = new Hyperlink(assetName + " (" + size / 1024 / 1024 + " MB, " + downloads + " downloads)");
|
||||
assetLink.setOnAction(event -> hostServices.showDocument(downloadUrl));
|
||||
|
||||
releasePane.getChildren().add(assetLink);
|
||||
}
|
||||
}
|
||||
|
||||
layout.getChildren().add(releasePane);
|
||||
VBox.setMargin(releasePane, new Insets(10));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ public class Flirt4FreeUpdateService extends PaginatedScheduledService {
|
|||
var name = modelData.getString("model_seo_name");
|
||||
Flirt4FreeModel model = (Flirt4FreeModel) flirt4Free.createModel(name);
|
||||
model.setDisplayName(Entities.unescape(modelData.getString("display")));
|
||||
model.setDescription(modelData.getString("topic"));
|
||||
model.setDescription("");
|
||||
model.setUrl(Flirt4Free.BASE_URI + "/rooms/" + model.getName() + '/');
|
||||
model.setNew(modelData.optString("is_new", "0").equals("1"));
|
||||
var videoHost = modelData.getString("video_host");
|
||||
|
|
|
@ -24,9 +24,9 @@ public class DonateTabFx extends Tab {
|
|||
|
||||
var headerVbox = new VBox(10);
|
||||
headerVbox.setAlignment(Pos.CENTER);
|
||||
var beer = new Label("Buy me some beer?!");
|
||||
var beer = new Label("Buy me a drink?!");
|
||||
beer.setFont(new Font(36));
|
||||
var desc = new Label("If you like this software and want to buy me some beer or pizza, here are some possibilities!");
|
||||
var desc = new Label("If you like this software and want to buy me some MtDew or Energy Drinks(boost ctbrec dev productivity! :), Beer or Pizza, here are some possibilities!");
|
||||
desc.setFont(new Font(24));
|
||||
headerVbox.getChildren().addAll(beer, desc);
|
||||
var header = new HBox();
|
||||
|
@ -36,7 +36,7 @@ public class DonateTabFx extends Tab {
|
|||
container.setTop(header);
|
||||
|
||||
var prefWidth = 360;
|
||||
var bitcoinAddress = new TextField("15sLWZon8diPqAX4UdPQU1DcaPuvZs2GgA");
|
||||
var bitcoinAddress = new TextField("bc1q7fvtkx8wklvd4zttsec7sfgxqh9zadk0x236lt");
|
||||
bitcoinAddress.setEditable(false);
|
||||
bitcoinAddress.setPrefWidth(prefWidth);
|
||||
var bitcoinQrCode = new ImageView(getClass().getResource("/html/bitcoin-address.png").toString());
|
||||
|
@ -46,7 +46,7 @@ public class DonateTabFx extends Tab {
|
|||
bitcoinBox.setAlignment(Pos.TOP_CENTER);
|
||||
bitcoinBox.getChildren().addAll(bitcoinLabel, bitcoinAddress, bitcoinQrCode);
|
||||
|
||||
var ethereumAddress = new TextField("0x996041638eEAE7E31f39Ef6e82068d69bA7C090e");
|
||||
var ethereumAddress = new TextField("0x2e687A5628ff16c8f9624A914C1f727000089C3A");
|
||||
ethereumAddress.setEditable(false);
|
||||
ethereumAddress.setPrefWidth(prefWidth);
|
||||
var ethereumQrCode = new ImageView(getClass().getResource("/html/ethereum-address.png").toString());
|
||||
|
@ -56,7 +56,7 @@ public class DonateTabFx extends Tab {
|
|||
ethereumBox.setAlignment(Pos.TOP_CENTER);
|
||||
ethereumBox.getChildren().addAll(ethereumLabel, ethereumAddress, ethereumQrCode);
|
||||
|
||||
var moneroAddress = new TextField("871K7xaLR2X8E84CUBi7D88diXgKjbhjZHTEFfJv9ec9eo4NVPCQ2UsGxkroseCcKQbZsHMgW3kg6HR4tfct3fX2HoFDzK6");
|
||||
var moneroAddress = new TextField("47tjD1z63wu3FEnDCvWnFaRAZbpDKc3Ys1WCbgzvB2Gg8XbqU8bARpcCC37mWzuWBAeZPu2UGY4TAcYGhb6fptoTR8X9vjc");
|
||||
moneroAddress.setEditable(false);
|
||||
moneroAddress.setPrefWidth(prefWidth);
|
||||
var moneroQrCode = new ImageView(getClass().getResource("/html/monero-address.png").toString());
|
||||
|
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 4.9 KiB |
0
client/src/main/resources/html/static/vendor/jquery-easing/jquery.easing.compatibility.js
vendored
Executable file → Normal file
0
client/src/main/resources/html/static/vendor/jquery-easing/jquery.easing.js
vendored
Executable file → Normal file
0
client/src/main/resources/html/static/vendor/jquery-easing/jquery.easing.min.js
vendored
Executable file → Normal file
|
@ -8,7 +8,7 @@
|
|||
<parent>
|
||||
<groupId>ctbrec</groupId>
|
||||
<artifactId>master</artifactId>
|
||||
<version>5.3.2</version>
|
||||
<version>5.3.4</version>
|
||||
<relativePath>../master</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
|||
public class Chaturbate extends AbstractSite {
|
||||
|
||||
static String baseUrl = "https://chaturbate.com";
|
||||
public static final String AFFILIATE_LINK = "https://chaturbate.com/in/?track=default&tour=grq0&campaign=55vTi";
|
||||
public static final String REGISTRATION_LINK = "https://chaturbate.com/in/?track=default&tour=g4pe&campaign=55vTi";
|
||||
public static final String AFFILIATE_LINK = "https://chaturbate.com/in/?track=default&tour=grq0&campaign=97mhH";
|
||||
public static final String REGISTRATION_LINK = "https://chaturbate.com/in/?track=default&tour=g4pe&campaign=97mhH";
|
||||
private ChaturbateHttpClient httpClient;
|
||||
|
||||
@Override
|
||||
|
@ -46,7 +46,7 @@ public class Chaturbate extends AbstractSite {
|
|||
|
||||
@Override
|
||||
public String getAffiliateLink() {
|
||||
return getBaseUrl() + "/in/?track=default&tour=LQps&campaign=55vTi&room=0xb00bface";
|
||||
return getBaseUrl() + "/in/?track=default&tour=LQps&campaign=97mhH&room=0xb00bface";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -311,6 +311,7 @@ public class ChaturbateModel extends AbstractModel {
|
|||
String content = response.body().string();
|
||||
log.trace("Raw stream info for model {}: {}", getName(), content);
|
||||
streamInfo = mapper.readValue(content, StreamInfo.class);
|
||||
//streamInfo.url = streamInfo.url.replaceAll("live-hls", "live-c-fhls").replaceAll("playlist\\.m3u8", "playlist_sfm4s.m3u8");
|
||||
return streamInfo;
|
||||
} else {
|
||||
int code = response.code();
|
||||
|
|
1094
docs/index.html
|
@ -6,7 +6,7 @@
|
|||
<groupId>ctbrec</groupId>
|
||||
<artifactId>master</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>5.3.2</version>
|
||||
<version>5.3.4</version>
|
||||
|
||||
<modules>
|
||||
<module>../common</module>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<parent>
|
||||
<groupId>ctbrec</groupId>
|
||||
<artifactId>master</artifactId>
|
||||
<version>5.3.2</version>
|
||||
<version>5.3.4</version>
|
||||
<relativePath>../master</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
0
server/src/main/resources/html/static/vendor/flowplayer/skin/icons/flowplayer.eot
vendored
Executable file → Normal file
0
server/src/main/resources/html/static/vendor/flowplayer/skin/icons/flowplayer.svg
vendored
Executable file → Normal file
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
0
server/src/main/resources/html/static/vendor/flowplayer/skin/icons/flowplayer.ttf
vendored
Executable file → Normal file
0
server/src/main/resources/html/static/vendor/flowplayer/skin/icons/flowplayer.woff
vendored
Executable file → Normal file
0
server/src/main/resources/html/static/vendor/jquery-easing/jquery.easing.compatibility.js
vendored
Executable file → Normal file
0
server/src/main/resources/html/static/vendor/jquery-easing/jquery.easing.js
vendored
Executable file → Normal file
0
server/src/main/resources/html/static/vendor/jquery-easing/jquery.easing.min.js
vendored
Executable file → Normal file
After Width: | Height: | Size: 9.4 KiB |