Have you ever noticed the spiky graph above the YouTube timeline showing "Most Replayed" sections?
In this post, we’ll build a simplified version of that system using Ruby on Rails — to track, store, and visualize which parts of a video are being re-watched the most.
🧱 Step 1: Database Design
We'll use two tables:
-
videos
– to store video metadata -
video_interactions
– to log when a user scrubs, replays, or pauses at a specific second
✅ Migration: videos
# db/migrate/20250515120000_create_videos.rb
class CreateVideos < ActiveRecord::Migration[7.0]
def change
create_table :videos do |t|
t.string :title
t.integer :duration_seconds
t.references :uploaded_by, foreign_key: { to_table: :users }
t.timestamps
end
end
end
✅ Migration: video_interactions
# db/migrate/20250515120500_create_video_interactions.rb
class CreateVideoInteractions < ActiveRecord::Migration[7.0]
def change
create_table :video_interactions do |t|
t.references :video, null: false, foreign_key: true
t.references :user, foreign_key: true
t.string :interaction_type # e.g. 'replay', 'scrub'
t.integer :timestamp_in_video # second in timeline
t.timestamps
end
end
end
🧠 Step 2: Models
# app/models/video.rb
class Video < ApplicationRecord
has_many :video_interactions, dependent: :destroy
end
# app/models/video_interaction.rb
class VideoInteraction < ApplicationRecord
belongs_to :video
belongs_to :user, optional: true
enum interaction_type: { replay: 'replay', scrub: 'scrub', pause: 'pause' }
validates :timestamp_in_video, presence: true
end
🔁 Step 3: API Endpoint to Track Interactions
Let’s make an endpoint to log interactions.
# config/routes.rb
Rails.application.routes.draw do
resources :videos do
resources :video_interactions, only: [:create]
end
end
# app/controllers/video_interactions_controller.rb
class VideoInteractionsController < ApplicationController
def create
interaction = VideoInteraction.new(interaction_params)
if interaction.save
render json: { status: "ok" }, status: :created
else
render json: { errors: interaction.errors.full_messages }, status: :unprocessable_entity
end
end
private
def interaction_params
params.require(:video_interaction).permit(
:video_id,
:user_id,
:interaction_type,
:timestamp_in_video
)
end
end
📈 Step 4: Aggregating the "Most Replayed" Data
Now let’s create a method to find which parts of the video were replayed the most.
# app/models/video.rb
def replay_heatmap
video_interactions
.where(interaction_type: 'replay')
.group(:timestamp_in_video)
.order(:timestamp_in_video)
.count
end
def most_replayed_second
replay_heatmap.max_by { |_, count| count }&.first
end
This gives you a hash like:
{
5 => 3,
6 => 3,
7 => 14, # 👈 most replayed second
8 => 2
}
💻 Step 5: Visualizing on the Frontend
You can send the replay_heatmap
hash to your frontend via an API, and render a chart using any JS library like:
- Chart.js
- ApexCharts
- D3.js
GET /videos/123/replay_heatmap
{
"5": 3,
"6": 3,
"7": 14,
"8": 2
}
You can then show a hoverable graph above your video player like YouTube.
🚀 Bonus: Background Summary Table (Optional)
To speed up performance on popular videos, you can pre-aggregate data using a background job:
rails generate model VideoReplaySummary video:references timestamp_in_video:integer replay_count:integer
Use a job (e.g. with Sidekiq or ActiveJob) to refresh this hourly.
🧪 Testing It Locally
Use curl
or Postman to simulate a replay:
curl -X POST http://localhost:3000/videos/1/video_interactions \
-H "Content-Type: application/json" \
-d '{
"video_interaction": {
"user_id": 1,
"interaction_type": "replay",
"timestamp_in_video": 142
}
}'
📦 Summary
Part | Description |
---|---|
videos table |
Stores basic video info |
video_interactions |
Logs every replay/scrub with timestamp |
Aggregation method | Groups interactions by timestamp for heatmap |
Visualization | Displays the graph on video timeline |
Top comments (0)