Merge branch 'dev' into pp
This commit is contained in:
commit
4f8e7dbca2
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -1,12 +1,24 @@
|
||||||
3.9.0
|
3.9.0
|
||||||
========================
|
========================
|
||||||
* Added support for Manyvids Live.
|
* Added support for Manyvids Live.
|
||||||
|
Things that work:
|
||||||
|
* Recording streams. Even more than one (this was a problem first, because
|
||||||
|
they allow only one stream per session)
|
||||||
|
* Search
|
||||||
Things that don't work:
|
Things that don't work:
|
||||||
* login / favorites
|
* login / favorites
|
||||||
|
* tipping
|
||||||
* media player isn't working because of their authetication mechanism
|
* media player isn't working because of their authetication mechanism
|
||||||
* Fixed bug in recorder servlet. Actions for unpin and notes were mixed up
|
* Fixed bug in recorder servlet. Actions for unpin and notes were mixed up
|
||||||
|
and not properly synchronized between the server and the client
|
||||||
* Recordings now start immediately for newly added models
|
* Recordings now start immediately for newly added models
|
||||||
* Added confirmation dialog for "Pause All" and "Resume All"
|
* Added confirmation dialog for "Pause All", "Resume All" and shutdown
|
||||||
|
* Fix: recording started event was not fired in client / server mode
|
||||||
|
* CTB Recorder now stops recording, if less than 100 MiB space is left
|
||||||
|
* New event, which is fired, if the disk is full (or less than the configured
|
||||||
|
threshold is available)
|
||||||
|
* Fixed: MFC models changing to other models (I think, I found the problem.
|
||||||
|
Can't be sure 100%)
|
||||||
|
|
||||||
3.8.6
|
3.8.6
|
||||||
========================
|
========================
|
||||||
|
|
|
@ -14,7 +14,6 @@ import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -317,24 +316,12 @@ public class CamrecApplication extends Application {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerAlertSystem() {
|
private void registerAlertSystem() {
|
||||||
new Thread(() -> {
|
for (EventHandlerConfiguration eventHandlerConfig : Config.getInstance().getSettings().eventHandlers) {
|
||||||
try {
|
EventHandler handler = new EventHandler(eventHandlerConfig);
|
||||||
// don't register before 1 minute has passed, because directly after
|
EventBusHolder.register(handler);
|
||||||
// the start of ctbrec, an event for every online model would be fired,
|
LOG.debug("Registered event handler for {} {}", eventHandlerConfig.getEvent(), eventHandlerConfig.getName());
|
||||||
// which is annoying as f
|
}
|
||||||
Thread.sleep(TimeUnit.MINUTES.toMillis(1));
|
LOG.debug("Alert System registered");
|
||||||
|
|
||||||
for (EventHandlerConfiguration eventHandlerConfig : Config.getInstance().getSettings().eventHandlers) {
|
|
||||||
EventHandler handler = new EventHandler(eventHandlerConfig);
|
|
||||||
EventBusHolder.register(handler);
|
|
||||||
LOG.debug("Registered event handler for {} {}", eventHandlerConfig.getEvent(), eventHandlerConfig.getName());
|
|
||||||
}
|
|
||||||
LOG.debug("Alert System registered");
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
LOG.info("Interrupted before alter system has been registered");
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerActiveRecordingsCounter() {
|
private void registerActiveRecordingsCounter() {
|
||||||
|
|
|
@ -22,6 +22,9 @@ public class ShowNotification extends Action {
|
||||||
switch(evt.getType()) {
|
switch(evt.getType()) {
|
||||||
case MODEL_STATUS_CHANGED:
|
case MODEL_STATUS_CHANGED:
|
||||||
ModelStateChangedEvent modelEvent = (ModelStateChangedEvent) evt;
|
ModelStateChangedEvent modelEvent = (ModelStateChangedEvent) evt;
|
||||||
|
if (modelEvent.getOldState() == Model.State.UNKNOWN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Model m = modelEvent.getModel();
|
Model m = modelEvent.getModel();
|
||||||
msg = m.getDisplayName() + " is now " + modelEvent.getNewState().toString();
|
msg = m.getDisplayName() + " is now " + modelEvent.getNewState().toString();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import ctbrec.event.EventHandlerConfiguration;
|
||||||
import ctbrec.event.EventHandlerConfiguration.ActionConfiguration;
|
import ctbrec.event.EventHandlerConfiguration.ActionConfiguration;
|
||||||
import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration;
|
import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration;
|
||||||
import ctbrec.event.ExecuteProgram;
|
import ctbrec.event.ExecuteProgram;
|
||||||
|
import ctbrec.event.MatchAllPredicate;
|
||||||
import ctbrec.event.ModelPredicate;
|
import ctbrec.event.ModelPredicate;
|
||||||
import ctbrec.event.ModelStatePredicate;
|
import ctbrec.event.ModelStatePredicate;
|
||||||
import ctbrec.event.RecordingStatePredicate;
|
import ctbrec.event.RecordingStatePredicate;
|
||||||
|
@ -152,6 +153,11 @@ public class ActionSettingsPanel extends GridPane {
|
||||||
pc.getConfiguration().put("state", recordingState.getValue().name());
|
pc.getConfiguration().put("state", recordingState.getValue().name());
|
||||||
pc.setName("state = " + recordingState.getValue().toString());
|
pc.setName("state = " + recordingState.getValue().toString());
|
||||||
config.getPredicates().add(pc);
|
config.getPredicates().add(pc);
|
||||||
|
} else if(event.getValue() == Event.Type.NO_SPACE_LEFT) {
|
||||||
|
PredicateConfiguration pc = new PredicateConfiguration();
|
||||||
|
pc.setType(MatchAllPredicate.class.getName());
|
||||||
|
pc.setName("no space left");
|
||||||
|
config.getPredicates().add(pc);
|
||||||
}
|
}
|
||||||
if(!modelSelectionPane.isAllSelected()) {
|
if(!modelSelectionPane.isAllSelected()) {
|
||||||
PredicateConfiguration pc = new PredicateConfiguration();
|
PredicateConfiguration pc = new PredicateConfiguration();
|
||||||
|
@ -200,7 +206,7 @@ public class ActionSettingsPanel extends GridPane {
|
||||||
if(event.getValue() == Event.Type.RECORDING_STATUS_CHANGED && recordingState.getValue() == null) {
|
if(event.getValue() == Event.Type.RECORDING_STATUS_CHANGED && recordingState.getValue() == null) {
|
||||||
throw new IllegalStateException("Select a state");
|
throw new IllegalStateException("Select a state");
|
||||||
}
|
}
|
||||||
if(modelSelectionPane.getSelectedItems().isEmpty() && !modelSelectionPane.isAllSelected()) {
|
if(event.getValue() != Event.Type.NO_SPACE_LEFT && modelSelectionPane.getSelectedItems().isEmpty() && !modelSelectionPane.isAllSelected()) {
|
||||||
throw new IllegalStateException("Select one or more models or tick off \"all\"");
|
throw new IllegalStateException("Select one or more models or tick off \"all\"");
|
||||||
}
|
}
|
||||||
if(!(showNotification.isSelected() || playSound.isSelected() || executeProgram.isSelected())) {
|
if(!(showNotification.isSelected() || playSound.isSelected() || executeProgram.isSelected())) {
|
||||||
|
@ -231,10 +237,23 @@ public class ActionSettingsPanel extends GridPane {
|
||||||
event.getItems().clear();
|
event.getItems().clear();
|
||||||
event.getItems().add(Event.Type.MODEL_STATUS_CHANGED);
|
event.getItems().add(Event.Type.MODEL_STATUS_CHANGED);
|
||||||
event.getItems().add(Event.Type.RECORDING_STATUS_CHANGED);
|
event.getItems().add(Event.Type.RECORDING_STATUS_CHANGED);
|
||||||
|
event.getItems().add(Event.Type.NO_SPACE_LEFT);
|
||||||
event.setOnAction(evt -> modelState.setVisible(event.getSelectionModel().getSelectedItem() == Event.Type.MODEL_STATUS_CHANGED));
|
event.setOnAction(evt -> modelState.setVisible(event.getSelectionModel().getSelectedItem() == Event.Type.MODEL_STATUS_CHANGED));
|
||||||
event.getSelectionModel().select(Event.Type.MODEL_STATUS_CHANGED);
|
event.getSelectionModel().select(Event.Type.MODEL_STATUS_CHANGED);
|
||||||
layout.add(event, 1, row++);
|
layout.add(event, 1, row++);
|
||||||
|
|
||||||
|
event.getSelectionModel().selectedItemProperty().addListener((obs, oldV, newV) -> {
|
||||||
|
boolean modelRelatedStuffDisabled = false;
|
||||||
|
if(newV == Event.Type.NO_SPACE_LEFT) {
|
||||||
|
modelRelatedStuffDisabled = true;
|
||||||
|
modelSelectionPane.selectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
modelState.setDisable(modelRelatedStuffDisabled);
|
||||||
|
recordingState.setDisable(modelRelatedStuffDisabled);
|
||||||
|
modelSelectionPane.setDisable(modelRelatedStuffDisabled);
|
||||||
|
});
|
||||||
|
|
||||||
layout.add(new Label("State"), 0, row);
|
layout.add(new Label("State"), 0, row);
|
||||||
modelState.getItems().clear();
|
modelState.getItems().clear();
|
||||||
modelState.getItems().addAll(Model.State.values());
|
modelState.getItems().addAll(Model.State.values());
|
||||||
|
|
|
@ -118,4 +118,8 @@ public class ListSelectionPane<T extends Comparable<T>> extends GridPane {
|
||||||
public boolean isAllSelected() {
|
public boolean isAllSelected() {
|
||||||
return selectAll.isSelected();
|
return selectAll.isSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void selectAll() {
|
||||||
|
selectAll.setSelected(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package ctbrec.ui.sites.cam4;
|
package ctbrec.ui.sites.cam4;
|
||||||
|
|
||||||
|
import static ctbrec.Model.State.*;
|
||||||
|
import static ctbrec.io.HttpConstants.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
@ -64,7 +68,11 @@ public class Cam4UpdateService extends PaginatedScheduledService {
|
||||||
if(loginRequired) {
|
if(loginRequired) {
|
||||||
SiteUiFactory.getUi(site).login();
|
SiteUiFactory.getUi(site).login();
|
||||||
}
|
}
|
||||||
Request request = new Request.Builder().url(url).build();
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
|
||||||
|
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||||
|
.build();
|
||||||
try (Response response = site.getHttpClient().execute(request)) {
|
try (Response response = site.getHttpClient().execute(request)) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
JSONObject json = new JSONObject(response.body().string());
|
JSONObject json = new JSONObject(response.body().string());
|
||||||
|
@ -76,11 +84,15 @@ public class Cam4UpdateService extends PaginatedScheduledService {
|
||||||
Element profileLink = HtmlParser.getTag(boxHtml, "a.profile-preview");
|
Element profileLink = HtmlParser.getTag(boxHtml, "a.profile-preview");
|
||||||
String path = profileLink.attr("href");
|
String path = profileLink.attr("href");
|
||||||
String slug = path.substring(1);
|
String slug = path.substring(1);
|
||||||
Cam4Model model = (Cam4Model) site.createModel(slug);
|
Cam4Model model = site.createModel(slug);
|
||||||
String playlistUrl = profileLink.attr("data-hls-preview-url");
|
String playlistUrl = profileLink.attr("data-hls-preview-url");
|
||||||
model.setPlaylistUrl(playlistUrl);
|
model.setPlaylistUrl(playlistUrl);
|
||||||
model.setPreview("https://snapshots.xcdnpro.com/thumbnails/" + model.getName() + "?s=" + System.currentTimeMillis());
|
model.setPreview("https://snapshots.xcdnpro.com/thumbnails/" + model.getName() + "?s=" + System.currentTimeMillis());
|
||||||
model.setDescription(parseDesription(boxHtml));
|
model.setDescription(parseDesription(boxHtml));
|
||||||
|
model.setOnlineState(ONLINE);
|
||||||
|
if(boxHtml.contains("In private show")) {
|
||||||
|
model.setOnlineState(PRIVATE);
|
||||||
|
}
|
||||||
models.add(model);
|
models.add(model);
|
||||||
}
|
}
|
||||||
return models;
|
return models;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ctbrec.ui.sites.streamate;
|
package ctbrec.ui.sites.streamate;
|
||||||
|
|
||||||
|
import static ctbrec.Model.State.*;
|
||||||
import static ctbrec.io.HttpConstants.*;
|
import static ctbrec.io.HttpConstants.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -81,6 +82,7 @@ public class StreamateFollowedService extends PaginatedScheduledService {
|
||||||
model.setPreview(p.getString("thumbnail"));
|
model.setPreview(p.getString("thumbnail"));
|
||||||
boolean online = p.optBoolean("online") && notPrivateEtc(p);
|
boolean online = p.optBoolean("online") && notPrivateEtc(p);
|
||||||
model.setOnline(online);
|
model.setOnline(online);
|
||||||
|
model.setOnlineState(online ? ONLINE : OFFLINE);
|
||||||
if (online == showOnline) {
|
if (online == showOnline) {
|
||||||
models.add(model);
|
models.add(model);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package ctbrec.ui.sites.streamate;
|
package ctbrec.ui.sites.streamate;
|
||||||
|
|
||||||
|
import static ctbrec.Model.State.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -52,6 +54,7 @@ public class StreamateUpdateService extends PaginatedScheduledService {
|
||||||
List<Model> models = new ArrayList<>();
|
List<Model> models = new ArrayList<>();
|
||||||
String content = response.body().string();
|
String content = response.body().string();
|
||||||
JSONObject json = new JSONObject(content);
|
JSONObject json = new JSONObject(content);
|
||||||
|
System.err.println(json.toString(2));
|
||||||
JSONArray performers = json.getJSONArray("performers");
|
JSONArray performers = json.getJSONArray("performers");
|
||||||
for (int i = 0; i < performers.length(); i++) {
|
for (int i = 0; i < performers.length(); i++) {
|
||||||
JSONObject p = performers.getJSONObject(i);
|
JSONObject p = performers.getJSONObject(i);
|
||||||
|
@ -59,7 +62,9 @@ public class StreamateUpdateService extends PaginatedScheduledService {
|
||||||
StreamateModel model = (StreamateModel) streamate.createModel(nickname);
|
StreamateModel model = (StreamateModel) streamate.createModel(nickname);
|
||||||
model.setId(p.getLong("id"));
|
model.setId(p.getLong("id"));
|
||||||
model.setPreview(p.getString("thumbnail"));
|
model.setPreview(p.getString("thumbnail"));
|
||||||
model.setOnline(p.optBoolean("online"));
|
boolean online = p.optBoolean("online");
|
||||||
|
model.setOnline(online);
|
||||||
|
model.setOnlineState(online ? ONLINE : OFFLINE);
|
||||||
// TODO figure out, what all the states mean
|
// TODO figure out, what all the states mean
|
||||||
// liveState {…}
|
// liveState {…}
|
||||||
// exclusiveShow false
|
// exclusiveShow false
|
||||||
|
@ -69,6 +74,11 @@ public class StreamateUpdateService extends PaginatedScheduledService {
|
||||||
// preGoldShow true
|
// preGoldShow true
|
||||||
// privateChat false
|
// privateChat false
|
||||||
// specialShow false
|
// specialShow false
|
||||||
|
if (p.optBoolean("onBreak")) {
|
||||||
|
model.setOnlineState(AWAY);
|
||||||
|
} else if (p.optBoolean("goldShow") || p.optBoolean("privateChat") || p.optBoolean("exclusiveShow")) {
|
||||||
|
model.setOnlineState(PRIVATE);
|
||||||
|
}
|
||||||
models.add(model);
|
models.add(model);
|
||||||
}
|
}
|
||||||
return models;
|
return models;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ctbrec.ui.sites.stripchat;
|
package ctbrec.ui.sites.stripchat;
|
||||||
|
|
||||||
|
import static ctbrec.Model.State.*;
|
||||||
import static ctbrec.io.HttpConstants.*;
|
import static ctbrec.io.HttpConstants.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -68,6 +69,7 @@ public class StripchatFollowedUpdateService extends PaginatedScheduledService {
|
||||||
try (Response response = stripchat.getHttpClient().execute(request)) {
|
try (Response response = stripchat.getHttpClient().execute(request)) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
JSONObject json = new JSONObject(response.body().string());
|
JSONObject json = new JSONObject(response.body().string());
|
||||||
|
System.err.println(json.toString(2));
|
||||||
if (json.has("models")) {
|
if (json.has("models")) {
|
||||||
JSONArray users = json.getJSONArray("models");
|
JSONArray users = json.getJSONArray("models");
|
||||||
for (int i = 0; i < users.length(); i++) {
|
for (int i = 0; i < users.length(); i++) {
|
||||||
|
@ -75,6 +77,7 @@ public class StripchatFollowedUpdateService extends PaginatedScheduledService {
|
||||||
StripchatModel model = stripchat.createModel(user.optString("username"));
|
StripchatModel model = stripchat.createModel(user.optString("username"));
|
||||||
model.setDescription(user.optString("description"));
|
model.setDescription(user.optString("description"));
|
||||||
model.setPreview(user.optString("previewUrlThumbBig"));
|
model.setPreview(user.optString("previewUrlThumbBig"));
|
||||||
|
model.setOnlineState(mapStatus(user.optString("status")));
|
||||||
models.add(model);
|
models.add(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,4 +117,17 @@ public class StripchatFollowedUpdateService extends PaginatedScheduledService {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ctbrec.Model.State mapStatus(String status) {
|
||||||
|
switch (status) {
|
||||||
|
case "public":
|
||||||
|
return ONLINE;
|
||||||
|
case "idle":
|
||||||
|
return AWAY;
|
||||||
|
case "off":
|
||||||
|
return OFFLINE;
|
||||||
|
default:
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ public class StripchatUpdateService extends PaginatedScheduledService {
|
||||||
model.setDescription("");
|
model.setDescription("");
|
||||||
model.setPreview(jsonModel.optString("snapshotUrl"));
|
model.setPreview(jsonModel.optString("snapshotUrl"));
|
||||||
model.setDisplayName(model.getName());
|
model.setDisplayName(model.getName());
|
||||||
|
model.setOnlineState(Model.State.ONLINE);
|
||||||
models.add(model);
|
models.add(model);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("Couldn't parse one of the models: {}", jsonModel, e);
|
LOG.warn("Couldn't parse one of the models: {}", jsonModel, e);
|
||||||
|
|
|
@ -18,7 +18,12 @@ public abstract class Event {
|
||||||
/**
|
/**
|
||||||
* This event is fired whenever the state of a recording changes.
|
* This event is fired whenever the state of a recording changes.
|
||||||
*/
|
*/
|
||||||
RECORDING_STATUS_CHANGED("recording status changed");
|
RECORDING_STATUS_CHANGED("recording status changed"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is fired when the disk space is exhausted
|
||||||
|
*/
|
||||||
|
NO_SPACE_LEFT("no space left");
|
||||||
|
|
||||||
private String desc;
|
private String desc;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package ctbrec.event;
|
||||||
|
|
||||||
|
import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration;
|
||||||
|
|
||||||
|
public class MatchAllPredicate extends EventPredicate {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(Event evt) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(PredicateConfiguration pc) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package ctbrec.event;
|
||||||
|
|
||||||
|
public class NoSpaceLeftEvent extends Event {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return Type.NO_SPACE_LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "No space left on device";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "No space left on device";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getExecutionParams() {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ import ctbrec.Recording.State;
|
||||||
import ctbrec.event.Event;
|
import ctbrec.event.Event;
|
||||||
import ctbrec.event.EventBusHolder;
|
import ctbrec.event.EventBusHolder;
|
||||||
import ctbrec.event.ModelIsOnlineEvent;
|
import ctbrec.event.ModelIsOnlineEvent;
|
||||||
|
import ctbrec.event.NoSpaceLeftEvent;
|
||||||
import ctbrec.event.RecordingStateChangedEvent;
|
import ctbrec.event.RecordingStateChangedEvent;
|
||||||
import ctbrec.io.HttpClient;
|
import ctbrec.io.HttpClient;
|
||||||
import ctbrec.recorder.download.Download;
|
import ctbrec.recorder.download.Download;
|
||||||
|
@ -111,6 +112,7 @@ public class NextGenLocalRecorder implements Recorder {
|
||||||
if (!recordingProcesses.isEmpty() && !enoughSpaceForRecording()) {
|
if (!recordingProcesses.isEmpty() && !enoughSpaceForRecording()) {
|
||||||
LOG.info("No space left -> Stopping all recordings");
|
LOG.info("No space left -> Stopping all recordings");
|
||||||
stopRecordingProcesses();
|
stopRecordingProcesses();
|
||||||
|
EventBusHolder.BUS.post(new NoSpaceLeftEvent());
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error("Couldn't check space left on device", e);
|
LOG.error("Couldn't check space left on device", e);
|
||||||
|
@ -577,7 +579,7 @@ public class NextGenLocalRecorder implements Recorder {
|
||||||
boolean enoughSpaceForRecording() throws IOException {
|
boolean enoughSpaceForRecording() throws IOException {
|
||||||
long minimum = config.getSettings().minimumSpaceLeftInBytes;
|
long minimum = config.getSettings().minimumSpaceLeftInBytes;
|
||||||
if (minimum == 0) { // 0 means don't check
|
if (minimum == 0) { // 0 means don't check
|
||||||
return true;
|
return getFreeSpaceBytes() > 100 * 1024 * 1024; // leave at least 100 MiB free
|
||||||
} else {
|
} else {
|
||||||
return getFreeSpaceBytes() > minimum;
|
return getFreeSpaceBytes() > minimum;
|
||||||
}
|
}
|
||||||
|
@ -678,8 +680,7 @@ public class NextGenLocalRecorder implements Recorder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setNote(Recording rec, String note) throws IOException {
|
public void setNote(Recording rec, String note) throws IOException {
|
||||||
rec.setNote(note);
|
recordingManager.setNote(rec, note);
|
||||||
recordingManager.saveRecording(rec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,6 +11,8 @@ import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -217,8 +219,9 @@ public class RecordingManager {
|
||||||
public void pin(Recording recording) throws IOException {
|
public void pin(Recording recording) throws IOException {
|
||||||
recordingsLock.lock();
|
recordingsLock.lock();
|
||||||
try {
|
try {
|
||||||
recording.setPinned(true);
|
Recording local = getRecording(recording);
|
||||||
saveRecording(recording);
|
local.setPinned(true);
|
||||||
|
saveRecording(local);
|
||||||
} finally {
|
} finally {
|
||||||
recordingsLock.unlock();
|
recordingsLock.unlock();
|
||||||
}
|
}
|
||||||
|
@ -227,10 +230,31 @@ public class RecordingManager {
|
||||||
public void unpin(Recording recording) throws IOException {
|
public void unpin(Recording recording) throws IOException {
|
||||||
recordingsLock.lock();
|
recordingsLock.lock();
|
||||||
try {
|
try {
|
||||||
recording.setPinned(false);
|
Recording local = getRecording(recording);
|
||||||
saveRecording(recording);
|
local.setPinned(false);
|
||||||
|
saveRecording(local);
|
||||||
} finally {
|
} finally {
|
||||||
recordingsLock.unlock();
|
recordingsLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setNote(Recording rec, String note) throws IOException {
|
||||||
|
recordingsLock.lock();
|
||||||
|
try {
|
||||||
|
Recording local = getRecording(rec);
|
||||||
|
local.setNote(note);
|
||||||
|
saveRecording(local);
|
||||||
|
} finally {
|
||||||
|
recordingsLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Recording getRecording(Recording rec) {
|
||||||
|
for (Recording r : getAll()) {
|
||||||
|
if(Objects.equals(r, rec)) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException("Recording not found");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import ctbrec.Hmac;
|
||||||
import ctbrec.Model;
|
import ctbrec.Model;
|
||||||
import ctbrec.Recording;
|
import ctbrec.Recording;
|
||||||
import ctbrec.event.EventBusHolder;
|
import ctbrec.event.EventBusHolder;
|
||||||
|
import ctbrec.event.NoSpaceLeftEvent;
|
||||||
import ctbrec.event.RecordingStateChangedEvent;
|
import ctbrec.event.RecordingStateChangedEvent;
|
||||||
import ctbrec.io.BandwidthMeter;
|
import ctbrec.io.BandwidthMeter;
|
||||||
import ctbrec.io.HttpClient;
|
import ctbrec.io.HttpClient;
|
||||||
|
@ -57,6 +58,7 @@ public class RemoteRecorder implements Recorder {
|
||||||
private List<Site> sites;
|
private List<Site> sites;
|
||||||
private long spaceTotal = -1;
|
private long spaceTotal = -1;
|
||||||
private long spaceFree = -1;
|
private long spaceFree = -1;
|
||||||
|
private boolean noSpaceLeftDetected = false;
|
||||||
|
|
||||||
private Config config;
|
private Config config;
|
||||||
private HttpClient client;
|
private HttpClient client;
|
||||||
|
@ -184,6 +186,7 @@ public class RemoteRecorder implements Recorder {
|
||||||
private static final String COULDNT_SYNCHRONIZE_WITH_SERVER = "Couldn't synchronize with server";
|
private static final String COULDNT_SYNCHRONIZE_WITH_SERVER = "Couldn't synchronize with server";
|
||||||
private static final String COULDNT_SYNCHRONIZE_WITH_SERVER_HTTP_STATUS = "Couldn't synchronize with server. HTTP status: {} - {}";
|
private static final String COULDNT_SYNCHRONIZE_WITH_SERVER_HTTP_STATUS = "Couldn't synchronize with server. HTTP status: {} - {}";
|
||||||
private volatile boolean running = false;
|
private volatile boolean running = false;
|
||||||
|
private static final long ONE_HUNDRED_MIB = 100 * 1024 * 1024;
|
||||||
|
|
||||||
public SyncThread() {
|
public SyncThread() {
|
||||||
setName("RemoteRecorder SyncThread");
|
setName("RemoteRecorder SyncThread");
|
||||||
|
@ -218,6 +221,16 @@ public class RemoteRecorder implements Recorder {
|
||||||
long throughput = resp.getLong("throughput");
|
long throughput = resp.getLong("throughput");
|
||||||
Duration timeframe = Duration.ofSeconds(resp.getInt("throughputTimeframe"));
|
Duration timeframe = Duration.ofSeconds(resp.getInt("throughputTimeframe"));
|
||||||
BandwidthMeter.setThroughput(throughput, timeframe);
|
BandwidthMeter.setThroughput(throughput, timeframe);
|
||||||
|
long minimumSpaceLeftInBytes = resp.optLong("minimumSpaceLeftInBytes");
|
||||||
|
if (minimumSpaceLeftInBytes > 0 && minimumSpaceLeftInBytes >= spaceFree
|
||||||
|
|| minimumSpaceLeftInBytes == 0 && spaceFree < ONE_HUNDRED_MIB) {
|
||||||
|
if (!noSpaceLeftDetected) {
|
||||||
|
EventBusHolder.BUS.post(new NoSpaceLeftEvent());
|
||||||
|
}
|
||||||
|
noSpaceLeftDetected = true;
|
||||||
|
} else {
|
||||||
|
noSpaceLeftDetected = false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.error(COULDNT_SYNCHRONIZE_WITH_SERVER_HTTP_STATUS, response.code(), json);
|
LOG.error(COULDNT_SYNCHRONIZE_WITH_SERVER_HTTP_STATUS, response.code(), json);
|
||||||
}
|
}
|
||||||
|
@ -319,6 +332,18 @@ public class RemoteRecorder implements Recorder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// fire recording started event
|
||||||
|
List<Recording> justStarted = new ArrayList<>(newRecordings);
|
||||||
|
justStarted.removeAll(recordings);
|
||||||
|
for (Recording recording : justStarted) {
|
||||||
|
if (recording.getStatus() == Recording.State.RECORDING) {
|
||||||
|
File file = new File(recording.getPath());
|
||||||
|
RecordingStateChangedEvent evt = new RecordingStateChangedEvent(file, recording.getStatus(), recording.getModel(),
|
||||||
|
recording.getStartDate());
|
||||||
|
EventBusHolder.BUS.post(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
recordings = newRecordings;
|
recordings = newRecordings;
|
||||||
} else {
|
} else {
|
||||||
LOG.error(SERVER_RETURNED_ERROR, resp.status, resp.msg);
|
LOG.error(SERVER_RETURNED_ERROR, resp.status, resp.msg);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ctbrec.sites.manyvids;
|
package ctbrec.sites.manyvids;
|
||||||
|
|
||||||
|
import static ctbrec.StringUtil.*;
|
||||||
import static ctbrec.io.HttpConstants.*;
|
import static ctbrec.io.HttpConstants.*;
|
||||||
import static ctbrec.sites.manyvids.MVLive.*;
|
import static ctbrec.sites.manyvids.MVLive.*;
|
||||||
|
|
||||||
|
@ -182,7 +183,7 @@ public class MVLiveClient {
|
||||||
String respJson = jsonArray.getString(i);
|
String respJson = jsonArray.getString(i);
|
||||||
JSONObject response = new JSONObject(respJson);
|
JSONObject response = new JSONObject(respJson);
|
||||||
String address = response.optString("address");
|
String address = response.optString("address");
|
||||||
if (!address.isBlank()) {
|
if (isNotBlank(address)) {
|
||||||
Message message = futureResponses.get(address);
|
Message message = futureResponses.get(address);
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
message.handleResponse(response);
|
message.handleResponse(response);
|
||||||
|
|
|
@ -320,6 +320,7 @@ public class MyFreeCamsClient {
|
||||||
LOG.error("Error while decoding ctxenc URL", e);
|
LOG.error("Error while decoding ctxenc URL", e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.error("Exception occured while processing websocket message {}", msgBuffer, e);
|
LOG.error("Exception occured while processing websocket message {}", msgBuffer, e);
|
||||||
|
ws.close(1000, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import static ctbrec.Model.State.*;
|
||||||
import static ctbrec.io.HttpConstants.*;
|
import static ctbrec.io.HttpConstants.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
@ -52,23 +53,21 @@ public class Showup extends AbstractSite {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Model createModel(String name) {
|
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);
|
|
||||||
ShowupModel model = new ShowupModel();
|
ShowupModel model = new ShowupModel();
|
||||||
model.setSite(this);
|
model.setSite(this);
|
||||||
model.setName(name);
|
model.setName(name);
|
||||||
model.setUrl(BASE_URL + '/' + URLEncoder.encode(name, StandardCharsets.UTF_8));
|
model.setUrl(BASE_URL + '/' + safeUrlEncode(name));
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String safeUrlEncode(String s) {
|
||||||
|
try {
|
||||||
|
return URLEncoder.encode(s, StandardCharsets.UTF_8.name());
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<Model> getModelList() throws IOException {
|
public List<Model> getModelList() throws IOException {
|
||||||
if(Duration.between(lastModelListUpdate, Instant.now()).getSeconds() > 10) {
|
if(Duration.between(lastModelListUpdate, Instant.now()).getSeconds() > 10) {
|
||||||
lastModelListUpdate = Instant.now();
|
lastModelListUpdate = Instant.now();
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.json.JSONObject;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ctbrec.ReflectionUtil;
|
import ctbrec.ReflectionUtil;
|
||||||
|
import ctbrec.StringUtil;
|
||||||
|
|
||||||
public class MyFreeCamsClientTest {
|
public class MyFreeCamsClientTest {
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ public class MyFreeCamsClientTest {
|
||||||
assertEquals(439895060, msg.getReceiver());
|
assertEquals(439895060, msg.getReceiver());
|
||||||
assertEquals(1, msg.getArg1());
|
assertEquals(1, msg.getArg1());
|
||||||
assertEquals(0, msg.getArg2());
|
assertEquals(0, msg.getArg2());
|
||||||
assertTrue(msg.getMessage().isBlank());
|
assertTrue(StringUtil.isBlank(msg.getMessage()));
|
||||||
|
|
||||||
input = new StringBuilder("00207820 439461784 439895060 12 507930 %7B%22lv%22%3A4%2C%22nm%22%3A%22Nivea%22%2C%22pid%22%3A1%2C%22sid%22%3A439461784%2C%22uid%22%3A507930%2C%22vs%22%3A12%2C%22u%22%3A%7B%22age%22%3A33%2C%22avatar%22%3A1%2C%22blurb%22%3A%22I%20love%20when%20my%20nose%20touch%20your%20belly%20when%20I%20do%20you%20a%20blowjob!%20When%20I%20look%20into%20your%20horny%20eyes%20when%22%2C%22camserv%22%3A1367%2C%22chat_color%22%3A%22FF0000%22%2C%22chat_font%22%3A0%2C%22chat_opt%22%3A1%2C%22ethnic%22%3A%22Caucasian%22%2C%22photos%22%3A74%2C%22profile%22%3A1%2C%22status%22%3A%22%22%7D%2C%22m%22%3A%7B%22camscore%22%3A7505.700%2C%22continent%22%3A%22EU%22%2C%22flags%22%3A605224%2C%22hidecs%22%3Atrue%2C%22kbit%22%3A0%2C%22lastnews%22%3A0%2C%22mg%22%3A0%2C%22missmfc%22%3A2%2C%22new_model%22%3A0%2C%22rank%22%3A0%2C%22rc%22%3A20%2C%22sfw%22%3A0%2C%22topic%22%3A%22hi%253A)%255Bnone%255D-Topless%252C500-snap4life%252C50-spanks%252C180-flash%252C700-10%2520mins%2520of%2520Nora%2520fun%252C666-shot%252C27%252C270%2520%253C3%22%7D%2C%22x%22%3A%7B%22fcext%22%3A%7B%22sm%22%3A%22%22%2C%22sfw%22%3A0%7D%2C%22share%22%3A%7B%22follows%22%3A11%2C%22albums%22%3A0%2C%22clubs%22%3A0%2C%22tm_album%22%3A0%2C%22collections%22%3A0%2C%22stores%22%3A0%2C%22goals%22%3A0%2C%22polls%22%3A0%2C%22things%22%3A0%2C%22recent_album_tm%22%3A0%2C%22recent_club_tm%22%3A0%2C%22recent_collection_tm%22%3A0%2C%22recent_goal_tm%22%3A0%2C%22recent_item_tm%22%3A0%2C%22recent_poll_tm%22%3A0%2C%22recent_story_tm%22%3A0%2C%22recent_album_thumb%22%3A%22%22%2C%22recent_club_thumb%22%3A%22%22%2C%22recent_collection_thumb%22%3A%22%22%2C%22recent_goal_thumb%22%3A%22%22%2C%22recent_item_thumb%22%3A%22%22%2C%22recent_poll_thumb%22%3A%22%22%2C%22recent_story_thumb%22%3A%22%22%2C%22recent_album_title%22%3A%22%22%2C%22recent_club_title%22%3A%22%22%2C%22recent_collection_title%22%3A%22%22%2C%22recent_goal_title%22%3A%22%22%2C%22recent_item_title%22%3A%22%22%2C%22recent_poll_title%22%3A%22%22%2C%22recent_story_title%22%3A%22%22%2C%22recent_album_slug%22%3A%22%22%2C%22recent_collection_slug%22%3A%22%22%2C%22tipmenus%22%3A0%7D%7D%7D");
|
input = new StringBuilder("00207820 439461784 439895060 12 507930 %7B%22lv%22%3A4%2C%22nm%22%3A%22Nivea%22%2C%22pid%22%3A1%2C%22sid%22%3A439461784%2C%22uid%22%3A507930%2C%22vs%22%3A12%2C%22u%22%3A%7B%22age%22%3A33%2C%22avatar%22%3A1%2C%22blurb%22%3A%22I%20love%20when%20my%20nose%20touch%20your%20belly%20when%20I%20do%20you%20a%20blowjob!%20When%20I%20look%20into%20your%20horny%20eyes%20when%22%2C%22camserv%22%3A1367%2C%22chat_color%22%3A%22FF0000%22%2C%22chat_font%22%3A0%2C%22chat_opt%22%3A1%2C%22ethnic%22%3A%22Caucasian%22%2C%22photos%22%3A74%2C%22profile%22%3A1%2C%22status%22%3A%22%22%7D%2C%22m%22%3A%7B%22camscore%22%3A7505.700%2C%22continent%22%3A%22EU%22%2C%22flags%22%3A605224%2C%22hidecs%22%3Atrue%2C%22kbit%22%3A0%2C%22lastnews%22%3A0%2C%22mg%22%3A0%2C%22missmfc%22%3A2%2C%22new_model%22%3A0%2C%22rank%22%3A0%2C%22rc%22%3A20%2C%22sfw%22%3A0%2C%22topic%22%3A%22hi%253A)%255Bnone%255D-Topless%252C500-snap4life%252C50-spanks%252C180-flash%252C700-10%2520mins%2520of%2520Nora%2520fun%252C666-shot%252C27%252C270%2520%253C3%22%7D%2C%22x%22%3A%7B%22fcext%22%3A%7B%22sm%22%3A%22%22%2C%22sfw%22%3A0%7D%2C%22share%22%3A%7B%22follows%22%3A11%2C%22albums%22%3A0%2C%22clubs%22%3A0%2C%22tm_album%22%3A0%2C%22collections%22%3A0%2C%22stores%22%3A0%2C%22goals%22%3A0%2C%22polls%22%3A0%2C%22things%22%3A0%2C%22recent_album_tm%22%3A0%2C%22recent_club_tm%22%3A0%2C%22recent_collection_tm%22%3A0%2C%22recent_goal_tm%22%3A0%2C%22recent_item_tm%22%3A0%2C%22recent_poll_tm%22%3A0%2C%22recent_story_tm%22%3A0%2C%22recent_album_thumb%22%3A%22%22%2C%22recent_club_thumb%22%3A%22%22%2C%22recent_collection_thumb%22%3A%22%22%2C%22recent_goal_thumb%22%3A%22%22%2C%22recent_item_thumb%22%3A%22%22%2C%22recent_poll_thumb%22%3A%22%22%2C%22recent_story_thumb%22%3A%22%22%2C%22recent_album_title%22%3A%22%22%2C%22recent_club_title%22%3A%22%22%2C%22recent_collection_title%22%3A%22%22%2C%22recent_goal_title%22%3A%22%22%2C%22recent_item_title%22%3A%22%22%2C%22recent_poll_title%22%3A%22%22%2C%22recent_story_title%22%3A%22%22%2C%22recent_album_slug%22%3A%22%22%2C%22recent_collection_slug%22%3A%22%22%2C%22tipmenus%22%3A0%7D%7D%7D");
|
||||||
msg = ReflectionUtil.call(client, "parseMessage", input);
|
msg = ReflectionUtil.call(client, "parseMessage", input);
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import com.squareup.moshi.JsonAdapter;
|
import com.squareup.moshi.JsonAdapter;
|
||||||
import com.squareup.moshi.Moshi;
|
import com.squareup.moshi.Moshi;
|
||||||
|
|
||||||
|
import ctbrec.Config;
|
||||||
import ctbrec.Model;
|
import ctbrec.Model;
|
||||||
import ctbrec.Recording;
|
import ctbrec.Recording;
|
||||||
import ctbrec.io.BandwidthMeter;
|
import ctbrec.io.BandwidthMeter;
|
||||||
|
@ -211,6 +212,7 @@ public class RecorderServlet extends AbstractCtbrecServlet {
|
||||||
jsonResponse.put("spaceFree", recorder.getFreeSpaceBytes());
|
jsonResponse.put("spaceFree", recorder.getFreeSpaceBytes());
|
||||||
jsonResponse.put("throughput", BandwidthMeter.getThroughput());
|
jsonResponse.put("throughput", BandwidthMeter.getThroughput());
|
||||||
jsonResponse.put("throughputTimeframe", BandwidthMeter.MEASURE_TIMEFRAME.getSeconds());
|
jsonResponse.put("throughputTimeframe", BandwidthMeter.MEASURE_TIMEFRAME.getSeconds());
|
||||||
|
jsonResponse.put("minimumSpaceLeftInBytes", Config.getInstance().getSettings().minimumSpaceLeftInBytes);
|
||||||
resp.getWriter().write(jsonResponse.toString());
|
resp.getWriter().write(jsonResponse.toString());
|
||||||
break;
|
break;
|
||||||
case "changePriority":
|
case "changePriority":
|
||||||
|
|
Loading…
Reference in New Issue