diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..798cc316 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/rc_images +/dist +/release +/bin +/.gradle +/.settings +/config.ini +/css +/cdata.* +/*.jar \ No newline at end of file diff --git a/lib/Java-WebSocket-1.5.5.jar b/lib/Java-WebSocket-1.5.5.jar new file mode 100644 index 00000000..a3f8b805 Binary files /dev/null and b/lib/Java-WebSocket-1.5.5.jar differ diff --git a/src/com/tivo/kmttg/main/config.java b/src/com/tivo/kmttg/main/config.java index 77ba9bfb..cdbfb9da 100644 --- a/src/com/tivo/kmttg/main/config.java +++ b/src/com/tivo/kmttg/main/config.java @@ -36,6 +36,7 @@ import java.util.Stack; import com.tivo.kmttg.rpc.Remote; +import com.tivo.kmttg.rpc.TiVoRPCWS; import com.tivo.kmttg.util.*; import com.tivo.kmttg.gui.gui; import com.tivo.kmttg.httpserver.kmttgServer; @@ -248,6 +249,8 @@ public class config { public static Hashtable autoskip_ServiceItems = new Hashtable(); public static Boolean visualDetect_running = false; + private static Hashtable remoteTivos = new Hashtable(); + public static Stack parse() { debug.print(""); String result; @@ -630,13 +633,18 @@ public static void twpDeleteEnabledSet(Boolean state) { } public static Remote initRemote(String tivoName) { + Remote r = remoteTivos.get(tivoName); + if (r != null && r.isConnected()) { + return r; + } + if (rpcEnabled(tivoName)) { - Remote r = new Remote(tivoName); - return(r); + r = new Remote(tivoName); } else { - Remote r = new Remote(tivoName, true); - return(r); + r = new Remote(tivoName, true); } + remoteTivos.put(tivoName, r); + return(r); } public static String getTivoUsername() { diff --git a/src/com/tivo/kmttg/rpc/Remote.java b/src/com/tivo/kmttg/rpc/Remote.java index c3b7beae..48496511 100755 --- a/src/com/tivo/kmttg/rpc/Remote.java +++ b/src/com/tivo/kmttg/rpc/Remote.java @@ -23,6 +23,7 @@ import java.io.FileWriter; import java.net.DatagramSocket; import java.net.InetAddress; +import java.net.URISyntaxException; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -48,25 +49,32 @@ import com.tivo.kmttg.util.log; import com.tivo.kmttg.util.string; -public class Remote extends TiVoRPC { - public final Boolean success; - private final boolean away; +public class Remote{ + public Boolean success; + protected TiVoRPCWS ws; + protected boolean away = true; + private String tivoName; /** perform a socket setup and auth. all public constructors call this. */ private Remote(String tivoName, boolean away, String IP, String mak, String programDir, int port, String cdata) { - super(tivoName, IP, mak, programDir, port, cdata, - // oldSchema, debug - (config.rpcOld == 1), com.tivo.kmttg.util.debug.enabled); - // super calls RemoteInit which in turn calls Auth which is overridden in this class to also call Auth_web() or bodyId_get() - this.away = away; - - // record the init result in the expected public field - this.success = getSuccess(); + this.tivoName = tivoName; + try { + ws = TiVoRPCWS.init( + tivoName, + IP, + port + ); + this.success = ws.waitForReady(); + } catch (URISyntaxException | InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + this.success = false; + } } // This constructor designed to be use by kmttg public Remote(String tivoName) { - this(null, false, // doesn't actually set this.tivoName + this(tivoName, false, // doesn't actually set this.tivoName getIPForTivo(tivoName), config.MAK, config.programDir, getPortForTivo(tivoName), @@ -99,7 +107,7 @@ public Remote(String tivoName, String IP, int port, String MAK, String cdata) { port, cdata); } - private static String getIPForTivo(String tivoName) { + public static String getIPForTivo(String tivoName) { String tivoIP = config.TIVOS.get(tivoName); // if config for tivoName is null, pass tivoName as IP, else config @@ -110,7 +118,11 @@ private static String getIPForTivo(String tivoName) { } } - private static int getPortForTivo(String tivoName) { + public boolean isConnected() { + return this.ws.isOpen(); + } + + public static int getPortForTivo(String tivoName) { String wanrpc = config.getWanSetting(tivoName, "rpc"); // if "rpc" WanSetting for tivoName is null, use default port, else WanSetting @@ -121,165 +133,15 @@ private static int getPortForTivo(String tivoName) { } } - /** - * Override performs a tivo.com authentication if IP ends with tivo.com, else performs default followed by a bodyId_get() - */ - @Override - protected boolean Auth(String MAK) { - if (IP.endsWith("tivo.com") || IP.endsWith("tivoservice.com")) { - if ( ! Auth_web() ) { - return false; - } - } else { - if ( ! super.Auth(MAK) ) { - return false; - } - bodyId_get(); - } - return true; - } - - /** * Returns cached bodyId or performs a bodyConfigSearch and returns the bodyId. * NOTE: This retrieves and stores bodyId in config hashtable if not previously stored * @return bodyId or "-" */ public String bodyId_get() { - String id = config.bodyId_get(IP, port); - if (id.equals("")) { - JSONObject json = new JSONObject(); - try { - json.put("bodyId", "-"); - JSONObject reply = Command("bodyConfigSearch", json); - if (reply != null && reply.has("bodyConfig")) { - json = reply.getJSONArray("bodyConfig").getJSONObject(0); - if (json.has("bodyId")) { - id = json.getString("bodyId"); - config.bodyId_set(IP, port, id); - } else { - log.error("Failed to determine bodyId: IP=" + IP + " port=" + port); - } - } - } catch (JSONException e) { - log.error("bodyId_get failed - " + e.getMessage()); - } - } - if (id.equals("")) - id = "-"; - return id; - } - - /** - * Perform a middlemind tivo.com bodyAuthenticate with configured username/password - * @return - */ - private Boolean Auth_web() { - try { - if (config.getTivoUsername() == null || config.getTivoPassword() == null) { - log.error("tivo.com username & password not set in kmttg or pyTivo config"); - return false; - } - if (config.isDomainTokenExpired() || config.getDomainToken() == null) { - log.warn("Domain Token expired refreshing token"); - GetDomainToken getDT = new GetDomainToken(); - try { - getDT.getToken(); - } catch (Exception e) { - log.error("Failed to get domain token. Check your tivo.com username or password."); - return false; - } - if (config.isDomainTokenExpired()) { - log.error("Failed to get domain token. Check your tivo.com username or password."); - return false; - } - } - - JSONObject credential = new JSONObject(); - JSONObject h = new JSONObject(); - JSONObject domainToken = new JSONObject(); - domainToken.put("domain", "tivo"); - domainToken.put("type", "domainToken"); - domainToken.put("token", config.getDomainToken()); - - credential.put("type", "domainTokenCredential"); - credential.put("domainToken", domainToken); -// credential.put("username", config.getTivoUsername()); -// credential.put("password", config.getTivoPassword()); - h.put("credential", credential); - String req = RpcRequest("bodyAuthenticate", false, h); - if (Write(req) ) { - JSONObject result = ReadRemote(); - if (result.has("status")) { - if (result.get("status").equals("success")) { - // Look for tivoName bodyId in deviceId JSONArray - Boolean found = false; - if (result.has("deviceId")) { - JSONArray a = result.getJSONArray("deviceId"); - for (int i=0; i just use type - req = RpcRequest(type, false, json); + req = ws.RpcRequest(type, false, json); } if (req != null) { - if ( Write(req) ) { - return ReadRemote(); - } - else - return null; + return ws.sendRequestAndWaitForResponse(req); } else { error("rpc: unhandled Key type: " + type); return null; @@ -1591,7 +1450,7 @@ public JSONArray SeasonPremieres(JSONArray channelNumbers, jobData job, int tota long stop = start + day_increment; try { // Set shorter timeout in case some requests fail - setSoTimeout(20*1000); + //setSoTimeout(20*1000); // Search 1 day at a time int item = 0; int total_items = total_days*channelNumbers.length(); @@ -2222,7 +2081,7 @@ public JSONObject extendedSearch( } catch (JSONException e) { log.error("extendedSearch failed - " + e.getMessage()); } - r.disconnect(); + ws.close(); } return collections; @@ -2699,7 +2558,7 @@ public Stack getCategoryNames(String tivoName) { log.error("Remote getCategoryNames - " + e.getMessage()); return null; } - r.disconnect(); + ws.close(); } return categories; } @@ -2722,7 +2581,7 @@ private String getCategoryId(String tivoName, String categoryName) { for (int i=0; i responseMap = new HashMap(); + + protected void error(String msg) { + log.error(msg); + } + protected void print(String msg) { + log.print(msg); + } + protected void warn(String msg) { + log.warn(msg); + } + + public TiVoRPCWS(URI uri, Draft protocal, String tivoName, String IP, int port) { + super(uri, protocal); + this.tivoName = tivoName; + this.IP = IP; + this.port = port; + } + + + public static TiVoRPCWS init(String tivoName, String IP, int port) throws URISyntaxException, InterruptedException { + //System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE"); + Draft_6455 draft_mindrpc = new Draft_6455(Collections.emptyList(), + Collections.singletonList(new Protocol("com.tivo.mindrpc.2"))); + TiVoRPCWS ws = new TiVoRPCWS( + new URI("wss://xmind-tp2.tivoservice.com:2196/"), + draft_mindrpc, + tivoName, + IP, + port + ); + + ws.connectBlocking(); + + return ws; + } + + public boolean waitForReady() throws InterruptedException { + if (!this.isOpen()) { + return false; + } + if (this.ready) { + return true; + } + synchronized(this.ready){ + while (!this.ready) { + if (!this.isOpen()) { + return false; + } + this.ready.wait(); + } + return this.isOpen(); + } + } + + + @Override + public void onOpen(ServerHandshake handshakedata) { + //send("Hello, it is me. Mario :)"); + log.print("WS connection started"); + TiVoRPCWS wc = this; + Thread thread = new Thread(){ + public void run(){ + Boolean readyLock = wc.ready; + try { + JSONObject credential = new JSONObject(); + JSONObject h = new JSONObject(); + JSONObject domainToken = new JSONObject(); + domainToken.put("domain", "tivo"); + domainToken.put("type", "domainToken"); + domainToken.put("token", config.getDomainToken()); + + credential.put("type", "domainTokenCredential"); + credential.put("domainToken", domainToken); + h.put("credential", credential); + String req = RpcRequest("bodyAuthenticate", false, h); + String lock = ""; + synchronized(lock){ + responseMap.put(rpc_id, lock); + wc.send(req); + lock.wait(); + String response = responseMap.get(rpc_id); + responseMap.remove(rpc_id); + JSONObject result = new JSONObject(response); + if (result.has("status")) { + if (result.get("status").equals("success")) { + // Look for tivoName bodyId in deviceId JSONArray + boolean found = false; + if (result.has("deviceId")) { + JSONArray a = result.getJSONArray("deviceId"); + for (int i=0; i