Integrating Panda with your Ruby on Rails application

The following is a guide to integrating Panda in to your Ruby on Rails application. It is assumed you have already installed and configured Panda as described in the Getting Started guide.

The easiest way to integrate Panda into your application is by using one of the API libraries. The Ruby gem can be downloaded below:

Fetch panda_gem from github or download the latest tarball. Then run rake local_deploy inside the panda_gem directory to install the gem locally (note that you must have the hoe gem installed).

The full source of this example Ruby on Rails application is also available:

Fetch panda_example_rails from github or download the latest tarball

Configuration

You will need create a panda_config.rb file in your app’s config/ directory.

PANDA_ENCODING = "Flash video SD"
VIDEOS_DOMAIN = "s3.amazonaws.com/myvideosbucket"

Panda.account_key = "PANDA API KEY"
Panda.api_domain = "ec2-000-000-000-000.compute-1.amazonaws.com"
Panda.api_port = 80

All of the option must be set to the values determined in the Getting Started guide.

Using the Ruby library

The Panda library contains several methods for interacting with Panda.

Panda::Video.create

Creates an empty video on your account, ready to accept a file upload which will be subsequently encoded.

Panda::Video.find(id)

Retrieve details for a specific video.

Panda::Video.videos

Retrieve details for all videos in your account.

Implementing video uploads into your application

To integrate Panda fully you will need to store the id of your videos on Panda, along with the urls of encoded videos you want your users to be able to view. Typically you will want a Video model and VideosController to handle video uploading and viewing in your application.

Your database migration may look similar to this (see the example application for a more detailed setup):

class CreateVideos < ActiveRecord::Migration
  def self.up
    create_table :videos do |t|
      t.string :title
      t.string :panda_id
      t.string :filename
      t.integer :width
      t.integer :height
      t.timestamps
    end
  end

  def self.down
    drop_table :videos
  end
end

Integrating the upload form

We assume that users will visit /videos/new to upload a video to your application. Creating a video is a two stage process. Firstly, users will fill in the video’s title (and any other extra information you have added to the Video model). Next, they should be presented with an upload page that contains the Panda video upload form in an iframe, prompting the user to select a video file from their local filesystem. Using the default Panda form provides users with a progress bar for the video upload and greatly reduces the amount of work required to integrate Panda.

You should add two members to the videos resource in your routes.rb file:

map.resources :videos, :member => {:status => :post, :upload => :get, :done => :get}

The new action is standard:

def new
  @video = Video.new
end

In this example the title of the video is the only additional information we require. Your new.html.erb view should look something like this:

<% form_for :video, @video, :url => { :action => "create" } do |f| %>
  <p>
    <label>Title</label>
    <%= f.text_field :title %>
  </p>
  <%= submit_tag 'Next step: upload video' %>
<% end %>

The create action makes a call to the Panda API to create a new video and obtain the video id which should be stored in your video model.

def create
  @panda_video = Panda::Video.create
  @video = Video.create(params[:video].merge({:panda_id => @panda_video.id}))
  redirect_to :action => "upload", :id => @video.id
end

Next we redirect the user to the upload action which should display the Panda upload form in an iframe. The iframe url is generated from the video’s id.

def upload
  @video = Video.find(params[:id])
  @upload_form_url = %(http://#{Panda.api_domain}:#{Panda.api_port}/videos/#{@video.panda_id}/form)
end

Within your upload view you should embed the upload iframe.

<iframe src="<%= @upload_form_url %>" width="400" height="100" frameborder="0"></iframe>

Once the user has uploaded the video in the iframe, they will be redirected to the upload_redirect_url set in your Panda config. Usually we’ll called the action done:

def done
  @video = Video.find_by_panda_id(params[:id])
  render :layout => false
end

Then we’ll thank the user for their upload.

<p>Video uploaded! It will appear on the <%= link_to 'video page', video_path(@video), :target => "_parent"  %> once it has finished encoding.</p>

The video will now be put in the queued to be encoded.

Status callbacks

Once the user’s video has been encoded a notification will be sent to your application. The location of which is defined in the state_update_url setting of your Panda config.

When running your application on localhost, you won’t receive the callbacks, so you will need to get the video’s url and resolution every time you wish to view it. See the code for the show action in the next section for more information.

In the action which receives the notification (the status_update action in this case), you need to retrieve the details for that video from Panda using the find method. We’ll define a method in the Video model called update_panda_status which will process the details fetched from Panda:

def update_panda_status(panda_video)
  # If the video has been encoded, save the url of the standard quality flash video which users will watch
  if encoding = panda_video.find_encoding(PANDA_ENCODING)
    if encoding.status == 'success'
      self.video_url = encoding.filename
      self.width = encoding.width
      self.height = encoding.height
      self.save
    end
  end
end

If the video has been encoded and there is a successfully encoded standard quality flash version of the video, the url is saved in the database. For more information about video statuses and callbacks see the Documentation page.

In the status action which the callback hits, we call the update_panda_status method:

def status
  @video = Video.find_by_panda_id(params[:id])
  @panda_video = Panda::Video.new_with_attrs(YAML.load(params[:video])[:video])
  @video.update_panda_status(@panda_video)
end

Displaying the video

Once your application has received the notification that the video has been encoded, it can be viewed by users using the default Panda Flash player or your own custom player. The default player is based on the freely available JW FLV Media Player player.

For example, the show action of your VideosController may look like this:

def show
  @video = Video.find(params[:id])
  @panda_video = Panda::Video.find(@video.panda_id)
  @video.update_panda_status(@panda_video) if RAILS_ENV == "development"
end

The show view would then contain the embed code to display the video (using the video_url attribute we saved earlier). To keep the view clean, we can create a method in the model to build the embed html code:

def embed_html
  %(<embed src="http://#{VIDEOS_DOMAIN}/player.swf" width="#{self.width}" height="#{self.height}" allowfullscreen="true" allowscriptaccess="always" flashvars="&displayheight=#{self.height}&file=#{self.url}&image=#{self.screenshot_url}&width=#{self.width}&height=#{self.height}" />)
end

Before the embed_html will work we need to define the screenshot_url and url method in the Video class.

def url
  "http://#{VIDEOS_DOMAIN}/#{self.filename}"
end

def screenshot_url
  "#{self.url}.jpg"
end

Now we can use the embed_html method in the show.html.erb view:

<h2><%= @video.title %></h2>
<%= @video.embed_html %>