diff --git a/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java b/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java index 9a8784e4..b182b1da 100644 --- a/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java +++ b/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java @@ -62,7 +62,6 @@ public class DashDownload extends AbstractDownload { private boolean running = false; private File targetFile; - private File finalFile; public DashDownload(HttpClient httpClient, String manifestUrl) { this.httpClient = httpClient; @@ -92,7 +91,7 @@ public class DashDownload extends AbstractDownload { throw httpException; } else { LOG.debug("Couldn't load manifest", httpException); - waitSomeTime(100 * tries); + waitSomeTime(100l * tries); } } else { internalStop(); @@ -114,7 +113,7 @@ public class DashDownload extends AbstractDownload { int downloaded = downloadInitChunksForVideoAndAudio(isVideo, mpd, segmentTemplate, representation); String media = segmentTemplate.getMedia(); - media = media.replaceAll("\\$RepresentationID\\$", representation.getId()); // NOSONAR + media = media.replace("$RepresentationID$", representation.getId()); // NOSONAR List segments = segmentTemplate.getSegmentTimeline().getS(); if (!segments.isEmpty()) { @@ -128,7 +127,7 @@ public class DashDownload extends AbstractDownload { } else { lastAudioTimestamp = timestamp; } - String segmentUrl = media.replaceAll("\\$Time\\$", timestamp.toString()); + String segmentUrl = media.replace("$Time$", timestamp.toString()); duration = s.getD(); URL absUrl = new URL(new URL(mpd.getLocation().get(0)), segmentUrl); download(downloadDir.toFile().getCanonicalPath(), absUrl, isVideo); @@ -157,7 +156,7 @@ public class DashDownload extends AbstractDownload { int loadedFileCount = 0; if (isVideo && !videoInitLoaded || !isVideo && !audioInitLoaded) { String initialization = segmentTemplate.getInitializationAttribute(); - initialization = initialization.replaceAll("\\$RepresentationID\\$", representation.getId()); + initialization = initialization.replace("$RepresentationID$", representation.getId()); URL initUrl = new URL(new URL(mpd.getLocation().get(0)), initialization); File file = download(downloadDir.toFile().getCanonicalPath(), initUrl, isVideo); if (file != null) { @@ -198,7 +197,7 @@ public class DashDownload extends AbstractDownload { try (Response response = httpClient.execute(request)) { if (!response.isSuccessful()) { LOG.trace("Loading segment failed, try {}, {} size:{} {}", tries, response.code(), response.headers().values(CONTENT_LENGTH), url); - waitSomeTime(tries * 80); + waitSomeTime(tries * 80l); } else { InputStream in = response.body().byteStream(); String absFile = url.getFile(); @@ -229,7 +228,7 @@ public class DashDownload extends AbstractDownload { this.config = config; this.model = model; this.startTime = startTime; - finalFile = Config.getInstance().getFileForRecording(model, "mp4", startTime); + File finalFile = Config.getInstance().getFileForRecording(model, "mp4", startTime); targetFile = new File(finalFile.getParentFile(), finalFile.getName() + ".part"); downloadDir = targetFile.toPath(); } @@ -260,6 +259,9 @@ public class DashDownload extends AbstractDownload { } else { throw e; } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOG.error("Error while downloading dash stream", e); } catch (Exception e) { LOG.error("Error while downloading dash stream", e); } finally { @@ -422,8 +424,7 @@ public class DashDownload extends AbstractDownload { @Override public void finalizeDownload() { - // TODO Auto-generated method stub - + // nothing to do here } } diff --git a/common/src/main/java/ctbrec/sites/flirt4free/Flirt4FreeModel.java b/common/src/main/java/ctbrec/sites/flirt4free/Flirt4FreeModel.java index 22a6e831..efb49d55 100644 --- a/common/src/main/java/ctbrec/sites/flirt4free/Flirt4FreeModel.java +++ b/common/src/main/java/ctbrec/sites/flirt4free/Flirt4FreeModel.java @@ -51,7 +51,7 @@ public class Flirt4FreeModel extends AbstractModel { private String streamHost; private String streamUrl; int[] resolution = new int[2]; - private Object monitor = new Object(); + private transient Object monitor = new Object(); private boolean online = false; private boolean isInteractiveShow = false; private boolean isNew = false; @@ -81,21 +81,7 @@ public class Flirt4FreeModel extends AbstractModel { .build(); try (Response response = getSite().getHttpClient().execute(request)) { if (response.isSuccessful()) { - String body = response.body().string(); - if (body.trim().isEmpty()) { - return false; - } - JSONObject json = new JSONObject(body); - online = Objects.equals(json.optString("status"), "online"); // online is true, even if the model is in private or away - updateModelId(json); - if (online) { - try { - loadModelInfo(); - } catch (Exception e) { - online = false; - onlineState = Model.State.OFFLINE; - } - } + parseOnlineState(response.body().string()); } else { throw new HttpException(response.code(), response.message()); } @@ -108,6 +94,23 @@ public class Flirt4FreeModel extends AbstractModel { return online; } + private void parseOnlineState(String body) { + if (body.trim().isEmpty()) { + return; + } + JSONObject json = new JSONObject(body); + online = Objects.equals(json.optString("status"), "online"); // online is true, even if the model is in private or away + updateModelId(json); + if (online) { + try { + loadModelInfo(); + } catch (Exception e) { + online = false; + onlineState = Model.State.OFFLINE; + } + } + } + private void updateModelId(JSONObject json) { if (json.has("model_id")) { Object modelId = json.get("model_id"); @@ -120,7 +123,7 @@ public class Flirt4FreeModel extends AbstractModel { } } - private void loadModelInfo() throws IOException, InterruptedException { + private void loadModelInfo() throws IOException { String url = getSite().getBaseUrl() + "/webservices/chat-room-interface.php?a=login_room&model_id=" + id; LOG.trace("Loading url {}", url); Request request = new Request.Builder() @@ -135,7 +138,6 @@ public class Flirt4FreeModel extends AbstractModel { if (response.isSuccessful()) { JSONObject json = new JSONObject(response.body().string()); if (json.optString("status").equals("success")) { - // LOG.debug("chat-room-interface {}", json.toString(2)); JSONObject config = json.getJSONObject("config"); JSONObject performer = config.getJSONObject("performer"); setUrl(getSite().getBaseUrl() + "/rooms/" + getName() + '/'); @@ -191,6 +193,7 @@ public class Flirt4FreeModel extends AbstractModel { } masterPlaylist = getMasterPlaylist(); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); throw new ExecutionException(e); } List sources = new ArrayList<>(); @@ -236,7 +239,7 @@ public class Flirt4FreeModel extends AbstractModel { private void loadStreamUrl() throws IOException, InterruptedException { loadModelInfo(); Objects.requireNonNull(chatHost, "chatHost is null"); - String h = chatHost.replaceAll("chat", "chat-vip"); + String h = chatHost.replace("chat", "chat-vip"); String url = "https://" + h + "/chat?token=" + URLEncoder.encode(chatToken, "utf-8") + "&port_to_be=" + chatPort; LOG.trace("Opening chat websocket {}", url); Request req = new Request.Builder() @@ -258,10 +261,9 @@ public class Flirt4FreeModel extends AbstractModel { public void onMessage(WebSocket webSocket, String text) { LOG.trace("Chat wbesocket for {}: {}", getName(), text); JSONObject json = new JSONObject(text); - //LOG.debug("WS {}", text); if (json.optString("command").equals("8011")) { JSONObject data = json.getJSONObject("data"); - streamHost = data.getString("stream_host"); // TODO look, if the stream_host is equal to the one encoded in base64 in some of the ajax requests (parameters) + streamHost = data.getString("stream_host"); online = true; isInteractiveShow = data.optString("devices").equals("1"); String roomState = data.optString("room_state"); @@ -285,7 +287,7 @@ public class Flirt4FreeModel extends AbstractModel { public void onFailure(WebSocket webSocket, Throwable t, Response response) { LOG.error("Chat websocket for {} failed", getName(), t); synchronized (monitor) { - monitor.notify(); + monitor.notifyAll(); } response.close(); } @@ -294,7 +296,7 @@ public class Flirt4FreeModel extends AbstractModel { public void onClosed(WebSocket webSocket, int code, String reason) { LOG.trace("Chat websocket for {} closed {} {}", getName(), code, reason); synchronized (monitor) { - monitor.notify(); + monitor.notifyAll(); } } }); @@ -311,25 +313,15 @@ public class Flirt4FreeModel extends AbstractModel { @Override public void invalidateCacheEntries() { + // nothing to do here } @Override public void receiveTip(Double tokens) throws IOException { try { - // if(tokens < 50 || tokens > 750000) { - // throw new RuntimeException("Tip amount has to be between 50 and 750000"); - // } - // make sure we are logged in and all necessary model data is available getSite().login(); - acquireSlot(); - try { - loadStreamUrl(); - } catch (InterruptedException e) { - throw new IOException("Couldn't send tip", e); - } finally { - releaseSlot(); - } + fetchStreamUrl(); // send the tip int giftId = isInteractiveShow ? 775 : 171; @@ -370,10 +362,23 @@ public class Flirt4FreeModel extends AbstractModel { } } } catch (InterruptedException e) { + Thread.currentThread().interrupt(); throw new IOException("Couldn't acquire request slot", e); } } + private void fetchStreamUrl() throws InterruptedException, IOException { + acquireSlot(); + try { + loadStreamUrl(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Couldn't send tip", e); + } finally { + releaseSlot(); + } + } + private String getUserIdt() throws IOException, InterruptedException { if (userIdt.isEmpty()) { acquireSlot(); @@ -429,6 +434,7 @@ public class Flirt4FreeModel extends AbstractModel { try { return changeFavoriteStatus(true); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); throw new IOException("Couldn't change follow status for model " + getName(), e); } } @@ -438,7 +444,10 @@ public class Flirt4FreeModel extends AbstractModel { try { isOnline(true); return changeFavoriteStatus(false); - } catch (ExecutionException | InterruptedException e) { + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Couldn't change follow status for model " + getName(), e); + } catch (ExecutionException e) { throw new IOException("Couldn't change follow status for model " + getName(), e); } } @@ -509,12 +518,10 @@ public class Flirt4FreeModel extends AbstractModel { } private void acquireSlot() throws InterruptedException { - //LOG.debug("Acquire: {} - Queue: {}", requestThrottle.availablePermits(), requestThrottle.getQueueLength()); requestThrottle.acquire(); long now = System.currentTimeMillis(); long millisSinceLastRequest = now - lastRequest; if(millisSinceLastRequest < 500) { - //LOG.debug("Sleeping: {}", (500-millisSinceLastRequest)); Thread.sleep(500 - millisSinceLastRequest); } } @@ -522,6 +529,5 @@ public class Flirt4FreeModel extends AbstractModel { private void releaseSlot() { lastRequest = System.currentTimeMillis(); requestThrottle.release(); - // LOG.debug("Release: {}", requestThrottle.availablePermits()); } } diff --git a/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java b/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java index ca1c032e..59af16b8 100644 --- a/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java +++ b/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java @@ -4,6 +4,7 @@ import static javax.servlet.http.HttpServletResponse.*; import java.io.File; import java.io.IOException; +import java.io.PrintWriter; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.Instant; @@ -57,12 +58,11 @@ public class RecorderServlet extends AbstractCtbrecServlet { String json = null; try { + PrintWriter responseWriter = resp.getWriter(); json = body(req); boolean isRequestAuthenticated = checkAuthentication(req, json); if (!isRequestAuthenticated) { - resp.setStatus(SC_UNAUTHORIZED); - String response = "{\"status\": \"error\", \"msg\": \"HMAC does not match\"}"; - resp.getWriter().write(response); + sendError(resp, SC_UNAUTHORIZED, "{\"status\": \"error\", \"msg\": \"HMAC does not match\"}"); return; } @@ -76,204 +76,204 @@ public class RecorderServlet extends AbstractCtbrecServlet { JsonAdapter requestAdapter = moshi.adapter(Request.class); Request request = requestAdapter.fromJson(json); if (request.action != null) { - switch (request.action) { - case "start": - LOG.debug("Starting recording for model {} - {}", request.model.getName(), request.model.getUrl()); - recorder.addModel(request.model); - String response = "{\"status\": \"success\", \"msg\": \"Recording started\"}"; - resp.getWriter().write(response); - break; - case "startByUrl": - LOG.debug("Starting recording for model {}", request.model.getUrl()); - startByUrl(request); - response = "{\"status\": \"success\", \"msg\": \"Recording started\"}"; - resp.getWriter().write(response); - break; - case "startByName": - LOG.debug("Starting recording for model {}", request.model.getUrl()); - startByName(request); - response = "{\"status\": \"success\", \"msg\": \"Recording started\"}"; - resp.getWriter().write(response); - break; - case "stop": - GlobalThreadPool.submit(() -> { - try { - recorder.stopRecording(request.model); - } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { - LOG.error("Couldn't stop recording for model {}", request.model, e); - } - }); - response = "{\"status\": \"success\", \"msg\": \"Stopping recording\"}"; - resp.getWriter().write(response); - break; - case "stopAt": - GlobalThreadPool.submit(() -> { - try { - recorder.stopRecordingAt(request.model); - } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { - LOG.error("Couldn't stop recording for model {}", request.model, e); - } - }); - response = "{\"status\": \"success\", \"msg\": \"Stopping recording\"}"; - resp.getWriter().write(response); - break; - case "list": - resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of models\", \"models\": ["); - JsonAdapter modelAdapter = new ModelJsonAdapter(); - List models = recorder.getModels(); - for (Iterator iterator = models.iterator(); iterator.hasNext();) { - Model model = iterator.next(); - resp.getWriter().write(modelAdapter.toJson(model)); - if(iterator.hasNext()) { - resp.getWriter().write(','); - } - } - resp.getWriter().write("]}"); - break; - case "listOnline": - resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of online models\", \"models\": ["); - modelAdapter = new ModelJsonAdapter(); - models = recorder.getOnlineModels(); - for (Iterator iterator = models.iterator(); iterator.hasNext();) { - Model model = iterator.next(); - resp.getWriter().write(modelAdapter.toJson(model)); - if(iterator.hasNext()) { - resp.getWriter().write(','); - } - } - resp.getWriter().write("]}"); - break; - case "recordings": - resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of recordings\", \"recordings\": ["); - JsonAdapter recAdapter = moshi.adapter(Recording.class); - List recordings = recorder.getRecordings(); - for (Iterator iterator = recordings.iterator(); iterator.hasNext();) { - Recording recording = iterator.next(); - String recJSON = recAdapter.toJson(recording); - LOG.debug("Rec: {}", recJSON); - resp.getWriter().write(recJSON); - if (iterator.hasNext()) { - resp.getWriter().write(','); - } - } - resp.getWriter().write("]}"); - break; - case "delete": - recorder.delete(request.recording); - recAdapter = moshi.adapter(Recording.class); - resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of recordings\", \"recordings\": ["); - resp.getWriter().write(recAdapter.toJson(request.recording)); - resp.getWriter().write("]}"); - break; - case "pin": - recorder.pin(request.recording); - recAdapter = moshi.adapter(Recording.class); - resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of recordings\", \"recordings\": ["); - resp.getWriter().write(recAdapter.toJson(request.recording)); - resp.getWriter().write("]}"); - break; - case "unpin": - recorder.unpin(request.recording); - recAdapter = moshi.adapter(Recording.class); - resp.getWriter().write("{\"status\": \"success\", \"msg\": \"Note saved\", \"recordings\": ["); - resp.getWriter().write(recAdapter.toJson(request.recording)); - resp.getWriter().write("]}"); - break; - case "setNote": - recorder.setNote(request.recording, request.recording.getNote()); - recAdapter = moshi.adapter(Recording.class); - resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of recordings\", \"recordings\": ["); - resp.getWriter().write(recAdapter.toJson(request.recording)); - resp.getWriter().write("]}"); - break; - case "rerunPostProcessing": - recorder.rerunPostProcessing(request.recording); - recAdapter = moshi.adapter(Recording.class); - resp.getWriter().write("{\"status\": \"success\", \"msg\": \"Post-Processing triggered\"}"); - break; - case "switch": - recorder.switchStreamSource(request.model); - response = "{\"status\": \"success\", \"msg\": \"Resolution switched\"}"; - resp.getWriter().write(response); - break; - case "suspend": - LOG.debug("Suspend recording for model {} - {}", request.model.getName(), request.model.getUrl()); - GlobalThreadPool.submit(() -> { - try { - recorder.suspendRecording(request.model); - } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { - LOG.error("Couldn't suspend recording for model {}", request.model, e); - } - }); - response = "{\"status\": \"success\", \"msg\": \"Suspending recording\"}"; - resp.getWriter().write(response); - break; - case "resume": - LOG.debug("Resume recording for model {} - {}", request.model.getName(), request.model.getUrl()); - recorder.resumeRecording(request.model); - response = "{\"status\": \"success\", \"msg\": \"Recording resumed\"}"; - resp.getWriter().write(response); - break; - case "space": - JSONObject jsonResponse = new JSONObject(); - jsonResponse.put("status", "success"); - jsonResponse.put("spaceTotal", recorder.getTotalSpaceBytes()); - jsonResponse.put("spaceFree", recorder.getFreeSpaceBytes()); - jsonResponse.put("throughput", BandwidthMeter.getThroughput()); - jsonResponse.put("throughputTimeframe", BandwidthMeter.getTimeframe().toMillis()); - jsonResponse.put("minimumSpaceLeftInBytes", Config.getInstance().getSettings().minimumSpaceLeftInBytes); - resp.getWriter().write(jsonResponse.toString()); - break; - case "changePriority": - recorder.priorityChanged(request.model); - response = "{\"status\": \"success\"}"; - resp.getWriter().write(response); - break; - case "pauseRecorder": - recorder.pause(); - response = "{\"status\": \"success\"}"; - resp.getWriter().write(response); - break; - case "resumeRecorder": - recorder.resume(); - response = "{\"status\": \"success\"}"; - resp.getWriter().write(response); - break; - case "saveModelGroup": - recorder.saveModelGroup(request.modelGroup); - sendModelGroups(resp, recorder.getModelGroups()); - break; - case "deleteModelGroup": - recorder.deleteModelGroup(request.modelGroup); - sendModelGroups(resp, recorder.getModelGroups()); - break; - case "listModelGroups": - sendModelGroups(resp, recorder.getModelGroups()); - break; - default: - resp.setStatus(SC_BAD_REQUEST); - response = "{\"status\": \"error\", \"msg\": \"Unknown action ["+request.action+"]\"}"; - resp.getWriter().write(response); - break; - } - } else { - resp.setStatus(SC_BAD_REQUEST); - String response = "{\"status\": \"error\", \"msg\": \"action is missing\"}"; - resp.getWriter().write(response); + sendError(resp, SC_BAD_REQUEST, "{\"status\": \"error\", \"msg\": \"action is missing\"}"); + return; } - } catch(Throwable t) { + switch (request.action) { + case "start": + LOG.debug("Starting recording for model {} - {}", request.model.getName(), request.model.getUrl()); + recorder.addModel(request.model); + String response = "{\"status\": \"success\", \"msg\": \"Recording started\"}"; + responseWriter.write(response); + break; + case "startByUrl": + LOG.debug("Starting recording for model {}", request.model.getUrl()); + startByUrl(request); + response = "{\"status\": \"success\", \"msg\": \"Recording started\"}"; + responseWriter.write(response); + break; + case "startByName": + LOG.debug("Starting recording for model {}", request.model.getUrl()); + startByName(request); + response = "{\"status\": \"success\", \"msg\": \"Recording started\"}"; + responseWriter.write(response); + break; + case "stop": + GlobalThreadPool.submit(() -> { + try { + recorder.stopRecording(request.model); + } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { + LOG.error("Couldn't stop recording for model {}", request.model, e); + } + }); + response = "{\"status\": \"success\", \"msg\": \"Stopping recording\"}"; + responseWriter.write(response); + break; + case "stopAt": + GlobalThreadPool.submit(() -> { + try { + recorder.stopRecordingAt(request.model); + } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { + LOG.error("Couldn't stop recording for model {}", request.model, e); + } + }); + response = "{\"status\": \"success\", \"msg\": \"Stopping recording\"}"; + responseWriter.write(response); + break; + case "list": + responseWriter.write("{\"status\": \"success\", \"msg\": \"List of models\", \"models\": ["); + JsonAdapter modelAdapter = new ModelJsonAdapter(); + List models = recorder.getModels(); + for (Iterator iterator = models.iterator(); iterator.hasNext();) { + Model model = iterator.next(); + responseWriter.write(modelAdapter.toJson(model)); + if (iterator.hasNext()) { + responseWriter.write(','); + } + } + responseWriter.write("]}"); + break; + case "listOnline": + responseWriter.write("{\"status\": \"success\", \"msg\": \"List of online models\", \"models\": ["); + modelAdapter = new ModelJsonAdapter(); + models = recorder.getOnlineModels(); + for (Iterator iterator = models.iterator(); iterator.hasNext();) { + Model model = iterator.next(); + responseWriter.write(modelAdapter.toJson(model)); + if (iterator.hasNext()) { + responseWriter.write(','); + } + } + responseWriter.write("]}"); + break; + case "recordings": + responseWriter.write("{\"status\": \"success\", \"msg\": \"List of recordings\", \"recordings\": ["); + JsonAdapter recAdapter = moshi.adapter(Recording.class); + List recordings = recorder.getRecordings(); + for (Iterator iterator = recordings.iterator(); iterator.hasNext();) { + Recording recording = iterator.next(); + String recJSON = recAdapter.toJson(recording); + LOG.debug("Rec: {}", recJSON); + responseWriter.write(recJSON); + if (iterator.hasNext()) { + responseWriter.write(','); + } + } + responseWriter.write("]}"); + break; + case "delete": + recorder.delete(request.recording); + recAdapter = moshi.adapter(Recording.class); + responseWriter.write("{\"status\": \"success\", \"msg\": \"List of recordings\", \"recordings\": ["); + responseWriter.write(recAdapter.toJson(request.recording)); + responseWriter.write("]}"); + break; + case "pin": + recorder.pin(request.recording); + recAdapter = moshi.adapter(Recording.class); + responseWriter.write("{\"status\": \"success\", \"msg\": \"List of recordings\", \"recordings\": ["); + responseWriter.write(recAdapter.toJson(request.recording)); + responseWriter.write("]}"); + break; + case "unpin": + recorder.unpin(request.recording); + recAdapter = moshi.adapter(Recording.class); + responseWriter.write("{\"status\": \"success\", \"msg\": \"Note saved\", \"recordings\": ["); + responseWriter.write(recAdapter.toJson(request.recording)); + responseWriter.write("]}"); + break; + case "setNote": + recorder.setNote(request.recording, request.recording.getNote()); + recAdapter = moshi.adapter(Recording.class); + responseWriter.write("{\"status\": \"success\", \"msg\": \"List of recordings\", \"recordings\": ["); + responseWriter.write(recAdapter.toJson(request.recording)); + responseWriter.write("]}"); + break; + case "rerunPostProcessing": + recorder.rerunPostProcessing(request.recording); + responseWriter.write("{\"status\": \"success\", \"msg\": \"Post-Processing triggered\"}"); + break; + case "switch": + recorder.switchStreamSource(request.model); + response = "{\"status\": \"success\", \"msg\": \"Resolution switched\"}"; + responseWriter.write(response); + break; + case "suspend": + LOG.debug("Suspend recording for model {} - {}", request.model.getName(), request.model.getUrl()); + GlobalThreadPool.submit(() -> { + try { + recorder.suspendRecording(request.model); + } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { + LOG.error("Couldn't suspend recording for model {}", request.model, e); + } + }); + response = "{\"status\": \"success\", \"msg\": \"Suspending recording\"}"; + responseWriter.write(response); + break; + case "resume": + LOG.debug("Resume recording for model {} - {}", request.model.getName(), request.model.getUrl()); + recorder.resumeRecording(request.model); + response = "{\"status\": \"success\", \"msg\": \"Recording resumed\"}"; + responseWriter.write(response); + break; + case "space": + JSONObject jsonResponse = new JSONObject(); + jsonResponse.put("status", "success"); + jsonResponse.put("spaceTotal", recorder.getTotalSpaceBytes()); + jsonResponse.put("spaceFree", recorder.getFreeSpaceBytes()); + jsonResponse.put("throughput", BandwidthMeter.getThroughput()); + jsonResponse.put("throughputTimeframe", BandwidthMeter.getTimeframe().toMillis()); + jsonResponse.put("minimumSpaceLeftInBytes", Config.getInstance().getSettings().minimumSpaceLeftInBytes); + responseWriter.write(jsonResponse.toString()); + break; + case "changePriority": + recorder.priorityChanged(request.model); + response = "{\"status\": \"success\"}"; + responseWriter.write(response); + break; + case "pauseRecorder": + recorder.pause(); + response = "{\"status\": \"success\"}"; + responseWriter.write(response); + break; + case "resumeRecorder": + recorder.resume(); + response = "{\"status\": \"success\"}"; + responseWriter.write(response); + break; + case "saveModelGroup": + recorder.saveModelGroup(request.modelGroup); + sendModelGroups(resp, recorder.getModelGroups()); + break; + case "deleteModelGroup": + recorder.deleteModelGroup(request.modelGroup); + sendModelGroups(resp, recorder.getModelGroups()); + break; + case "listModelGroups": + sendModelGroups(resp, recorder.getModelGroups()); + break; + default: + sendError(resp, SC_BAD_REQUEST, "{\"status\": \"error\", \"msg\": \"Unknown action [" + request.action + "]\"}"); + break; + } + } catch(Exception e) { resp.setStatus(SC_INTERNAL_SERVER_ERROR); JSONObject response = new JSONObject(); response.put("status", "error"); - response.put("msg", t.getMessage()); + response.put("msg", e.getMessage()); resp.getWriter().write(response.toString()); - LOG.error("Unexpected error", t); + LOG.error("Unexpected error", e); if (json != null) { LOG.debug("Request: {}", json); } } } + private void sendError(HttpServletResponse resp, int code, String body) throws IOException { + resp.setStatus(code); + resp.getWriter().write(body); + } + private void sendModelGroups(HttpServletResponse resp, Set modelGroups) throws IOException { JSONObject jsonResponse = new JSONObject(); jsonResponse.put("status", "success");