Merge branch 'dev' into pp

This commit is contained in:
0xb00bface 2020-08-21 19:15:08 +02:00
commit 4f8e7dbca2
21 changed files with 212 additions and 45 deletions

View File

@ -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
========================

View File

@ -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();
}
private void registerActiveRecordingsCounter() {

View File

@ -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;

View File

@ -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());

View File

@ -118,4 +118,8 @@ public class ListSelectionPane<T extends Comparable<T>> extends GridPane {
public boolean isAllSelected() {
return selectAll.isSelected();
}
public void selectAll() {
selectAll.setSelected(true);
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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<Model> 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;

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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
}
}

View File

@ -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];
}
}

View File

@ -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

View File

@ -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");
}
}

View File

@ -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<Site> 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<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;
} else {
LOG.error(SERVER_RETURNED_ERROR, resp.status, resp.msg);

View File

@ -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);

View File

@ -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, "");
}
}

View File

@ -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<Model> getModelList() throws IOException {
if(Duration.between(lastModelListUpdate, Instant.now()).getSeconds() > 10) {
lastModelListUpdate = Instant.now();

View File

@ -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);

View File

@ -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":