Skip to content

Commit aa05ff1

Browse files
authored
Merge pull request #9 from sampie777/add_more_user_control_of_websocket
Added better error feedback and websocket control
2 parents 182d95d + ee9cefe commit aa05ff1

File tree

10 files changed

+387
-34
lines changed

10 files changed

+387
-34
lines changed

src/main/java/net/twasi/obsremotejava/OBSCommunicator.java

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
import com.google.gson.JsonElement;
55
import com.google.gson.JsonObject;
66
import com.google.gson.JsonParser;
7+
import net.twasi.obsremotejava.callbacks.Callback;
8+
import net.twasi.obsremotejava.callbacks.ErrorCallback;
9+
import net.twasi.obsremotejava.callbacks.StringCallback;
710
import net.twasi.obsremotejava.events.EventType;
811
import net.twasi.obsremotejava.events.responses.ScenesChangedResponse;
912
import net.twasi.obsremotejava.events.responses.SwitchScenesResponse;
1013
import net.twasi.obsremotejava.events.responses.TransitionBeginResponse;
1114
import net.twasi.obsremotejava.events.responses.TransitionEndResponse;
15+
import net.twasi.obsremotejava.objects.throwables.InvalidResponseTypeError;
1216
import net.twasi.obsremotejava.requests.Authenticate.AuthenticateRequest;
1317
import net.twasi.obsremotejava.requests.Authenticate.AuthenticateResponse;
1418
import net.twasi.obsremotejava.requests.GetAuthRequired.GetAuthRequiredRequest;
@@ -101,6 +105,7 @@ public class OBSCommunicator {
101105
private Callback onConnect;
102106
private Callback onDisconnect;
103107
private StringCallback onConnectionFailed;
108+
private ErrorCallback onError;
104109

105110
// Optional callbacks
106111
private Callback onReplayStarted;
@@ -157,16 +162,16 @@ public void onConnect(Session session) {
157162

158163
@OnWebSocketMessage
159164
public void onMessage(String msg) {
160-
try {
161-
if (msg == null) {
162-
System.out.println("Ignored empty message");
163-
return;
164-
}
165+
if (msg == null) {
166+
System.out.println("Ignored empty message");
167+
return;
168+
}
165169

166-
if (debug) {
167-
System.out.println(msg);
168-
}
170+
if (debug) {
171+
System.out.println(msg);
172+
}
169173

174+
try {
170175
if (new Gson().fromJson(msg, JsonObject.class).has("message-id")) {
171176
// Response
172177
ResponseBase responseBase = new Gson().fromJson(msg, ResponseBase.class);
@@ -178,6 +183,7 @@ public void onMessage(String msg) {
178183
} catch (Throwable t) {
179184
System.err.println("Failed to process response '" + type.getSimpleName() + "' from websocket.");
180185
t.printStackTrace();
186+
runOnError("Failed to process response '" + type.getSimpleName() + "' from websocket", t);
181187
}
182188

183189
} else {
@@ -195,11 +201,13 @@ public void onMessage(String msg) {
195201
} catch (Throwable t) {
196202
System.err.println("Failed to execute callback for event: " + eventType);
197203
t.printStackTrace();
204+
runOnError("Failed to execute callback for event: " + eventType, t);
198205
}
199206
}
200207
} catch (Throwable t) {
201208
System.err.println("Failed to process message from websocket.");
202209
t.printStackTrace();
210+
runOnError("Failed to process message from websocket", t);
203211
}
204212
}
205213

@@ -235,6 +243,7 @@ private void processIncomingResponse(ResponseBase responseBase, Class type) {
235243
default:
236244
if (!callbacks.containsKey(type)) {
237245
System.out.println("Invalid type received: " + type.getName());
246+
runOnError("Invalid response type received", new InvalidResponseTypeError(type.getName()));
238247
return;
239248
}
240249

@@ -243,6 +252,7 @@ private void processIncomingResponse(ResponseBase responseBase, Class type) {
243252
} catch (Throwable t) {
244253
System.err.println("Failed to execute callback for response: " + type);
245254
t.printStackTrace();
255+
runOnError("Failed to execute callback for response: " + type, t);
246256
}
247257
}
248258
}
@@ -295,28 +305,40 @@ private void authenticateWithServer(String challenge, String salt) {
295305
return;
296306
}
297307

308+
// Generate authentication response secret
309+
String authResponse = generateAuthenticationResponseString(challenge, salt);
310+
311+
if (authResponse == null) {
312+
return;
313+
}
314+
315+
// Send authentication response secret to server
316+
session.getRemote()
317+
.sendStringByFuture(new Gson().toJson(new AuthenticateRequest(this, authResponse)));
318+
}
319+
320+
private String generateAuthenticationResponseString(String challenge, String salt) {
298321
MessageDigest digest;
299322
try {
300323
digest = MessageDigest.getInstance("SHA-256");
301324
} catch (NoSuchAlgorithmException e) {
302325
System.err.println("Failed to perform password authentication with server");
303326
e.printStackTrace();
304327
runOnConnectionFailed("Failed to perform password authentication with server");
305-
return;
328+
return null;
306329
}
307330

308-
// Generate authentication response secret
309331
String secretString = password + salt;
310332
byte[] secretHash = digest.digest(secretString.getBytes(StandardCharsets.UTF_8));
311333
String encodedSecret = Base64.getEncoder().encodeToString(secretHash);
312334

313335
String authResponseString = encodedSecret + challenge;
314336
byte[] authResponseHash = digest.digest(authResponseString.getBytes(StandardCharsets.UTF_8));
315-
String authResponse = Base64.getEncoder().encodeToString(authResponseHash);
337+
return Base64.getEncoder().encodeToString(authResponseHash);
338+
}
316339

317-
// Send authentication response secret to server
318-
session.getRemote()
319-
.sendStringByFuture(new Gson().toJson(new AuthenticateRequest(this, authResponse)));
340+
public void registerOnError(ErrorCallback onError) {
341+
this.onError = onError;
320342
}
321343

322344
public void registerOnConnect(Callback onConnect) {
@@ -555,13 +577,26 @@ public void getStudioModeEnabled(Callback callback) {
555577
callbacks.put(GetStudioModeEnabledResponse.class, callback);
556578
}
557579

558-
public void setStudioModeEnabled(boolean enabled, Callback callback){
580+
public void setStudioModeEnabled(boolean enabled, Callback callback) {
559581
SetStudioModeEnabledRequest request = new SetStudioModeEnabledRequest(this, enabled);
560582

561583
session.getRemote().sendStringByFuture(new Gson().toJson(request));
562584
callbacks.put(SetStudioModeEnabledResponse.class, callback);
563585
}
564586

587+
private void runOnError(String message, Throwable throwable) {
588+
if (onError == null) {
589+
return;
590+
}
591+
592+
try {
593+
onError.run(message, throwable);
594+
} catch (Throwable t) {
595+
System.err.println("Exception during callback execution for 'onError'");
596+
t.printStackTrace();
597+
}
598+
}
599+
565600
private void runOnConnectionFailed(String message) {
566601
if (onConnectionFailed == null) {
567602
return;

src/main/java/net/twasi/obsremotejava/OBSRemoteController.java

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package net.twasi.obsremotejava;
22

3-
import net.twasi.obsremotejava.requests.ResponseBase;
3+
import net.twasi.obsremotejava.callbacks.Callback;
4+
import net.twasi.obsremotejava.callbacks.ErrorCallback;
5+
import net.twasi.obsremotejava.callbacks.StringCallback;
6+
import net.twasi.obsremotejava.objects.throwables.OBSResponseError;
47
import org.eclipse.jetty.websocket.api.Session;
58
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
69
import org.eclipse.jetty.websocket.client.WebSocketClient;
@@ -10,27 +13,58 @@
1013
import java.util.Map;
1114
import java.util.concurrent.ExecutionException;
1215
import java.util.concurrent.Future;
16+
import java.util.concurrent.TimeUnit;
1317

1418
public class OBSRemoteController {
1519
private String address;
16-
private OBSCommunicator communicator;
17-
private WebSocketClient client;
20+
private final boolean debug;
21+
private final OBSCommunicator communicator;
22+
private final String password;
23+
private final WebSocketClient client;
24+
25+
private StringCallback onConnectionFailed;
26+
private ErrorCallback onError;
1827

1928
private StringCallback onConnectionFailed;
2029

2130
private boolean failed;
2231

23-
public OBSRemoteController(String address, boolean debug, String password) {
32+
public OBSRemoteController(String address, boolean debug, String password, boolean autoConnect) {
2433
this.address = address;
25-
communicator = new OBSCommunicator(debug, password);
34+
this.debug = debug;
35+
this.password = password;
36+
2637
client = new WebSocketClient();
38+
communicator = new OBSCommunicator(debug, password);
39+
40+
if (autoConnect) {
41+
connect();
42+
}
43+
}
44+
45+
public OBSRemoteController(String address, boolean debug, String password) {
46+
this(address, debug, password, true);
47+
}
48+
49+
public OBSRemoteController(String address, boolean debug) {
50+
this(address, debug, null);
51+
}
52+
53+
public void connect() {
2754
try {
2855
client.start();
56+
} catch (Exception e) {
57+
System.err.println("Failed to start WebSocketClient.");
58+
e.printStackTrace();
59+
runOnError("Failed to start WebSocketClient", e);
60+
return;
61+
}
2962

63+
try {
3064
URI uri = new URI(address);
3165
ClientUpgradeRequest request = new ClientUpgradeRequest();
3266
Future<Session> connection = client.connect(communicator, uri, request);
33-
System.out.printf("Connecting to: %s%s%n", uri, (password != null ? " with password" : ""));
67+
System.out.printf("Connecting to: %s%s.%n", uri, (password != null ? " with password" : ""));
3468

3569
try {
3670
connection.get();
@@ -43,16 +77,40 @@ public OBSRemoteController(String address, boolean debug, String password) {
4377
failed = true;
4478

4579
runOnConnectionFailed("Failed to connect to OBS");
80+
} else {
81+
throw e;
4682
}
4783
}
4884
} catch (Throwable t) {
85+
System.err.println("Failed to setup connection with OBS.");
4986
t.printStackTrace();
5087
runOnConnectionFailed("Failed to setup connection with OBS");
5188
}
5289
}
5390

54-
public OBSRemoteController(String address, boolean debug) {
55-
this(address, debug, null);
91+
public void disconnect() {
92+
// wait for closed socket connection
93+
try {
94+
if (debug) {
95+
System.out.println("Closing connection.");
96+
}
97+
communicator.awaitClose(1, TimeUnit.SECONDS);
98+
} catch (InterruptedException e) {
99+
e.printStackTrace();
100+
runOnError("Error during closing websocket connection", e);
101+
}
102+
103+
if (!client.isStopped() && !client.isStopping()) {
104+
try {
105+
if (debug) {
106+
System.out.println("Stopping client.");
107+
}
108+
client.stop();
109+
} catch (Exception e) {
110+
e.printStackTrace();
111+
runOnError("Error during stopping websocket client", e);
112+
}
113+
}
56114
}
57115

58116
public boolean isFailed() {
@@ -63,6 +121,11 @@ public void getScenes(Callback callback) {
63121
communicator.getScenes(callback);
64122
}
65123

124+
public void registerOnError(ErrorCallback onError) {
125+
this.onError = onError;
126+
communicator.registerOnError(onError);
127+
}
128+
66129
public void registerConnectCallback(Callback onConnect) {
67130
communicator.registerOnConnect(onConnect);
68131
}
@@ -121,14 +184,12 @@ public void setCurrentTransition(String transition, Callback callback) {
121184
}
122185

123186
public void changeSceneWithTransition(final String scene, String transition, final Callback callback) {
124-
communicator.setCurrentTransition(transition, new Callback() {
125-
@Override
126-
public void run(ResponseBase response) {
127-
if (!response.getStatus().equals("ok")) {
128-
System.out.println("Failed to change transition. Pls fix.");
129-
}
130-
communicator.setCurrentScene(scene, callback);
187+
communicator.setCurrentTransition(transition, response -> {
188+
if (!response.getStatus().equals("ok")) {
189+
System.out.println("Failed to change transition. Pls fix.");
190+
runOnError("Error response for changeSceneWithTransition", new OBSResponseError(response.getError()));
131191
}
192+
communicator.setCurrentScene(scene, callback);
132193
});
133194
}
134195

@@ -233,6 +294,18 @@ public void saveReplayBuffer(Callback callback) {
233294
communicator.saveReplayBuffer(callback);
234295
}
235296

297+
private void runOnError(String message, Throwable throwable) {
298+
if (onError == null) {
299+
return;
300+
}
301+
302+
try {
303+
onError.run(message, throwable);
304+
} catch (Exception e) {
305+
e.printStackTrace();
306+
}
307+
}
308+
236309
private void runOnConnectionFailed(String message) {
237310
if (onConnectionFailed == null) {
238311
return;

src/main/java/net/twasi/obsremotejava/Callback.java renamed to src/main/java/net/twasi/obsremotejava/callbacks/Callback.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package net.twasi.obsremotejava;
1+
package net.twasi.obsremotejava.callbacks;
22

33
import net.twasi.obsremotejava.requests.ResponseBase;
44

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package net.twasi.obsremotejava.callbacks;
2+
3+
public interface ErrorCallback {
4+
void run(String message, Throwable throwable);
5+
}

src/main/java/net/twasi/obsremotejava/StringCallback.java renamed to src/main/java/net/twasi/obsremotejava/callbacks/StringCallback.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package net.twasi.obsremotejava;
1+
package net.twasi.obsremotejava.callbacks;
22

33
public interface StringCallback {
44
void run(String message);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package net.twasi.obsremotejava.objects.throwables;
2+
3+
import javax.management.openmbean.InvalidKeyException;
4+
5+
public class InvalidResponseTypeError extends InvalidKeyException {
6+
public InvalidResponseTypeError(String s) {
7+
super(s);
8+
}
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package net.twasi.obsremotejava.objects.throwables;
2+
3+
public class OBSResponseError extends Error {
4+
public OBSResponseError(String s) {
5+
super(s);
6+
}
7+
}

0 commit comments

Comments
 (0)