Tạo ứng dụng chia sẻ video với Ruby on Rails
Bài đăng này đã không được cập nhật trong 8 năm
Trong hướng dẫn này, bạn sẽ biết cách tạo ứng dụng chia sẻ video cơ bản sử dụng Rails
.
Các tính năng bao gồm:
- Sign up, in, out - sử dụng gem
devise
. - Upload video - xử lý mã hoá video.
- Play video - sử dụng
videojs
tạo trình chạy video đơn giản. - Thông báo - thông báo cho người dùng khi mã hoá xong video, sử dụng
pubnub
.
Để thực hiện, bạn cần một số kiến thức về Ruby on Rails
, CoffeeScript
và HAML
. Biết cách cài đặt ffmpeg
và redis
.
Khởi tạo
Tạo mới một ứng dụng Rails
$ rails new VideoShrimp
Chúng ta sẽ sử dụng cơ sở dữ liệu SQLite
, tất nhiên bạn có thể sử dụng postgresql
, mysql
hay bất cứ cơ sở dữ liệu nào bạn muốn.
Sử dụng Rails version 4.1
trở lên:
$ rails -v
Rails 4.2.4
$ ruby -v
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin15]
Tiếp theo là thêm các gem cần dùng vào Gemfile
.
gem 'haml'
gem 'devise'
gem 'simple_form'
gem 'paperclip'
gem 'bootstrap-sass'
gem 'sidekiq'
gem 'sidetiq', github: 'sfroehler/sidetiq', branch: 'celluloid-0-17-compatibility'
gem 'pubnub', github: 'pubnub/ruby', branch: 'celluloid'
gem 'sinatra', :require => nil
group :development do
gem 'pry'
gem 'pry-rails'
end
Giải thích:
haml
- tương tự template*html.erb
.devise
- xác thực người dùng.simple_form
- thiết kế form chuẩn trongRails
.paperclip
- quản lý tệp.bootstrap-sass
- thiết kế giao diện.sidekiq
- xử lý video ở chế độ nền.sidetiq
- là một plugin củasidekiq
. Dùng để kiểm tra những video đã được mã hoá mà chưa được công bố.pubnub
- để thông báo và lấy các thông tin liên lạc giữa backend-frontend.sinatra
-sidekiq frontend
, tuỳ chọn.pry
- tốt hơnirb
.pry-rails
- dùngpry
thay choirb
trongrails c
.
Chú ý #1: Như bạn thấy, tôi dùng gem sidekiq
từ repository ngoài, vì gem chuẩn khồn tương thích với celluloid
hiện tại (thời điểm viết bài).
Chú ý #2: gem pubnub
lấy từ branch khác master vì bản celluloid
chính chưa ổn định. Hiện dùng pubnub
bản beta.
Tiếp theo, chạy bundle install
để tải gem.
$ bundle install
Cài đặt devise và simple_form
$ rails generate simple_form:install --bootstrap
$ rails generate devise:install
Lệnh tạo devise sẽ tạo ra file config/initializers/devise.rb
và config/locales/devise.en.yml
nhưng bạn không cần quan tâm đến hai file này, hiện tại không cần cấu hình thêm gì từ devise.
Tạo view để sử dụng simple_form
:
$ rails generate devise:views
Tiếp theo là tạo model và migrate.
Tạo model
rails generate devise user
invoke active_record
create db/migrate/20151117181300_devise_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
insert app/models/user.rb
route devise_for :users
Với ứng dụng cơ bản này, chúng ta không cần thêm gì khác vì sẽ sử dụng user email mặc định để đăng ký.
Tạo video model
rails generate model Video name:string video_file:attachment mp4_file:attachment webm_file:attachment ogg_file:attachment thumbnail:attachment published:boolean likes:integer user:references
File migration sẽ có dạng:
class CreateVideos < ActiveRecord::Migration
def change
create_table :videos do |t|
t.string :name
t.attachment :video_file
t.attachment :mp4_file
t.attachment :webm_file
t.attachment :ogg_file
t.attachment :thumbnail
t.boolean :published
t.integer :likes, default: 0
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
end
end
Và giờ sẽ là viết code cho model.
class Video < ActiveRecord::Base
# Association declaration
belongs_to :user
# Paperclip attachments declaration
has_attached_file :video_file
has_attached_file :mp4_file
has_attached_file :webm_file
has_attached_file :ogg_file
# Styles declaration makes paperclip to use imagemagick to resize image to given size
has_attached_file :thumbnail, styles: { medium_nr: "250x150!" }
# Paperclip requires to set attachment validators
validates_attachment_content_type :video_file, content_type: /\Avideo/
validates_attachment_content_type :mp4_file, content_type: /.*/
validates_attachment_content_type :webm_file, content_type: /.*/
validates_attachment_content_type :ogg_file, content_type: /.*/
# We want video model always to have :video_file attachment
validates_attachment_presence :video_file
# Publish video makes it available
def publish!
self.published = true
save
end
# Increment likes counter
def like!
self.likes += 1
save
end
# Decrease likes counter
def dislike!
self.likes -= 1
save
end
# Checks if all formats are already encoded, the simplest way
def all_formats_encoded?
self.webm_file.path && self.mp4_file.path && self.ogg_file.path ? true : false
end
end
Thêm relation cho video:
class User < ActiveRecord::Base
# Association declaration
has_many :videos
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Chạy migration
$ rake db:migrate
Tạo controller và view
Bây giờ, chúng ta sẽ tạo controller và view cho ứng dụng, nên cho phép người dùng tạo tài khoản và đăng nhập.
Thêm code cho file app/controllers/application_controller.rb
như sau:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
layout :layout_by_resource
protected
def layout_by_resource
if devise_controller?
'devise'
else
'application'
end
end
end
Đoạn code trên kiểm tra controller có phải là của devise không, nếu đúng sẽ trả ra devise layout, nếu không sẽ trả ra layout chuẩn.
Tiếp tục với file app/views/layouts/devise.haml
.
!!!
%html
%head
%title VideoShrimp
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
= javascript_include_tag 'application', 'data-turbolinks-track' => true
= csrf_meta_tags
%body{'data-no-turbolink': true}
.container
.row
.col-md-4.col-md-offset-4
%h1.text-center VideoShrimp
= yield
Trong devise layout, có các thành phần cơ bản như container
, .row
, .col-md-4
và .col-md-offset-4
trong bootstrap. Nếu bạn không biết các thành phần này, tôi khuyến khích đọc về Bootstrap Grid System.
Bây giờ tạo users_controller.rb
trong app/controllers
.
class UsersController < ApplicationController
# Checks if user is signed in before running controller, functionality provided by devise
before_action :authenticate_user!
before_action :set_user, only: [:show]
before_action :set_current_user, only: [:edit, :update]
def index
@@users = User.all
end
def show
end
def update
respond_to do |format|
if @@user.update(user_params)
format.html { redirect_to @@user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: @@user }
else
format.html { render :edit }
format.json { render json: @@user.errors, status: :unprocessable_entity }
end
end
end
private
def set_user
@@user = User.find(params[:id])
end
def set_current_user
@@user = current_user
end
def user_params
params.require(:user).permit(:email)
end
end
Controller trên dùng để hiển thị hồ sơ người dùng và cho phép thay đổi email. Tiếp tục với video controller
class VideosController < ApplicationController
before_action :authenticate_user!
before_action :set_video, only: [:show, :edit, :like, :dislike]
# All published videos
def index
@@videos = Video.where(published: true)
end
def show
end
def new
@@video = Video.new
end
def edit
end
def create
@@video = Video.new(video_params)
respond_to do |format|
if @@video.save
format.html { redirect_to @@video, notice: 'Video was successfully created.' }
format.json { render :show, status: :created, location: @video }
else
format.html { render :new }
format.json { render json: @@video.errors, status: :unprocessable_entity }
end
end
end
# Likes video, increment likes count
def like
@@video.like!
end
# Dislikes video, increment likes count
def dislike
@@video.dislike!
end
private
def set_video
@@video = Video.find(params[:id])
end
def video_params
params.require(:video).permit(:video_file, :name)
end
end
Khá đơn giản, phải không?
Bây giờ xem lại. Hãy loại bỏ file application.html.erb
từ app/views/layouts
và tạo application.haml
.
!!!
%html
%head
%title VideoShrimp
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
= stylesheet_link_tag '//vjs.zencdn.net/5.0.2/video-js.css'
= javascript_include_tag 'application', 'data-turbolinks-track' => true
= javascript_include_tag '//cdn.pubnub.com/pubnub-dev.js'
= csrf_meta_tags
%body{'data-no-turbolink': true}
- if current_user
.navbar.navbar-default.navbar-fixed-top
.container
.navbar-header
= link_to 'VideoShrimp', root_url, class: 'navbar-brand'
%ul.nav.navbar-nav.navbar-right
%li
= link_to 'Browse videos', videos_path
%li
= link_to 'Upload video', new_video_path
%li.dropdown
%a.dropdown-toggle{"aria-expanded" => "false", "aria-haspopup" => "true", "data-toggle" => "dropdown", :href => "#", :role => "button"}
= current_user.email
%span.caret
%ul.dropdown-menu
%li
= link_to 'Profile', current_user
%li
= link_to 'Edit profile', edit_user_path(current_user)
%li.divider{:role => "separator"}
%li
= link_to 'Log out', destroy_user_session_path, :method => :delete
%li#user-notifications.dropdown{"data-pn-auth-key": current_user.pn_auth_key, "data-pn-notification-channel": current_user.notification_channel}
%a.dropdown-toggle{"aria-expanded" => "false", "aria-haspopup" => "true", "data-toggle" => "dropdown", :href => "#", :role => "button"}
%span.glyphicon.glyphicon-bell
%ul.dropdown-menu
= yield
Đó là layout trống, với một số điều hướng cơ bản bootstrapped. Như vậy là còn thẻ cho pubnub mà chúng ta sẽ sử dụng để thông báo và videojs
để chạy video.
Hãy thêm bootstrap cho stylesheets!
Đầu tiên, gỡ bỏ app/assets/stylesheets/application.css
và tạo app/assets/stylesheets/application.scss
với:
@import "bootstrap-sprockets";
@import "bootstrap";
@import "global";
@import "video";
Tạo global.scss
và video.scss
cũng tương tự.
Bootstrap cần được thêm vào app/assets/javascripts/applications.js
, trông sẽ thế này:
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require bootstrap
//= require_tree .
Điều cuối cùng thêm vào assets là sửa global.scss
trong app/assets/stylesheets/
và thêm:
body {
padding-top: 60px;
}
Nhờ đó nội dung trang web sẽ không bị ẩn dưới navigation.
Tất cả các công cụ này sẽ không làm việc? Tất nhiên, bởi vì chúng tôi đã không tạo ra các route. Vì vậy, hãy làm điều đó và chỉnh sửa file config/routes.rb
như sau:
require 'sidekiq/web'
Rails.application.routes.draw do
root 'videos#index'
resources :videos
get '/videos/:id/like' => 'videos#like'
get '/videos/:id/dislike' => 'videos#dislike'
devise_for :users
resources :users
mount Sidekiq::Web => '/sidekiq'
end
Vì vậy, trang chủ của ứng dụng sẽ là danh sách các video được tải lên. Resource cho video và user là quá mức cần thiết bởi vì chúng sẽ không được sử dụng một phần của route được tạo ra nhưng nếu bạn muốn mở rộng ứng dụng thì bạn sẽ cần nó.
Sidekiq web là interface của sidekiq. Đừng quên require 'sidekiq/web'
trên đầu file.
Hãy bắt đầu với user. Dưới app/views/users/
chúng ta sẽ tạo ra hai file:
show.haml
.container
.row
.col-md-12
%h2= @user.email
.container
.row
- @user.videos.each do |video|
.col-md-3.thumb.video-thumb{ 'data-video-id': video.id }
.likes
%span.likes-count= video.likes
%span.glyphicon.glyphicon-heart
= link_to video, class: 'thumbnail' do
= image_tag video.thumbnail.url(:medium_nr)
và edit.haml
.container
.row
.col-md-6.col-sm-12
%h2 Edit profile
= simple_form_for @user do |f|
= f.input :email
= f.button :submit, class: 'btn-primary', value: 'Update Profile'
Và giờ tạo video views, thêm vào thư mục app/views/videos index.haml
.container
.row.video-full{ 'data-video-id': @video.id }
.col-md-10
%h1= @video.name
%p.small
= link_to 'Back to videos' ,videos_path
- if @video.all_formats_encoded?
%video#my-video.video-js{:controls => "", "data-setup" => "{}", :height => "264", :preload => "auto", :width => "640"}
%source{:src => @video.mp4_file.url, :type => "video/mp4"}
%source{:src => @video.webm_file.url, :type => "video/webm"}
%source{:src => @video.ogg_file.url, :type => "video/ogg"}
%p.vjs-no-js
To view this video please enable JavaScript, and consider upgrading to a web browser that
%a{:href => "http://videojs.com/html5-video-support/", :target => "_blank"} supports HTML5 video
- else
%p Video is still being encoded.
.col-md-2
%h1
%span.likes-count= @video.likes
%span.glyphicon.glyphicon-heart
Hiển thị video và cho phép thích hay không thích. Nó không cập nhật truy cập bởi vì ta sẽ sử dụng pubnub notification để làm điều đó.
new.haml
.container
.row
.col-md-6.col-sm-12
%h2 Upload new video
= simple_form_for @video, html: { multipart: true } do |f|
= f.input :name
= f.input :video_file, as: :file
= f.button :submit, class: 'btn-primary', value: 'Upload Video'
Vậy là đã có một form đơn giản để tải lên video. Sẽ có tên video và nút thêm tệp tin.
Xin chúc mừng! Một số nội dung đã sẵn sàng!
Mã hoá video và hiển thị thông báo
Bây giờ chúng ta cần phải mã hóa video được tải lên, hiển thị chúng cho người dùng và gửi thông báo. Hãy dùng pubnub để làm điều đó.
Đi đến trang web Pubnub và tạo tài khoản mới. Sau khi tạo tài khoản mới, bạn nên thêm ứng dụng mới và tạo ra các khoá cho ứng dụng. Chúng tôi sẽ sử dụng tính năng Storeage & Playback cho lịch sử thông báo và Access manager để làm thông báo tin nhắn.
Bạn sẽ cần phải sao chép các khoá của bạn vào file config/secrets.yml
như pubnub_subscribe_key, pubnub_publish_key, pubnub_secret_key
và bạn nên tạo cho mình một khoá pubnub_auth_key
duy nhất cho máy chủ.
Bây giờ chúng ta cần thêm pubnub.rb
trong config/initializers
, code đó sẽ chạy khi ứng dụng được khởi động.
pubnub.rb
# Initialize pubnub client with our keys
$pubnub = Pubnub.new(
subscribe_key: Rails.application.secrets.pubnub_subscribe_key,
publish_key: Rails.application.secrets.pubnub_publish_key,
secret_key: Rails.application.secrets.pubnub_secret_key,
auth_key: Rails.application.secrets.pubnub_auth_key
)
# As we have PAM enabled, we have to grant access to channels.
# That grants read right to any channel that begins with 'video.' to everyone.
$pubnub.grant(
read: true,
write: false,
auth_key: nil,
channel: 'video.*',
http_sync: true,
ttl: 0
)
# That grants read and write right to any channel that begins with 'video.' to this client.
$pubnub.grant(
read: true,
write: true,
auth_key: Rails.application.secrets.pubnub_auth_key,
channel: 'video.*',
http_sync: true,
ttl: 0
)
# That grants read and write right to any channel that begins with 'notifications.' to this client.
$pubnub.grant(
read: true,
write: true,
auth_key: Rails.application.secrets.pubnub_auth_key,
channel: 'notifications.*',
http_sync: true,
ttl: 0
)
Như bạn thấy, sau khi khởi tạo biến toàn cục $pubnub
, chúng tôi đang chạy 3 cấp với ttl: 0
- mà cấp sẽ không bao giờ hết hạn.
Tiếp theo, chúng ta phải cung cấp cho người dùng khả năng để đọc thông báo cá nhân của mình. Chúng tôi sẽ làm điều đó bằng cách tạo ra auth_key
duy nhất và cấp quyền đọc kênh thông báo của mình cho mỗi người dùng. Hãy tạo migration:
$ rails generate migration add_pn_auth_key_to_users
Tạo migration như sau:
class AddPnAuthKeyToUsers < ActiveRecord::Migration
def change
add_column :users, :pn_auth_key, :string
end
end
Tiếp tục, sửa file user.rb trong devise:
after_create :gen_auth_and_grant_perms
def notification_channel
"notifications.#{self.id}"
end
def gen_auth_and_grant_perms
generate_pn_auth!
$pubnub.grant(
channel: notification_channel,
auth_key: pn_auth_key,
ttl: 0,
http_sync: true
)
end
def generate_pn_auth
self.pn_auth_key = SecureRandom.hex
end
def generate_pn_auth!
self.generate_pn_auth
save
end
Nó sẽ chạy method gen_auth_and_grant_perms
sau khi người dùng được tạo ra. Người dùng sẽ nhận được auth_key
duy nhất của mình sẽ được sử dụng bởi javascript client và được quyền để đọc trên kênh riêng của mình.
Bây giờ, ta sẽ sửa Video model. Chỉnh sửa video.rb
và thực hiện thay đổi các method publish!, like!, dislike!
.
# Publish video makes it available
def publish!
self.published = true
save
$pubnub.publish(channel: "video.#{id}", message: {event: :published}, http_sync: true)
$pubnub.publish(channel: self.user.notification_channel, message: {event: :published, scope: :videos, id: self.id, name: name.truncate(20)}, http_sync: true)
end
# Increment likes counter
def like!
self.likes += 1
save
$pubnub.publish(channel: "video.#{id}", message: {event: :liked}, http_sync: true)
end
# Decrease likes counter
def dislike!
self.likes -= 1
save
$pubnub.publish(channel: "video.#{id}", message: {event: :disliked}, http_sync: true)
end
Khi thích hoặc không thích sẽ gửi tin nhắn tương ứng đến kênh video vì vậy ta có thể cập nhật trực tiếp khi đăng ký kênh. Khi publish sẽ gửi thông báo cho chủ video rằng video đã được công bố trên kênh tương ứng.
Trước khi làm việc trên frontend, cần mã hoá video được tải lên. Trước tiên, tạo thư mục app/workers
, trong đó cần tạo file:
mp4_video_encoder.rb
class Mp4VideoEncoder
include Sidekiq::Worker
def perform(video_id)
video = Video.find(video_id)
path = video.video_file.path
output = "/tmp/#{Time.now.getutc.to_f.to_s.delete('.')}.mp4"
_command = `ffmpeg -i #{path} -f mp4 -vcodec h264 -acodec aac -strict -2 #{output}`
if $?.to_i == 0
video.mp4_file = File.open(output, 'r')
video.save
FileUtils.rm(output)
else
raise $?
end
end
end
Kịch bản rất đơn giản, ta lấy video từ db bằng id của nó, tạo ra đường dẫn đầu ra ngẫu nhiên và chạy lệnh shell để mã hoá sang dạng mp4. Nếu lệnh shell thành công, video được cập nhật với file mới và file video tạm tạo ra bởi ffmpeg
sẽ bị xoá.
Hai file tiếp theo là gần giống nhau, nếu bạn muốn DRY hơn, bạn có thể viết module riêng. Dù sao thì 2 file tiếp theo sẽ là:
ogv_video_encoder.rb
class OgvVideoEncoder
include Sidekiq::Worker
def perform(video_id)
video = Video.find(video_id)
path = video.video_file.path
output = "/tmp/#{Time.now.getutc.to_f.to_s.delete('.')}.ogv"
_command = `ffmpeg -i #{path} -codec:v libtheora -qscale:v 7 -codec:a libvorbis -qscale:a 7 #{output}`
if $?.to_i == 0
video.ogg_file = File.open(output, 'r')
video.save
FileUtils.rm(output)
else
raise $?
end
end
end
và webm_video_encoder.rb
class WebmVideoEncoder
include Sidekiq::Worker
def perform(video_id)
video = Video.find(video_id)
path = video.video_file.path
output = "/tmp/#{Time.now.getutc.to_f.to_s.delete('.')}.webm"
_command = `ffmpeg -i #{path} -f webm -c:v libvpx -b:v 1M -c:a libvorbis #{output}`
if $?.to_i == 0
video.webm_file = File.open(output, 'r')
video.save
FileUtils.rm(output)
else
raise $?
end
end
end
File thứ 3 sẽ là một frame tại thời điểm 00:01:00
của video và lưu dưới dạng thu nhỏ (thumbnail).
thumbnail_cutter.rb
class ThumbnailCutter
include Sidekiq::Worker
def perform(video_id)
video = Video.find(video_id)
output = "/tmp/#{Time.now.getutc.to_f.to_s.delete('.')}.png"
_command = `ffmpeg -i #{video.video_file.path} -ss 00:00:01.000 -vframes 1 #{output}`
if $?.to_i == 0
video.thumbnail = File.open(output, 'r')
video.save
FileUtils.rm(output)
else
raise $?
end
end
end
Từ đây, bạn phải khởi động sidekiq
cùng với rails server
bằng cách vào consle và chạy lệnh
``
$ bundle exec sidekiq
Khi muốn sử dụng ngay khi video được tải lên, cần chỉnh sửa Video model và thêm vào:
```ruby
after_create :run_encoders
private
def run_encoders
ThumbnailCutter.perform_async(self.id)
Mp4VideoEncoder.perform_async(self.id)
OgvVideoEncoder.perform_async(self.id)
WebmVideoEncoder.perform_async(self.id)
end
Thiếu một chút, video tải lên nhưng chưa được công bố, khi đó ta sẽ tạo trình làm việc để tìm kiếm những video chưa công bố và kiểm tra xem nó đã sẵn sàng để công bố hay chưa. Nếu có, sẽ công bố video và gửi thông báo. Tạo file app/workers/video_publisher.rb:
class VideoPublisher
include Sidekiq::Worker
include Sidetiq::Schedulable
recurrence { minutely }
def perform
Video.where(published: false).each do |video|
video.publish! if video.all_formats_encoded?
end
end
end
Bạn có thể thấy có thay đổi so với những xử lý trước.
include Sidetiq::Schedulable
- tạo lịch xử lý.
reccurence { minutely }
- xử lý trong từng phút.
Thông báo
Tạo 2 file trong thư mục app/assets/javascripts/
:
notifications.js.coffee.erb
$ ->
if $("#user-notifications").length
auth_key = $("#user-notifications").attr("data-pn-auth-key")
notification_channel = $("#user-notifications").attr('data-pn-notification-channel')
add_notifications = (msg) ->
for notification in msg[0]
switch notification.event
when 'published'
notification_html = "<li><a href='/" + notification.scope + "/" + notification.id + "'>"
notification_html += "Your video " + notification.name + " has been published"
notification_html += "</a></li>"
$("#user-notifications .dropdown-menu").prepend notification_html
window.pubnub = PUBNUB.init
subscribe_key: "<%= Rails.application.secrets.pubnub_subscribe_key %>",
publish_key: "<%= Rails.application.secrets.pubnub_publish_key %>",
auth_key: auth_key
window.pubnub.history
channel: notification_channel,
count: 10,
reverse: false,
callback: (msg) ->
add_notifications(msg)
window.pubnub.subscribe
channel: notification_channel,
callback: (msg) ->
add_notifications(msg)
Đoạn script chạy sau khi document được load và có nút #user-notifications
trong DOM. Nó khởi tạo pubnub client với user auth_key. Tải trước 10 thông báo từ người dùng, sau đó đăng ký kênh và chờ thông báo mới.
Và file thứ 2:
videos.coffee
$ ->
for video in $('.video-full, .video-thumb')
window.pubnub.subscribe
channel: 'video.' + $(video).attr('data-video-id')
message: (msg, env, chan) ->
id = chan.split('.')[1]
switch msg.event
when 'published'
location.reload()
when 'liked'
for likes in $("[data-video-id=" + id + "]").find('.likes-count')
console.log('add')
$(likes).html(parseInt($(likes).html()) + 1)
when 'disliked'
for likes in $("[data-video-id=" + id + "]").find('.likes-count')
console.log('remove')
$(likes).html(parseInt($(likes).html()) - 1)
Đó là kịch bản đăng ký thông tin kênh về tất cả các video trên trang (có 2 loại phần tử video. Một là video đầy đủ, còn lại là video-thumb, cả 2 đều dùng data video id để xác định tên kênh đăng ký). Khi người dùng sử dụng video chưa được công bố, thì sẽ được đăng ký với kênh video không công bố, và khi khách hàng nhận được thông báo về video đang được xuất bản thì đơn giản chỉ là load lại trang. Hai sự kiện thích và không thích có thể xảy ra. Nó sẽ tìm kiếm video với id đã có và cập nhật số lượt thích.
Vậy là chúng ta đã tạo xong ứng dụng, bạn chạy rails server
và sidekiq server
, sau đó truy cập localhost:3000
. Bạn sẽ thấy giao diện chính thức của trang được hiển thị. Hãy chọn đăng ký và tạo mới tài khoản. Vậy là bạn có thể sử dụng ứng dụng được rồi.
Có thể thêm khá nhiều thay đổi trong ứng dụng trên, ví dụ thêm nhiều thông báo hơn, hay đánh dấu khi người dùng xem thông báo, tải video chất lượng hơn. Bạn có thể giới hạn lượt thích (giống như mỗi người dùng được thích 1 lần cho mỗi video).
Bạn có thể lấy mã nguồn đầy đủ của ứng dụng tại đây.
Nguồn: Building a Video Sharing App with Server Messaging in Ruby
All rights reserved