I want to track video views for each user, with a table in the database called Viewings
. It belongs to both a user and a video, and records the duration watched, and percentage of the video completed.
When the play button is clicked, I want to either find an existing Viewing based on the id, which is stored in a 30-minute persisting cookie (which is created when the play button is first clicked, if a cookie doesn't already exist). I then want to periodically update the length
attribute of this Viewing
for every 10 seconds the user has watched the video, using a timeupdate
event listener.
I have a JavaScript function that returns lengthWatched
as an integer, which represents how many seconds in the video currently is.
I've tested to make sure this works with the following function:
video.addEventListener 'timeupdate', ((e) ->
console.log(lengthWatched(e));
return
), false
So now all I have to do is update length
whenever this function returns an integer divisible by 10.
I'm quite sure I'll have to use AJAX here. But I'm not too sure of the best way to proceed onto this next step, or maybe even if there's a better way entirely to record this data.
Any suggestions?
Edit:
Using the suggestions from agmcleod and some of my own, I ended up with these working JS additions:
#global variables
videoId = location['pathname'].split('/').pop()
viewingCookieName = "Video "+videoId.toString()
#taken from http://www.quirksmode.org/js/cookies.html for easy reading of cookies
readCookie = (name) ->
nameEQ = name + '='
ca = document.cookie.split(';')
i = 0
while i < ca.length
c = ca[i]
while c.charAt(0) == ' '
c = c.substring(1, c.length)
if c.indexOf(nameEQ) == 0
return c.substring(nameEQ.length, c.length)
i++
null
video.addEventListener 'timeupdate', ((e) ->
duration = lengthWatched(e)
if (duration % 5 == 0)
currentLength = 0
$.get "/viewings/" + readCookie(viewingCookieName), (data) ->
currentLength = data['length']
return
$.ajax(
url: "/viewings/" + readCookie(viewingCookieName) + ".json"
type: 'PUT'
data: { viewing: {length: currentLength + 5}}
dataType: 'json').success ->
console.log('put')
return
), false
#only including relevant code, took out other video play functionality
video.onplay = (e) ->
unless readCookie(viewingCookieName)
$.ajax(
url: "/viewings.json"
type: 'POST'
data: { viewing: { video_id: videoId, user_id: gon.current_user_id } },
dataType: 'json').success (data) ->
viewingId = data['id']
time = new Date(Date.now() + 1800000).toUTCString()
document.cookie = viewingCookieName+"="+ data['id']+"; expires="+time
return
Used basically the same controller provided by agmcleod. I'm still working on fixing timeupdate
to only hit every 1 second, but I'm sure that'll be fairly simple. May also add max_duration_reached
.
Ajax is precisely what you need. Given you're using rails, I presume you have the jquery kicking around. You can do something like this:
video.addEventListener 'timeupdate', ((e) ->
duration = lengthWatched(e);
console.log(duration);
if (duration % 10 === 0)
$.ajax({
url: "/viewings/" + viewingsId + ".json",
type: "PUT",
data: { viewing: { length: duration } },
dataType: "json"
}).done(() -> {
// callback
});
return
), false
Sorry if my coffeescript is off, it's been a while. Anyways that posts a json body of the length property to a controller end point using PUT (since we're updating a record). Be sure to populate the viewingsId from your cookie.
In the routes side, you can have a standard resource here:
resources :viewings
Then in the controller:
class ViewingsController < ApplicationController
def update
viewing = Viewing.find(params[:id])
if viewing.update_attributes viewing_attributes
respond_to do |format|
format.html
format.json { render json: viewing }
end
else
respond_to do |format|
format.html { redirect_to viewing }
format.json { render errors: viewing.errors, status: 422 }
end
end
end
private
def viewing_attributes
params.require(:viewing).permit(:length)
end
end
The strong parameters bit is optional, just trying to keep with good practices. Update the permit on any other parameters you might need. I wrote the update action so it can work with html requests as well (form updates). Hope that helps you.