All threads / How would you implement a feature like Youtube's "watched" feature.

Ask A Question

Notifications

You’re not receiving notifications from this thread.

How would you implement a feature like Youtube's "watched" feature.

James Ferguson asked in General

Hi Chris,

I'm interested in implementing a feature similar to Youtube's watched feature. Where after you've watched a video, a small semi transparent overlay is placed over the video thumbnail with the words "watched".

I've implemented something of my own, but it not very efficient. Each video on the page needs to be looked up to see if the member has watched it. Each response takes 0.4ms times n, where n is the number of videos on the page. So the page load might be more than 6-7 seconds (15 videos on the index page).

How would you suggest I do something like this, so I could achieve the same functionality but with less database requests?

Thanks, James

This is an awesome question. I've dealt with this in the past and I'm also going to be adding a "watched" feature to GoRails. So how do you tackle it efficiently?

There's a bunch of different ways to go about this but they'll all involve caching of some sort.

  1. Easiest option is to simply just render the thumbnails without the "watched" functionality and then change the thumbnail class when the page loads via AJAX. Use JS to gather all the thumbnail video IDs on the page, make one request to the server to see the user's status on each of those, return the result back, and then add a "watched" class to the thumbnails that triggers the CSS to display that.

This would be super easy to build and would be pretty efficient. Basecamp has talked about using this approach in their app in a few places. Super lightweight and also allows you to cache the thumbnails globally. The results of the watched AJAX request can also be cached and you can bust the cache when the user watches a video. This would make it pretty quick and the page load/parsing time should be fast enough that it wouldn't be hugely noticeable.

This approach breaks down when your "watched" AJAX response is slow. Users would see the thumbnail and then some time later it would change to be "watched" which could be an odd user experience. Therefore... option #2.

  1. The second option is to cache the "watched" state of the thumbnail for each video. You can render your page like you mentioned above, but cache the thumbnails and toss the current user in the scope so you can have a cache for each user and their watched status. When they watch a new episode, only the cache for that user and that episode would change.

Basically just doing this around the thumbnails:

<%= cache [current_user, video] do %>
  <% if current_user.watched?(video) %>
  <% else %>
  <% end %>
<% end %>

In both these cases, the best bet will be to make one single query of the user's activity to get all the user's watched videos at once instead of separate queries.

Instead of a bunch of queries hitting the databaselike this:

current_user.activities.where(status: "watched", video: @video).any?

Load them all up, and do a quick lookup instead

@watched_video_ids = Video.joins(:activities).where(activities: {status: "watched", user: current_user}).pluck(:id)

And in your views, you can just check if the video is in the instance variable instead of hitting the database

<% if @watched_videos.include?(video.id) %>
  # watched
<% else %>
<% end %>

In the short term, both of those solutions will be most efficient by loading up all the watched videos at once (until they've watched thousands of videos of course) and then just checking that array rather than going to the database each time.

Your mileage may vary, and the most efficient solution in the long term will really be determined by how usage of your app ends up being. For something like GoRails, there aren't that many episodes so loading up an array of each video ID that you've watched will be pretty quick. For YouTube...that's a whole different story and they'll probably do a much different solution. But they've got the time and money to spare for that. 😉

Awesome. Thanks for the in-depth response Chris. Really appreciate it.

I'll have a go at it tonight.

Cheers,
James

Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 37,629+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

    © 2020 GoRails, LLC. All rights reserved.