diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d9bec8e..5fc1f8af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,24 @@ 3.9.0 ======================== * 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: * login / favorites + * tipping * media player isn't working because of their authetication mechanism * 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 -* 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 ======================== diff --git a/client/src/main/java/ctbrec/ui/CamrecApplication.java b/client/src/main/java/ctbrec/ui/CamrecApplication.java index d5bb44cb..d1f04896 100644 --- a/client/src/main/java/ctbrec/ui/CamrecApplication.java +++ b/client/src/main/java/ctbrec/ui/CamrecApplication.java @@ -14,7 +14,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; -import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -317,24 +316,12 @@ public class CamrecApplication extends Application { } private void registerAlertSystem() { - new Thread(() -> { - try { - // don't register before 1 minute has passed, because directly after - // the start of ctbrec, an event for every online model would be fired, - // which is annoying as f - Thread.sleep(TimeUnit.MINUTES.toMillis(1)); - - 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(); + 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"); } private void registerActiveRecordingsCounter() { diff --git a/client/src/main/java/ctbrec/ui/event/ShowNotification.java b/client/src/main/java/ctbrec/ui/event/ShowNotification.java index 58c371ae..6a10559a 100644 --- a/client/src/main/java/ctbrec/ui/event/ShowNotification.java +++ b/client/src/main/java/ctbrec/ui/event/ShowNotification.java @@ -22,6 +22,9 @@ public class ShowNotification extends Action { switch(evt.getType()) { case MODEL_STATUS_CHANGED: ModelStateChangedEvent modelEvent = (ModelStateChangedEvent) evt; + if (modelEvent.getOldState() == Model.State.UNKNOWN) { + return; + } Model m = modelEvent.getModel(); msg = m.getDisplayName() + " is now " + modelEvent.getNewState().toString(); break; diff --git a/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java b/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java index 6e945d46..8ef600e4 100644 --- a/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java +++ b/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java @@ -23,6 +23,7 @@ import ctbrec.event.EventHandlerConfiguration; import ctbrec.event.EventHandlerConfiguration.ActionConfiguration; import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration; import ctbrec.event.ExecuteProgram; +import ctbrec.event.MatchAllPredicate; import ctbrec.event.ModelPredicate; import ctbrec.event.ModelStatePredicate; import ctbrec.event.RecordingStatePredicate; @@ -152,6 +153,11 @@ public class ActionSettingsPanel extends GridPane { pc.getConfiguration().put("state", recordingState.getValue().name()); pc.setName("state = " + recordingState.getValue().toString()); 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()) { PredicateConfiguration pc = new PredicateConfiguration(); @@ -200,7 +206,7 @@ public class ActionSettingsPanel extends GridPane { if(event.getValue() == Event.Type.RECORDING_STATUS_CHANGED && recordingState.getValue() == null) { 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\""); } if(!(showNotification.isSelected() || playSound.isSelected() || executeProgram.isSelected())) { @@ -231,10 +237,23 @@ public class ActionSettingsPanel extends GridPane { event.getItems().clear(); event.getItems().add(Event.Type.MODEL_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.getSelectionModel().select(Event.Type.MODEL_STATUS_CHANGED); 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); modelState.getItems().clear(); modelState.getItems().addAll(Model.State.values()); diff --git a/client/src/main/java/ctbrec/ui/settings/ListSelectionPane.java b/client/src/main/java/ctbrec/ui/settings/ListSelectionPane.java index e5cd9dc1..58c61824 100644 --- a/client/src/main/java/ctbrec/ui/settings/ListSelectionPane.java +++ b/client/src/main/java/ctbrec/ui/settings/ListSelectionPane.java @@ -118,4 +118,8 @@ public class ListSelectionPane> extends GridPane { public boolean isAllSelected() { return selectAll.isSelected(); } + + public void selectAll() { + selectAll.setSelected(true); + } } diff --git a/client/src/main/java/ctbrec/ui/sites/cam4/Cam4UpdateService.java b/client/src/main/java/ctbrec/ui/sites/cam4/Cam4UpdateService.java index 2317ff7c..6916f14a 100644 --- a/client/src/main/java/ctbrec/ui/sites/cam4/Cam4UpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/cam4/Cam4UpdateService.java @@ -1,9 +1,13 @@ package ctbrec.ui.sites.cam4; +import static ctbrec.Model.State.*; +import static ctbrec.io.HttpConstants.*; + import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; @@ -64,7 +68,11 @@ public class Cam4UpdateService extends PaginatedScheduledService { if(loginRequired) { 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)) { if (response.isSuccessful()) { JSONObject json = new JSONObject(response.body().string()); @@ -76,11 +84,15 @@ public class Cam4UpdateService extends PaginatedScheduledService { Element profileLink = HtmlParser.getTag(boxHtml, "a.profile-preview"); String path = profileLink.attr("href"); String slug = path.substring(1); - Cam4Model model = (Cam4Model) site.createModel(slug); + Cam4Model model = site.createModel(slug); String playlistUrl = profileLink.attr("data-hls-preview-url"); model.setPlaylistUrl(playlistUrl); model.setPreview("https://snapshots.xcdnpro.com/thumbnails/" + model.getName() + "?s=" + System.currentTimeMillis()); model.setDescription(parseDesription(boxHtml)); + model.setOnlineState(ONLINE); + if(boxHtml.contains("In private show")) { + model.setOnlineState(PRIVATE); + } models.add(model); } return models; diff --git a/client/src/main/java/ctbrec/ui/sites/streamate/StreamateFollowedService.java b/client/src/main/java/ctbrec/ui/sites/streamate/StreamateFollowedService.java index 6548f0e8..da94fdc6 100644 --- a/client/src/main/java/ctbrec/ui/sites/streamate/StreamateFollowedService.java +++ b/client/src/main/java/ctbrec/ui/sites/streamate/StreamateFollowedService.java @@ -1,5 +1,6 @@ package ctbrec.ui.sites.streamate; +import static ctbrec.Model.State.*; import static ctbrec.io.HttpConstants.*; import java.io.IOException; @@ -81,6 +82,7 @@ public class StreamateFollowedService extends PaginatedScheduledService { model.setPreview(p.getString("thumbnail")); boolean online = p.optBoolean("online") && notPrivateEtc(p); model.setOnline(online); + model.setOnlineState(online ? ONLINE : OFFLINE); if (online == showOnline) { models.add(model); } diff --git a/client/src/main/java/ctbrec/ui/sites/streamate/StreamateUpdateService.java b/client/src/main/java/ctbrec/ui/sites/streamate/StreamateUpdateService.java index 997f03e5..968046c9 100644 --- a/client/src/main/java/ctbrec/ui/sites/streamate/StreamateUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/streamate/StreamateUpdateService.java @@ -1,5 +1,7 @@ package ctbrec.ui.sites.streamate; +import static ctbrec.Model.State.*; + import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -52,6 +54,7 @@ public class StreamateUpdateService extends PaginatedScheduledService { List models = new ArrayList<>(); String content = response.body().string(); JSONObject json = new JSONObject(content); + System.err.println(json.toString(2)); JSONArray performers = json.getJSONArray("performers"); for (int i = 0; i < performers.length(); i++) { JSONObject p = performers.getJSONObject(i); @@ -59,7 +62,9 @@ public class StreamateUpdateService extends PaginatedScheduledService { StreamateModel model = (StreamateModel) streamate.createModel(nickname); model.setId(p.getLong("id")); 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 // liveState {…} // exclusiveShow false @@ -69,6 +74,11 @@ public class StreamateUpdateService extends PaginatedScheduledService { // preGoldShow true // privateChat 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); } return models; diff --git a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatFollowedUpdateService.java b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatFollowedUpdateService.java index 80b788ba..1efcbed1 100644 --- a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatFollowedUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatFollowedUpdateService.java @@ -1,5 +1,6 @@ package ctbrec.ui.sites.stripchat; +import static ctbrec.Model.State.*; import static ctbrec.io.HttpConstants.*; import java.io.IOException; @@ -68,6 +69,7 @@ public class StripchatFollowedUpdateService extends PaginatedScheduledService { try (Response response = stripchat.getHttpClient().execute(request)) { if (response.isSuccessful()) { JSONObject json = new JSONObject(response.body().string()); + System.err.println(json.toString(2)); if (json.has("models")) { JSONArray users = json.getJSONArray("models"); for (int i = 0; i < users.length(); i++) { @@ -75,6 +77,7 @@ public class StripchatFollowedUpdateService extends PaginatedScheduledService { StripchatModel model = stripchat.createModel(user.optString("username")); model.setDescription(user.optString("description")); model.setPreview(user.optString("previewUrlThumbBig")); + model.setOnlineState(mapStatus(user.optString("status"))); 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; + } + } } diff --git a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatUpdateService.java b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatUpdateService.java index 4a74b7a2..f44f2b4b 100644 --- a/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/stripchat/StripchatUpdateService.java @@ -75,6 +75,7 @@ public class StripchatUpdateService extends PaginatedScheduledService { model.setDescription(""); model.setPreview(jsonModel.optString("snapshotUrl")); model.setDisplayName(model.getName()); + model.setOnlineState(Model.State.ONLINE); models.add(model); } catch (Exception e) { LOG.warn("Couldn't parse one of the models: {}", jsonModel, e); diff --git a/common/src/main/java/ctbrec/event/Event.java b/common/src/main/java/ctbrec/event/Event.java index 9e67d032..f5f00710 100644 --- a/common/src/main/java/ctbrec/event/Event.java +++ b/common/src/main/java/ctbrec/event/Event.java @@ -18,7 +18,12 @@ public abstract class Event { /** * 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; diff --git a/common/src/main/java/ctbrec/event/MatchAllPredicate.java b/common/src/main/java/ctbrec/event/MatchAllPredicate.java new file mode 100644 index 00000000..c9141893 --- /dev/null +++ b/common/src/main/java/ctbrec/event/MatchAllPredicate.java @@ -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 + } + +} diff --git a/common/src/main/java/ctbrec/event/NoSpaceLeftEvent.java b/common/src/main/java/ctbrec/event/NoSpaceLeftEvent.java new file mode 100644 index 00000000..1f6ecbb5 --- /dev/null +++ b/common/src/main/java/ctbrec/event/NoSpaceLeftEvent.java @@ -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]; + } + +} diff --git a/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java b/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java index 4118a00e..f337c7c6 100644 --- a/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java +++ b/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java @@ -49,6 +49,7 @@ import ctbrec.Recording.State; import ctbrec.event.Event; import ctbrec.event.EventBusHolder; import ctbrec.event.ModelIsOnlineEvent; +import ctbrec.event.NoSpaceLeftEvent; import ctbrec.event.RecordingStateChangedEvent; import ctbrec.io.HttpClient; import ctbrec.recorder.download.Download; @@ -111,6 +112,7 @@ public class NextGenLocalRecorder implements Recorder { if (!recordingProcesses.isEmpty() && !enoughSpaceForRecording()) { LOG.info("No space left -> Stopping all recordings"); stopRecordingProcesses(); + EventBusHolder.BUS.post(new NoSpaceLeftEvent()); } } catch (IOException e) { LOG.error("Couldn't check space left on device", e); @@ -577,7 +579,7 @@ public class NextGenLocalRecorder implements Recorder { boolean enoughSpaceForRecording() throws IOException { long minimum = config.getSettings().minimumSpaceLeftInBytes; if (minimum == 0) { // 0 means don't check - return true; + return getFreeSpaceBytes() > 100 * 1024 * 1024; // leave at least 100 MiB free } else { return getFreeSpaceBytes() > minimum; } @@ -678,8 +680,7 @@ public class NextGenLocalRecorder implements Recorder { @Override public void setNote(Recording rec, String note) throws IOException { - rec.setNote(note); - recordingManager.saveRecording(rec); + recordingManager.setNote(rec, note); } @Override diff --git a/common/src/main/java/ctbrec/recorder/RecordingManager.java b/common/src/main/java/ctbrec/recorder/RecordingManager.java index 059c7a36..a710344d 100644 --- a/common/src/main/java/ctbrec/recorder/RecordingManager.java +++ b/common/src/main/java/ctbrec/recorder/RecordingManager.java @@ -11,6 +11,8 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; @@ -217,8 +219,9 @@ public class RecordingManager { public void pin(Recording recording) throws IOException { recordingsLock.lock(); try { - recording.setPinned(true); - saveRecording(recording); + Recording local = getRecording(recording); + local.setPinned(true); + saveRecording(local); } finally { recordingsLock.unlock(); } @@ -227,10 +230,31 @@ public class RecordingManager { public void unpin(Recording recording) throws IOException { recordingsLock.lock(); try { - recording.setPinned(false); - saveRecording(recording); + Recording local = getRecording(recording); + local.setPinned(false); + saveRecording(local); } finally { 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"); + } } diff --git a/common/src/main/java/ctbrec/recorder/RemoteRecorder.java b/common/src/main/java/ctbrec/recorder/RemoteRecorder.java index f331439e..5cec3296 100644 --- a/common/src/main/java/ctbrec/recorder/RemoteRecorder.java +++ b/common/src/main/java/ctbrec/recorder/RemoteRecorder.java @@ -24,6 +24,7 @@ import ctbrec.Hmac; import ctbrec.Model; import ctbrec.Recording; import ctbrec.event.EventBusHolder; +import ctbrec.event.NoSpaceLeftEvent; import ctbrec.event.RecordingStateChangedEvent; import ctbrec.io.BandwidthMeter; import ctbrec.io.HttpClient; @@ -57,6 +58,7 @@ public class RemoteRecorder implements Recorder { private List sites; private long spaceTotal = -1; private long spaceFree = -1; + private boolean noSpaceLeftDetected = false; private Config config; 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_HTTP_STATUS = "Couldn't synchronize with server. HTTP status: {} - {}"; private volatile boolean running = false; + private static final long ONE_HUNDRED_MIB = 100 * 1024 * 1024; public SyncThread() { setName("RemoteRecorder SyncThread"); @@ -218,6 +221,16 @@ public class RemoteRecorder implements Recorder { long throughput = resp.getLong("throughput"); Duration timeframe = Duration.ofSeconds(resp.getInt("throughputTimeframe")); 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 { 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 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; } else { LOG.error(SERVER_RETURNED_ERROR, resp.status, resp.msg); diff --git a/common/src/main/java/ctbrec/sites/manyvids/MVLiveClient.java b/common/src/main/java/ctbrec/sites/manyvids/MVLiveClient.java index 4b0180a5..370c00b0 100644 --- a/common/src/main/java/ctbrec/sites/manyvids/MVLiveClient.java +++ b/common/src/main/java/ctbrec/sites/manyvids/MVLiveClient.java @@ -1,5 +1,6 @@ package ctbrec.sites.manyvids; +import static ctbrec.StringUtil.*; import static ctbrec.io.HttpConstants.*; import static ctbrec.sites.manyvids.MVLive.*; @@ -182,7 +183,7 @@ public class MVLiveClient { String respJson = jsonArray.getString(i); JSONObject response = new JSONObject(respJson); String address = response.optString("address"); - if (!address.isBlank()) { + if (isNotBlank(address)) { Message message = futureResponses.get(address); if (message != null) { message.handleResponse(response); diff --git a/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsClient.java b/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsClient.java index bea83a7f..5cc44079 100644 --- a/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsClient.java +++ b/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsClient.java @@ -320,6 +320,7 @@ public class MyFreeCamsClient { LOG.error("Error while decoding ctxenc URL", e); } catch (Exception e) { LOG.error("Exception occured while processing websocket message {}", msgBuffer, e); + ws.close(1000, ""); } } diff --git a/common/src/main/java/ctbrec/sites/showup/Showup.java b/common/src/main/java/ctbrec/sites/showup/Showup.java index 3272c4e6..812a17a0 100644 --- a/common/src/main/java/ctbrec/sites/showup/Showup.java +++ b/common/src/main/java/ctbrec/sites/showup/Showup.java @@ -4,6 +4,7 @@ import static ctbrec.Model.State.*; import static ctbrec.io.HttpConstants.*; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.time.Duration; @@ -52,23 +53,21 @@ public class Showup extends AbstractSite { @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); ShowupModel model = new ShowupModel(); model.setSite(this); model.setName(name); - model.setUrl(BASE_URL + '/' + URLEncoder.encode(name, StandardCharsets.UTF_8)); + model.setUrl(BASE_URL + '/' + safeUrlEncode(name)); return model; } + private String safeUrlEncode(String s) { + try { + return URLEncoder.encode(s, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + return s; + } + } + public List getModelList() throws IOException { if(Duration.between(lastModelListUpdate, Instant.now()).getSeconds() > 10) { lastModelListUpdate = Instant.now(); diff --git a/common/src/test/java/ctbrec/sites/mfc/MyFreeCamsClientTest.java b/common/src/test/java/ctbrec/sites/mfc/MyFreeCamsClientTest.java index 7e171ffa..76633e43 100644 --- a/common/src/test/java/ctbrec/sites/mfc/MyFreeCamsClientTest.java +++ b/common/src/test/java/ctbrec/sites/mfc/MyFreeCamsClientTest.java @@ -8,6 +8,7 @@ import org.json.JSONObject; import org.junit.Test; import ctbrec.ReflectionUtil; +import ctbrec.StringUtil; public class MyFreeCamsClientTest { @@ -22,7 +23,7 @@ public class MyFreeCamsClientTest { assertEquals(439895060, msg.getReceiver()); assertEquals(1, msg.getArg1()); 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"); msg = ReflectionUtil.call(client, "parseMessage", input); diff --git a/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java b/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java index 0e8bf92f..d42478bd 100644 --- a/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java +++ b/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java @@ -21,6 +21,7 @@ import org.slf4j.LoggerFactory; import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.Moshi; +import ctbrec.Config; import ctbrec.Model; import ctbrec.Recording; import ctbrec.io.BandwidthMeter; @@ -211,6 +212,7 @@ public class RecorderServlet extends AbstractCtbrecServlet { jsonResponse.put("spaceFree", recorder.getFreeSpaceBytes()); jsonResponse.put("throughput", BandwidthMeter.getThroughput()); jsonResponse.put("throughputTimeframe", BandwidthMeter.MEASURE_TIMEFRAME.getSeconds()); + jsonResponse.put("minimumSpaceLeftInBytes", Config.getInstance().getSettings().minimumSpaceLeftInBytes); resp.getWriter().write(jsonResponse.toString()); break; case "changePriority":