videojs_user_track 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown ADDED
@@ -0,0 +1,36 @@
1
+ 在video-js框架里,记录用户重复播放视频里各部分的频度。
2
+ ==================================================================
3
+
4
+ ### 需求
5
+ 判断教学视频如何被用户观看的效果,最简单的是看用户如何重复播放或跳过播放。
6
+
7
+ ### 技术详解
8
+ 初始化一个长度为要播放视频的以秒为单位长度的数组,各元素初始为零。如果视频正在播放,则给该秒加一; 重复播放,则继续加一。
9
+
10
+ ### 使用
11
+ 1. 添加videojs_user_tracks数据库表,bundle exec rake db:migrate
12
+ 2. 实现VideojsUserTrack_Rails::Auth#auth方法
13
+ 3. 在js里引用//= require videojs_user_track.js
14
+ 4. 配置js参数,比如 _V_.user_track(eoe.video_player, section_id, eoe.uid, {uhash: eoe.uhash})
15
+ 5. 播放一个视频,看看Rails log或数据库里是否出现了更新
16
+
17
+ ### 用户认证
18
+ 在config/initializers/videojs_user_track_rails.rb添加如下代码,并实现你的auth方法
19
+
20
+ ```ruby
21
+ module VideojsUserTrack_Rails
22
+ module Auth
23
+ def auth
24
+ # your auth code, it can acces the scope in videojs controller
25
+ end
26
+ end
27
+ end
28
+ ```
29
+
30
+ ### TODO
31
+ 1. 支持其他浏览器内视频播放框架,去video-js化
32
+ 2. 管理页面
33
+
34
+
35
+ ### License
36
+ MIT
@@ -0,0 +1,84 @@
1
+ /*
2
+ * VideoJS视频播放秒数监控
3
+ * 两个setInterval对播放数组做 浏览器本地 和 服务器 同步。
4
+ *
5
+ * 附加 断点播放时间记录(为了重复利用setInterval事件以节省资源)
6
+ *
7
+ */
8
+ window.videojs_user_track = function(videojs, video_id, uid, auth_data_hash) {
9
+ if (!window.jQuery) { throw("Please require jQuery first"); }
10
+ if (!window._V_) { throw("Please require videojs first"); }
11
+
12
+ var local_a = [];
13
+ var last_second; // 用于排重,因为实际执行的setInterval一秒很可能超过一秒,所以和上一个读取值比较即可。
14
+ var video_length = 0;
15
+ auth_data_hash = (auth_data_hash || {});
16
+
17
+ var store_to_local = function(idx) {
18
+ if (idx != last_second) {
19
+ local_a.push(idx);
20
+ last_second = idx;
21
+ // 记录断点播放
22
+ if (idx > 5) { auth_data_hash.last_played_at = last_second; }
23
+ }
24
+ };
25
+ var sync_to_server = function() {
26
+ // save tmp length, to avoid another function modify it.
27
+ var _l = local_a.length;
28
+ if (video_length === 0) { video_length = Math.round(videojs.duration()); }
29
+
30
+ if (_l !== 0) {
31
+ var remote_a = local_a.splice(0, _l);
32
+ // TODO unshift if failure
33
+ $.ajax({
34
+ type: 'PUT',
35
+ url: '/videojs.json',
36
+ data: $.extend(auth_data_hash, {
37
+ video_length: video_length,
38
+ seconds: remote_a,
39
+ video_id: video_id,
40
+ uid: uid
41
+ })
42
+ }).done(function (data, textStatus) {
43
+ if ((data.result + '').match(/fail/)) {
44
+ clearInterval(intevals.local);
45
+ clearInterval(intevals.server);
46
+ console.log(data, "Stoped two setIntevals");
47
+ }
48
+ });
49
+ }
50
+ };
51
+ var inspect = function() {
52
+ console.log((new Date()), ' current seconds is ', local_a);
53
+ };
54
+
55
+ var intevals = {};
56
+ // TODO 优化,为了避免循环,只在播放时执行,也可以考虑缓存videojs.paused()
57
+ // 存储播放时间到浏览器本地
58
+ intevals.local = setInterval(function() {
59
+ if (!videojs.paused()) {
60
+ // NOTICE 有时可能因为浏览器资源竞争而导致部分秒没有获取
61
+ var time_idx = Math.round(videojs.currentTime());
62
+ store_to_local(time_idx);
63
+ }
64
+ }, 333);
65
+
66
+ // 同步播放时间到服务器
67
+ // 不和存储同步是会因为函数阻塞而错过部分时间段
68
+ setTimeout(function() {
69
+ intevals.server = setInterval(function() {
70
+ sync_to_server();
71
+ }, 3000);
72
+
73
+ // 在关闭窗口或离开页面前同步
74
+ // 得允许多个beforeunload事件
75
+ $(window).bind('beforeunload', function(event) {
76
+ console.log("send watching data before close the window...");
77
+ sync_to_server();
78
+ });
79
+ }, 5000); // 需观看5秒以上才考虑同步服务器
80
+
81
+ };
82
+
83
+ // 测试对浏览器内存和CPU基本无影响。视频播放完后,内存稳定在130.64MB,即使是重复播放。
84
+ // TODO 整理成videojs plugin,但目前videojs master似乎有bug,编译的js运行出错,有时间改下。
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+
3
+ class VideojsController < ApplicationController
4
+ include VideojsUserTrack_Rails::Auth
5
+
6
+ def update
7
+ @result = if auth
8
+ ut = VideojsUserTrack.find_or_create_by_video_id_and_uid(params[:video_id], params[:uid])
9
+ ut.update_attributes(:video_second_length => params[:video_length]) if ut.video_second_length.to_i.zero?
10
+ ut.inc_seconds(params[:seconds], params[:last_played_at])
11
+ 'success'
12
+ else
13
+ 'auth failure'
14
+ end
15
+
16
+ respond_to do |format|
17
+ format.json { render :json => {:result => @result}, :status => 200 }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: UTF-8
2
+
3
+ class VideojsUserTrack < ActiveRecord::Base
4
+ attr_accessible :video_id, :uid, :video_second_length, :status, :mark_watched
5
+
6
+ # like array index in Ruby, idx begins with 0
7
+ def inc_seconds seconds, last_played_at = nil
8
+ return false if seconds.blank?
9
+ seconds = seconds.map(&:to_i).uniq
10
+ return false if seconds.detect {|second| (second > ut.video_second_length) || (second < 0)} # 大于video_second_length的秒一定不合法
11
+
12
+ seconds.each do |second|
13
+ data[second] ||= 0
14
+ data[second] += 1
15
+ data[second] = 9 if data[second] >= 10 # 最多只记录9次
16
+ end
17
+ self.last_played_at = last_played_at if not last_played_at.to_i.zero?
18
+
19
+ resave!
20
+ end
21
+
22
+ # 返回未看的 最大连续区域的首个索引
23
+ def max_unwatched_range_begin_at
24
+ data_str = data.join
25
+ # 区分零和非零区域
26
+ flags = data_str.scan(/[1-9]+|0+/)
27
+ flag_zero_max = flags.select {|i| i.to_i.zero? }.max {|i| i.length }
28
+ return 0 if flag_zero_max.nil?
29
+ data_str.index(flag_zero_max) + 1
30
+ end
31
+
32
+ def played_percents
33
+ return 0.0 if data.size.zero?
34
+ data.reject {|i| i.to_i.zero? }.size / data.size.to_f
35
+ end
36
+
37
+ def finished?
38
+ # 避免视频没有初始化
39
+ (video_second_length > 10) &&
40
+ # 总时长误差不超过5秒
41
+ ((data.length - video_second_length).abs <= 5) &&
42
+ (
43
+ # 判断中断次数是否超过总秒数的二十分之一,否则就算看完。
44
+ # 比如60秒的视频,里面没看的只有3秒,不管连续还是不连续,就算看完了。
45
+ (played_percents >= 0.95) ||
46
+ # 或者强制标记看完
47
+ self.mark_watched
48
+ )
49
+ end
50
+
51
+ def data
52
+ @data ||= (begin
53
+ a = JSON.parse(ut.status.to_s) rescue []
54
+ (a[ut.video_second_length - 1] ||= 0) if not ut.video_second_length.zero?
55
+ a.map(&:to_i)
56
+ end)
57
+ end
58
+
59
+ # alias user_track
60
+ def ut; self end
61
+ def resave!
62
+ ut.status = data.to_json
63
+ ut.save!
64
+ end
65
+
66
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ match 'videojs'=> "videojs#update", :via => :put
3
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: UTF-8
2
+
3
+ class CreateVideojsUserTrack < ActiveRecord::Migration
4
+ def up
5
+ create_table :videojs_user_tracks, :options => 'ENGINE=Innodb DEFAULT CHARSET=utf8', :comment => "每个用户的播放视频小节的时间频度" do |t|
6
+ t.string :video_id, :default => 0, :comment => "视频ID"
7
+ t.integer :uid, :default => 0, :comment => "用户ID"
8
+ t.integer :video_second_length, :default => 0
9
+ t.text :status, :comment => "初始化一个长度为要播放视频的以秒为单位长度的数组,各元素初始为零。如果视频正在播放,则给该秒加一; 重复播放,则继续加一。示例格式为" # copied from README
10
+ t.timestamps
11
+ end
12
+ add_index :videojs_user_tracks, [:video_id, :uid]
13
+ end
14
+
15
+ def down
16
+ end
17
+
18
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: UTF-8
2
+
3
+ class AddLastPlayedAtToVideojsUserTrack < ActiveRecord::Migration
4
+ def change
5
+ add_column :videojs_user_tracks, :last_played_at, :integer, :comment => "上次播放时间"
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: UTF-8
2
+
3
+ class AddMarkWatchedToVideojsUserTrack < ActiveRecord::Migration
4
+ def change
5
+ add_column :videojs_user_tracks, :mark_watched, :boolean, :default => false, :comment => "手工标记看完"
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+
3
+ module VideojsUserTrack_Rails
4
+ module Auth
5
+ end
6
+
7
+ class Engine < Rails::Engine
8
+ initializer "videojs_user_track.load_app_instance_data" do |app|
9
+ app.class.configure do
10
+ ['db/migrate', 'app/models', 'app/controllers'].each do |path|
11
+ config.paths[path] += VideojsUserTrack_Rails::Engine.paths[path].existent
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path('../rails_engine.rb', __FILE__)
@@ -0,0 +1,12 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'videojs_user_track'
3
+ s.version = '0.1'
4
+ s.date = '2013-04-01'
5
+ s.summary = File.read("README.markdown").split(/===+/)[0].strip
6
+ s.description = s.summary
7
+ s.authors = ["David Chen"]
8
+ s.email = 'mvjome@gmail.com'
9
+ s.homepage = 'https://github.com/eoecn/videojs_user_track'
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: videojs_user_track
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Chen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-01 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: 在video-js框架里,记录用户重复播放视频里各部分的频度。
15
+ email: mvjome@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.markdown
21
+ - app/assets/javascripts/videojs_user_track.js
22
+ - app/controllers/videojs_controller.rb
23
+ - app/models/videojs_user_track.rb
24
+ - config/routes.rb
25
+ - db/migrate/20130401075529_create_videojs_user_track.rb
26
+ - db/migrate/20130530085750_add_last_played_at_to_videojs_user_track.rb
27
+ - db/migrate/20130607080640_add_mark_watched_to_videojs_user_track.rb
28
+ - lib/rails_engine.rb
29
+ - lib/videojs_user_track.rb
30
+ - videojs_user_track.gemspec
31
+ homepage: https://github.com/eoecn/videojs_user_track
32
+ licenses: []
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 1.8.25
52
+ signing_key:
53
+ specification_version: 3
54
+ summary: 在video-js框架里,记录用户重复播放视频里各部分的频度。
55
+ test_files: []