<|start_filename|>web/public/js/app.js<|end_filename|>
//------------------------------------------------------------------------------
// 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.
//------------------------------------------------------------------------------
(function () {
  // listen for request sent over XHR and automatically show/hide spinner
  angular.module('ngLoadingSpinner', [])
    .directive('spinner', ['$http', function ($http) {
      return {
        link: function (scope, elm, attrs) {
          scope.isLoading = function () {
            return $http.pendingRequests.length > 0;
          };
          scope.$watch(scope.isLoading, function (loading) {
            if (loading) {
              document.getElementById('loadingProgress').style.visibility = "visible";
            } else {
              document.getElementById('loadingProgress').style.visibility = "hidden";
            }
          });
        }
      };
    }]);

  // angular app initialization
  var app = angular.module('app', [
    'ngMaterial',
    'ngLoadingSpinner',
    'ui.router',
    'angularCSS',
    'ngFileUpload',
  ]);

  app.config(function($stateProvider, $urlRouterProvider) {

    $urlRouterProvider.otherwise('/');

    $stateProvider
      .state('home', {
        url: '/',
        templateUrl: 'routes/home/home.html',
        css: 'routes/home/home.css'
      });

    $stateProvider
      .state('about', {
        url: '/about',
        templateUrl: 'routes/about/about.html',
        css: 'routes/about/about.css'
      });

    $stateProvider
      .state('video', {
        url: '/videos/:videoId',
        templateUrl: 'routes/video/video.html',
        css: 'routes/video/video.css',
      });
  });

  app.config(function($mdProgressCircularProvider) {
    $mdProgressCircularProvider.configure({
      strokeWidth: 4
    });
  });

  app
    .filter('formatSentence', [
      function() {
        return function(value) {
          var result = value.trim().replace(/%HESITATION/g, '...');
          return result.substr(0, 1).toUpperCase() + result.substr(1);
        }
      }
    ])
    .filter('formatPercent', [
      function () {
        return function (value) {
          return Math.round(value * 100);
        }
      }
    ])
    .filter('formatSeconds', [
    function () {
        return function (value) {
          var date = new Date(null);
          date.setSeconds(value);
          return date.toISOString().substr(14, 5);
        }
    }
    ]);

  app.controller('MainController', ['$scope', '$rootScope', '$state', '$http', '$mdDialog', 'Upload', function($scope, $rootScope, $state, $http, $mdDialog, Upload) {
    $scope.lightTheme = true;
    $scope.toggleLight = function() {
      $scope.lightTheme = !$scope.lightTheme;
      var theme = $scope.lightTheme ? 'css/darkvision-light.css' : 'css/darkvision-dark.css'
      document.getElementById("theme").href = theme;
    };

    $scope.isUploading = false;
    $scope.uploadProgress = 0;

    $scope.showUploadForm = function(ev) {
      $mdDialog.show({
        controller: UploadDialogController,
        templateUrl: 'routes/upload-dialog.tmpl.html',
        parent: angular.element(document.body),
        targetEvent: ev,
        clickOutsideToClose: true
      })
      .then(function(upload) {
        uploadFile(upload)
      }, function() {
      });
    };

    function uploadFile(fileData) {
      console.log('Uploading', fileData);
        $scope.isUploading = true;
        $scope.uploadProgress = 0;

        fileData.file.upload = Upload.upload({
          url: '/upload',
          data: fileData
        });

        // this should trigger the basic auth login/password
        $http.get('/upload').then(function(response) {
          fileData.file.upload.then(function(response) {
            $scope.isUploading = false;
            console.log('Upload complete', response.data);
          }, function(response) {
            $scope.isUploading = false;
            console.log('Upload failed', response.status, response.data);
          }, function(evt) {
            $scope.uploadProgress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
          });
        }).catch(function () {
          $scope.isUploading = false;
        });
    };

    function UploadDialogController($scope, $mdDialog) {

      $scope.upload = {
        file: null,
        title: null,
        language_model: 'en-US_BroadbandModel'
      };

      $scope.selectFile = function(file) {
        console.log('Select file to upload', file);
        if (file && !$scope.upload.title && file.type === 'video/mp4') {
          // initialize the title, removing the extension
          $scope.upload.title = file.name.substr(0, file.name.lastIndexOf('.')) || file.name;
        }
        $scope.upload.file = file;
      }

      $scope.cancel = function() {
        $mdDialog.cancel();
      };

      $scope.submit = function() {
        $mdDialog.hide($scope.upload);
      };
    }
  }]);

}());


<|start_filename|>ios/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h<|end_filename|>
#import <UIKit/UIKit.h>


FOUNDATION_EXPORT double AlamofireVersionNumber;
FOUNDATION_EXPORT const unsigned char AlamofireVersionString[];



<|start_filename|>web/public/routes/home/home.css<|end_filename|>
.home {
  padding: 18px 40px;
  overflow-y: scroll;
}

.width-wrapper {
  max-width: 1440px;
  margin: 0 auto;
}

.button-group {
  text-transform: none;
  border-radius: 5px;
  margin: 0;
  min-width: inherit;
  margin-left: 15px;
  padding-left: 15px;
  padding-right: 15px;
}

.button-group:not([disabled]):hover {
  background-color: inherit !important;
  transition: all 0.25s ease;
}

.button-group:first-of-type {
  margin-left: 15px;
}

.showing {
  font-weight: 500;
}

.video-box-title {
  margin-top: 10px;
  display: block; /* Fallback for non-webkit */
  display: -webkit-box;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 14px;
  height: 32px;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.video-box-title a {
  color: inherit;
  text-decoration: none;
}

.video-box-tags {
  position: absolute;
  bottom: 10px;
  right: 10px;
}

.image-uploads-title {
  margin-top:30px;
}

.image-thumbnail {
  width: 220px;
  max-width: 220px;
  max-height: 300px;
}

@media (max-width: 600px) {
  .image-thumbnail {
    width: 100%;
    max-width: none;
    max-height: none;
  }
}

.video-box {
}

.image-box {
  margin-bottom: 20px;
  padding-bottom: 20px;
  border-bottom: 1px solid;
}

.video-box {
  width: 100%;
  height: 100%;
}

.video-box > a {
  display: block;
  position: relative;
  -webkit-background-size: cover;
  -moz-background-size: cover;
  -o-background-size: cover;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center center;
}

.video-box-title a {
  background-color: transparent;
}


<|start_filename|>ios/Pods/Target Support Files/SwiftMoment/SwiftMoment-umbrella.h<|end_filename|>
#import <UIKit/UIKit.h>

#import "SwiftMoment.h"

FOUNDATION_EXPORT double SwiftMomentVersionNumber;
FOUNDATION_EXPORT const unsigned char SwiftMomentVersionString[];



<|start_filename|>web/public/css/darkvision-dark.css<|end_filename|>
body, html {
  background: -moz-linear-gradient(top, #24242c 0%, #1a191f 100%); /* FF3.6-15 */
  background: -webkit-linear-gradient(top, #24242c 0%,#1a191f 100%); /* Chrome10-25,Safari5.1-6 */
  background: linear-gradient(to bottom, #24242c 0%,#1a191f 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#24242c', endColorstr='#1a191f',GradientType=0 ); /* IE6-9 */
  color: #cccccc;
}

/**
  Header
  **/

md-toolbar {
  background-color: #262626 !important;
}

md-menu-content {
  background-color: #262626 !important;
  color: #cccccc;
}

md-menu-item button {
  color: #cccccc !important;
}

md-toolbar md-icon {
  fill: white !important;
}

.title {
  color: #ffffff;
}

.search, #search {
  background-color: #262626;
  color: white;
}

.search md-icon {
  color: #ed215e;
}

.upload-button {
  color: #ffffff !important;
}

.upload-button[disabled] {
  color: rgba(255,255,255,0.26) !important;
}

md-progress-circular path {
  stroke: white !important;
}

md-progress-circular.loading path {
  stroke: #ed215e !important;
}

md-progress-linear .md-container {
  background-color: #333333;
}

md-progress-linear .md-container .md-bar {
  background-color: #ed215e;
}

/**
  Status
  **/

.status {
  background-color: #333333;
}

.status-progress .md-container {
  background-color: #cccccc;
}

.status-progress .md-container .md-bar {
  background-color: #595959;
}

.status-view-details,
.status-view-details md-icon,
.status-hide-details {
  color: #cccccc !important;
}

/**
  Footer
  **/

footer {
  background-color: black !important;
}

/**
  Common
  **/

a {
  color: #eaeaea;
}

h3 {
  color: #ed215e;
  border-bottom-color: #ed215e;
}

button.md-button {
  color: black;
}

label.md-button {
  color: black;
}

.border-box {
  border-color: #373636 !important;
  border-width: 2px;
}

.tag, .tag a, .tag a md-icon {
  background-color: #22557F;
  color: #90CDFF;
}

.tag-keyword {
  background-color: #22557F;
  color: #90CDFF;
}

.tag-face {
  background-color: #3B7F4F;
  color: #C2FFD4;
}

.face-overlay-box {
  border-color: #3B7F4F !important;
}

.face-overlay-text {
  color: #C2FFD4;
  background-color: #3B7F4F;
}

/**
  Home
  **/

.button-group {
  color: #bcc0c1 !important;
}

.button-group[disabled] {
  background-color: #ed215e !important;
  color: white !important;
}

.video-box a,
.image-box-image {
  background-color: #24242c;
}

.video-box-tag {
  background-color: rgba(0, 0, 0, 0.3);
  color: #eaeaea;
}

.video-box-title {
  color: #eaeaea;
}

/**
  Video
  **/

.breadcrumb a md-icon {
  color: #ed215e;
}

.entry-overview-counter-value {
  color: #ed215e;
}

.video-frame-count {
  background-color: #ed215e;
  color: #ffffff;
}

.video-navigator {
  border-color: #1f1f25 !important;
  background-color: #262626;
}

.video-overview-box {
}

.video-overview-title {
  color: #ffffff;
}

.video-menu-button md-icon {
  color: #ed215e;
}

.video-frame-timeline {
  width: 2px;
  background-color: #ed215e !important;
}

.timecode {
  color: #ffffff;
  border-color: #ed215e !important;
  background-color: #ed215e;
}

.selected-frame .timecode {
  color: #ed215e;
  border-color: #ffffff !important;
  background-color: #ffffff;
}

.selected-frame-indicator {
  color: #ed215e;
  border-right-color: #ed215e;
}

.selected-frame-indicator md-icon {
  color: #ed215e;
}

.selected-frame {
  background-color: #71102d;
}

.transcript {
  background-color: #24242c;
}

.transcript-timestamp {
  color: #ed215e;
}


<|start_filename|>web/public/routes/about/about.css<|end_filename|>
.twitter-icon {
  fill: #1b95e0 !important;
}


<|start_filename|>ios/Pods/Target Support Files/AlamofireImage/AlamofireImage-umbrella.h<|end_filename|>
#import <UIKit/UIKit.h>


FOUNDATION_EXPORT double AlamofireImageVersionNumber;
FOUNDATION_EXPORT const unsigned char AlamofireImageVersionString[];



<|start_filename|>ios/Pods/Target Support Files/TTTAttributedLabel/TTTAttributedLabel-umbrella.h<|end_filename|>
#import <UIKit/UIKit.h>

#import "TTTAttributedLabel.h"

FOUNDATION_EXPORT double TTTAttributedLabelVersionNumber;
FOUNDATION_EXPORT const unsigned char TTTAttributedLabelVersionString[];



<|start_filename|>processing/extractor/server/app.js<|end_filename|>
/**
 * Copyright 2017 IBM Corp. All Rights Reserved.
 *
 * 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
 *
 *  https://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.
 */
const bodyParser = require('body-parser');
const express = require('express');

const extractor = value => new Promise((resolve) => {
  console.log('[running] value =', value);
  const spawn = require('child_process').spawn;
  const proc = spawn('node', ['/blackbox/client/extract.js', value], {
    cwd: '/blackbox/client'
  });

  let output = '';
  proc.stdout.on('data', (data) => {
    console.log('[stdout] ' + data); // eslint-disable-line prefer-template
    output += data;
  });
  proc.stderr.on('data', (data) => {
    console.log('[stderr] ' + data); // eslint-disable-line prefer-template
    output += data;
  });
  proc.on('close', (code) => {
    console.log('[exit] with code', code);
    const result = {
      result: {
        msg: output
      }
    };
    resolve(result);
  });
});

const invokeExtractor = args => extractor(JSON.stringify(args));

const app = express();
app.set('port', 8080);
app.use(bodyParser.json());

const server = app.listen(app.get('port'), () => {
  const host = server.address().address;
  const port = server.address().port;
  console.log('[start] listening at http://%s:%s', host, port);
});

app.post('/init', (req, res) => res.send());
app.post('/run', (req, res) => {
  console.log('[run]', req.body);
  const args = req.body.value;
  invokeExtractor(args).then((result) => {
    res.json(result);
  }).catch((err) => {
    console.log(err);
    res.status(500).json({ error: err });
  });
});


<|start_filename|>processing/textanalysis/textanalysis.js<|end_filename|>
/**
 * Copyright 2017 IBM Corp. All Rights Reserved.
 *
 * 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
 *
 *  https://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.
 */

/**
 * Called by Whisk.
 *
 * It expects the following parameters as attributes of 'args'
 * - cloudantUrl: "https://username:password@host"
 * - cloudantDbName: "openwhisk-darkvision"
 * - doc: "audio document in cloudant"
 */
function main(args) {
  return new Promise((resolve, reject) => {
    mainImpl(args, (err, result) => {
      if (err) {
        reject(err);
      } else {
        resolve(result);
      }
    });
  });
}

exports.main = main;

/**
 * @param mainCallback(err, analysis)
 */
function mainImpl(args, mainCallback) {
  const startTime = (new Date()).getTime();

  if (args.doc) {
    const audioDocumentId = args.doc._id;
    console.log('[', audioDocumentId, '] Processing audio transcript from document');

    const mediaStorage = require('./lib/cloudantstorage')({
      cloudantUrl: args.cloudantUrl,
      cloudantDbName: args.cloudantDbName
    });

    const async = require('async');
    async.waterfall([
      // get the document from the db
      (callback) => {
        mediaStorage.get(audioDocumentId, (err, audio) => {
          callback(err, audio);
        });
      },
      // trigger the analysis on the audio file
      (audio, callback) => {
        const fullText = audio.transcript.results.reduce((text, transcript) =>
          `${text}${transcript.alternatives[0].transcript}. `
        , '');
        processTranscript(args, fullText, (err, analysis) => {
          callback(err, audio, analysis);
        });
      },
      // write result in the db
      (audio, analysis, callback) => {
        audio.analysis = analysis;
        mediaStorage.insert(audio, (err) => {
          if (err) {
            callback(err);
          } else {
            callback(null, analysis);
          }
        });
      }
    ], (err, analysis) => {
      const durationInSeconds = ((new Date()).getTime() - startTime) / 1000;

      if (err) {
        console.log('[', audioDocumentId, '] KO (', durationInSeconds, 's)', err);
        mainCallback(err);
      } else {
        console.log('[', audioDocumentId, '] OK (', durationInSeconds, 's)');
        mainCallback(null, analysis);
      }
    });
    return true;
  }

  console.log('Parameter "doc" not found', args);
  mainCallback('Parameter "doc" not found');
  return false;
}

/**
 * Sends the transcript to different text processing service
 */
function processTranscript(args, text, processCallback) {
  const async = require('async');
  const analysis = {
  };
  async.parallel([
    // nlu
    (callback) => {
      const request = require('request');
      request({
        url: `${args.nluUrl}/v1/analyze?version=2017-02-27`,
        method: 'POST',
        json: true,
        body: {
          text,
          features: {
            // categories: { },
            concepts: { },
            emotion: { },
            entities: {
              sentiment: true
            },
            keywords: { },
            // relations: { },
          }
        },
        auth: {
          username: args.nluUsername,
          password: args.nluPassword
        },
      }, (err, response, body) => {
        if (err) {
          console.log('Natural Language Understanding', err);
        } else {
          analysis.nlu = body;
        }
        callback(null);
      });
    },
  ], (err) => {
    processCallback(err, analysis);
  });
}


<|start_filename|>web/public/js/videos.service.js<|end_filename|>
//------------------------------------------------------------------------------
// 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.
//------------------------------------------------------------------------------
(function () {
  function VideosService($http, $q) {
    console.log("Initializing VideosService...");

    return {
      all: function () {
        var deferred = $q.defer();
        $http.get("/api/videos").then(function(response) { return response.data; }).then(function (data) {
          deferred.resolve(data);
        }).catch(function () {
          deferred.reject();
        });
        return deferred.promise;
      },
      get: function (videoId) {
        var deferred = $q.defer();
        $http.get("/api/videos/" + encodeURIComponent(videoId)).then(function(response) { return response.data; }).then(function (data) {
          deferred.resolve(data);
        }).catch(function () {
          deferred.reject();
        });
        return deferred.promise;
      },
      reset: function (videoId) {
        var deferred = $q.defer();
        $http.post("/api/videos/" + encodeURIComponent(videoId) + "/reset").then(function(response) { return response.data; }).then(function (data) {
          deferred.resolve(data);
        }).catch(function () {
          deferred.reject();
        });
        return deferred.promise;
      },
      resetImages: function (videoId) {
        var deferred = $q.defer();
        $http.post("/api/videos/" + encodeURIComponent(videoId) + "/reset-images").then(function(response) { return response.data; }).then(function (data) {
          deferred.resolve(data);
        }).catch(function () {
          deferred.reject();
        });
        return deferred.promise;
      },
      resetAudio: function (videoId) {
        var deferred = $q.defer();
        $http.post("/api/videos/" + encodeURIComponent(videoId) + "/reset-audio").then(function(response) { return response.data; }).then(function (data) {
          deferred.resolve(data);
        }).catch(function () {
          deferred.reject();
        });
        return deferred.promise;
      },
      delete: function (videoId) {
        var deferred = $q.defer();
        $http.delete("/api/videos/" + encodeURIComponent(videoId)).then(function(response) { return response.data; }).then(function (data) {
          deferred.resolve(data);
        }).catch(function () {
          deferred.reject();
        });
        return deferred.promise;
      }
    };
  }

  angular.module('app')
    .service('VideosService', ['$http', '$q', VideosService]);
}());
