diff -r 0055b4bee4e3 -r 8c3f0b94d056 web/static/res/js/incplayer.js --- a/web/static/res/js/incplayer.js Tue Jan 15 17:17:14 2013 +0100 +++ b/web/static/res/js/incplayer.js Wed Jan 16 08:26:00 2013 +0100 @@ -7,24 +7,33 @@ // Global this.initDone = false; this.playerIsReady = false; - this.iOS = false; + this.playerWaitForBeingReady = false; // used on ipad + this.mobile; + this.ipad; + this.nextPage; + this.reloadPage; // Sequences this.allSequencesData = []; this.sequences = []; + this.singleVideo; // Popcorn objects this.videoDivId = ""; this.videoExt = ""; - this.preferOgg = true; // debug this.popSeq = null; // Controls this.playButton = null; + this.hdSdButton = null this.progressCurrent = null; this.progressDuration = null; - this.hd = false; - this.seekTime = 0.0; + this.progressCursor = null; + this.progressBar = null; + this.progressCursorX = 0; + this.progressCursorDrag; + this.hd; + this.seekTime = -1; // Tools this.logiEnable = true; @@ -34,15 +43,24 @@ // Functions // -------------------------------------------------------------------------------------------------------------------- - this.init = function(videoDivId, playButtonId, progressCurrentId, progressDurationId, jsonFile, preferOgg) + this.init = function(nextPage, videoDivId, playButtonId, hdSdButtonId, progressCurrentId, progressDurationId, progressCursorId, progressBarId, jsonFile) { + this.nextPage = nextPage; this.videoDivId = videoDivId; - this.preferOgg = preferOgg; // Control this.playButton = $("#" + playButtonId).get(0); + this.hdSdButton = $("#" + hdSdButtonId).get(0); this.progressCurrent = $("#" + progressCurrentId); this.progressDuration = $("#" + progressDurationId); + this.progressCursor = $("#" + progressCursorId); + this.progressBar = $("#" + progressBarId); + this.ctrlSetCursorDragFunction(); + this.setCursorPosition(0); + + // HD ? + this.hd = incChoice.getHD(); + $(this.hdSdButton).css({"background-position" : this.hd ? '0 -12px' : '0 0'}); // Video extention this.videoExt = this.getSupportedVideoExt(); @@ -51,16 +69,20 @@ return false; } - // Detect iOS - this.detectIOS(); - if (this.iOS) { - this.logi("we are on iOS"); + if (jsonFile !== null & jsonFile !== undefined) { + // Load all sequences data + this.allSequencesData = this.loadJson(jsonFile); + + // Add index to the videos + for (var i = 0; i < this.allSequencesData.videos.length; ++i) { + this.allSequencesData.videos[i].index = i; + } } - // Load all sequences data - this.allSequencesData = this.loadJson(jsonFile); + this.initDone = true; + this.mobile = IsSmartphone(); + this.ipad = !this.mobile && IsIpad(); - this.initDone = true; return true; }; @@ -75,7 +97,7 @@ } }; - this.createPopSequence = function(words) + this.createPopSequence = function(words, videosIndex) { if (!this.initDone) { this.loge("incplayer not initialized"); @@ -85,86 +107,220 @@ // Delete previous popcorn objects this.destroySequence(); - // Choose the 3 video - this.choosePopSequence(words); + // Choose the 3 videos + this.choosePopSequence(words, videosIndex); - // And cerate the popcorn sequence + // And init the popcorn sequence this.initPopSequence(); }; - this.choosePopSequence = function(words) + this.playSingleVideo = function(videoUrl) + { + this.sequences = []; + var video = { name : "xxx", src: videoUrl, in: 0, out: "1.44" }; + this.sequences.push(video); + this.formatPopSequence(); + this.initPopSequence(); + this.singleVideo = true; + } + + this.choosePopSequence = function(wordsIndex, videosIndex) { this.sequences = []; - var videos = this.allSequencesData.videos; + + if (videosIndex.length == 3) { + // The player got reloded, we use the previously choosen videos + for (var i = 0; i < videosIndex.length; ++i) { + this.sequences.push(this.allSequencesData.videos[videosIndex[i]]); + } + } else { + // We choose new videos - // Choose first video - var v1 = this.getRandomVideos(words[0]); - this.sequences.push(v1); + // Get previous used videos + var var32bits = incChoice.getChoosenVideosFlags(); - var v2, v3; + // Choose videos according to the chosen words + for (var i = 0; i < wordsIndex.length; ++i) { + var v = this.getRandomVideos(wordsIndex[i], var32bits, this.sequences); + if (v == null) { + // We didn't find free video + // So we mark some as unplayed, we must get flags again and retry + var32bits = incChoice.getChoosenVideosFlags(); + v = this.getRandomVideos(wordsIndex[i], var32bits, this.sequences); - // Choose second video - do { - v2 = this.getRandomVideos(words[1]) ; - } while (v2.src == v1.src); - this.sequences.push(v2); + if (v == null) { + this.loge("We didn't find a free video"); + } + } + this.sequences.push(v); + } + + // Save played videos with cookies + this.savePlayedVideos(this.sequences, true); - // Choose third video - do { - v3 = this.getRandomVideos(words[2]) ; - } while (v3.src == v1.src || v3.src == v2.src); - this.sequences.push(v3); + // Save chosen video with cookies + this.saveChosenVideos(this.sequences); + } + // Format pop sequence + this.formatPopSequence(); + + // Debug + this.logi("choosed sequences:"); + for (i = 0; i < this.sequences.length; ++i) { + this.logi(this.sequenceToString(i)); + } + }; + + this.formatPopSequence = function() + { // Set the video file name var i; for (i = 0; i < this.sequences.length; ++i) { var v = this.sequences[i]; // HD - if(this.hd) { - v.src += "hd"; + if(!this.hd) { + v.src = v.src.replace("HD", "SD"); } // Extention - v.src += "." + /*this.videoExt*/ "mp4"; // todo + v.src += "." + this.videoExt; // Set the final file this.sequences[i] = v; } for (i = 0; i < this.sequences.length; ++i) { + + // Get the sequence var seq = this.sequences[i]; + // Compute time in seconds var integer = Math.floor(seq.duration); var decimal = Math.floor((seq.duration - integer) * 100); var duration = integer * 60 + decimal; this.sequences[i] = { src: seq.src, in: 0, out: duration }; - } + } + } + + this.getRandomVideos = function(wordIndex, var32bits, otherVideos) + { + var i; + var index = parseInt(wordIndex); + var videos = []; + var allVideosPlayed = true; + var videosMatchWord = []; - this.logi("choosed sequences:"); - for (i = 0; i < this.sequences.length; ++i) { - this.logi(this.sequenceToString(i)); + // Debug + var freeVideosCount = 0; + var wordScoreAllVideos = 0; + + // Get all videos affected by this word + for (i = 0; i < this.allSequencesData.videos.length; ++i) { + var video = this.allSequencesData.videos[i]; + var wordScore = video.scoreWord[index]; + + if (wordScore <= 0) { + // This video can't be choosen with this word + continue; + } + + videosMatchWord.push(video); // Debug + + video.choosen = incChoice.IsThisVideoWasPlayed(var32bits, video.index); + if (video.choosen) { + // This video was already played in an another session + continue; + } + + allVideosPlayed = false; + + // Compare categories + video.samecat = false; + for (var j = 0; j < otherVideos.length; ++j) { + if (video.cat == otherVideos[j].cat) { + video.samecat = true; + break; + } + } + if (video.samecat == true) { + // This video have the same cat that an other selected video + continue; + } + + // We push has many time the video that the score for the word + for (var j = 0; j < wordScore; ++j) { + videos.push(video); + } + + // Debug + wordScoreAllVideos += wordScore; + ++freeVideosCount; } + + if (allVideosPlayed || !videos.length) { + // All the videos asociated with the word have been played + // or there is no videos to choose because we skipped the one with the same cat + // We must mark the videos asociated with the word as unplayed + this.savePlayedVideos(videosMatchWord, false); + return null; + } + + // Choose random video + var v = videos[this.random(0, videos.length)]; + + // Debug + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + this.logi("---------------------------------------------------------------------"); + this.logi("The word " + this.allSequencesData.mots[index] + "(" + index + ")" +" give us " + videosMatchWord.length + " videos, we can choose " + freeVideosCount); + this.logi("---------------------------------------------------------------------"); + + for (i = 0; i < videosMatchWord.length; ++i) { + var video = videosMatchWord[i]; + var message = this.resizeString(video.name, 15) + this.resizeString(" cat:" + video.cat, 15); + if (video.choosen) { + message += this.resizeString("played", 15); + } else if (video.samecat) { + message += this.resizeString("same cat", 15); + } else { + message += this.resizeString("OK", 15) + this.resizeString("proba:" + video.scoreWord[index] + "/" + wordScoreAllVideos, 15); + } + this.logi(message); + } + this.logi(""); + this.logi("We choose " + v.name + "(" + v.index + ")" + " cat:" + v.cat + " proba: " + v.scoreWord[index] + "/" + wordScoreAllVideos + "\n\n"); + + return v; + } + + this.savePlayedVideos = function(videos, markPlayed) + { + var indexs = []; + for (var i = 0; i < videos.length; ++i) { + indexs.push(videos[i].index); + } + incChoice.savePlayedVideos(indexs, markPlayed); }; - this.getRandomVideos = function(word) + this.saveChosenVideos = function(videos) { - var index = this.getWordIndex(word); - var videos = []; - - // Get all video affected by this word - for (var i = 0; i < this.allSequencesData.videos.length; ++i) { - var video = this.allSequencesData.videos[i]; - - // We push has many time the url that the score for the word - for (var j = 0; j < video.scoreWord[index]; ++j) { - videos.push(video); - } + var indexs = []; + for (var i = 0; i < videos.length; ++i) { + indexs.push(videos[i].index); } - - return videos[this.random(0, videos.length)]; - } + incChoice.saveChosenVideos(indexs); + }; + + this.resizeString = function (str, size) + { + while (str.length < size) { + str += " "; + } + return str; + }; this.getWordIndex = function(word) { @@ -174,10 +330,18 @@ return i; } } - console.log("getWordIndex erreur"); + this.loge("getWordIndex erreur"); return -1; }; + this.debugDisplayWordsIndex = function() + { + var words = this.allSequencesData.mots; + for (var i = 0; i < words.length; ++i) { + this.logi(words[i] + " -> " + i); + } + }; + this.initPopSequence = function() { var self = this; @@ -192,53 +356,66 @@ // Hide controls pop.controls(false); - self.listenEvent(pop, "playing", false, function() { + // Play event + self.listenEvent(pop, "playing", false, i, function(index) { self.displayPlayButton(false); self.logi("play sequence: " + self.sequenceToString(self.popSeq.active)); }); - self.listenEvent(pop, "pause", false, function() { + // Pause event + self.listenEvent(pop, "pause", false, i, function(index) { self.displayPlayButton(true); self.logi("pause sequence: " + self.sequenceToString(self.popSeq.active)); }); - self.listenEvent(pop, "timeupdate", false, function() { - // Update the current time position - var currentTime = self.popSeq.currentTime(); - $(self.progressCurrent).html(self.secondsToTime(currentTime)); + // Ended event + self.listenEvent(pop, "ended", false, i, function(index) { + self.logi("ended sequence: " + self.sequenceToString(self.popSeq.active)); + }); + + // Timeupdate event + self.listenEvent(pop, "timeupdate", false, i, function(index) { - // Detect vsquence end - if (self.popSeq.active == 2 && currentTime >= self.popSeq.duration() - 2) { + var currentTime = self.popSeq.currentTime(); + if (!self.progressCursorDrag) { + // Move timeline and display the time + self.progressCursorX = self.setCursorPosition(currentTime); + } + + // Detect the sequence end + if (self.popSeq.active == self.sequences.length-1 && currentTime >= self.popSeq.duration() - 1) { setTimeout(function() { - location.href = "transition.html"; - }, 4000); - + location.href = self.nextPage; + }, 1500); } }); - self.listenEvent(pop, "canplaythrough", true, function() { + /* + // Canplaythrough event + self.listenEvent(pop, "canplaythrough", true, i, function(index) { + }); + */ + } + + if (!this.ipad) { + self.popSeq.on("canplaythrough", function() { + + self.playerIsReady = true; + + // Recompute Duration + self.recomputeDuration(); + + // Automatic play + self.ctrlPlay(); + + // Unlisten event + self.popSeq.off("canplaythrough"); + self.logi("the player is ready"); }); } - self.popSeq.on("loadedmetadata", function() { - - self.playerIsReady = true; - - // todo - // Set total duration - $(self.progressDuration).html(self.secondsToTime(self.popSeq.duration())); - - if (!self.iOS) { - // Automatic play - self.ctrlPlay(); - } - - // Unlisten event - self.popSeq.off("loadedmetadata"); - self.logi("the player is ready"); - }); - self.popSeq.on("cycle", function() { + console.log("CYCLE"); }); self.popSeq.on("ended", function() { @@ -246,11 +423,11 @@ }; - this.listenEvent = function(pop, event, unlisten, func) + this.listenEvent = function(pop, event, unlisten, index, func) { pop.on(event, function() { // Execute the function - func(); + func(index); if (unlisten) { // Unlisten event @@ -259,6 +436,50 @@ }); }; + this.recomputeDuration = function() + { + // Recompute duration + var totalDuration = 0; + for (var j = 0; j < this.sequences.length; ++j) { + var duration = this.popSeq.eq(j).duration(); + + // ofVideos + this.popSeq.inOuts.ofVideos[j].out = duration; + + // ofClips + this.popSeq.inOuts.ofClips[j].in = totalDuration; + totalDuration += duration; + this.popSeq.inOuts.ofClips[j].out = totalDuration; + } + + // Set total duration + $(this.progressDuration).html(this.secondsToTime(this.popSeq.duration())); + }; + + this.recomputeDurationNoAutoPlay = function() + { + // Recompute duration + var totalDuration = 0; + for (var j = 0; j <= this.popSeq.active; ++j) { + var duration = this.popSeq.eq(j).duration(); + + // ofVideos + this.popSeq.inOuts.ofVideos[j].out = duration; + + // ofClips + this.popSeq.inOuts.ofClips[j].in = totalDuration; + totalDuration += duration; + this.popSeq.inOuts.ofClips[j].out = totalDuration; + } + + // Set total duration + $(this.progressDuration).html(this.secondsToTime(this.popSeq.duration())); + + if (incHideBar !== undefined) { + incHideBar.showBarPointerOnAction(6000); + } + }; + this.getCurrentPop = function() { var index = this.popSeq.active; @@ -268,11 +489,180 @@ return this.popSeq.eq(index); }; + this.setCursorPosition = function(time) + { + // Display time + $(this.progressCurrent).html(this.secondsToTime(time)); + + // Move cursor timeline + var progressCursorX = time ? ((time / this.popSeq.duration()) * (240 - 16)) : 0; + $(this.progressCursor).css({"left" : progressCursorX }); + $("#progress").css({"width" : progressCursorX + 8}); + + return progressCursorX; + } + + this.forceCheckPlayerIsReady = function() + { + this.playerIsReady = false; + this.playerWaitForBeingReady = false; + this.checkPlayerIsReady(); + } + + this.checkPlayerIsReady = function() + { + if (this.playerIsReady) { + return true; + } + + if (this.ipad) { + + // We "manualy" play the video (we are in a click event handler) + var media = this.popSeq.eq(this.popSeq.active).media; + media.play(); + + if (media.readystate === 4) { + this.playerIsReady = true; + this.recomputeDurationNoAutoPlay(); + return false; // Ready but we don't want to do anything after + } + + if (this.playerWaitForBeingReady) { + return false; + } + + this.playerWaitForBeingReady = true; + + var self = this; + var mediaCanPlay = function () { + // Remove event + media.removeEventListener('canplaythrough', mediaCanPlay, false); + media.removeEventListener('load', mediaCanPlay, false); + + // The video is ready, play + media.play(); + self.playerIsReady = true; + self.recomputeDurationNoAutoPlay(); + console.log("ipad play event"); + } + + media.addEventListener('canplaythrough', mediaCanPlay, false); + media.addEventListener('load', mediaCanPlay, false); + + setTimeout(function() { + media.pause(); // Block play so it buffers before playing + }, 1); + } + + this.logi("can't play, the player is not ready"); + return false; + } + + this.ctrlSetCursorDragFunction = function() + { + var self = this; + var origineX = null; + var posX; + var maxX = 240 - 16; + + var setCursorPositionX = function (x) { + if (x < 0) { + x = 0; + } else if (x > maxX) { + x = maxX; + } + var time = self.popSeq.duration() * x / maxX; + self.setCursorPosition(time); + return x; + } + + var setCursorPosition = function (x) { + self.progressCursorDrag = true; + if (origineX == null) { + origineX = x; + } else { + posX = self.progressCursorX + x - origineX; + posX = setCursorPositionX(posX); + } + } + + this.progressCursor.on({ + mousedown: function() { + + if (!self.checkPlayerIsReady()) { + return; + } + + $(document).on( + self.ipad ? { + touchmove: function (e) { + e.preventDefault(); + var touches = ( typeof( event.touches ) != "undefined" ) ? event.touches : event.changedTouches; + setCursorPosition(touches[0].pageX); + } + } : { + mousemove: function (e) { + self.progressCursorDrag = true; + setCursorPosition(e.pageX); + } + } + ); + } + }); + + var progressBarX = $(self.progressBar).position().left; + if (self.ipad) { + this.progressBar.on({ + touchdown: function(e) { + if (!self.checkPlayerIsReady()) { + return; + } + + e.preventDefault(); + var touches = ( typeof( event.touches ) != "undefined" ) ? event.touches : event.changedTouches; + PosX = touches[0].pageX - progressBarX - 8; + PosX = setCursorPositionX(PosX); + + // Seek time + var jumpTime = self.popSeq.duration() * PosX / maxX; + self.popSeq.jumpTo(jumpTime); + } + }); + } else { + this.progressBar.on({ + mousedown: function(e) { + if (!self.checkPlayerIsReady()) { + return; + } + + PosX = e.pageX - progressBarX - 8; + PosX = setCursorPositionX(PosX); + + // Seek time + var jumpTime = self.popSeq.duration() * PosX / maxX; + self.popSeq.jumpTo(jumpTime); + } + }); + } + + $(document).mouseup(function(){ + if (self.progressCursorDrag) { + self.progressCursorDrag = false; + origineX = null; + + // Remove event + $(document).off(self.ipad ? "touchmove" : "mousemove"); + + // Seek time + var jumpTime = self.popSeq.duration() * posX / maxX; + self.popSeq.jumpTo(jumpTime); + } + }); + } + this.ctrlPlay = function() { - if (!this.iOS && !this.playerIsReady) { - // The video are not ready - this.logi("can't play, the player is not ready"); + if (!this.checkPlayerIsReady()) { return; } @@ -287,87 +677,62 @@ this.ctrlNext = function() { - if (!this.playerIsReady) { - // The video are not ready - this.logi("can't play, the player is not ready"); + if (!this.checkPlayerIsReady()) { return; } if (this.popSeq.active == this.sequences.length - 1) { // We are at the last video - location.href = "transition.html"; + location.href = this.nextPage; return; } + if (this.ipad) { + this.playerIsReady = false; + this.playerWaitForBeingReady = false; + } + // Go to the next video - var jumpTime = this.popSeq.durationSeqs(this.popSeq.active + 1); + var jumpTime = this.popSeq.durationSeqs(this.popSeq.active + 1) + 0.001; this.popSeq.jumpTo(jumpTime); }; this.ctrlPrev = function() { - if (!this.playerIsReady) { - // The video are not ready - this.logi("can't play, the player is not ready"); + if (!this.checkPlayerIsReady()) { return; } var videoIndex = this.popSeq.active; - if (videoIndex !== 0) { - // If we are a less than 1 sec from the sequence start, we just to the prev sequence - // else we jump to the start of the current sequence - var jumpTimeStartCurrent = this.popSeq.durationSeqs(videoIndex); - if (this.popSeq.currentTime() - jumpTimeStartCurrent < 1) { - --videoIndex; - } - } + if (videoIndex === 0) { + // We jump to the start + this.popSeq.jumpTo(0); + } else { - // Go to the next video - var jumpTime = this.popSeq.durationSeqs(videoIndex); - this.popSeq.jumpTo(jumpTime); - }; - - this.ctrlFullScreen = function() - { - if (!this.playerIsReady) { - // The video are not ready - this.logi("can't play, the player is not ready"); - return; + // If we are a less than 1 sec from the sequence start, we jump to the prev sequence + // else we jump to the start of the current sequence + var jumpTime = this.popSeq.durationSeqs(videoIndex); + if (this.popSeq.currentTime() - jumpTime < 1) { + jumpTime = this.popSeq.durationSeqs(videoIndex-1); + } + this.popSeq.jumpTo(jumpTime + 0.001); } - - this.logi("full screen"); }; - this.ctrlHd = function() + this.ctrlHdSd = function() { - if (!this.playerIsReady) { - // The video are not ready - this.logi("can't play, the player is not ready"); - return; - } + // We swape between hd/sd + incChoice.setHD(this.hd ? 0 : 1); - this.seekTime = this.popSeq.currentTime(); - this.hd = false; - this.createPopSequence(); + // We record the current time - this.logi("hd"); + // Reload + location.href = document.URL; }; this.displayPlayButton = function(playIcon) { - /* - if (playIcon) { - // Controller display - this.playButton.src = 'static/res/img/ctrlplayover.jpg'; - this.playButton.onmouseover = function() {this.src='static/res/img/ctrlplayover.jpg';}; - this.playButton.onmouseout = function() {this.src='static/res/img/ctrlplay.jpg';}; - } else { - // Controller display - this.playButton.src = 'static/res/img/ctrlpauseover.jpg'; - this.playButton.onmouseover = function() {this.src='static/res/img/ctrlpauseover.jpg';}; - this.playButton.onmouseout = function() {this.src='static/res/img/ctrlpause.jpg';}; - } - */ + $(this.playButton).css({"background-position" : playIcon ? '-77px 0' : '-77px -17px'}); }; // -------------------------------------------------------------------------------------------------------------------- @@ -443,39 +808,22 @@ { var v = document.createElement("video"); - if (v.canPlayType) { - - // Check for Ogg support - if (this.preferOgg && v.canPlayType('video/ogg; codecs="theora"') !== "") { - return "ogg"; - } - + if (v.canPlayType) { // Check for Webm support if (v.canPlayType('video/webm; codecs="vp8, vorbis"') !== "") { return "webm"; } - // Check for MPEG-4 support if (v.canPlayType('video/mp4; codecs="mp4v.20.8"' ) !== "") { return "mp4"; } - // Check for h264 support if ((v.canPlayType('video/mp4; codecs="avc1.42E01E"' ) !== "" || v.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'))) { return "mp4"; } } - return ""; }; - - this.detectIOS = function() - { - var p = navigator.platform; - if (p === 'iPad' || p === 'iPhone' || p === 'iPod') { - this.iOS = true; - } - }; } var incPlayer = new IncPlayer();