mep_feature_time_rail_thumbnails 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +108 -0
- data/lib/mep_feature_time_rail_thumbnails.rb +4 -0
- data/lib/mep_feature_time_rail_thumbnails/engine.rb +5 -0
- data/lib/mep_feature_time_rail_thumbnails/version.rb +3 -0
- data/vendor/assets/javascripts/mep-feature-time-rail-thumbnails.js +126 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 581863f9c3305a8e58bab3fd847f0ca4749057ab
|
4
|
+
data.tar.gz: 805a293d56f27f0331601cddb794444564212403
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e0ad1312a6e0028608934e253ca0e065fc72e7a36cdeaa0d93bc5f27be8885b6277919236ee7f8ccaa52f2c6cdade1df701d624c8d42e073fa88eb2491c92034
|
7
|
+
data.tar.gz: cef2e3b4387e7696ab2a6e032ddb9a1221c2d8c69e4185ae33b013e0aa1a22b44cf956ca3dfdad0a6bc4e1b3e8065cbb37ed874089cc2c544dd9d01545c39ed4
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 North Carolina State Univesity
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# MediaElement.js Plugin for Preview Thumbnails
|
2
|
+
|
3
|
+
Hover over the time rail on a [MediaElement.js](http://mediaelementjs.com/) player, and see video thumbnails.
|
4
|
+
|
5
|
+
## Use
|
6
|
+
|
7
|
+
To use this feature you will first need to create an image sprite and a metadata WebVTT file.
|
8
|
+
|
9
|
+
### Create an Image Sprite
|
10
|
+
|
11
|
+
The idea is to take a snapshot of your video every 5 seconds and then stitch the images together into a sprite. This means that only one image file needs to be requested from the server and switching between thumbnails can be very quick.
|
12
|
+
|
13
|
+
Here's one way you could create the image sprite using ffmpeg and montage (from ImageMagick):
|
14
|
+
|
15
|
+
```
|
16
|
+
ffmpeg -i "video-name.mp4" -f image2 -vf fps=fps=1/5 video-name-%05d.jpg
|
17
|
+
montage video-name*jpg -tile 5x -geometry 150x video-name-sprite.jpg
|
18
|
+
```
|
19
|
+
|
20
|
+
First ffmpeg takes a snapshot of your video every 5 seconds. Then montage reduces each image to 150px across and tiles them from left to right 5 across.
|
21
|
+
|
22
|
+
### Create a WebVTT metadata file
|
23
|
+
|
24
|
+
Once you have the image sprite you'll also need to create a [WebVTT](http://docs.webplatform.org/wiki/concepts/VTT_Captioning) metadata file that contains the URL to your image sprite. For each time range of 5 seconds the URL is given including a [spatial Media Fragment](http://www.w3.org/TR/media-frags/) hash. This gives information about where in your sprite to look for the thumbnail for that time range. Here's an example of what one looks like:
|
25
|
+
|
26
|
+
```
|
27
|
+
WEBVTT
|
28
|
+
|
29
|
+
00:00:00.000 --> 00:00:05.000
|
30
|
+
http://example.com/video-name-sprite.jpg#xywh=0,0,150,100
|
31
|
+
|
32
|
+
00:00:05.000 --> 00:00:10.000
|
33
|
+
http://example.com/video-name-sprite.jpg#xywh=150,0,150,100
|
34
|
+
|
35
|
+
00:00:10.000 --> 00:00:15.000
|
36
|
+
http://example.com/video-name-sprite.jpg#xywh=300,0,150,100
|
37
|
+
|
38
|
+
00:00:15.000 --> 00:00:20.000
|
39
|
+
http://example.com/video-name-sprite.jpg#xywh=450,0,150,100
|
40
|
+
|
41
|
+
00:00:20.000 --> 00:00:25.000
|
42
|
+
http://example.com/video-name-sprite.jpg#xywh=600,0,150,100
|
43
|
+
|
44
|
+
00:00:25.000 --> 00:00:30.000
|
45
|
+
http://example.com/video-name-sprite.jpg#xywh=0,100,150,100
|
46
|
+
```
|
47
|
+
|
48
|
+
#### Current Sprite Limitations
|
49
|
+
- All the images embedded in the sprite should have exactly the same dimensions. The styling for the thumbnail hover area is calculated based on the dimensions of the first URL in the WebVTT file.
|
50
|
+
- Images should only be taken every 5 seconds. While this ought to be configurable in the future it currently is not.
|
51
|
+
|
52
|
+
### Markup
|
53
|
+
|
54
|
+
Add a new track element to your video element like the following:
|
55
|
+
|
56
|
+
```html
|
57
|
+
<track kind="metadata" class="time-rail-thumbnails" src="http://example.com/video-name-sprite.vtt"></track>
|
58
|
+
```
|
59
|
+
|
60
|
+
The kind attribute must be "metadata" and the class must be "time-rail-thumbnails". The WebVTT file should also be accessible via an AJAX request, so either have it on the same domain or allow cross-origin requests.
|
61
|
+
|
62
|
+
### Initialization
|
63
|
+
|
64
|
+
Add 'timerailsthumbnails' as the last feature when initializing the mediaelement player:
|
65
|
+
|
66
|
+
```javascript
|
67
|
+
$('video').mediaelementplayer({
|
68
|
+
features: ['playpause','progress','current','duration','tracks','volume', 'timerailthumbnails']
|
69
|
+
});
|
70
|
+
```
|
71
|
+
|
72
|
+
## Installation
|
73
|
+
|
74
|
+
This JavaScript plugin is made available as both a Rails Engine gem for the asset pipeline (unreleased) and a bower package. Choose your poison.
|
75
|
+
|
76
|
+
### Rails
|
77
|
+
|
78
|
+
Include it in your Gemfile:
|
79
|
+
```ruby
|
80
|
+
gem 'mep_feature_time_rail_thumbnails', git: 'git@github.com:jronallo/mep-feature-time-rail-thumbnails.git'
|
81
|
+
```
|
82
|
+
|
83
|
+
Add it to your application.js:
|
84
|
+
```javascript
|
85
|
+
//= require mep-feature-time-rail-thumbnails
|
86
|
+
```
|
87
|
+
|
88
|
+
### Bower
|
89
|
+
|
90
|
+
Install with bower:
|
91
|
+
```
|
92
|
+
bower i mep_feature_time_rail_thumbnails
|
93
|
+
```
|
94
|
+
|
95
|
+
The file you want is at `bower_components/mep_feature_time_rail_thumbnails/vendor/assets/javascripts/mep-feature-time-rail-thumbnails.js`
|
96
|
+
|
97
|
+
## TODO
|
98
|
+
|
99
|
+
- Make the interval of thumbnails configurable or really use the timestamps in the WebVTT file rather than relying on 5 second increments.
|
100
|
+
- Allow to work over HTTP and HTTPS.
|
101
|
+
|
102
|
+
## Author
|
103
|
+
|
104
|
+
Jason Ronallo
|
105
|
+
|
106
|
+
## License
|
107
|
+
|
108
|
+
This project rocks and uses MIT-LICENSE.
|
@@ -0,0 +1,126 @@
|
|
1
|
+
(function($) {
|
2
|
+
|
3
|
+
$.extend(MediaElementPlayer.prototype, {
|
4
|
+
buildtimerailthumbnails : function(player, controls, layers, media) {
|
5
|
+
if (!player.isVideo)
|
6
|
+
return;
|
7
|
+
|
8
|
+
function getVttCues(url) {
|
9
|
+
var vtt,
|
10
|
+
parser = new WebVTT.Parser(window, WebVTT.StringDecoder()),
|
11
|
+
cues = [];
|
12
|
+
|
13
|
+
// FIXME: Is there a way to do this with promises?
|
14
|
+
$.ajax({
|
15
|
+
url: url,
|
16
|
+
async: false,
|
17
|
+
success: function(data){
|
18
|
+
vtt = data;
|
19
|
+
},
|
20
|
+
error:function (xhr, ajaxOptions, thrownError){
|
21
|
+
if(xhr.status==404) {
|
22
|
+
vtt = null;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
});
|
26
|
+
if (vtt) {
|
27
|
+
parser.oncue = function(cue) {
|
28
|
+
cues.push(cue);
|
29
|
+
};
|
30
|
+
parser.parse(vtt);
|
31
|
+
parser.flush();
|
32
|
+
}
|
33
|
+
return cues;
|
34
|
+
}
|
35
|
+
|
36
|
+
function parseMediaFragmentHash(url) {
|
37
|
+
var hash = url.substring(url.indexOf('#')+1);
|
38
|
+
return hash.split('=')[1].split(',');
|
39
|
+
}
|
40
|
+
|
41
|
+
function setThumbnailImage(url) {
|
42
|
+
$('.mejs-plugin-time-float-thumbnail').css('background-image','url(' + url.split('#')[0] + ')');
|
43
|
+
}
|
44
|
+
|
45
|
+
var
|
46
|
+
mediaContainer = player.container.find('.mejs-mediaelement').parent(),
|
47
|
+
element_to_observe = player.container.find('.mejs-time-float-current')[0],
|
48
|
+
video_thumbnail_vtt_url,
|
49
|
+
cues,
|
50
|
+
preview_thumbnails_track = mediaContainer.find("track[kind='metadata'].time-rail-thumbnails");
|
51
|
+
|
52
|
+
if (preview_thumbnails_track.length > 0) {
|
53
|
+
video_thumbnail_vtt_url = preview_thumbnails_track.attr('src');
|
54
|
+
} else {
|
55
|
+
return;
|
56
|
+
}
|
57
|
+
cues = getVttCues(video_thumbnail_vtt_url);
|
58
|
+
|
59
|
+
// If there is only one cue then there's no need to show thumbnails at all so don't do anything.
|
60
|
+
if (cues.length > 1) {
|
61
|
+
// Set up the container to hold the thumbnail.
|
62
|
+
var time_float = mediaContainer.find('.mejs-time-float');
|
63
|
+
time_float.prepend('<span class="mejs-plugin-time-float-thumbnail"></span>');
|
64
|
+
|
65
|
+
// Set necessary styles.
|
66
|
+
var xywh = parseMediaFragmentHash(cues[0].text);
|
67
|
+
var x = xywh[0];
|
68
|
+
var y = xywh[1];
|
69
|
+
var w = xywh[2];
|
70
|
+
var h = xywh[3];
|
71
|
+
var new_height = parseInt(h) + time_float.height();
|
72
|
+
time_float.css('top', '-' + new_height + 'px');
|
73
|
+
time_float.find('.mejs-time-float-corner').css('top', new_height - 3 + 'px');
|
74
|
+
time_float.css('height', 'auto');
|
75
|
+
time_float.css('width', w + 'px');
|
76
|
+
time_float.find('.mejs-time-float-current').css('position', 'static');
|
77
|
+
time_float.find('.mejs-plugin-time-float-thumbnail').css('position', 'static');
|
78
|
+
|
79
|
+
setThumbnailImage(cues[0].text);
|
80
|
+
|
81
|
+
// Add an observer to the .mejs-time-float-current and change the thumbnail
|
82
|
+
// when the observer is triggered
|
83
|
+
var observer = new MutationObserver(function(mutations){
|
84
|
+
var time_code_current = $('.mejs-time-float-current').text();
|
85
|
+
|
86
|
+
var sections = time_code_current.split(':');
|
87
|
+
if (sections.length < 3) {
|
88
|
+
time_code_current = "00:" + time_code_current;
|
89
|
+
}
|
90
|
+
|
91
|
+
// If the mouse is hovering over the 0 seconds mark, then show the first frame.
|
92
|
+
// Otherwise show something deeper into the video.
|
93
|
+
var seconds = mejs.Utility.timeCodeToSeconds(time_code_current),
|
94
|
+
cue;
|
95
|
+
if (seconds == 0) {
|
96
|
+
cue = cues[0];
|
97
|
+
} else {
|
98
|
+
// FIXME: Change 5 to be configurable for the number of tiles across
|
99
|
+
// or actually look up the time cues to see which cue to look into.
|
100
|
+
var tile = Math.floor(seconds / 5);
|
101
|
+
cue = cues[tile + 1];
|
102
|
+
}
|
103
|
+
|
104
|
+
// The text of the cue will be the background image of the thumbnail container.
|
105
|
+
setThumbnailImage(cue.text);
|
106
|
+
|
107
|
+
// Use the spatial media fragment hash of the url to determine the coordinates and size
|
108
|
+
// of the image to be displayed.
|
109
|
+
var xywh = parseMediaFragmentHash(cue.text);
|
110
|
+
var x = xywh[0];
|
111
|
+
var y = xywh[1];
|
112
|
+
var w = xywh[2];
|
113
|
+
var h = xywh[3];
|
114
|
+
|
115
|
+
// Set the background position and height and width.
|
116
|
+
$('.mejs-plugin-time-float-thumbnail').css('background-position', '-' + x + 'px -' + y + 'px' );
|
117
|
+
$('.mejs-plugin-time-float-thumbnail').css('height', h);
|
118
|
+
$('.mejs-plugin-time-float-thumbnail').css('width', w);
|
119
|
+
|
120
|
+
});
|
121
|
+
|
122
|
+
observer.observe(element_to_observe, {attributes: true, childList: true, characterData: true, subtree:true});
|
123
|
+
}
|
124
|
+
}
|
125
|
+
});
|
126
|
+
})(mejs.$);
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mep_feature_time_rail_thumbnails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jason Ronallo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.2.17
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.2.17
|
27
|
+
description: Add a thumbnail preview to the MediaElement.js player.
|
28
|
+
email:
|
29
|
+
- jronallo@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- MIT-LICENSE
|
35
|
+
- README.md
|
36
|
+
- lib/mep_feature_time_rail_thumbnails.rb
|
37
|
+
- lib/mep_feature_time_rail_thumbnails/engine.rb
|
38
|
+
- lib/mep_feature_time_rail_thumbnails/version.rb
|
39
|
+
- vendor/assets/javascripts/mep-feature-time-rail-thumbnails.js
|
40
|
+
homepage: https://github.com/jronallo/mep-feature-time-rail-thumbnails
|
41
|
+
licenses: []
|
42
|
+
metadata: {}
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.2.2
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: MediaElement.js Plugin for Preview Thumbnails
|
63
|
+
test_files: []
|