|
1 IriSP.contentplayer = function(opts) { |
|
2 var directory = new IriSP.Model.Directory(), |
|
3 project = directory.remoteSource({ |
|
4 url: opts.project_url, |
|
5 serializer: IriSP.serializers.ldt |
|
6 }), |
|
7 apidirectory = new IriSP.Model.Directory(), |
|
8 currentIndex = 0, |
|
9 currentSegment, |
|
10 currentMedia, |
|
11 globalTags = {}, |
|
12 seqCount; |
|
13 |
|
14 var ratio = 2.37; |
|
15 |
|
16 function resizeVideo() { |
|
17 var currentwidth = $(window).width(), |
|
18 maxheight = $(window).height() - 220, |
|
19 height = Math.min(maxheight, currentwidth / ratio), |
|
20 width = ratio * height; |
|
21 $("#video_sequence").css({ |
|
22 width: Math.floor(width), |
|
23 height: Math.floor(height) |
|
24 }); |
|
25 } |
|
26 |
|
27 $(window).on("resize", resizeVideo); |
|
28 resizeVideo(); |
|
29 |
|
30 var timeSlider = $("#progressBar"), |
|
31 slidersRange = 1000, |
|
32 wasPaused = true, |
|
33 lastVal = 0, |
|
34 isClicking = false; |
|
35 timeSlider.slider({ |
|
36 range: "min", |
|
37 value: 0, |
|
38 min: 0, |
|
39 max: slidersRange, |
|
40 slide: function(event, ui) { |
|
41 if (isClicking && Math.abs(lastVal - ui.value) > 10) { |
|
42 isClicking = false; |
|
43 } |
|
44 if (!isClicking && currentMedia) { |
|
45 currentMedia.setCurrentTime(currentMedia.duration * ui.value / slidersRange); |
|
46 } |
|
47 }, |
|
48 start: function(event, ui) { |
|
49 isClicking = true; |
|
50 lastVal = ui.value; |
|
51 }, |
|
52 stop: function(event, ui) { |
|
53 if (isClicking && currentMedia) { |
|
54 timeSlider.slider("value", slidersRange * currentMedia.getCurrentTime() / currentMedia.duration); |
|
55 playOrPause(); |
|
56 } |
|
57 isClicking = false; |
|
58 } |
|
59 }); |
|
60 |
|
61 function playOrPause() { |
|
62 if (currentMedia) { |
|
63 if (currentMedia.paused) { |
|
64 currentMedia.play(); |
|
65 } else { |
|
66 currentMedia.pause(); |
|
67 } |
|
68 } |
|
69 } |
|
70 |
|
71 $(".video-wait, #btnPlayPause").click(function() { |
|
72 playOrPause(); |
|
73 return false; |
|
74 }); |
|
75 |
|
76 var segmentdragout = $("#title_sequence"), |
|
77 segmentdragin = segmentdragout.find("ul") |
|
78 segmentdragging = false; |
|
79 |
|
80 function resizeSegmentDrag() { |
|
81 var segmentdelta = segmentdragout.width() - segmentdragin.width(); |
|
82 segmentdragin.draggable("option","containment",segmentdelta < 0 ? [ segmentdelta - 20, 0, 20, 0 ] : "parent") |
|
83 } |
|
84 |
|
85 segmentdragin.draggable({ |
|
86 axis: "x", |
|
87 start: function() { |
|
88 segmentdragging = true; |
|
89 }, |
|
90 stop: function() { |
|
91 segmentdragging = false; |
|
92 } |
|
93 }); |
|
94 |
|
95 $(window).on("resize", resizeSegmentDrag); |
|
96 resizeSegmentDrag(); |
|
97 |
|
98 segmentdragin.on("mouseup", "li", function() { |
|
99 if (!segmentdragging) { |
|
100 var s = project.getElement($(this).attr("data-segment-id")); |
|
101 if (s) { |
|
102 currentMedia.setCurrentTime(s.begin); |
|
103 } |
|
104 } |
|
105 }).click(function() { |
|
106 return false; |
|
107 }); |
|
108 |
|
109 var tagsdragout = $("#tag_sequence"), |
|
110 tagsdragin = tagsdragout.find("ul") |
|
111 tagsdragging = false; |
|
112 |
|
113 function resizeTagsDrag() { |
|
114 var tagsdelta = tagsdragout.width() - tagsdragin.width(); |
|
115 tagsdragin.draggable("option","containment",tagsdelta < 0 ? [ tagsdelta - 20, 0, 20, 0 ] : "parent"); |
|
116 tagsdragin.css("left",Math.floor(tagsdelta/2)); |
|
117 } |
|
118 |
|
119 tagsdragin.draggable({ |
|
120 axis: "x", |
|
121 start: function() { |
|
122 tagsdragging = true; |
|
123 }, |
|
124 stop: function() { |
|
125 tagsdragging = false; |
|
126 } |
|
127 }); |
|
128 |
|
129 $(window).on("resize", resizeTagsDrag); |
|
130 resizeTagsDrag(); |
|
131 |
|
132 var taginput = $("#form_tag input[type=text]"); |
|
133 taginput.autocomplete({ |
|
134 source: function(params, response) { |
|
135 var rx = new RegExp(params.term,"gi"); |
|
136 response( |
|
137 _(globalTags) |
|
138 .chain() |
|
139 .keys() |
|
140 .filter(function(tag) { |
|
141 return rx.test(tag) |
|
142 }) |
|
143 .shuffle() |
|
144 .first(5) |
|
145 .value() |
|
146 ); |
|
147 } |
|
148 }); |
|
149 taginput.on("keyup input paste", function() { |
|
150 taginput.val(taginput.val().toUpperCase()); |
|
151 }); |
|
152 $("#form_tag").on("submit", function() { |
|
153 var _tagvalue = taginput.val().toUpperCase(); |
|
154 if (_tagvalue && currentSegment) { |
|
155 /* Création d'une liste d'annotations contenant une annotation afin de l'envoyer au serveur */ |
|
156 var _exportedAnnotations = new IriSP.Model.List(directory), |
|
157 /* Création d'un objet source utilisant un sérialiseur spécifique pour l'export */ |
|
158 _export = directory.newLocalSource({ |
|
159 serializer: IriSP.serializers.ldt_annotate |
|
160 }), |
|
161 /* Création d'une annotation dans cette source avec un ID généré à la volée (param. false) */ |
|
162 _annotation = new IriSP.Model.Annotation(false, _export), |
|
163 /* Si le Type d'Annotation n'existe pas, il est créé à la volée */ |
|
164 _annotationType = new IriSP.Model.AnnotationType(false, _export), |
|
165 /* L'objet Tag qui sera envoyé */ |
|
166 _tag = new IriSP.Model.Tag(false, _export); |
|
167 /* L'objet Tag doit avoir pour titre le texte du tag envoyé */ |
|
168 _tag.title = _tagvalue; |
|
169 /* Si nous avons dû générer un ID d'annotationType à la volée... */ |
|
170 _annotationType.dont_send_id = true; |
|
171 /* Il faut inclure le titre dans le type d'annotation */ |
|
172 _annotationType.title = "Contribution"; |
|
173 |
|
174 _annotation.setMedia(currentSegment.getMedia().id); |
|
175 _annotation.setBegin(currentSegment.begin); |
|
176 _annotation.setEnd(currentSegment.end); |
|
177 |
|
178 _annotation.setAnnotationType(_annotationType.id); |
|
179 |
|
180 _annotation.title = _tagvalue; |
|
181 _annotation.created = new Date(); /* Date de création de l'annotation */ |
|
182 _annotation.description = _tagvalue; |
|
183 |
|
184 _annotation.setTags([_tag.id]); /*Liste des ids de tags */ |
|
185 |
|
186 /* Les données créateur/date de création sont envoyées non pas dans l'annotation, mais dans le projet */ |
|
187 _export.creator = "theend"; |
|
188 _export.created = new Date(); |
|
189 /* Ajout de l'annotation à la liste à exporter */ |
|
190 _exportedAnnotations.push(_annotation); |
|
191 /* Ajout de la liste à exporter à l'objet Source */ |
|
192 _export.addList("annotation",_exportedAnnotations); |
|
193 |
|
194 var segmentAtPost = currentSegment; |
|
195 |
|
196 IriSP.jQuery.ajax({ |
|
197 url: IriSP.endpoints.post_annotation, |
|
198 type: "POST", |
|
199 contentType: 'application/json', |
|
200 data: _export.serialize(), /* L'objet Source est sérialisé */ |
|
201 success: function(_data) { |
|
202 var n = 1 + (segmentAtPost.__tags[_tagvalue] || 0) |
|
203 segmentAtPost.__tags[_tagvalue] = n; |
|
204 showCurrentTags(); |
|
205 }, |
|
206 error: function(_xhr, _error, _thrown) { |
|
207 console.log("Error when sending annotation", _thrown); |
|
208 } |
|
209 }); |
|
210 } |
|
211 return false; |
|
212 }); |
|
213 |
|
214 project.onLoad(function() { |
|
215 |
|
216 currentMedia = project.currentMedia; |
|
217 |
|
218 addMedia(currentMedia); |
|
219 |
|
220 $("#duration").text(currentMedia.duration.toString()); |
|
221 $("h1").text(currentMedia.title); //TODO: Remove when on platform |
|
222 var segmentsHtml = ""; |
|
223 |
|
224 project.getAnnotations().forEach(function(s) { |
|
225 segmentsHtml += '<li data-segment-id="' + s.id + '"><a href="#">' + s.title + ' </a> </li>'; |
|
226 var url = IriSP.endpoints.annotations_by_timecode |
|
227 .replace('__CONTENT_ID__', s.getMedia().id) |
|
228 .replace('__BEGIN__', s.begin.valueOf()) |
|
229 .replace('__END__', s.end.valueOf()); |
|
230 var proj = apidirectory.remoteSource({ |
|
231 url: url, |
|
232 serializer: IriSP.serializers.ldt |
|
233 }); |
|
234 proj.__segment = s; |
|
235 proj.onLoad(function() { |
|
236 proj.__segment.__tags = {}; |
|
237 proj.getAnnotations().forEach(function(a) { |
|
238 var tags = a.getTagTexts(); |
|
239 _(tags).each(function(t) { |
|
240 var upt = t.toUpperCase(), |
|
241 nl = 1 + (proj.__segment.__tags[upt] || 0), |
|
242 ng = 1 + (globalTags[upt] || 0); |
|
243 proj.__segment.__tags[upt] = nl; |
|
244 globalTags[upt] = ng; |
|
245 }); |
|
246 if (proj.__segment === currentSegment) { |
|
247 showCurrentTags(); |
|
248 } |
|
249 }); |
|
250 }); |
|
251 s.on("enter", function() { |
|
252 currentSegment = s; |
|
253 showCurrentTags(); |
|
254 $("#title_sequence li").removeClass("here"); |
|
255 $("#title_sequence li[data-segment-id='" + s.id + "']").addClass("here"); |
|
256 }); |
|
257 }); |
|
258 |
|
259 segmentdragin.html(segmentsHtml); |
|
260 resizeSegmentDrag(); |
|
261 |
|
262 currentMedia.play(); |
|
263 }); |
|
264 |
|
265 function showCurrentTags() { |
|
266 var vals = _(currentSegment.__tags).values(), |
|
267 max = Math.max.apply(Math, vals), |
|
268 min = Math.min(max - 1, Math.min.apply(Math, vals)), |
|
269 b = 160 / (max - min); |
|
270 var html = _(currentSegment.__tags) |
|
271 .chain() |
|
272 .map(function(v, k) { |
|
273 var c = Math.floor( 95 + (v - min) * b ); |
|
274 return '<li><a href="' |
|
275 + IriSP.endpoints.tag_page.replace("__TAG__",encodeURIComponent(k)) |
|
276 + '" style="color: rgb(' |
|
277 + [c,c,c].join(",") |
|
278 + ')">' |
|
279 + k |
|
280 + ' </a> </li>' |
|
281 }) |
|
282 .shuffle() |
|
283 .value() |
|
284 .join(""); |
|
285 tagsdragin.html(html); |
|
286 resizeTagsDrag(); |
|
287 } |
|
288 |
|
289 function addMedia(media) { |
|
290 if (media.has_player) { |
|
291 return; |
|
292 } |
|
293 media.has_player = true; |
|
294 media.loaded = false; |
|
295 media.paused = true; |
|
296 var videourl = media.video; |
|
297 if (typeof IriSP.video_url_transform === "function") { |
|
298 videourl = IriSP.video_url_transform(media.video); |
|
299 } |
|
300 var videoid = "video_" + media.id, |
|
301 videoEl = $('<video>'), |
|
302 seekCache = undefined, |
|
303 mp4_file = videourl.replace(/\.webm$/i,'.mp4'), |
|
304 webm_file = videourl.replace(/\.mp4$/i,'.webm'), |
|
305 mp4_src = $('<source>'), |
|
306 webm_src = $('<source>'); |
|
307 |
|
308 mp4_src.attr({ |
|
309 src: mp4_file, |
|
310 type: "video/mp4" |
|
311 }) |
|
312 webm_src.attr({ |
|
313 src: webm_file, |
|
314 type: "video/webm" |
|
315 }); |
|
316 |
|
317 videoEl.attr({ |
|
318 id : videoid |
|
319 }).css({ |
|
320 position : "absolute", |
|
321 left: 0, |
|
322 top: 0, |
|
323 width : "100%", |
|
324 height : "100%" |
|
325 }); |
|
326 videoEl.append(mp4_src).append(webm_src); |
|
327 $("#video_sequence").append(videoEl); |
|
328 var mediaEl = videoEl[0]; |
|
329 |
|
330 media.show = function() { |
|
331 videoEl.show(); |
|
332 } |
|
333 media.hide = function() { |
|
334 videoEl.hide(); |
|
335 } |
|
336 |
|
337 // Binding functions to Media Element Functions |
|
338 |
|
339 media.on("setcurrenttime", function(_milliseconds) { |
|
340 try { |
|
341 mediaEl.currentTime = (_milliseconds / 1000); |
|
342 } |
|
343 catch (err) {} |
|
344 }); |
|
345 |
|
346 media.on("setvolume", function(_vol) { |
|
347 try { |
|
348 media.volume = _vol; |
|
349 mediaEl.volume = _vol; |
|
350 } |
|
351 catch (err) {} |
|
352 }); |
|
353 |
|
354 media.on("setmuted", function(_muted) { |
|
355 try { |
|
356 media.muted = _muted; |
|
357 mediaEl.muted = _muted; |
|
358 } |
|
359 catch (err) {} |
|
360 }); |
|
361 |
|
362 media.on("setplay", function() { |
|
363 try { |
|
364 mediaEl.play(); |
|
365 } |
|
366 catch (err) {} |
|
367 }); |
|
368 |
|
369 media.on("setpause", function() { |
|
370 try { |
|
371 mediaEl.pause(); |
|
372 } |
|
373 catch (err) {} |
|
374 }); |
|
375 |
|
376 // Binding DOM events to media |
|
377 |
|
378 function getVolume() { |
|
379 media.muted = mediaEl.muted; |
|
380 media.volume = mediaEl.volume; |
|
381 } |
|
382 |
|
383 videoEl.on("loadedmetadata", function() { |
|
384 getVolume(); |
|
385 media.loaded = true; |
|
386 media.trigger("loadedmetadata"); |
|
387 media.trigger("volumechange"); |
|
388 }) |
|
389 |
|
390 videoEl.on("timeupdate", function() { |
|
391 media.trigger("timeupdate", new IriSP.Model.Time(1000*mediaEl.currentTime)); |
|
392 }); |
|
393 |
|
394 videoEl.on("volumechange", function() { |
|
395 getVolume(); |
|
396 media.trigger("volumechange"); |
|
397 }) |
|
398 |
|
399 videoEl.on("play", function() { |
|
400 media.trigger("play"); |
|
401 }); |
|
402 |
|
403 videoEl.on("pause", function() { |
|
404 media.trigger("pause"); |
|
405 }); |
|
406 |
|
407 videoEl.on("seeking", function() { |
|
408 media.trigger("seeking"); |
|
409 }); |
|
410 |
|
411 videoEl.on("seeked", function() { |
|
412 media.trigger("seeked"); |
|
413 }); |
|
414 |
|
415 // Binding UI Events and Mashup Playing to Media |
|
416 |
|
417 media.on("play", function() { |
|
418 $("#btnPlayPause, .video-wait, #progressBar .ui-slider-handle").addClass("pause"); |
|
419 }); |
|
420 |
|
421 media.on("pause", function() { |
|
422 $("#btnPlayPause, .video-wait, #progressBar .ui-slider-handle").removeClass("pause"); |
|
423 }); |
|
424 |
|
425 media.on("timeupdate", function(_time) { |
|
426 $("#current").text(_time.toString()); |
|
427 if (!isClicking) { |
|
428 timeSlider.slider("value", slidersRange * _time / media.duration); |
|
429 } |
|
430 }); |
|
431 |
|
432 } |
|
433 |
|
434 } |