Tải Video với Rails và Ziggeo (phần 3)

Thiết lập Callbacks

Chuẩn bị và tạo Video

Callbacks được cấu hình cho mỗi ứng dụng, vì thế khi mở trang tổng quan, bạn chọn một ứng dụng và click vào Manage > Web Hooks. Ở đây nhập một URL và chọn "JSON encoding" từ dropdown. Bây giờ các sự kiện sẽ được chuyển tiếp tới /api/videocallbacks dưới dạng POST request. Dưới đây là danh sách tất cả callbacks mà bạn có thể sử dụng. Lưu ý rằng không phải tất cả các sự kiện được chuyển tiếp - chỉ những cái quan trọng nhất.

Trước hết, tạo một model mới là Video, bao gồm các trường sau:

  • uid (string, indexed, unique) – token của video
  • user_id (integer, indexed) – khóa ngoài quan hệ giưa 1 user và 1 video
  • duration (decimal) – thời gian của video tính theo giây
  • ziggeo_created_at (datetime) –thời gian video đươc tạo bằng Ziggeo
  • approved (boolean, indexed) – video được thêm bởi người kiểm duyệt, mặc định là false

Tạo migration:

$ rails g model Video user:belongs_to uid:string duration:decimal ziggeo_created_at:datetime approved:boolean

db/migrate/xyz/createvideos.rb

# ...
create_table :videos do |t|
  t.string :uid
  t.belongs_to :user, foreign_key: true
  t.decimal :duration, scale: 2, precision: 5
  t.datetime :ziggeo_created_at
  t.boolean :approved, default: false

  t.timestamps
end

add_index :videos, :approved
add_index :videos, :uid, unique: true
$ rails db:migrate

Thiết lập quan hệ và validation:

models/user.rb

# ...
has_many :videos, dependent: :destroy

models/video.rb

# ...
belongs_to :user
validates :uid, presence: true, uniqueness: true

Giờ thêm 1 route mới :

config/routes.rb

namespace :api do
  resources :video_callbacks, only: [:create]
end

Tạo một controller trong folder api:

controllers/api/videocallbackscontroller.rb

class Api::VideoCallbacksController < ActionController::Base
  def create
  end
end

Khi có một sự kiện mới đến, param eventtype thiết lập một số giá trị. Hiện tại chúng ta sẽ quan tâm đến sự kiện video_ready. Cho phép lấy dữ liệu của video và tạo một bản ghi mới dựa trên nó:

controllers/api/videocallbackscontroller.rb

class Api::VideoCallbacksController < ActionController::Base
  def create
    type = params['event_type']
        respond_to do |format|
          @result = if type == 'video_ready'
                      Video.from_api(params['data']['video'])
                    end
        end
  end
end

Dữ liệu video được lưu key ['data']['video'].

Respond trả về status 204 (không nội dung) nếu mọi thứ OK, hoặc 500 (lỗi máy chủ) nếu có lỗi:

controllers/api/videocallbackscontroller.rb

def create
  type = params['event_type']
  respond_to do |format|
    @result = if type == 'video_ready'
                Video.from_api(params['data']['video'])
              end
    format.html { @result ? head(:no_content) : head(500) }
  end
end

Thêm class method from_api. Cần tìm user dựa trên thẻ của video (hãy nhớ rằng chúng ta đang sử dụng UID của user như một thẻ) và tạo một bản ghi mới thuộc về user đó:

models/video.rb

# ...
def self.from_api(data)
  user = User.find_by(uid: data['tags'][0])
  video = user.videos.find_or_initialize_by(uid: data['token'])
  video.ziggeo_created_at = Time.at(data['created'])
  video.duration = data['duration']
  video.save
end

Tôi đã nhận thấy rằng đôi khi một sự kiện có thể được gửi hai lần, do đó sử dụng find_or_initialize_by để tránh tạo bản ghi trùng lặp. Vâng, điều này sẽ không thể xảy ra được khi đặt uid là duy nhất, nhưng vẫn còn.

Video Approval

Khi một video được phê duyệt hoặc từ chối bởi người kiểm duyệt, sự kiện tương ứng cũng được gửi. Chúng ta sẽ làm việc với loại sự kiện video_approve. Khi có sự kiện, tìm video trong cơ sở dữ liệu dựa trên uid và đặt thuộc tính approved thành true:

controllers/api/video_callbacks_controller.rb

def create
  type = params['event_type']
  respond_to do |format|
    @result = if type == 'video_ready'
                Video.from_api(params['data']['video'])
              else
                if type == 'video_approve'
                  video = Video.find_by(uid: params['data']['video']['token'])
                  video.approve! if video
                else
                  true
                end
              end
    format.html { @result ? head(:no_content) : head(500) }
  end
end

Chúng ta gán true cho biến @result nếu sự kiện là loại khác. Đây là phương thức approve! :

models/video.rb

# ...
def approve!
  self.approved = true
  self.save
end

Video Deletion

Một video có thể bị xóa bằng bảng điều khiển Ziggeo. Khi điều đó xảy ra, chúng ta cũng muốn xóa video này khỏi cơ sở dữ liệu của mình. Loại sự kiện mà chúng tôi quan tâm được gọi là video_delete. Một lần nữa, tìm video thích hợp và sau đó chỉ cần xóa nó:

controllers/api/videocallbackscontroller.rb

# ...
def create
  type = params['event_type']
  respond_to do |format|
    @result = if type == 'video_ready'
                Video.from_api(params['data']['video'])
              else
                if type == 'video_approve' || type == 'video_delete'
                  video = Video.find_by(uid: params['data']['video']['token'])
                  if video
                    type == 'video_approve' ?
                        video.approve! :
                        video.destroy
                  end
                else
                  true
                end
              end
    format.html { @result ? head(:no_content) : head(500) }
  end
end

Tốt! Giờ chúng ta có những callback, action index trong VideosController có thể được viết lại.

Hiển thị Video và thông tin về Meta

Chúng ta không cần dùng Ziggeo nữa tronga action index nữa. Thay vào đó, chỉ cần lấy các video của người dùng hiện tại - chỉ những video đã được phê duyệt:

videoscontroller.rb

# ...
def index
  @videos = current_user.videos.where(approved: true)
end

Mỗi video giờ có thêm thông tin meta, chúng ta cũng có thể hiển thị nó trên trang chính:

views/videos/video.html.erb

<div class="card">
  <div class="card-block">
    <ziggeo ziggeo-video='<%= video.uid %>'
            ziggeo-width="320"
            ziggeo-height="240" ziggeo-popup>
    </ziggeo>
    <p>
      <strong>Duration:</strong> <%= video.duration %>s<br>
      <strong>Created:</strong> <%= video.ziggeo_created_at  %>
    </p>
  </div>
</div>

Xóa Video qua API

Phần cuối cùng của chức năng mà chúng tôi sẽ code hôm nay là khả năng xóa một video từ ứng dụng của chúng ta. Chỉ người dùng sở hữu video mới có thể thực hiện hành động này.

Đầu tiên, thêm link "delete":

views/videos/video.html.erb

<ziggeo ziggeo-video='<%= video.uid %>'
        ziggeo-width="320"
        ziggeo-height="240" ziggeo-popup>
</ziggeo>
<!-- ... -->
<p><%= link_to 'Delete', video_path(video.uid), method: :delete %></p>

Chú ý rằng tôi đang truyền một uid của video, không phải id - chúng tôi sẽ không thực sự xóa video khỏi cơ sở dữ liệu.

In order to remove a video, a Ziggeo API client is needed. The deletion is performed using the delete method that accepts a uid. Để xóa video, cần phải có khách hàng API Ziggeo. Xoá bỏ được thực hiện bằng cách sử dụng phương pháp xóa chấp nhận một uid.

videoscontroller.rb

# ...
def destroy
  video = current_user.videos.find_by(uid: params[:id])
  if video
    ziggeo = Ziggeo.new(ENV['ZIGGEO_KEY'], ENV['ZIGGEO_SECRET'], ENV['ZIGGEO_ENCRYPTION'])
    ziggeo.videos.delete(video.uid)
    flash[:success] = 'Video removed! It may take some time to reflect changes on the website.'
  else
    flash[:warning] = 'Cannot find such video...'
  end
  redirect_to root_path
end

Chúng ta chỉ xóa video khỏi Ziggeo. Sau khi hoàn thành, sự kiện video_delete sẽ được gửi đến callback của chúng ta và bản ghi tương ứng sẽ bị xóa khỏi cơ sở dữ liệu. Quá trình này không phải là ngay lập tức, đó là lý do tại sao chúng tôi cảnh báo người dùng rằng có thể mất một thời gian để phản ánh sự thay đổi.

Kết luận

Cuối cùng cũng đã kết thúc bài viết này, Ziggeo còn nhiều tính năng thú vị khác, chúng ta mới chỉ thảo luận về các tính năng cơ bản của nó. Hãy tìm hiểu thêm về Ziggeo và khám phá nó.

I hope you’ve enjoyed reading this article and I thank you for staying with me. Happy coding and see you!

Tài liệu dịch: https://www.sitepoint.com/video-uploads-with-rails-and-ziggeo/