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