diff --git a/README.md b/README.md
index 573c8867a6..0435e0ddd2 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,6 @@ on Google Cloud Platform.
* [Google Cloud Prediction API](#google-cloud-prediction-api)
* [Google Cloud Speech API (Beta)](#google-cloud-speech-api-beta)
* [Google Translate API](#google-translate-api)
- * [Google Cloud Video Intelligence API](#google-cloud-video-intelligence-api)
* [Google Cloud Vision API](#google-cloud-vision-api)
* [**Management Tools**](#management-tools)
* [Stackdriver Debugger](#stackdriver-debugger)
@@ -335,16 +334,6 @@ View the [Translate API Node.js samples][translate_samples].
[translate_docs]: https://cloud.google.com/translate/docs/
[translate_samples]: translate
-#### Google Cloud Video Intelligence API
-
-The [Cloud Video Intelligence API][video_intelligence_docs] allows developers to
-use Google video analysis technology as part of their applications.
-
-View the [Cloud Video Intelligence API Node.js samples][video_intelligence_samples].
-
-[video_intelligence_docs]: https://cloud.google.com/video-intelligence/docs/
-[video_intelligence_samples]: https://github.com/googleapis/nodejs-video-intelligence/tree/master/samples
-
#### Google Cloud Vision API
The [Cloud Vision API][vision_docs] allows developers to easily integrate vision
diff --git a/circle.yml b/circle.yml
index 42635db152..e04fddfc2e 100644
--- a/circle.yml
+++ b/circle.yml
@@ -108,4 +108,6 @@ deployment:
- node scripts/build "storage-transfer"
- node scripts/build "trace"
- node scripts/build "translate"
+ # TODO: This build times out. Does video need more than 10 minutes?
+ # - node scripts/build "video"
- node scripts/build "vision"
diff --git a/video/README.md b/video/README.md
index 8d997187cc..05da66feb4 100644
--- a/video/README.md
+++ b/video/README.md
@@ -1,5 +1,77 @@
-Samples for the [Google Cloud Video Intelligence API Node.js Client][client]
-have moved to [github.com/googleapis/nodejs-video-intelligence/tree/master/samples/][samples].
+
-[client]: https://github.com/googleapis/nodejs-video-intelligence
-[samples]: https://github.com/googleapis/nodejs-video-intelligence/tree/master/samples
+# Google Cloud Video Intelligence API Node.js Samples
+
+[![Build](https://storage.googleapis.com/cloud-docs-samples-badges/GoogleCloudPlatform/nodejs-docs-samples/nodejs-docs-samples-videointelligence.svg)]()
+
+The [Cloud Video Intelligence API](https://cloud.google.com/video-intelligence) allows developers to use Google video analysis technology as part of their applications.
+
+## Table of Contents
+
+* [Setup](#setup)
+* [Samples](#samples)
+ * [Video Intelligence](#video-intelligence)
+* [Running the tests](#running-the-tests)
+
+## Setup
+
+1. Read [Prerequisites][prereq] and [How to run a sample][run] first.
+1. Install dependencies:
+
+ With **npm**:
+
+ npm install
+
+ With **yarn**:
+
+ yarn install
+
+[prereq]: ../README.md#prerequisites
+[run]: ../README.md#how-to-run-a-sample
+
+## Samples
+
+### Video Intelligence
+
+View the [documentation][video_0_docs] or the [source code][video_0_code].
+
+__Usage:__ `node analyze.js --help`
+
+```
+Commands:
+ faces Analyzes faces in a video stored in Google Cloud Storage using the Cloud Video Intelligence API.
+ shots Analyzes shot angles in a video stored in Google Cloud Storage using the Cloud Video
+ Intelligence API.
+ labels-gcs Labels objects in a video stored in Google Cloud Storage using the Cloud Video Intelligence API.
+ labels-file Labels objects in a video stored locally using the Cloud Video Intelligence API.
+ safe-search Detects explicit content in a video stored in Google Cloud Storage.
+
+Options:
+ --help Show help [boolean]
+
+Examples:
+ node analyze.js faces gs://demomaker/larry_sergey_ice_bucket_short.mp4
+ node analyze.js shots gs://demomaker/sushi.mp4
+ node analyze.js labels-gcs gs://demomaker/tomatoes.mp4
+ node analyze.js labels-file resources/cat.mp4
+ node analyze.js safe-search gs://demomaker/tomatoes.mp4
+
+For more information, see https://cloud.google.com/video-intelligence/docs
+```
+
+[video_0_docs]: https://cloud.google.com/video-intelligence/docs
+[video_0_code]: analyze.js
+
+## Running the tests
+
+1. Set the **GCLOUD_PROJECT** and **GOOGLE_APPLICATION_CREDENTIALS** environment variables.
+
+1. Run the tests:
+
+ With **npm**:
+
+ npm test
+
+ With **yarn**:
+
+ yarn test
diff --git a/video/analyze.js b/video/analyze.js
new file mode 100644
index 0000000000..f82c904a8c
--- /dev/null
+++ b/video/analyze.js
@@ -0,0 +1,357 @@
+/**
+ * Copyright 2017, Google, Inc.
+ * Licensed under the Apache License, Version 2.0 (the `License`);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an `AS IS` BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+function analyzeFaces (gcsUri) {
+ // [START analyze_faces]
+ // Imports the Google Cloud Video Intelligence library
+ const Video = require('@google-cloud/video-intelligence');
+
+ // Instantiates a client
+ const video = Video();
+
+ // The GCS filepath of the video to analyze
+ // const gcsUri = 'gs://my-bucket/my-video.mp4';
+
+ const request = {
+ inputUri: gcsUri,
+ features: ['FACE_DETECTION']
+ };
+
+ // Detects faces in a video
+ video.annotateVideo(request)
+ .then((results) => {
+ const operation = results[0];
+ console.log('Waiting for operation to complete...');
+ return operation.promise();
+ })
+ .then((results) => {
+ // Gets faces
+ const faces = results[0].annotationResults[0].faceAnnotations;
+ faces.forEach((face, faceIdx) => {
+ console.log(`Face #${faceIdx}`);
+ console.log(`\tThumbnail size: ${face.thumbnail.length}`);
+ face.segments.forEach((segment, segmentIdx) => {
+ segment = segment.segment;
+ if (segment.startTimeOffset.seconds === undefined) {
+ segment.startTimeOffset.seconds = 0;
+ }
+ if (segment.startTimeOffset.nanos === undefined) {
+ segment.startTimeOffset.nanos = 0;
+ }
+ if (segment.endTimeOffset.seconds === undefined) {
+ segment.endTimeOffset.seconds = 0;
+ }
+ if (segment.endTimeOffset.nanos === undefined) {
+ segment.endTimeOffset.nanos = 0;
+ }
+ console.log(`\tAppearance #${segmentIdx}:`);
+ console.log(`\t\tStart: ${segment.startTimeOffset.seconds}` +
+ `.${(segment.startTimeOffset.nanos / 1e6).toFixed(0)}s`);
+ console.log(`\t\tEnd: ${segment.endTimeOffset.seconds}.` +
+ `${(segment.endTimeOffset.nanos / 1e6).toFixed(0)}s`);
+ });
+ console.log(`\tLocations:`);
+ });
+ })
+ .catch((err) => {
+ console.error('ERROR:', err);
+ });
+ // [END analyze_faces]
+}
+
+function analyzeLabelsGCS (gcsUri) {
+ // [START analyze_labels_gcs]
+ // Imports the Google Cloud Video Intelligence library
+ const Video = require('@google-cloud/video-intelligence');
+
+ // Instantiates a client
+ const video = Video({
+ servicePath: `videointelligence.googleapis.com`
+ });
+
+ // The GCS filepath of the video to analyze
+ // const gcsUri = 'gs://my-bucket/my-video.mp4';
+
+ const request = {
+ inputUri: gcsUri,
+ features: ['LABEL_DETECTION']
+ };
+
+ // Detects labels in a video
+ video.annotateVideo(request)
+ .then((results) => {
+ const operation = results[0];
+ console.log('Waiting for operation to complete...');
+ return operation.promise();
+ })
+ .then((results) => {
+ // Gets annotations for video
+ const annotations = results[0].annotationResults[0];
+
+ const labels = annotations.segmentLabelAnnotations;
+ labels.forEach((label) => {
+ console.log(`Label ${label.entity.description} occurs at:`);
+ label.segments.forEach((segment) => {
+ let time = segment.segment;
+ if (time.startTimeOffset.seconds === undefined) {
+ time.startTimeOffset.seconds = 0;
+ }
+ if (time.startTimeOffset.nanos === undefined) {
+ time.startTimeOffset.nanos = 0;
+ }
+ if (time.endTimeOffset.seconds === undefined) {
+ time.endTimeOffset.seconds = 0;
+ }
+ if (time.endTimeOffset.nanos === undefined) {
+ time.endTimeOffset.nanos = 0;
+ }
+ console.log(`\tStart: ${time.startTimeOffset.seconds}` +
+ `.${(time.startTimeOffset.nanos / 1e6).toFixed(0)}s`);
+ console.log(`\tEnd: ${time.endTimeOffset.seconds}.` +
+ `${(time.endTimeOffset.nanos / 1e6).toFixed(0)}s`);
+ console.log(`\tConfidence: ${segment.confidence}`);
+ });
+ });
+ })
+ .catch((err) => {
+ console.error('ERROR:', err);
+ });
+ // [END analyze_labels_gcs]
+}
+
+function analyzeLabelsLocal (path) {
+ // [START analyze_labels_local]
+ // Imports the Google Cloud Video Intelligence library + Node's fs library
+ const Video = require('@google-cloud/video-intelligence');
+ const fs = require('fs');
+
+ // Instantiates a client
+ const video = Video();
+
+ // The local filepath of the video to analyze
+ // const path = 'my-file.mp4';
+
+ // Reads a local video file and converts it to base64
+ const file = fs.readFileSync(path);
+ const inputContent = file.toString('base64');
+
+ // Constructs request
+ const request = {
+ inputContent: inputContent,
+ features: ['LABEL_DETECTION']
+ };
+
+ // Detects labels in a video
+ video.annotateVideo(request)
+ .then((results) => {
+ const operation = results[0];
+ console.log('Waiting for operation to complete...');
+ return operation.promise();
+ })
+ .then((results) => {
+ // Gets annotations for video
+ const annotations = results[0].annotationResults[0];
+
+ const labels = annotations.segmentLabelAnnotations;
+ labels.forEach((label) => {
+ console.log(`Label ${label.entity.description} occurs at:`);
+ label.segments.forEach((segment) => {
+ let time = segment.segment;
+ if (time.startTimeOffset.seconds === undefined) {
+ time.startTimeOffset.seconds = 0;
+ }
+ if (time.startTimeOffset.nanos === undefined) {
+ time.startTimeOffset.nanos = 0;
+ }
+ if (time.endTimeOffset.seconds === undefined) {
+ time.endTimeOffset.seconds = 0;
+ }
+ if (time.endTimeOffset.nanos === undefined) {
+ time.endTimeOffset.nanos = 0;
+ }
+ console.log(`\tStart: ${time.startTimeOffset.seconds}` +
+ `.${(time.startTimeOffset.nanos / 1e6).toFixed(0)}s`);
+ console.log(`\tEnd: ${time.endTimeOffset.seconds}.` +
+ `${(time.endTimeOffset.nanos / 1e6).toFixed(0)}s`);
+ console.log(`\tConfidence: ${segment.confidence}`);
+ });
+ });
+ })
+ .catch((err) => {
+ console.error('ERROR:', err);
+ });
+ // [END analyze_labels_local]
+}
+
+function analyzeShots (gcsUri) {
+ // [START analyze_shots]
+ // Imports the Google Cloud Video Intelligence library
+ const Video = require('@google-cloud/video-intelligence');
+
+ // Instantiates a client
+ const video = Video();
+
+ // The GCS filepath of the video to analyze
+ // const gcsUri = 'gs://my-bucket/my-video.mp4';
+
+ const request = {
+ inputUri: gcsUri,
+ features: ['SHOT_CHANGE_DETECTION']
+ };
+
+ // Detects camera shot changes
+ video.annotateVideo(request)
+ .then((results) => {
+ const operation = results[0];
+ console.log('Waiting for operation to complete...');
+ return operation.promise();
+ })
+ .then((results) => {
+ // Gets shot changes
+ const shotChanges = results[0].annotationResults[0].shotAnnotations;
+ console.log('Shot changes:');
+
+ if (shotChanges.length === 1) {
+ console.log(`The entire video is one shot.`);
+ } else {
+ shotChanges.forEach((shot, shotIdx) => {
+ console.log(`Scene ${shotIdx} occurs from:`);
+ if (shot.startTimeOffset === undefined) {
+ shot.startTimeOffset = {};
+ }
+ if (shot.endTimeOffset === undefined) {
+ shot.endTimeOffset = {};
+ }
+ if (shot.startTimeOffset.seconds === undefined) {
+ shot.startTimeOffset.seconds = 0;
+ }
+ if (shot.startTimeOffset.nanos === undefined) {
+ shot.startTimeOffset.nanos = 0;
+ }
+ if (shot.endTimeOffset.seconds === undefined) {
+ shot.endTimeOffset.seconds = 0;
+ }
+ if (shot.endTimeOffset.nanos === undefined) {
+ shot.endTimeOffset.nanos = 0;
+ }
+ console.log(`\tStart: ${shot.startTimeOffset.seconds}` +
+ `.${(shot.startTimeOffset.nanos / 1e6).toFixed(0)}s`);
+ console.log(`\tEnd: ${shot.endTimeOffset.seconds}.` +
+ `${(shot.endTimeOffset.nanos / 1e6).toFixed(0)}s`);
+ });
+ }
+ })
+ .catch((err) => {
+ console.error('ERROR:', err);
+ });
+ // [END analyze_shots]
+}
+
+function analyzeSafeSearch (gcsUri) {
+ // [START analyze_safe_search]
+ // Imports the Google Cloud Video Intelligence library
+ const Video = require('@google-cloud/video-intelligence');
+
+ // Instantiates a client
+ const video = Video();
+
+ // The GCS filepath of the video to analyze
+ // const gcsUri = 'gs://my-bucket/my-video.mp4';
+
+ const request = {
+ inputUri: gcsUri,
+ features: ['EXPLICIT_CONTENT_DETECTION']
+ };
+
+ // Human-readable likelihoods
+ const likelihoods = ['UNKNOWN', 'VERY_UNLIKELY', 'UNLIKELY', 'POSSIBLE', 'LIKELY', 'VERY_LIKELY'];
+
+ // Detects unsafe content
+ video.annotateVideo(request)
+ .then((results) => {
+ const operation = results[0];
+ console.log('Waiting for operation to complete...');
+ return operation.promise();
+ })
+ .then((results) => {
+ // Gets unsafe content
+ const explicitContentResults = results[0].annotationResults[0].explicitAnnotation;
+ console.log('Explicit annotation results:');
+ explicitContentResults.frames.forEach((result) => {
+ if (result.timeOffset === undefined) {
+ result.timeOffset = {};
+ }
+ if (result.timeOffset.seconds === undefined) {
+ result.timeOffset.seconds = 0;
+ }
+ if (result.timeOffset.nanos === undefined) {
+ result.timeOffset.nanos = 0;
+ }
+ console.log(`\tTime: ${result.timeOffset.seconds}` +
+ `.${(result.timeOffset.nanos / 1e6).toFixed(0)}s`);
+ console.log(`\t\tPornography liklihood: ${likelihoods[result.pornographyLikelihood]}`);
+ });
+ })
+ .catch((err) => {
+ console.error('ERROR:', err);
+ });
+ // [END analyze_safe_search]
+}
+
+require(`yargs`) // eslint-disable-line
+ .demand(1)
+ .command(
+ `faces `,
+ `Analyzes faces in a video stored in Google Cloud Storage using the Cloud Video Intelligence API.`,
+ {},
+ (opts) => analyzeFaces(opts.gcsUri)
+ )
+ .command(
+ `shots `,
+ `Analyzes shot angles in a video stored in Google Cloud Storage using the Cloud Video Intelligence API.`,
+ {},
+ (opts) => analyzeShots(opts.gcsUri)
+ )
+ .command(
+ `labels-gcs `,
+ `Labels objects in a video stored in Google Cloud Storage using the Cloud Video Intelligence API.`,
+ {},
+ (opts) => analyzeLabelsGCS(opts.gcsUri)
+ )
+ .command(
+ `labels-file `,
+ `Labels objects in a video stored locally using the Cloud Video Intelligence API.`,
+ {},
+ (opts) => analyzeLabelsLocal(opts.gcsUri)
+ )
+ .command(
+ `safe-search `,
+ `Detects explicit content in a video stored in Google Cloud Storage.`,
+ {},
+ (opts) => analyzeSafeSearch(opts.gcsUri)
+ )
+ .example(`node $0 faces gs://demomaker/larry_sergey_ice_bucket_short.mp4`)
+ .example(`node $0 shots gs://demomaker/sushi.mp4`)
+ .example(`node $0 labels-gcs gs://demomaker/tomatoes.mp4`)
+ .example(`node $0 labels-file cat.mp4`)
+ .example(`node $0 safe-search gs://demomaker/tomatoes.mp4`)
+ .wrap(120)
+ .recommendCommands()
+ .epilogue(`For more information, see https://cloud.google.com/video-intelligence/docs`)
+ .help()
+ .strict()
+ .argv;
diff --git a/video/package.json b/video/package.json
new file mode 100644
index 0000000000..e17c092aa4
--- /dev/null
+++ b/video/package.json
@@ -0,0 +1,44 @@
+{
+ "name": "nodejs-docs-samples-videointelligence",
+ "version": "0.0.1",
+ "private": true,
+ "license": "Apache-2.0",
+ "author": "Google Inc.",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
+ },
+ "engines": {
+ "node": ">=4.3.2"
+ },
+ "scripts": {
+ "lint": "samples lint",
+ "pretest": "npm run lint",
+ "test": "samples test run --cmd ava -- -T 5m --verbose system-test/*.test.js"
+ },
+ "dependencies": {
+ "@google-cloud/video-intelligence": "^0.3.2",
+ "long": "^3.2.0",
+ "safe-buffer": "5.1.1",
+ "yargs": "8.0.2"
+ },
+ "devDependencies": {
+ "@google-cloud/nodejs-repo-tools": "1.4.17",
+ "ava": "0.22.0",
+ "proxyquire": "1.8.0"
+ },
+ "cloud-repo-tools": {
+ "requiresKeyFile": true,
+ "requiresProjectId": true,
+ "product": "video",
+ "samples": [
+ {
+ "id": "video",
+ "name": "Video Intelligence",
+ "file": "analyze.js",
+ "docs_link": "https://cloud.google.com/video-intelligence/docs",
+ "usage": "node analyze.js --help"
+ }
+ ]
+ }
+}
diff --git a/video/quickstart.js b/video/quickstart.js
new file mode 100644
index 0000000000..bb40eafe9f
--- /dev/null
+++ b/video/quickstart.js
@@ -0,0 +1,73 @@
+/**
+ * Copyright 2017, Google, Inc.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// [START videointelligence_quickstart]
+// Imports the Google Cloud Video Intelligence library
+const Video = require('@google-cloud/video-intelligence');
+
+// Instantiates a client
+const video = Video();
+
+// The GCS filepath of the video to analyze
+const gcsUri = 'gs://nodejs-docs-samples-video/quickstart_short.mp4';
+
+// Construct request
+const request = {
+ inputUri: gcsUri,
+ features: ['LABEL_DETECTION']
+};
+
+// Execute request
+video.annotateVideo(request)
+ .then((results) => {
+ const operation = results[0];
+ console.log('Waiting for operation to complete... (this may take a few minutes)');
+ return operation.promise();
+ })
+ .then((results) => {
+ // Gets annotations for video
+ const annotations = results[0].annotationResults[0];
+
+ // Gets labels for video from its annotations
+ const labels = annotations.segmentLabelAnnotations;
+ labels.forEach((label) => {
+ console.log(`Label ${label.entity.description} occurs at:`);
+ label.segments.forEach((segment) => {
+ segment = segment.segment;
+ if (segment.startTimeOffset.seconds === undefined) {
+ segment.startTimeOffset.seconds = 0;
+ }
+ if (segment.startTimeOffset.nanos === undefined) {
+ segment.startTimeOffset.nanos = 0;
+ }
+ if (segment.endTimeOffset.seconds === undefined) {
+ segment.endTimeOffset.seconds = 0;
+ }
+ if (segment.endTimeOffset.nanos === undefined) {
+ segment.endTimeOffset.nanos = 0;
+ }
+ console.log(`\tStart: ${segment.startTimeOffset.seconds}` +
+ `.${(segment.startTimeOffset.nanos / 1e6).toFixed(0)}s`);
+ console.log(`\tEnd: ${segment.endTimeOffset.seconds}.` +
+ `${(segment.endTimeOffset.nanos / 1e6).toFixed(0)}s`);
+ });
+ });
+ })
+ .catch((err) => {
+ console.error('ERROR:', err);
+ });
+// [END videointelligence_quickstart]
diff --git a/video/resources/cat.mp4 b/video/resources/cat.mp4
new file mode 100644
index 0000000000..0e071b9ec6
Binary files /dev/null and b/video/resources/cat.mp4 differ
diff --git a/video/system-test/analyze.test.js b/video/system-test/analyze.test.js
new file mode 100644
index 0000000000..ce54904743
--- /dev/null
+++ b/video/system-test/analyze.test.js
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2017, Google, Inc.
+ * Licensed under the Apache License, Version 2.0 (the `License`);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an `AS IS` BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// https://cloud.google.com/video-intelligence/docs/
+
+'use strict';
+
+const path = require(`path`);
+const test = require(`ava`);
+const tools = require(`@google-cloud/nodejs-repo-tools`);
+
+const cmd = `node analyze.js`;
+const cwd = path.join(__dirname, `..`);
+
+const url = `gs://nodejs-docs-samples-video/quickstart.mp4`;
+const shortUrl = `gs://nodejs-docs-samples-video/quickstart_short.mp4`;
+const file = `resources/cat.mp4`;
+
+// analyze_faces
+test.serial(`should analyze faces in a GCS file`, async (t) => {
+ const output = await tools.runAsync(`${cmd} faces ${url}`, cwd);
+ t.regex(output, /Thumbnail size: \d+/);
+ t.regex(output, /Start:.*\d+\.\d+s/);
+ t.regex(output, /End:.*\d+\.\d+s/);
+});
+
+// analyze_labels_gcs (one scene)
+test.serial(`should analyze labels in a GCS file with one scene`, async (t) => {
+ const output = await tools.runAsync(`${cmd} labels-gcs ${shortUrl}`, cwd);
+ t.regex(output, /Label shirt occurs at:/);
+ t.regex(output, /Confidence: \d+\.\d+/);
+});
+
+// analyze_labels_gcs (multiple scenes)
+test.serial(`should analyze labels in a GCS file with multiple scenes`, async (t) => {
+ const output = await tools.runAsync(`${cmd} labels-gcs ${url}`, cwd);
+ t.regex(output, /Label shirt occurs at:/);
+ t.regex(output, /Confidence: \d+\.\d+/);
+});
+
+// analyze_labels_local
+test.serial(`should analyze labels in a local file`, async (t) => {
+ const output = await tools.runAsync(`${cmd} labels-file ${file}`, cwd);
+ t.regex(output, /Label whiskers occurs at:/);
+ t.regex(output, /Confidence: \d+\.\d+/);
+});
+
+// analyze_shots (multiple shots)
+test.serial(`should analyze shots in a GCS file with multiple shots`, async (t) => {
+ const output = await tools.runAsync(`${cmd} shots ${url}`, cwd);
+ t.regex(output, /Scene 0 occurs from:/);
+});
+
+// analyze_shots (one shot)
+test.serial(`should analyze shots in a GCS file with one shot`, async (t) => {
+ const output = await tools.runAsync(`${cmd} shots ${shortUrl}`, cwd);
+ t.regex(output, /The entire video is one shot./);
+});
+
+// analyze_safe_search
+test.serial(`should analyze safe search results in a GCS file`, async (t) => {
+ const output = await tools.runAsync(`${cmd} safe-search ${url}`, cwd);
+ t.regex(output, /Time: \d+\.\d+s/);
+ t.regex(output, /Explicit annotation results:/);
+});
diff --git a/video/system-test/quickstart.test.js b/video/system-test/quickstart.test.js
new file mode 100644
index 0000000000..20965be0c6
--- /dev/null
+++ b/video/system-test/quickstart.test.js
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2017, Google, Inc.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const path = require(`path`);
+const test = require(`ava`);
+const tools = require(`@google-cloud/nodejs-repo-tools`);
+
+const cmd = `node quickstart.js`;
+const cwd = path.join(__dirname, `..`);
+
+test(`should analyze a hardcoded video`, async (t) => {
+ const output = await tools.runAsync(cmd, cwd);
+ t.regex(output, /Label standing occurs at:/);
+});