Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VP-211: subtitlesFinder component for a VideoPlayer #125

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
02560be
VP-211: First draft for the new version of VideoPlayer to work with U…
anvk Feb 28, 2013
c1450b7
VP-211: Creating a separate transcript menu languages array in a mode…
anvk Mar 4, 2013
53d215f
VP-211: Do not allow to initialize unisub if there are no links to th…
anvk Mar 5, 2013
c93087d
VP-211: Making uniSub component to work with Amara api 2.0
anvk Mar 5, 2013
4ce84ac
VP-211: Amara component will try to get captions only from the first …
anvk Mar 7, 2013
8cd58ed
VP-211: First working version of video player which queries amara for…
anvk Mar 11, 2013
349b267
VP-211: Removing unused flag. Do not call requestChange for captions …
anvk Mar 11, 2013
b384b92
VP-211: First attempt to fix video player tests.
anvk Mar 11, 2013
f4f6e43
VP-282: Updating the README for the 0.1 release
jobara Feb 28, 2013
4eb8e6d
VP-282: Fixing a typo in the readme.
michelled Feb 28, 2013
abe30ff
VP-283: updating the licence info for captionator
jobara Feb 28, 2013
f07847c
VP-282: Updating the known issues
jobara Mar 4, 2013
82b22ec
VP-290: Fixing failing VideoPlayerShowHide-test
anvk Mar 11, 2013
0488d2f
VP-287
kmithemanth Mar 8, 2013
81f19fe
VP-211: Fixing up transcripts to use languages and captions pulled fr…
anvk Mar 11, 2013
f9b89a6
VP-211: More UniSub component refactoring. Trying to use free functio…
anvk Mar 12, 2013
544a360
VP-211: A tiny step towards unisub integration tests.
anvk Mar 15, 2013
c37ebf0
VP-211: Finally a working test for a delayed language caption retrieval
anvk Mar 15, 2013
95a94f5
VP-211: Adding more structure to the UniSub component. Changing integ…
anvk Mar 18, 2013
57fb6b3
Merge branch 'umaster' into VP-211
anvk Mar 18, 2013
c645cbc
VP-211: Adding more tests for UniSub component. Adding extra function…
anvk Mar 18, 2013
a7b2da5
VP-211: Renaming "UniSub" to be a "subtitlesFinder" component
anvk Mar 18, 2013
e907342
VP-211: Merging master branch with its latest changes. Fixing up test…
anvk Mar 26, 2013
12d1a5f
VP-211: Cleanup files from my changes which were put temporarily for …
anvk Mar 26, 2013
4d8e90a
VP-211: Refactoring subtitleFinder to use DataSource component.
anvk Mar 27, 2013
a9f073b
VP-211: Me and Justin, refactoring subtitleFinder component and refac…
anvk Apr 4, 2013
4bf4f2e
VP-211: Refactoring integration tests in order to comply with a new s…
anvk Apr 4, 2013
5d024dd
VP-211: Made few cleanups and quick changes after Justin's review
anvk Apr 4, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
VP-211: First draft for the new version of VideoPlayer to work with U…
…niSubComponent. Based on the acheetham branch https://github.com/acheetham/videoPlayer/tree/FLUID-4851
anvk committed Mar 12, 2013
commit 02560be25a2d9de0e4bbcea0efa1edc4c77ff250
11 changes: 9 additions & 2 deletions demos/VideoPlayer.html
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@
<script type="text/javascript" src="../js/VideoPlayer_controllers.js"></script>
<script type="text/javascript" src="../js/ToggleButton.js"></script>
<script type="text/javascript" src="../js/MenuButton.js"></script>
<script type="text/javascript" src="../js/UniSubComponent.js"></script>
<script type="text/javascript" src="../js/VideoPlayer_media.js"></script>
<script type="text/javascript" src="../js/VideoPlayer_transcript.js"></script>
<script type="text/javascript" src="../js/VideoPlayer_intervalEventsConductor.js"></script>
@@ -100,13 +101,19 @@ <h1>Infusion HTML 5 Video Player</h1>
src: "http://www.youtube.com/watch?v=_VxQEPw1x9E&language=en",
type: "text/amarajson",
srclang: "en",
label: "English"
label: "English local"
},
{
src: "videos/ReorganizeFuture/ReorganizeFuture.fr.vtt",
type: "text/vtt",
srclang: "fr",
label: "French"
label: "French local"
},
{
src: "http://www.youtube.com/watch?v=_VxQEPw1x9E&language=en",
type: "text/amarajson",
srclang: "en",
label: "English amara manual"
}
],
transcripts: [
26 changes: 17 additions & 9 deletions js/MenuButton.js
Original file line number Diff line number Diff line change
@@ -34,7 +34,6 @@ var fluid_1_5 = fluid_1_5 || {};
postInitFunction: "fluid.videoPlayer.languageMenu.postInit",
finalInitFunction: "fluid.videoPlayer.languageMenu.finalInit",
produceTree: "fluid.videoPlayer.languageMenu.produceTree",
languages: [],
currentLanguagePath: "activeLanguages",
showHidePath: "showLanguage",
model: {},
@@ -48,7 +47,8 @@ var fluid_1_5 = fluid_1_5 || {};
},
activated: null,
hiddenByKeyboard: null,
onControlledElementReady: null
onControlledElementReady: null,
onLanguageListUpdated: null
},
listeners: {
onControlledElementReady: {
@@ -80,14 +80,12 @@ var fluid_1_5 = fluid_1_5 || {};
// TODO: Could this be specified declaratively, in a "protoTree" option?
// Ans: not very effectively... the renderer still needs to be burned to the ground
fluid.videoPlayer.languageMenu.produceTree = function (that) {
// Silly damn renderer with its crazy JSON idiolect!
that.model.languages = that.options.languages;
var tree = {
// create a menu item for each language in the model
expander: {
type: "fluid.renderer.repeat",
repeatID: "language",
controlledBy: "languages",
controlledBy: "languageList",
pathAs: "lang",
tree: {
value: "${{lang}.label}",
@@ -216,6 +214,12 @@ var fluid_1_5 = fluid_1_5 || {};
fluid.videoPlayer.languageMenu.bindEventListeners(that);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most if not all contents of this final init function should be eliminated. We can use default component events such as onCreate or onAttach to finish up initialization.

fluid.videoPlayer.languageMenu.setUpKeyboardA11y(that);

that.events.onLanguageListUpdated.addListener(function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be defined in defaults rather than in final init function.

that.refreshView();
fluid.videoPlayer.languageMenu.bindEventListeners(that);
fluid.videoPlayer.languageMenu.setUpKeyboardA11y(that);
});

that.container.attr("role", "menu");
that.container.css("z-index", 9999);
that.hideMenu();
@@ -243,6 +247,10 @@ var fluid_1_5 = fluid_1_5 || {};
onReady: null,
onRenderingComplete: null,
onControlledElementReady: null,
onLanguageListUpdated: null,
// private event for testing
afterMenuRender: null,

afterFetchResources: null
},
listeners: {
@@ -251,7 +259,6 @@ var fluid_1_5 = fluid_1_5 || {};
priority: "last"
}
},
languages: [],
currentLanguagePath: "",
showHidePath: "",
strings: {
@@ -286,13 +293,13 @@ var fluid_1_5 = fluid_1_5 || {};
container: "{languageControls}.dom.menu",
options: {
model: "{languageControls}.model",
languages: "{languageControls}.options.languages",
applier: "{languageControls}.applier",
showHidePath: "{languageControls}.options.showHidePath",
currentLanguagePath: "{languageControls}.options.currentLanguagePath",
strings: "{languageControls}.options.strings",
events: {
onControlledElementReady: "{languageControls}.events.onControlledElementReady"
onControlledElementReady: "{languageControls}.events.onControlledElementReady",
onLanguageListUpdated: "{languageControls}.events.onLanguageListUpdated"
}
}
},
@@ -394,7 +401,8 @@ var fluid_1_5 = fluid_1_5 || {};
fluid.defaults("fluid.videoPlayer.languageControls.eventBinder", {
gradeNames: ["fluid.eventedComponent", "autoInit"],
listeners: {
"{button}.events.onPress": "{menu}.toggleView"
"{button}.events.onPress": "{menu}.toggleView",
"{menu}.events.afterRender": "{languageControls}.events.afterMenuRender.fire"
}
});
})(jQuery, fluid_1_5);
134 changes: 134 additions & 0 deletions js/UniSubComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
Copyright 2012 OCAD University

Licensed under the Educational Community License (ECL), Version 2.0 or the New
BSD license. You may not use this file except in compliance with one these
Licenses.

You may obtain a copy of the ECL 2.0 License and BSD License at
https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt
*/

/*global jQuery, window, fluid*/

// JSLint options
/*jslint white: true, funcinvoke: true, undef: true, newcap: true, nomen: true, regexp: true, bitwise: true, browser: true, forin: true, maxerr: 100, indent: 4 */

(function ($) {

"use strict";

fluid.defaults("fluid.unisubComponent", {
gradeNames: ["fluid.eventedComponent", "fluid.modelComponent", "autoInit"],
finalInitFunction: "fluid.unisubComponent.finalInit",
preInitFunction: "fluid.unisubComponent.preInit",
model: {
languages: []
},
events: {
onReady: null,
modelReady: null,
onVideo: null
},
listeners: {
onVideo: "{fluid.unisubComponent}.onVideoHandler"
},
"api-key": "0c01f5ca0ec8d1dc4e9e0f320a4d1afb1a50273d",
"api-password": "idrcunisub",
"api-username": "idrc",
urls: {
//api: "https://www.universalsubtitles.org/api2/partners/videos",
apiLanguages: "http://www.universalsubtitles.org/api/1.0/subtitles/languages/",
apiVideo: "http://www.universalsubtitles.org/api/1.0/video/",
video: null
},
hrefTemplate: "http://www.universalsubtitles.org/en/videos/g2QoNQgjJd5y/%lang/%subtitleId/",
queryAmaraForCaptions: true
});

fluid.unisubComponent.preInit = function (that) {
that.languageList = [];
that.videoCount = 0;
that.onVideoHandler = function (options) {
$.ajax({
dataType: "jsonp",
url: options.url
}).done(function (data) {
// that.languageList = that.languageList.concat(data);

fluid.each(data, function (capSpec, index) {
that.languageList = that.languageList.concat({
// BROKEN: this is the wrong url: this is the url to the actual subtitles,
// but the video player just needs the right url to the video itself
src: fluid.stringTemplate(that.options.hrefTemplate, {
lang: capSpec.code,
subtitleId: capSpec.id
}),
type: "text/amarajson",
srclang: capSpec.code,
label: capSpec.name
});
});

if (--that.videoCount <= 0) {
that.applier.requestChange("languages", that.languageList);
that.events.modelReady.fire(that.languageList);
that.events.onReady.fire(that);
}
});
};

that.loadVideoMetaData = function (options) {
$.ajax({
dataType: "jsonp",
url: options.url
}).done(function (data) {
that.applier.requestChange("video", data);
that.events.onVideo.fire({
url: that.buildUrl(that.options.urls.apiLanguages, {
video_url: data.video_url
})
});
});
};

that.buildUrl = function (baseURL, params) {
return [baseURL, "?", $.param(params)].join("");
};
};

fluid.unisubComponent.finalInit = function (that) {
if (!that.options.queryAmaraForCaptions || !that.options.urls.video) {
return;
}

var videoUrlsArray = [];
if (typeof that.options.urls.video[0] === "string") {
videoUrlsArray = fluid.makeArray(that.options.urls.video);
} else {
fluid.each(that.options.urls.video, function (vid, index) {
videoUrlsArray[index] = vid.src;
});
}

that.videoCount = videoUrlsArray.length;
fluid.each(videoUrlsArray, function (vidUrl, index) {
if (vidUrl.substr(0, 7) === "http://") {
that.loadVideoMetaData({
url: that.buildUrl(that.options.urls.apiVideo, {
username: that.options["api-username"],
password: that.options["api-password"],
video_url: vidUrl
})
});
} else {
that.videoCount--;
}
});
if (that.videoCount <= 0) {
that.events.modelReady.fire(that.languageList);
that.events.onReady.fire(that);
}
};

})(jQuery);
60 changes: 58 additions & 2 deletions js/VideoPlayer.js
Original file line number Diff line number Diff line change
@@ -165,7 +165,9 @@ var fluid_1_5 = fluid_1_5 || {};
onScrub: "{videoPlayer}.events.onScrub",
afterScrub: "{videoPlayer}.events.afterScrub",
onTranscriptsReady: "{videoPlayer}.events.canBindTranscriptMenu",
onCaptionsReady: "{videoPlayer}.events.canBindCaptionMenu"
onCaptionsReady: "{videoPlayer}.events.canBindCaptionMenu",
onCaptionControlsRendered: "{videoPlayer}.events.onCaptionControlsRendered",
onCaptionListUpdated: "{videoPlayer}.events.onCaptionListUpdated"
},
listeners: {
onReady: "{videoPlayer}.events.onControllersReady"
@@ -179,6 +181,19 @@ var fluid_1_5 = fluid_1_5 || {};
type: "fluid.videoPlayer.captionator",
container: "{videoPlayer}.dom.videoPlayer",
createOnEvent: "onMediaReady"
},
amara: {
type: "fluid.unisubComponent",
createOnEvent: "onReady",
options: {
urls: {
video: "{videoPlayer}.options.video.sources"
},
events: {
modelReady: "{videoPlayer}.events.onAmaraCaptionsReady"
},
queryAmaraForCaptions: "{videoPlayer}.options.queryAmaraForCaptions"
}
}
},
preInitFunction: "fluid.videoPlayer.preInit",
@@ -209,6 +224,19 @@ var fluid_1_5 = fluid_1_5 || {};
args: ["{videoPlayer}"]
},

onCaptionListUpdated: null,
onAmaraCaptionsReady: null,
onAmaraCaptionsReadyBoiled: {
event: "onAmaraCaptionsReady",
args: ["{videoPlayer}", "{arguments}.0"]
},
// private events used for testing
onCaptionControlsRendered: null,
onCaptionControlsRenderedBoiled: {
event: "onCaptionControlsRendered",
args: ["{videoPlayer}", "{arguments}.0"]
},

// public, time events
onTimeUpdate: null,

@@ -281,7 +309,8 @@ var fluid_1_5 = fluid_1_5 || {};
volume: 60,
muted: false,
canPlay: false,
play: false
play: false,
languageList: []
},
templates: {
videoPlayer: {
@@ -290,6 +319,7 @@ var fluid_1_5 = fluid_1_5 || {};
}
},
videoTitle: "unnamed video",
queryAmaraForCaptions: true,
invokers: {
showControllers: "fluid.videoPlayer.showControllers",
hideControllers: "fluid.videoPlayer.hideControllers"
@@ -465,6 +495,8 @@ var fluid_1_5 = fluid_1_5 || {};
that.toggleFullscreen = function () {
that.applier.requestChange("fullscreen", !that.model.fullscreen);
};

that.model.languageList = that.options.video.captions;
};

fluid.videoPlayer.postInit = function (that) {
@@ -503,6 +535,30 @@ var fluid_1_5 = fluid_1_5 || {};
fluid.videoPlayer.finalInit = function (that) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This final init function should only contain model/applier binding material. Everything else should be moved out into onCreate or onAttach. The fetchResources call should result in an event firing and the callback should be registered through the defaults and bind to a global function.

that.container.attr("role", "application");

that.applier.modelChanged.addListener("languageList", function (newModel, oldModel, requestSpec) {
that.events.onCaptionListUpdated.fire();
});
that.events.onAmaraCaptionsReadyBoiled.addListener(function (that, captionData) {

/*
var capList = fluid.copy(that.model.languageList);
fluid.each(captionData, function (cap, index) {
var lang = fluid.copy(cap);
capList.push({
href: fluid.stringTemplate("http://www.universalsubtitles.org/en/videos/g2QoNQgjJd5y/%lang/%subtitleId/", {
lang: cap.code,
subtitleId: cap.id
}),
type: "text/amarajson",
srclang: cap.code,
label: cap.name
});
});
*/
var capList = fluid.copy(that.model.languageList).concat(captionData);
that.applier.requestChange("languageList", capList);
});

// Render each media source with its custom renderer, registered by type.
// If we aren't on an HTML 5 video-enabled browser, don't bother setting up the controller, captions or transcripts.

Loading