{"action":"created","comment":{"url":"https://api.github.com/repos/google/sagetv/pulls/comments/71446182","id":71446182,"diff_hunk":"@@ -0,0 +1,489 @@\n+/*\n+ * Copyright 2015 The SageTV Authors. All Rights Reserved.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ *  http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+package sage.io;\n+\n+import sage.Sage;\n+\n+import java.io.*;\n+import java.net.InetSocketAddress;\n+import java.net.Socket;\n+import java.nio.charset.StandardCharsets;\n+\n+public class RemoteSageFile implements SageFileSource\n+{\n+  private final String hostname;\n+  private final String remoteFilename;\n+  private final String transcodeMode;\n+  private final boolean readonly;\n+\n+  private boolean activeFile = false;\n+  private long currTotalSize = 0;\n+  private long maxRemoteSize = 0;\n+\n+  private long remoteOffset = 0;\n+  private Socket socket = null;\n+  private DataOutputStream outStream = null;\n+  private DataInputStream inStream = null;\n+\n+  public RemoteSageFile(String hostname, File file, boolean readonly) throws IOException\n+  {\n+    this(hostname, file.getPath(), readonly, null);\n+  }\n+\n+  public RemoteSageFile(String hostname, String name, boolean readonly) throws IOException\n+  {\n+    this(hostname, name, readonly, null);\n+  }\n+\n+  public RemoteSageFile(String hostname, File file, boolean readonly, String transcodeMode) throws IOException\n+  {\n+    this(hostname, file.getPath(), readonly, transcodeMode);\n+  }\n+\n+  /**\n+   *\n+   *\n+   * @param hostname The hostname of the media server hosting this file.\n+   * @param name The full path and file to be opened.\n+   * @param readonly Set the channel to be read only.\n+   * @param transcodeMode Set the transcode mode.\n+   * @throws IOException\n+   */\n+  public RemoteSageFile(String hostname, String name, boolean readonly, String transcodeMode) throws IOException\n+  {\n+    this.hostname = hostname;\n+    remoteFilename = name;\n+    this.transcodeMode = transcodeMode;\n+    this.readonly = readonly;\n+    connect();\n+  }\n+\n+  private void connect() throws IOException\n+  {\n+    if (socket != null)\n+      return;\n+\n+    Socket newSocket = new Socket();\n+    newSocket.connect(new InetSocketAddress(hostname, 7818));\n+    newSocket.setSoTimeout(30000);\n+    newSocket.setTcpNoDelay(true);\n+    socket = newSocket;\n+\n+    outStream = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));\n+    inStream = new DataInputStream(new BufferedInputStream(socket.getInputStream()));\n+\n+    if (transcodeMode != null && transcodeMode.length() > 0)\n+    {\n+      outStream.write((\"XCODE_SETUP \" + transcodeMode + \"\\r\\n\").getBytes(Sage.BYTE_CHARSET));\n+      outStream.flush();\n+      String response = Sage.readLineBytes(inStream);\n+      if (!\"OK\".equals(response))\n+        throw new java.io.IOException(\"Error with remote transcode setup for \" + transcodeMode + \" of: \" + response);\n+    }\n+\n+    outStream.write(\"OPENW \".getBytes(Sage.BYTE_CHARSET));\n+    outStream.write(remoteFilename.getBytes(StandardCharsets.UTF_16BE));\n+    outStream.write(\"\\r\\n\".getBytes(Sage.BYTE_CHARSET));\n+    outStream.flush();\n+\n+    String response = Sage.readLineBytes(inStream);\n+    if (!\"OK\".equals(response))\n+    {\n+      // Don't leave the connection open. We can't read anything if we are seeing this error anyway.\n+      disconnect();\n+      throw new IOException(\"Error opening remote file of:\" + response);\n+    }\n+  }\n+\n+  private void disconnect()\n+  {\n+    if (socket != null)\n+    {\n+      try\n+      {\n+        outStream.write(\"QUIT\\r\\n\".getBytes(Sage.BYTE_CHARSET));\n+        outStream.flush();\n+      }\n+      catch (IOException e) {}\n+\n+      try\n+      {\n+        socket.close();\n+      }\n+      catch (IOException e) {}\n+      socket = null;\n+    }\n+    if (outStream != null)\n+    {\n+      try\n+      {\n+        outStream.close();\n+      }\n+      catch (IOException e) {}\n+      outStream = null;\n+    }\n+    if (inStream != null)\n+    {\n+      try\n+      {\n+        inStream.close();\n+      }\n+      catch (IOException e) {}\n+      inStream = null;\n+    }\n+  }\n+\n+  private synchronized void reconnect() throws IOException\n+  {\n+    disconnect();\n+    connect();\n+  }\n+\n+  @Override\n+  public synchronized int read() throws IOException\n+  {\n+    int returnValue;\n+\n+    if (remoteOffset >= maxRemoteSize)\n+    {\n+      // We don't positively know if we will get the next byte. This will update the file length to\n+      // the latest size.\n+      if (remoteOffset >= length())\n+      {\n+        // End of the file.\n+        return -1;\n+      }\n+    }\n+\n+    try\n+    {\n+      outStream.write((\"READ \" + remoteOffset + \" 1\\r\\n\").getBytes(Sage.BYTE_CHARSET));\n+      outStream.flush();\n+      returnValue = inStream.read();\n+    }\n+    catch (IOException e)\n+    {\n+      reconnect();\n+      outStream.write((\"READ \" + remoteOffset + \" 1\\r\\n\").getBytes(Sage.BYTE_CHARSET));\n+      outStream.flush();\n+      returnValue = inStream.read();\n+    }\n+\n+    remoteOffset += 1;\n+    return returnValue & 0xFF;\n+  }\n+\n+  @Override\n+  public int read(byte[] b) throws IOException\n+  {\n+    return read(b, 0, b.length);\n+  }\n+\n+  @Override\n+  public synchronized int read(byte[] b, int off, int len) throws IOException\n+  {\n+    if (len == 0)\n+      return 0;\n+\n+    if (remoteOffset + len >= maxRemoteSize)\n+    {\n+      // We don't positively know if we will get the next byte. This will update the file length to\n+      // the latest size.\n+      if (remoteOffset >= length())\n+      {\n+        // End of the file.\n+        return -1;\n+      }\n+    }\n+\n+    // len is an int, so that will keep us from exceeding Integer.MAX_VALUE\n+    len = (int)Math.min((long)len, maxRemoteSize - remoteOffset);\n+\n+    try\n+    {\n+      outStream.write((\"READ \" + remoteOffset + \" \" + len + \"\\r\\n\").getBytes(Sage.BYTE_CHARSET));\n+      outStream.flush();\n+      inStream.readFully(b, off, len);\n+    }\n+    catch (IOException e)\n+    {\n+      reconnect();\n+      outStream.write((\"READ \" + remoteOffset + \" \" + len + \"\\r\\n\").getBytes(Sage.BYTE_CHARSET));\n+      outStream.flush();\n+      inStream.readFully(b, off, len);\n+    }\n+\n+    remoteOffset += len;\n+    return len;\n+  }\n+\n+  @Override\n+  public void readFully(byte[] b) throws IOException\n+  {\n+    readFully(b, 0, b.length);\n+  }\n+\n+  @Override\n+  public void readFully(byte[] b, int off, int len) throws IOException\n+  {\n+    if (len == 0)\n+      return;\n+\n+    int bytesRead = read(b, off, len);\n+\n+    while (len > 0)\n+    {\n+      if (bytesRead == -1)\n+        throw new EOFException(\"End of file reached!\");\n+\n+      len -= bytesRead;\n+      off += bytesRead;\n+\n+      if (len <= 0)\n+        break;\n+\n+      bytesRead = read(b, off, len);\n+    }\n+  }\n+\n+  @Override\n+  public synchronized void write(int b) throws IOException\n+  {\n+    if (readonly)\n+      throw new IOException(\"Remote file is read only.\");\n+\n+    try\n+    {\n+      outStream.write((\"WRITE \" + remoteOffset + \" 1\\r\\n\").getBytes(Sage.BYTE_CHARSET));\n+      outStream.flush();\n+      outStream.write(b);\n+    }\n+    catch (IOException e)\n+    {\n+      reconnect();\n+      outStream.write((\"WRITE \" + remoteOffset + \" 1\\r\\n\").getBytes(Sage.BYTE_CHARSET));\n+      outStream.flush();\n+      outStream.write(b);\n+    }\n+\n+    remoteOffset += 1;\n+  }\n+\n+  @Override\n+  public void write(byte[] b) throws IOException\n+  {\n+    write(b, 0, b.length);\n+  }\n+\n+  @Override\n+  public synchronized void write(byte[] b, int off, int len) throws IOException\n+  {\n+    if (readonly)\n+      throw new IOException(\"Remote file is read only.\");\n+\n+    if (len == 0)\n+      return;\n+\n+    try\n+    {\n+      outStream.write((\"WRITE \" + remoteOffset + \" \" + len + \"\\r\\n\").getBytes(Sage.BYTE_CHARSET));\n+      outStream.flush();\n+      outStream.write(b, off, len);\n+    }\n+    catch (IOException e)\n+    {\n+      reconnect();\n+      outStream.write((\"WRITE \" + remoteOffset + \" \" + len + \"\\r\\n\").getBytes(Sage.BYTE_CHARSET));\n+      outStream.flush();\n+      outStream.write(b, off, len);\n+    }\n+\n+    remoteOffset += len;\n+  }\n+\n+  @Override\n+  public void randomWrite(long pos, byte[] b, int off, int len) throws IOException\n+  {\n+    if (readonly)\n+      throw new IOException(\"Remote file is read only.\");\n+\n+    long oldPosition = remoteOffset;\n+    remoteOffset = pos;\n+    try\n+    {\n+      write(b, off, len);\n+    }\n+    finally\n+    {\n+      remoteOffset = oldPosition;\n+    }\n+  }\n+\n+  @Override\n+  public synchronized long skip(long n) throws IOException\n+  {\n+    if (n <= 0)\n+      return 0;\n+\n+    long pos = remoteOffset;\n+    long seek = Math.min(pos + n, length());\n+\n+    remoteOffset = seek;\n+\n+    return seek - pos;\n+  }\n+\n+  @Override\n+  public void seek(long pos)\n+  {\n+    remoteOffset = pos;\n+  }\n+\n+  @Override\n+  public long position()\n+  {\n+    return remoteOffset;\n+  }\n+\n+  @Override\n+  public long length() throws IOException\n+  {\n+    String response = executeCommand(\"SIZE\\r\\n\");","path":"java/sage/io/RemoteSageFile.java","position":364,"original_position":364,"commit_id":"9d81f0f08211457f395736cf975afda6dd547b27","original_commit_id":"9c5b6fab255bf1d958f8f3b613e600a418d5876a","user":{"login":"enternoescape","id":4162345,"avatar_url":"https://avatars.githubusercontent.com/u/4162345?v=3","gravatar_id":"","url":"https://api.github.com/users/enternoescape","html_url":"https://github.com/enternoescape","followers_url":"https://api.github.com/users/enternoescape/followers","following_url":"https://api.github.com/users/enternoescape/following{/other_user}","gists_url":"https://api.github.com/users/enternoescape/gists{/gist_id}","starred_url":"https://api.github.com/users/enternoescape/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/enternoescape/subscriptions","organizations_url":"https://api.github.com/users/enternoescape/orgs","repos_url":"https://api.github.com/users/enternoescape/repos","events_url":"https://api.github.com/users/enternoescape/events{/privacy}","received_events_url":"https://api.github.com/users/enternoescape/received_events","type":"User","site_admin":false},"body":"I re-optimized the file length() method per your suggestion and it broke the live subtitles. It's clear that it uses length() to see if there's more data.","created_at":"2016-07-20T00:03:44Z","updated_at":"2016-07-20T00:03:44Z","html_url":"https://github.com/google/sagetv/pull/156#discussion_r71446182","pull_request_url":"https://api.github.com/repos/google/sagetv/pulls/156","_links":{"self":{"href":"https://api.github.com/repos/google/sagetv/pulls/comments/71446182"},"html":{"href":"https://github.com/google/sagetv/pull/156#discussion_r71446182"},"pull_request":{"href":"https://api.github.com/repos/google/sagetv/pulls/156"}}},"pull_request":{"url":"https://api.github.com/repos/google/sagetv/pulls/156","id":77894828,"html_url":"https://github.com/google/sagetv/pull/156","diff_url":"https://github.com/google/sagetv/pull/156.diff","patch_url":"https://github.com/google/sagetv/pull/156.patch","issue_url":"https://api.github.com/repos/google/sagetv/issues/156","number":156,"state":"open","locked":false,"title":"Fixed issue #87 so that now SRT subtitles are read live and made local and remote file access consistent and easily extendable.","user":{"login":"enternoescape","id":4162345,"avatar_url":"https://avatars.githubusercontent.com/u/4162345?v=3","gravatar_id":"","url":"https://api.github.com/users/enternoescape","html_url":"https://github.com/enternoescape","followers_url":"https://api.github.com/users/enternoescape/followers","following_url":"https://api.github.com/users/enternoescape/following{/other_user}","gists_url":"https://api.github.com/users/enternoescape/gists{/gist_id}","starred_url":"https://api.github.com/users/enternoescape/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/enternoescape/subscriptions","organizations_url":"https://api.github.com/users/enternoescape/orgs","repos_url":"https://api.github.com/users/enternoescape/repos","events_url":"https://api.github.com/users/enternoescape/events{/privacy}","received_events_url":"https://api.github.com/users/enternoescape/received_events","type":"User","site_admin":false},"body":"This does not address multi-part recordings that have a subtitle file for each segment.","created_at":"2016-07-18T23:11:46Z","updated_at":"2016-07-20T00:03:44Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f5c2499b1c81cdfef08bed1d2615e2b883135c28","assignee":{"login":"Narflex","id":13699084,"avatar_url":"https://avatars.githubusercontent.com/u/13699084?v=3","gravatar_id":"","url":"https://api.github.com/users/Narflex","html_url":"https://github.com/Narflex","followers_url":"https://api.github.com/users/Narflex/followers","following_url":"https://api.github.com/users/Narflex/following{/other_user}","gists_url":"https://api.github.com/users/Narflex/gists{/gist_id}","starred_url":"https://api.github.com/users/Narflex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Narflex/subscriptions","organizations_url":"https://api.github.com/users/Narflex/orgs","repos_url":"https://api.github.com/users/Narflex/repos","events_url":"https://api.github.com/users/Narflex/events{/privacy}","received_events_url":"https://api.github.com/users/Narflex/received_events","type":"User","site_admin":false},"assignees":[{"login":"Narflex","id":13699084,"avatar_url":"https://avatars.githubusercontent.com/u/13699084?v=3","gravatar_id":"","url":"https://api.github.com/users/Narflex","html_url":"https://github.com/Narflex","followers_url":"https://api.github.com/users/Narflex/followers","following_url":"https://api.github.com/users/Narflex/following{/other_user}","gists_url":"https://api.github.com/users/Narflex/gists{/gist_id}","starred_url":"https://api.github.com/users/Narflex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Narflex/subscriptions","organizations_url":"https://api.github.com/users/Narflex/orgs","repos_url":"https://api.github.com/users/Narflex/repos","events_url":"https://api.github.com/users/Narflex/events{/privacy}","received_events_url":"https://api.github.com/users/Narflex/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/google/sagetv/pulls/156/commits","review_comments_url":"https://api.github.com/repos/google/sagetv/pulls/156/comments","review_comment_url":"https://api.github.com/repos/google/sagetv/pulls/comments{/number}","comments_url":"https://api.github.com/repos/google/sagetv/issues/156/comments","statuses_url":"https://api.github.com/repos/google/sagetv/statuses/9d81f0f08211457f395736cf975afda6dd547b27","head":{"label":"enternoescape:live-srt-playback","ref":"live-srt-playback","sha":"9d81f0f08211457f395736cf975afda6dd547b27","user":{"login":"enternoescape","id":4162345,"avatar_url":"https://avatars.githubusercontent.com/u/4162345?v=3","gravatar_id":"","url":"https://api.github.com/users/enternoescape","html_url":"https://github.com/enternoescape","followers_url":"https://api.github.com/users/enternoescape/followers","following_url":"https://api.github.com/users/enternoescape/following{/other_user}","gists_url":"https://api.github.com/users/enternoescape/gists{/gist_id}","starred_url":"https://api.github.com/users/enternoescape/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/enternoescape/subscriptions","organizations_url":"https://api.github.com/users/enternoescape/orgs","repos_url":"https://api.github.com/users/enternoescape/repos","events_url":"https://api.github.com/users/enternoescape/events{/privacy}","received_events_url":"https://api.github.com/users/enternoescape/received_events","type":"User","site_admin":false},"repo":{"id":58501382,"name":"sagetv","full_name":"enternoescape/sagetv","owner":{"login":"enternoescape","id":4162345,"avatar_url":"https://avatars.githubusercontent.com/u/4162345?v=3","gravatar_id":"","url":"https://api.github.com/users/enternoescape","html_url":"https://github.com/enternoescape","followers_url":"https://api.github.com/users/enternoescape/followers","following_url":"https://api.github.com/users/enternoescape/following{/other_user}","gists_url":"https://api.github.com/users/enternoescape/gists{/gist_id}","starred_url":"https://api.github.com/users/enternoescape/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/enternoescape/subscriptions","organizations_url":"https://api.github.com/users/enternoescape/orgs","repos_url":"https://api.github.com/users/enternoescape/repos","events_url":"https://api.github.com/users/enternoescape/events{/privacy}","received_events_url":"https://api.github.com/users/enternoescape/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/enternoescape/sagetv","description":"SageTV is a cross-platform networked DVR and media management system","fork":true,"url":"https://api.github.com/repos/enternoescape/sagetv","forks_url":"https://api.github.com/repos/enternoescape/sagetv/forks","keys_url":"https://api.github.com/repos/enternoescape/sagetv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/enternoescape/sagetv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/enternoescape/sagetv/teams","hooks_url":"https://api.github.com/repos/enternoescape/sagetv/hooks","issue_events_url":"https://api.github.com/repos/enternoescape/sagetv/issues/events{/number}","events_url":"https://api.github.com/repos/enternoescape/sagetv/events","assignees_url":"https://api.github.com/repos/enternoescape/sagetv/assignees{/user}","branches_url":"https://api.github.com/repos/enternoescape/sagetv/branches{/branch}","tags_url":"https://api.github.com/repos/enternoescape/sagetv/tags","blobs_url":"https://api.github.com/repos/enternoescape/sagetv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/enternoescape/sagetv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/enternoescape/sagetv/git/refs{/sha}","trees_url":"https://api.github.com/repos/enternoescape/sagetv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/enternoescape/sagetv/statuses/{sha}","languages_url":"https://api.github.com/repos/enternoescape/sagetv/languages","stargazers_url":"https://api.github.com/repos/enternoescape/sagetv/stargazers","contributors_url":"https://api.github.com/repos/enternoescape/sagetv/contributors","subscribers_url":"https://api.github.com/repos/enternoescape/sagetv/subscribers","subscription_url":"https://api.github.com/repos/enternoescape/sagetv/subscription","commits_url":"https://api.github.com/repos/enternoescape/sagetv/commits{/sha}","git_commits_url":"https://api.github.com/repos/enternoescape/sagetv/git/commits{/sha}","comments_url":"https://api.github.com/repos/enternoescape/sagetv/comments{/number}","issue_comment_url":"https://api.github.com/repos/enternoescape/sagetv/issues/comments{/number}","contents_url":"https://api.github.com/repos/enternoescape/sagetv/contents/{+path}","compare_url":"https://api.github.com/repos/enternoescape/sagetv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/enternoescape/sagetv/merges","archive_url":"https://api.github.com/repos/enternoescape/sagetv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/enternoescape/sagetv/downloads","issues_url":"https://api.github.com/repos/enternoescape/sagetv/issues{/number}","pulls_url":"https://api.github.com/repos/enternoescape/sagetv/pulls{/number}","milestones_url":"https://api.github.com/repos/enternoescape/sagetv/milestones{/number}","notifications_url":"https://api.github.com/repos/enternoescape/sagetv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/enternoescape/sagetv/labels{/name}","releases_url":"https://api.github.com/repos/enternoescape/sagetv/releases{/id}","deployments_url":"https://api.github.com/repos/enternoescape/sagetv/deployments","created_at":"2016-05-11T00:11:40Z","updated_at":"2016-05-11T00:11:46Z","pushed_at":"2016-07-19T23:10:05Z","git_url":"git://github.com/enternoescape/sagetv.git","ssh_url":"git@github.com:enternoescape/sagetv.git","clone_url":"https://github.com/enternoescape/sagetv.git","svn_url":"https://github.com/enternoescape/sagetv","homepage":"http://forums.sagetv.com/","size":68284,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"google:master","ref":"master","sha":"b2480ec532890c542cfc5eabd9f5d99e71b630ee","user":{"login":"google","id":1342004,"avatar_url":"https://avatars.githubusercontent.com/u/1342004?v=3","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"repo":{"id":40379991,"name":"sagetv","full_name":"google/sagetv","owner":{"login":"google","id":1342004,"avatar_url":"https://avatars.githubusercontent.com/u/1342004?v=3","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/google/sagetv","description":"SageTV is a cross-platform networked DVR and media management system","fork":false,"url":"https://api.github.com/repos/google/sagetv","forks_url":"https://api.github.com/repos/google/sagetv/forks","keys_url":"https://api.github.com/repos/google/sagetv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/google/sagetv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/google/sagetv/teams","hooks_url":"https://api.github.com/repos/google/sagetv/hooks","issue_events_url":"https://api.github.com/repos/google/sagetv/issues/events{/number}","events_url":"https://api.github.com/repos/google/sagetv/events","assignees_url":"https://api.github.com/repos/google/sagetv/assignees{/user}","branches_url":"https://api.github.com/repos/google/sagetv/branches{/branch}","tags_url":"https://api.github.com/repos/google/sagetv/tags","blobs_url":"https://api.github.com/repos/google/sagetv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/google/sagetv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/google/sagetv/git/refs{/sha}","trees_url":"https://api.github.com/repos/google/sagetv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/google/sagetv/statuses/{sha}","languages_url":"https://api.github.com/repos/google/sagetv/languages","stargazers_url":"https://api.github.com/repos/google/sagetv/stargazers","contributors_url":"https://api.github.com/repos/google/sagetv/contributors","subscribers_url":"https://api.github.com/repos/google/sagetv/subscribers","subscription_url":"https://api.github.com/repos/google/sagetv/subscription","commits_url":"https://api.github.com/repos/google/sagetv/commits{/sha}","git_commits_url":"https://api.github.com/repos/google/sagetv/git/commits{/sha}","comments_url":"https://api.github.com/repos/google/sagetv/comments{/number}","issue_comment_url":"https://api.github.com/repos/google/sagetv/issues/comments{/number}","contents_url":"https://api.github.com/repos/google/sagetv/contents/{+path}","compare_url":"https://api.github.com/repos/google/sagetv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/google/sagetv/merges","archive_url":"https://api.github.com/repos/google/sagetv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/google/sagetv/downloads","issues_url":"https://api.github.com/repos/google/sagetv/issues{/number}","pulls_url":"https://api.github.com/repos/google/sagetv/pulls{/number}","milestones_url":"https://api.github.com/repos/google/sagetv/milestones{/number}","notifications_url":"https://api.github.com/repos/google/sagetv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/google/sagetv/labels{/name}","releases_url":"https://api.github.com/repos/google/sagetv/releases{/id}","deployments_url":"https://api.github.com/repos/google/sagetv/deployments","created_at":"2015-08-07T20:37:14Z","updated_at":"2016-07-18T01:54:02Z","pushed_at":"2016-07-19T23:10:06Z","git_url":"git://github.com/google/sagetv.git","ssh_url":"git@github.com:google/sagetv.git","clone_url":"https://github.com/google/sagetv.git","svn_url":"https://github.com/google/sagetv","homepage":"http://forums.sagetv.com/","size":68286,"stargazers_count":115,"watchers_count":115,"language":"Java","has_issues":true,"has_downloads":false,"has_wiki":true,"has_pages":false,"forks_count":111,"mirror_url":null,"open_issues_count":59,"forks":111,"open_issues":59,"watchers":115,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/google/sagetv/pulls/156"},"html":{"href":"https://github.com/google/sagetv/pull/156"},"issue":{"href":"https://api.github.com/repos/google/sagetv/issues/156"},"comments":{"href":"https://api.github.com/repos/google/sagetv/issues/156/comments"},"review_comments":{"href":"https://api.github.com/repos/google/sagetv/pulls/156/comments"},"review_comment":{"href":"https://api.github.com/repos/google/sagetv/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/google/sagetv/pulls/156/commits"},"statuses":{"href":"https://api.github.com/repos/google/sagetv/statuses/9d81f0f08211457f395736cf975afda6dd547b27"}}}}
{"actor":{"display_login":"enternoescape"}}