{"action":"created","comment":{"url":"https://api.github.com/repos/appium/appium-support/pulls/comments/200465622","pull_request_review_id":134789100,"id":200465622,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwMDQ2NTYyMg==","diff_hunk":"@@ -0,0 +1,148 @@\n+import _ from 'lodash';\n+import request from 'request';\n+import log from './logger';\n+import http from 'http';\n+import path from 'path';\n+import B from 'bluebird';\n+import MJpegConsumer from 'mjpeg-consumer';\n+import mJpegServer from 'mjpeg-server';\n+import { Writable } from 'stream';\n+import { fs } from 'appium-support';\n+\n+const IMAGE_PATH = path.resolve(__dirname, '..', '..', 'test', 'images',\n+                                'appium-diagram.jpg');\n+\n+// amount of time to wait for the first image in the stream\n+const MJPEG_SERVER_TIMEOUT_MS = 10000;\n+\n+/** Class which stores the last bit of data streamed into it */\n+class MJpegStream extends Writable {\n+\n+  /**\n+   * Create an MJpegStream\n+   * @param {string} mJpegUrl - URL of MJPEG-over-HTTP stream\n+   * @param {function} [errorHandler=noop] - additional function that will be\n+   * called in the case of any errors.\n+   * @param {object} [options={}] - Options to pass to the Writable constructor\n+   */\n+  constructor (mJpegUrl, errorHandler = _.noop, options = {}) {\n+    super(options);\n+    this.errorHandler = errorHandler;\n+    this.url = mJpegUrl;\n+    this.clear();\n+  }\n+\n+  /**\n+   * Get the base64-encoded version of the JPEG\n+   * @returns {string}\n+   */\n+  get lastChunkBase64 () {\n+    return _.isBuffer(this.lastChunk) ?\n+      this.lastChunk.toString('base64') :\n+      null;\n+  }\n+\n+  /**\n+   * Reset internal state\n+   */\n+  clear () {\n+    this.registerStartSuccess = null;\n+    this.registerStartFailure = null;\n+    this.request = null;\n+    this.consumer = null;\n+    this.lastChunk = null;\n+    this.updateCount = 0;\n+  }\n+\n+  /**\n+   * Start reading the MJpeg stream and storing the last image\n+   */\n+  async start (serverTimeout = MJPEG_SERVER_TIMEOUT_MS) {\n+    this.consumer = new MJpegConsumer();\n+\n+    // use the deferred pattern so we can wait for the start of the stream\n+    // based on what comes in from an external pipe\n+    let startPromise = new B((res, rej) => {\n+      this.registerStartSuccess = res;\n+      this.registerStartFailure = rej;\n+    })\n+    // start a timeout so that if the server does not return data, we don't\n+    // block forever.\n+      .timeout(serverTimeout,\n+        `Waited ${serverTimeout}ms but the MJPEG server never sent any images`);\n+\n+    const onErr = (err) => {\n+      log.error(`Error getting MJpeg screenshot chunk: ${err}`);\n+      this.errorHandler(err);\n+      if (this.registerStartFailure) {\n+        this.registerStartFailure(err);\n+      }\n+    };\n+\n+    this.request = request(this.url);\n+\n+    this.request\n+      .on('error', onErr)   // ensure we do something with errors\n+      .pipe(this.consumer)  // allow chunking and transforming of jpeg data\n+      .pipe(this);          // send the actual jpegs to ourself\n+\n+    await startPromise;\n+  }\n+\n+  /**\n+   * Stop reading the MJpeg stream. Ensure we disconnect all the pipes and stop\n+   * the HTTP request itself. Then reset the state.\n+   */\n+  stop () {\n+    if (!this.consumer) {\n+      return;\n+    }\n+\n+    this.consumer.unpipe();\n+    this.request.end();\n+    this.clear();\n+  }\n+\n+  /**\n+   * Override the Writable write() method in order to save the last image and\n+   * log the number of images we have received\n+   * @override\n+   * @param {Buffer} data - binary data streamed from the MJpeg consumer\n+   */\n+  write (data) {\n+    this.lastChunk = data;\n+    this.updateCount++;\n+\n+    if (this.registerStartSuccess) {\n+      this.registerStartSuccess();\n+      this.registerStartSuccess = null;\n+    }\n+  }\n+}\n+\n+/**\n+ * Start an mjpeg server for the purpose of testing, which just sends the same\n+ * image over and over. Caller is responsible for closing the server.\n+ * @param {int} port - port the server should listen on\n+ * @param {int} [intMs] - how often the server should push an image\n+ * @param {int} [times] - how many times the server should push an image before\n+ * it closes the connection\n+ * @returns {http.Server}\n+ */\n+function initMJpegServer (port, intMs = 300, times = 20) {","path":"lib/mjpeg.js","position":132,"original_position":132,"commit_id":"8cb82fa903bb40a4013016aa1047a29a4aa67e4b","original_commit_id":"8cb82fa903bb40a4013016aa1047a29a4aa67e4b","user":{"login":"jlipps","id":605053,"node_id":"MDQ6VXNlcjYwNTA1Mw==","avatar_url":"https://avatars3.githubusercontent.com/u/605053?v=4","gravatar_id":"","url":"https://api.github.com/users/jlipps","html_url":"https://github.com/jlipps","followers_url":"https://api.github.com/users/jlipps/followers","following_url":"https://api.github.com/users/jlipps/following{/other_user}","gists_url":"https://api.github.com/users/jlipps/gists{/gist_id}","starred_url":"https://api.github.com/users/jlipps/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jlipps/subscriptions","organizations_url":"https://api.github.com/users/jlipps/orgs","repos_url":"https://api.github.com/users/jlipps/repos","events_url":"https://api.github.com/users/jlipps/events{/privacy}","received_events_url":"https://api.github.com/users/jlipps/received_events","type":"User","site_admin":false},"body":"it will be used by the drivers though, so it needs to be exported from appium-support","created_at":"2018-07-05T19:32:25Z","updated_at":"2018-07-05T19:32:25Z","html_url":"https://github.com/appium/appium-support/pull/75#discussion_r200465622","pull_request_url":"https://api.github.com/repos/appium/appium-support/pulls/75","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/appium/appium-support/pulls/comments/200465622"},"html":{"href":"https://github.com/appium/appium-support/pull/75#discussion_r200465622"},"pull_request":{"href":"https://api.github.com/repos/appium/appium-support/pulls/75"}},"in_reply_to_id":200465469},"pull_request":{"url":"https://api.github.com/repos/appium/appium-support/pulls/75","id":199563085,"node_id":"MDExOlB1bGxSZXF1ZXN0MTk5NTYzMDg1","html_url":"https://github.com/appium/appium-support/pull/75","diff_url":"https://github.com/appium/appium-support/pull/75.diff","patch_url":"https://github.com/appium/appium-support/pull/75.patch","issue_url":"https://api.github.com/repos/appium/appium-support/issues/75","number":75,"state":"open","locked":false,"title":"add mjpeg stream helper class","user":{"login":"jlipps","id":605053,"node_id":"MDQ6VXNlcjYwNTA1Mw==","avatar_url":"https://avatars3.githubusercontent.com/u/605053?v=4","gravatar_id":"","url":"https://api.github.com/users/jlipps","html_url":"https://github.com/jlipps","followers_url":"https://api.github.com/users/jlipps/followers","following_url":"https://api.github.com/users/jlipps/following{/other_user}","gists_url":"https://api.github.com/users/jlipps/gists{/gist_id}","starred_url":"https://api.github.com/users/jlipps/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jlipps/subscriptions","organizations_url":"https://api.github.com/users/jlipps/orgs","repos_url":"https://api.github.com/users/jlipps/repos","events_url":"https://api.github.com/users/jlipps/events{/privacy}","received_events_url":"https://api.github.com/users/jlipps/received_events","type":"User","site_admin":false},"body":"the new version of https://github.com/appium/appium-base-driver/pull/230","created_at":"2018-07-05T19:16:39Z","updated_at":"2018-07-05T19:32:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5d702fef303905edd50c965ed39992ecb36dc7e2","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/appium/appium-support/pulls/75/commits","review_comments_url":"https://api.github.com/repos/appium/appium-support/pulls/75/comments","review_comment_url":"https://api.github.com/repos/appium/appium-support/pulls/comments{/number}","comments_url":"https://api.github.com/repos/appium/appium-support/issues/75/comments","statuses_url":"https://api.github.com/repos/appium/appium-support/statuses/8cb82fa903bb40a4013016aa1047a29a4aa67e4b","head":{"label":"appium:jlipps-mjpeg","ref":"jlipps-mjpeg","sha":"8cb82fa903bb40a4013016aa1047a29a4aa67e4b","user":{"login":"appium","id":3221291,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMyMjEyOTE=","avatar_url":"https://avatars2.githubusercontent.com/u/3221291?v=4","gravatar_id":"","url":"https://api.github.com/users/appium","html_url":"https://github.com/appium","followers_url":"https://api.github.com/users/appium/followers","following_url":"https://api.github.com/users/appium/following{/other_user}","gists_url":"https://api.github.com/users/appium/gists{/gist_id}","starred_url":"https://api.github.com/users/appium/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/appium/subscriptions","organizations_url":"https://api.github.com/users/appium/orgs","repos_url":"https://api.github.com/users/appium/repos","events_url":"https://api.github.com/users/appium/events{/privacy}","received_events_url":"https://api.github.com/users/appium/received_events","type":"Organization","site_admin":false},"repo":{"id":28532365,"node_id":"MDEwOlJlcG9zaXRvcnkyODUzMjM2NQ==","name":"appium-support","full_name":"appium/appium-support","owner":{"login":"appium","id":3221291,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMyMjEyOTE=","avatar_url":"https://avatars2.githubusercontent.com/u/3221291?v=4","gravatar_id":"","url":"https://api.github.com/users/appium","html_url":"https://github.com/appium","followers_url":"https://api.github.com/users/appium/followers","following_url":"https://api.github.com/users/appium/following{/other_user}","gists_url":"https://api.github.com/users/appium/gists{/gist_id}","starred_url":"https://api.github.com/users/appium/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/appium/subscriptions","organizations_url":"https://api.github.com/users/appium/orgs","repos_url":"https://api.github.com/users/appium/repos","events_url":"https://api.github.com/users/appium/events{/privacy}","received_events_url":"https://api.github.com/users/appium/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/appium/appium-support","description":"Support libs used across appium packages.","fork":false,"url":"https://api.github.com/repos/appium/appium-support","forks_url":"https://api.github.com/repos/appium/appium-support/forks","keys_url":"https://api.github.com/repos/appium/appium-support/keys{/key_id}","collaborators_url":"https://api.github.com/repos/appium/appium-support/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/appium/appium-support/teams","hooks_url":"https://api.github.com/repos/appium/appium-support/hooks","issue_events_url":"https://api.github.com/repos/appium/appium-support/issues/events{/number}","events_url":"https://api.github.com/repos/appium/appium-support/events","assignees_url":"https://api.github.com/repos/appium/appium-support/assignees{/user}","branches_url":"https://api.github.com/repos/appium/appium-support/branches{/branch}","tags_url":"https://api.github.com/repos/appium/appium-support/tags","blobs_url":"https://api.github.com/repos/appium/appium-support/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/appium/appium-support/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/appium/appium-support/git/refs{/sha}","trees_url":"https://api.github.com/repos/appium/appium-support/git/trees{/sha}","statuses_url":"https://api.github.com/repos/appium/appium-support/statuses/{sha}","languages_url":"https://api.github.com/repos/appium/appium-support/languages","stargazers_url":"https://api.github.com/repos/appium/appium-support/stargazers","contributors_url":"https://api.github.com/repos/appium/appium-support/contributors","subscribers_url":"https://api.github.com/repos/appium/appium-support/subscribers","subscription_url":"https://api.github.com/repos/appium/appium-support/subscription","commits_url":"https://api.github.com/repos/appium/appium-support/commits{/sha}","git_commits_url":"https://api.github.com/repos/appium/appium-support/git/commits{/sha}","comments_url":"https://api.github.com/repos/appium/appium-support/comments{/number}","issue_comment_url":"https://api.github.com/repos/appium/appium-support/issues/comments{/number}","contents_url":"https://api.github.com/repos/appium/appium-support/contents/{+path}","compare_url":"https://api.github.com/repos/appium/appium-support/compare/{base}...{head}","merges_url":"https://api.github.com/repos/appium/appium-support/merges","archive_url":"https://api.github.com/repos/appium/appium-support/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/appium/appium-support/downloads","issues_url":"https://api.github.com/repos/appium/appium-support/issues{/number}","pulls_url":"https://api.github.com/repos/appium/appium-support/pulls{/number}","milestones_url":"https://api.github.com/repos/appium/appium-support/milestones{/number}","notifications_url":"https://api.github.com/repos/appium/appium-support/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/appium/appium-support/labels{/name}","releases_url":"https://api.github.com/repos/appium/appium-support/releases{/id}","deployments_url":"https://api.github.com/repos/appium/appium-support/deployments","created_at":"2014-12-27T08:32:36Z","updated_at":"2018-07-02T02:32:39Z","pushed_at":"2018-07-05T19:16:40Z","git_url":"git://github.com/appium/appium-support.git","ssh_url":"git@github.com:appium/appium-support.git","clone_url":"https://github.com/appium/appium-support.git","svn_url":"https://github.com/appium/appium-support","homepage":null,"size":1254,"stargazers_count":5,"watchers_count":5,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":13,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":13,"open_issues":1,"watchers":5,"default_branch":"master"}},"base":{"label":"appium:master","ref":"master","sha":"42a2ab3f605d3cd3f78fe26a609c95790fbab963","user":{"login":"appium","id":3221291,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMyMjEyOTE=","avatar_url":"https://avatars2.githubusercontent.com/u/3221291?v=4","gravatar_id":"","url":"https://api.github.com/users/appium","html_url":"https://github.com/appium","followers_url":"https://api.github.com/users/appium/followers","following_url":"https://api.github.com/users/appium/following{/other_user}","gists_url":"https://api.github.com/users/appium/gists{/gist_id}","starred_url":"https://api.github.com/users/appium/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/appium/subscriptions","organizations_url":"https://api.github.com/users/appium/orgs","repos_url":"https://api.github.com/users/appium/repos","events_url":"https://api.github.com/users/appium/events{/privacy}","received_events_url":"https://api.github.com/users/appium/received_events","type":"Organization","site_admin":false},"repo":{"id":28532365,"node_id":"MDEwOlJlcG9zaXRvcnkyODUzMjM2NQ==","name":"appium-support","full_name":"appium/appium-support","owner":{"login":"appium","id":3221291,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMyMjEyOTE=","avatar_url":"https://avatars2.githubusercontent.com/u/3221291?v=4","gravatar_id":"","url":"https://api.github.com/users/appium","html_url":"https://github.com/appium","followers_url":"https://api.github.com/users/appium/followers","following_url":"https://api.github.com/users/appium/following{/other_user}","gists_url":"https://api.github.com/users/appium/gists{/gist_id}","starred_url":"https://api.github.com/users/appium/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/appium/subscriptions","organizations_url":"https://api.github.com/users/appium/orgs","repos_url":"https://api.github.com/users/appium/repos","events_url":"https://api.github.com/users/appium/events{/privacy}","received_events_url":"https://api.github.com/users/appium/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/appium/appium-support","description":"Support libs used across appium packages.","fork":false,"url":"https://api.github.com/repos/appium/appium-support","forks_url":"https://api.github.com/repos/appium/appium-support/forks","keys_url":"https://api.github.com/repos/appium/appium-support/keys{/key_id}","collaborators_url":"https://api.github.com/repos/appium/appium-support/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/appium/appium-support/teams","hooks_url":"https://api.github.com/repos/appium/appium-support/hooks","issue_events_url":"https://api.github.com/repos/appium/appium-support/issues/events{/number}","events_url":"https://api.github.com/repos/appium/appium-support/events","assignees_url":"https://api.github.com/repos/appium/appium-support/assignees{/user}","branches_url":"https://api.github.com/repos/appium/appium-support/branches{/branch}","tags_url":"https://api.github.com/repos/appium/appium-support/tags","blobs_url":"https://api.github.com/repos/appium/appium-support/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/appium/appium-support/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/appium/appium-support/git/refs{/sha}","trees_url":"https://api.github.com/repos/appium/appium-support/git/trees{/sha}","statuses_url":"https://api.github.com/repos/appium/appium-support/statuses/{sha}","languages_url":"https://api.github.com/repos/appium/appium-support/languages","stargazers_url":"https://api.github.com/repos/appium/appium-support/stargazers","contributors_url":"https://api.github.com/repos/appium/appium-support/contributors","subscribers_url":"https://api.github.com/repos/appium/appium-support/subscribers","subscription_url":"https://api.github.com/repos/appium/appium-support/subscription","commits_url":"https://api.github.com/repos/appium/appium-support/commits{/sha}","git_commits_url":"https://api.github.com/repos/appium/appium-support/git/commits{/sha}","comments_url":"https://api.github.com/repos/appium/appium-support/comments{/number}","issue_comment_url":"https://api.github.com/repos/appium/appium-support/issues/comments{/number}","contents_url":"https://api.github.com/repos/appium/appium-support/contents/{+path}","compare_url":"https://api.github.com/repos/appium/appium-support/compare/{base}...{head}","merges_url":"https://api.github.com/repos/appium/appium-support/merges","archive_url":"https://api.github.com/repos/appium/appium-support/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/appium/appium-support/downloads","issues_url":"https://api.github.com/repos/appium/appium-support/issues{/number}","pulls_url":"https://api.github.com/repos/appium/appium-support/pulls{/number}","milestones_url":"https://api.github.com/repos/appium/appium-support/milestones{/number}","notifications_url":"https://api.github.com/repos/appium/appium-support/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/appium/appium-support/labels{/name}","releases_url":"https://api.github.com/repos/appium/appium-support/releases{/id}","deployments_url":"https://api.github.com/repos/appium/appium-support/deployments","created_at":"2014-12-27T08:32:36Z","updated_at":"2018-07-02T02:32:39Z","pushed_at":"2018-07-05T19:16:40Z","git_url":"git://github.com/appium/appium-support.git","ssh_url":"git@github.com:appium/appium-support.git","clone_url":"https://github.com/appium/appium-support.git","svn_url":"https://github.com/appium/appium-support","homepage":null,"size":1254,"stargazers_count":5,"watchers_count":5,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":13,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":13,"open_issues":1,"watchers":5,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/appium/appium-support/pulls/75"},"html":{"href":"https://github.com/appium/appium-support/pull/75"},"issue":{"href":"https://api.github.com/repos/appium/appium-support/issues/75"},"comments":{"href":"https://api.github.com/repos/appium/appium-support/issues/75/comments"},"review_comments":{"href":"https://api.github.com/repos/appium/appium-support/pulls/75/comments"},"review_comment":{"href":"https://api.github.com/repos/appium/appium-support/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/appium/appium-support/pulls/75/commits"},"statuses":{"href":"https://api.github.com/repos/appium/appium-support/statuses/8cb82fa903bb40a4013016aa1047a29a4aa67e4b"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"jlipps"}}