summaryrefslogtreecommitdiff
path: root/www
diff options
context:
space:
mode:
authorArjun Roychowdhury <pliablepixels@gmail.com>2015-11-16 10:36:14 -0500
committerArjun Roychowdhury <pliablepixels@gmail.com>2015-11-16 10:36:14 -0500
commit311b2a1395dfa83a70b92be30e660de18ce1822c (patch)
treef7819fbcadb61c0d24ede3dfa96d294460d56bb5 /www
parenteb7ae17dc34a12446d8484a2029fe865fc641902 (diff)
#85 - timeline event gapless playback. In Timeline mode, it always sticks to the same monitor
Former-commit-id: 3becbaaf3725f5f9e2b453cdb0b58b1908071e2f
Diffstat (limited to 'www')
-rw-r--r--www/js/EventCtrl.js5
-rw-r--r--www/js/ModalCtrl.js3
-rw-r--r--www/js/TimelineCtrl.js608
3 files changed, 452 insertions, 164 deletions
diff --git a/www/js/EventCtrl.js b/www/js/EventCtrl.js
index a2b79638..20c4f627 100644
--- a/www/js/EventCtrl.js
+++ b/www/js/EventCtrl.js
@@ -1237,13 +1237,14 @@ angular.module('zmApp.controllers')
}
- $scope.toggleGapless = function()
+ $scope.toggleGapless = function()
{
-
+ console.log ("GAPLESS TOGGLE");
$scope.loginData.gapless = !$scope.loginData.gapless;
ZMDataModel.setLogin($scope.loginData);
};
+
//--------------------------------------------------------
//Navigate to next/prev event in full screen mode
diff --git a/www/js/ModalCtrl.js b/www/js/ModalCtrl.js
index 4aa56eb7..df2c560c 100644
--- a/www/js/ModalCtrl.js
+++ b/www/js/ModalCtrl.js
@@ -441,6 +441,9 @@ angular.module('zmApp.controllers').controller('ModalCtrl', ['$scope', '$rootSco
ZMDataModel.zmLog("Error saving image: " + e.message);
console.log("***ERROR");
}
+
+
+
//-----------------------------------------------------------------------
// Saves a snapshot of the monitor image to phone storage
diff --git a/www/js/TimelineCtrl.js b/www/js/TimelineCtrl.js
index f2e8885b..b8321e00 100644
--- a/www/js/TimelineCtrl.js
+++ b/www/js/TimelineCtrl.js
@@ -9,7 +9,11 @@
// I've disabled pan and zoom and used buttons instead
// also limits # of items to maxItems (currently 200)
-angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPlatform', '$scope', 'zm', 'ZMDataModel', '$ionicSideMenuDelegate', '$rootScope', '$http', '$q', 'message', '$state', '$ionicLoading', '$ionicPopover', '$ionicScrollDelegate', '$ionicModal', '$timeout', '$ionicContentBanner', '$ionicHistory','$sce', function ($ionicPlatform, $scope, zm, ZMDataModel, $ionicSideMenuDelegate, $rootScope, $http, $q, message, $state, $ionicLoading, $ionicPopover, $ionicScrollDelegate, $ionicModal, $timeout, $ionicContentBanner, $ionicHistory, $sce) {
+
+// FIXME: too much redundant code between EventCtrl and Timeline
+// Move to ModalCtrl and see if it works
+
+angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPlatform', '$scope', 'zm', 'ZMDataModel', '$ionicSideMenuDelegate', '$rootScope', '$http', '$q', 'message', '$state', '$ionicLoading', '$ionicPopover', '$ionicScrollDelegate', '$ionicModal', '$timeout', '$ionicContentBanner', '$ionicHistory','$sce', '$stateParams', function ($ionicPlatform, $scope, zm, ZMDataModel, $ionicSideMenuDelegate, $rootScope, $http, $q, message, $state, $ionicLoading, $ionicPopover, $ionicScrollDelegate, $ionicModal, $timeout, $ionicContentBanner, $ionicHistory, $sce,$stateParams) {
console.log("Inside Timeline controller");
$scope.openMenu = function () {
@@ -113,151 +117,34 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
// To show a modal dialog with the event tapped on in timeline
// FIXME : code repeat from Events
//--------------------------------------------------------
- function openModal(eid, ename, edur, eframes, basepath, relativepath, evideo) {
- ZMDataModel.zmDebug("TimelineCtrl: Open Modal with path " + relativepath);
- $scope.eventName = ename;
- $scope.eventId = eid;
- $scope.eFramesNum = eframes;
- $scope.eventDur = Math.round(edur);
- $scope.loginData = ZMDataModel.getLogin();
- $scope.eventBasePath = basepath;
- $scope.relativePath = relativepath;
- $rootScope.rand = Math.floor(Math.random() * (999999 - 111111 + 1)) + 111111;
- console.log ("**** defaultVideo is " + evideo);
-
- $scope.playbackURL = $scope.loginData.url;
- /* if ($rootScope.platformOS == "desktop") {
- $scope.playbackURL = zm.desktopUrl;
- }*/
-
-
- $scope.defaultVideo = evideo;
- $scope.videoObject = {};
- var videoURL = $scope.loginData.url + "/events/" + relativepath + evideo;
- console.log ("Video path in openModal: " + videoURL);
- $scope.videoObject.config = {
- autoPlay: true,
- sources: [
- {
- src: $sce.trustAsResourceUrl(videoURL),
- type: "video/mp4"
- }
-
- ],
-
- theme: "lib/videogular-themes-default/videogular.css",
-
- };
-
-
- $scope.slider_modal_options = {
- from: 1,
- to: eframes,
- realtime: true,
- step: 1,
- className: "mySliderClass",
- callback: function (value, released) {
- //console.log("CALLBACK"+value+released);
- $ionicScrollDelegate.freezeScroll(!released);
-
-
- },
- //modelLabels:function(val) {return "";},
- smooth: false,
- css: {
- background: {
- "background-color": "silver"
- },
- before: {
- "background-color": "purple"
- },
- default: {
- "background-color": "white"
- }, // default value: 1px
- after: {
- "background-color": "green"
- }, // zone after default value
- pointer: {
- "background-color": "red"
- }, // circle pointer
- range: {
- "background-color": "red"
- } // use it if double value
- },
- scale: []
-
- };
-
- $scope.mycarousel.index = 0;
- $scope.ionRange.index = 1;
- //console.log("**Resetting range");
- $scope.slides = [];
- var i;
- for (i = 1; i <= eframes; i++) {
- var fname = padToN(i, eventImageDigits) + "-capture.jpg";
- // console.log ("Building " + fname);
- $scope.slides.push({
- id: i,
- img: fname
- });
- }
+ function openModal(event) {
+ ZMDataModel.setAwake(ZMDataModel.getKeepAwake());
+
+ currentEvent = event;
- // now get event details to show alarm frames
- var loginData = ZMDataModel.getLogin();
- var myurl = loginData.apiurl + '/events/' + eid + ".json";
- ZMDataModel.zmLog("*** Constructed API for detailed events: " + myurl);
- $http.get(myurl)
- .success(function (data) {
- $scope.FrameArray = data.event.Frame;
- // $scope.slider_options.scale=[];
- $scope.slider_modal_options.scale = [];
- //$scope.slider_options.modelLabels={2:'X'};
- //$scope.slider_options.dimension="arjun";
- var i;
- for (i = 0; i < data.event.Frame.length; i++) {
- if (data.event.Frame[i].Type == "Alarm") {
-
- // console.log ("**ALARM AT " + i);
- $scope.slider_modal_options.scale.push({
- val: i + 1,
- label: ' '
- });
- } else {
- //$scope.slider_options.scale.push(' ');
- }
+ prepareModalEvent(event.Event.Id);
- }
+ $ionicModal.fromTemplateUrl('templates/events-modal.html', {
+ scope: $scope,
+ animation: 'slide-in-up'
+ })
+ .then(function (modal) {
+ $scope.modal = modal;
- })
- .error(function (err) {
- ZMDataModel.zmLog("Error retrieving detailed frame API " + JSON.stringify(err));
- ZMDataModel.displayBanner('error', ['error retrieving event details', 'please try again']);
- });
+ $ionicLoading.show({
+ template: "please wait...",
+ noBackdrop: true,
+ duration: 10000
+ });
- $scope.totalEventTime = Math.round(parseFloat(edur)) - 1;
- $scope.currentEventTime = 0;
+ $scope.modal.show();
- ZMDataModel.setAwake(ZMDataModel.getKeepAwake());
+ var ld = ZMDataModel.getLogin();
- $ionicModal.fromTemplateUrl('templates/events-modal.html', {
- scope: $scope,
- animation: 'slide-in-up'
- })
- .then(function (modal) {
- $scope.modal = modal;
+
- $ionicLoading.show({
- template: "please wait...",
- noBackdrop: true,
- duration: 10000
});
- $scope.modal.show();
-
- var ld = ZMDataModel.getLogin();
-
- });
-
}
//--------------------------------------------------------
@@ -274,6 +161,14 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
}
};
+
+ $scope.toggleGapless = function()
+ {
+ console.log ("GAPLESS TOGGLE");
+ $scope.loginData.gapless = !$scope.loginData.gapless;
+ ZMDataModel.setLogin($scope.loginData);
+
+ };
//-------------------------------------------------------------------------
@@ -293,32 +188,413 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
// which in turn calls openModal
//--------------------------------------------------------
- function showEvent(start, mid, edur, eframes, eid, ename, evideo) {
- ZMDataModel.zmDebug("TimelineCtrl/showevent called with start:" +
- start + " monitorId:" + mid + " dur:" + edur + " frames:" + eframes +
- " eventId:" + eid + " eventName:" + ename + "video:"+evideo);
- //console.log("Event STARTED WITH " + start);
- var str = start;
- var yy = moment(str).format('YY');
- var mm = moment(str).format('MM');
- var dd = moment(str).format('DD');
- var hh = moment(str).format('HH');
- var min = moment(str).format('mm');
- var sec = moment(str).format('ss');
- var relativepath =
- mid + "/" +
- yy + "/" +
- mm + "/" +
- dd + "/" +
- hh + "/" +
- min + "/" +
- sec + "/";
- console.log("PATH IS " + relativepath);
-
- openModal(eid, ename, edur, eframes, "", relativepath, evideo);
+ function showEvent(event) {
+
+
+ openModal(event);
}
+
+
+ // This function returns neighbor events if applicable
+ function neighborEvents(eid) {
+ var d = $q.defer();
+ // now get event details to show alarm frames
+ var loginData = ZMDataModel.getLogin();
+ var myurl = loginData.apiurl + '/events/' + eid + ".json";
+ var neighbors = {
+ prev: "",
+ next: ""
+ };
+ $http.get(myurl)
+ .success(function (data) {
+
+ // In Timeline view, gapless should stick to the same monitor
+ if (1) // we are viewing only one monitor
+ {
+ ZMDataModel.zmDebug ("Getting next event for same monitor Id ");
+ neighbors.prev = data.event.Event.PrevOfMonitor ? data.event.Event.PrevOfMonitor : "";
+ neighbors.next = data.event.Event.NextOfMonitor ? data.event.Event.NextOfMonitor : "";
+ }
+ /*else
+ {
+ neighbors.prev = data.event.Event.Prev ? data.event.Event.Prev : "";
+ neighbors.next = data.event.Event.Next ? data.event.Event.Next : "";
+ }*/
+ ZMDataModel.zmDebug("Neighbor events of " + eid + "are Prev:" +
+ neighbors.prev + " and Next:" + neighbors.next);
+
+
+ d.resolve(neighbors);
+ return (d.promise);
+ })
+ .error(function (err) {
+ ZMDataModel.zmLog("Error retrieving neighbors" + JSON.stringify(err));
+ d.reject(neighbors);
+ return (d.promise);
+
+
+ });
+ return (d.promise);
+
+ }
+
+ $scope.toggleGapless = function()
+ {
+ console.log ("GAPLESS TOGGLE");
+ $scope.loginData.gapless = !$scope.loginData.gapless;
+ ZMDataModel.setLogin($scope.loginData);
+
+ };
+
+
+ //--------------------------------------------------------
+ //Navigate to next/prev event in full screen mode
+ //--------------------------------------------------------
+
+ $scope.onSwipeEvent = function(eid,dirn)
+ {
+ console.log ("HERE");
+ var ld = ZMDataModel.getLogin();
+ if (!ld.canSwipeMonitors) return;
+
+ if
+ ($ionicScrollDelegate.$getByHandle("imgscroll").getScrollPosition().zoom!=1)
+ {
+ console.log("Image is zoomed in - not honoring swipe");
+ return;
+ }
+ console.log ("JUMPING");
+ jumpToEvent(eid,dirn);
+
+ };
+
+ $scope.jumpToEvent = function (eid, dirn) {
+ console.log ("jumptoevent");
+
+ jumpToEvent(eid, dirn);
+
+ };
+
+ function jumpToEvent (eid, dirn)
+ {
+ ZMDataModel.zmLog("Event jump called with:" + eid);
+ if (eid == "") {
+ $ionicLoading.show({
+ template: "no more events",
+ noBackdrop: true,
+ duration: 2000
+ });
+
+ return;
+ }
+
+ var slidein;
+ var slideout;
+ if (dirn==1)
+ {
+ slideout = "animated slideOutLeft";
+ slidein = "animated slideInRight";
+ }
+ else
+ {
+ slideout = "animated slideOutRight";
+ slidein = "animated slideInLeft";
+ }
+ var element = angular.element(document.getElementById("full-screen-event"));
+ element.addClass(slideout).one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', outWithOld);
+
+
+
+ function outWithOld()
+ {
+
+
+ $scope.animationInProgress = true;
+ // give digest time for image to swap
+ // 100 should be enough
+ $timeout(function()
+ {
+ element.removeClass(slideout);
+ element.addClass(slidein)
+ .one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', inWithNew );
+ prepareModalEvent(eid);
+ },200);
+ }
+
+ function inWithNew()
+ {
+ element.removeClass(slidein);
+ $scope.animationInProgress = false;
+ }
+
+ }
+
+ //--------------------------------------------------------
+ // utility function
+ //--------------------------------------------------------
+
+ function computeRelativePath(event) {
+ var relativePath = "";
+ var loginData = ZMDataModel.getLogin();
+ var str = event.Event.StartTime;
+ var yy = moment(str).format('YY');
+ var mm = moment(str).format('MM');
+ var dd = moment(str).format('DD');
+ var hh = moment(str).format('HH');
+ var min = moment(str).format('mm');
+ var sec = moment(str).format('ss');
+ relativePath = event.Event.MonitorId + "/" +
+ yy + "/" +
+ mm + "/" +
+ dd + "/" +
+ hh + "/" +
+ min + "/" +
+ sec + "/";
+ return relativePath;
+
+ }
+
+ //--------------------------------------------------------
+ // utility function
+ //--------------------------------------------------------
+
+ function computeBasePath(event) {
+ var basePath = "";
+ var loginData = ZMDataModel.getLogin();
+ var str = event.Event.StartTime;
+ var yy = moment(str).format('YY');
+ var mm = moment(str).format('MM');
+ var dd = moment(str).format('DD');
+ var hh = moment(str).format('HH');
+ var min = moment(str).format('mm');
+ var sec = moment(str).format('ss');
+
+ basePath = loginData.url + "/events/" +
+ event.Event.MonitorId + "/" +
+ yy + "/" +
+ mm + "/" +
+ dd + "/" +
+ hh + "/" +
+ min + "/" +
+ sec + "/";
+ return basePath;
+ }
+
+
+ //-------------------------------------------------------------------------
+ // Called when rncarousel or video player finished playing event
+ //-------------------------------------------------------------------------
+
+ $scope.playbackFinished = function()
+ {
+ playbackFinished();
+ };
+
+ function playbackFinished()
+ {
+ // currentEvent is updated with the currently playing event in prepareModalEvent()
+ ZMDataModel.zmLog ("Playback of event " + currentEvent.Event.Id + " is finished");
+
+ if ($scope.loginData.gapless)
+ {
+
+ neighborEvents(currentEvent.Event.Id)
+ .then(function (success) {
+
+ // lets give a second before gapless transition to the next event
+ $timeout ( function() {
+ $scope.nextId = success.next;
+ $scope.prevId = success.prev;
+ ZMDataModel.zmDebug ("Gapless move to event " + $scope.nextId);
+ jumpToEvent($scope.nextId, 1);
+ },1000);
+ },
+ function (error) {
+ ZMDataModel.zmDebug("Error in neighbor call " +
+ JSON.stringify(error));
+ });
+ }
+ else
+ {
+ ZMDataModel.zmDebug ("not going to next event, gapless is off");
+ }
+ }
+
+
+ //--------------------------------------------------------
+ // Called by openModal as well as jump to event
+ // what it basically does is get a detailed event API
+ // for an event ID and constructs required playback
+ // parameters
+ // Note that openModal is called with the top level event
+ // API. Some parameters are repeated across both
+ //--------------------------------------------------------
+
+
+ function prepareModalEvent(eid) {
+
+ // Lets get the detailed event API
+ var loginData = ZMDataModel.getLogin();
+ var myurl = loginData.apiurl + '/events/' + eid + ".json";
+ ZMDataModel.zmLog("*** Constructed API for detailed events: " + myurl);
+ $http.get(myurl)
+ .then(function (success) {
+
+
+
+ var event = success.data.event;
+ currentEvent = event;
+
+ event.Event.BasePath = computeBasePath(event);
+ event.Event.relativePath = computeRelativePath(event);
+
+
+ //console.log (JSON.stringify( success));
+ $scope.eventName = event.Event.Name;
+ $scope.eventId = event.Event.Id;
+ $scope.eFramesNum = event.Event.Frames;
+ $scope.eventDur = Math.round(event.Event.Length);
+ $scope.loginData = ZMDataModel.getLogin();
+
+ //console.log("**** VIDEO STATE IS " + event.Event.DefaultVideo);
+ if (typeof event.Event.DefaultVideo === 'undefined')
+ event.Event.DefaultVideo = "";
+
+ $scope.defaultVideo = event.Event.DefaultVideo;
+
+
+ neighborEvents(event.Event.Id)
+ .then(function (success) {
+ $scope.nextId = success.next;
+ $scope.prevId = success.prev;
+ },
+ function (error) {
+ console.log(JSON.stringify(error));
+ });
+
+ $scope.nextId = "...";
+ $scope.prevId = "...";
+
+
+
+
+ event.Event.video = {};
+ var videoURL = $scope.loginData.url + "/events/" + event.Event.relativePath + event.Event.DefaultVideo;
+
+ //console.log("************** VIDEO IS " + videoURL);
+ event.Event.video.config = {
+ autoPlay: true,
+ sources: [
+ {
+ src: $sce.trustAsResourceUrl(videoURL),
+ type: "video/mp4"
+ }
+
+ ],
+
+ theme: "lib/videogular-themes-default/videogular.css",
+
+ };
+
+ $scope.videoObject = event.Event.video;
+
+ $scope.playbackURL = $scope.loginData.url;
+
+ /* we don't need this for electron
+ if ($rootScope.platformOS == "desktop") {
+ $scope.playbackURL = zm.desktopUrl;
+ } */
+
+ $scope.eventBasePath = event.Event.BasePath;
+ $scope.relativePath = event.Event.relativePath;
+ $rootScope.rand = Math.floor(Math.random() * (999999 - 111111 + 1)) + 111111;
+
+ $scope.slider_modal_options = {
+ from: 1,
+ to: event.Event.Frames,
+ realtime: true,
+ step: 1,
+ className: "mySliderClass",
+ callback: function (value, released) {
+ //console.log("CALLBACK"+value+released);
+ $ionicScrollDelegate.freezeScroll(!released);
+
+
+ },
+ //modelLabels:function(val) {return "";},
+ smooth: false,
+ css: {
+ background: {
+ "background-color": "silver"
+ },
+ before: {
+ "background-color": "purple"
+ },
+ default: {
+ "background-color": "white"
+ }, // default value: 1px
+ after: {
+ "background-color": "green"
+ }, // zone after default value
+ pointer: {
+ "background-color": "red"
+ }, // circle pointer
+ range: {
+ "background-color": "red"
+ } // use it if double value
+ },
+ scale: []
+
+ };
+
+
+ $scope.mycarousel.index = 0;
+ $scope.ionRange.index = 1;
+ //console.log("**Resetting range");
+ $scope.slides = [];
+ var i;
+ for (i = 1; i <= event.Event.Frames; i++) {
+ var fname = padToN(i, eventImageDigits) + "-capture.jpg";
+ // console.log ("Building " + fname);
+ $scope.slides.push({
+ id: i,
+ img: fname
+ });
+ }
+
+
+ // now get event details to show alarm frames
+
+ $scope.FrameArray = event.Frame;
+ // $scope.slider_options.scale=[];
+ $scope.slider_modal_options.scale = [];
+
+
+ for (i = 0; i < event.Frame.length; i++) {
+ if (event.Frame[i].Type == "Alarm") {
+
+ $scope.slider_modal_options.scale.push({
+ val: i + 1,
+ label: ' '
+ });
+ } else {
+ //$scope.slider_options.scale.push(' ');
+ }
+
+ }
+ $scope.totalEventTime = Math.round(parseFloat(event.Event.Length)) - 1;
+ $scope.currentEventTime = 0;
+ },
+ function (err) {
+ ZMDataModel.zmLog("Error retrieving detailed frame API " + JSON.stringify(err));
+ ZMDataModel.displayBanner('error', ['could not retrieve frame details', 'please try again']);
+ });
+
+
+ }
+
//-------------------------------------------------
// Make sure we delete the timeline
@@ -375,6 +651,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
//-------------------------------------------------
// Controller main
//-------------------------------------------------
+
+ var currentEvent="";
// Make sure sliding for menu is disabled so it
// does not interfere with graph panning
@@ -394,6 +672,11 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
$scope.$watch('mycarousel.index', function () {
$scope.ionRange.index = ($scope.mycarousel.index + 1).toString();
+
+ if (currentEvent && $scope.ionRange.index == parseInt(currentEvent.Event.Frames))
+ {
+ playbackFinished();
+ }
});
$scope.mycarousel = {
@@ -695,7 +978,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
mydur: myevents[i].Event.Length,
myeid: myevents[i].Event.Id,
myename: myevents[i].Event.Name,
- myvideo: myevents[i].Event.DefaultVideo
+ myvideo: myevents[i].Event.DefaultVideo,
+ myevent:myevents[i]
});
graphIndex++;
@@ -733,7 +1017,7 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
ZMDataModel.zmDebug("TimelineCtrl/drawGraph:You clicked on item " + properties.items);
var item = graphData.get(properties.items);
ZMDataModel.zmDebug("TimelineCtrl/drawGraph: clicked item details:" + JSON.stringify(item));
- showEvent(item[0].start, item[0].group, item[0].mydur, item[0].myframes, item[0].myeid, item[0].myename, item[0].myvideo);
+ showEvent(item[0].myevent);
} else {