Coub API Tutorial
Recently we released the Coub API. Now you can make apps to watch Coub timelines, like, recoub and literally do anything that you can do on the site. But the most valuable feature is that you can make coubs via API.
I’ll show you how to make a simple Coub client on Ruby on Rails.
This application will allow you to login via Coub and generate a similar coub with text of your choice.
You can find the working example of this app here: http://fantozzi.dev2.workisfun.ru, and the source code of this tutorial is on GitHub: https://github.com/igorgladkoborodov/memegenerator
OAuth
Coub uses the standard authorisation protocol OAuth 2.0. It is used in most of online services that provide external API (Facebook, for example). It has a lot of documentation and libraries for most development platforms.
In general the authorisation works this way:
- The Application with it’s special key goes to a specific page on coub.com where the user is asked to grant permissions to act on Coub on behalf of their account.
- If the user agrees, Coub returns them to the app and provides it with a special token, which is used on all API requests by this user.
Most likely you have already seen this when you login to some service via Facebook or Twitter.
We’ll write the RoR app, which already has the gem for OAuth. And we’ll use the omniauth-oauth2 gem and Coub’s official omniauth-coub gem.
Creating the app and authorisation
- First we’ll create a new app with the self-explanatory name “memegenerator” and attach it to Pow (or any other RoR server you use for development):
$ cd ~/apps/
$ rails new memegenerator
$ ln -s ~/apps/memegenerator/ ~/.pow/memegenerator
Then we check the URL http://memegenerator.dev in our browser to ensure that we have an empty Rails app there.
2. Next we need to register our new app on Coub: http://coub.com/dev/applications/

We type our app URL in the Website field; in Callback URL we put http://memegenerator.dev/auth/coub/callback
After the creation of the app, Coub will provide us with an Application ID and Secret, which we’ll need later:

3. Install omniauth-coub gem:
Gemfile:
gem "omniauth-coub"
$ bundle install
4. Add Coub omniauth provider:
config/initializers/omniauth.rb:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :coub, ENV["COUB_KEY"], ENV["COUB_SECRET"], scope: "logged_in,create,like"
end
COUB_KEY and COUB_SECRET are the Application ID and Secret from the previous step. You can add them to ENV variables or just type them in an omniauth.rb file, but for security reasons it is not recommended to leave keys here.
If you use Pow, you can add these lines on .powenv file:
.powenv:
export COUB_KEY="[Application ID]"
export COUB_SECRET="[Secret]"
In the scope param you can define what permissions for your app you are going to be asked for. Our test app will only need the permissions to login, create coubs and like, so we only set: “logged_in,create,like”. The full list of access modes you can be found in the API documentation.
5. Create the User model with the from_omniauth method, which will find or create users in database by the auth data that server has passed to us.
This and the few next steps have detailed explanations in RailsCasts OnmiAuth episode.
$ rails g model user provider:string uid:string auth_token:string name:string
$ rake db:migrate
app/models/user.rb:
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
user.auth_token = auth.credentials.token
user.name = auth.extra.raw_info.name
user.save!
end
end
end
6. Create Sessions controller. We are going to create and destroy a user session through it.
Method create is the place where we return from Coub the auth server with a token and other auth data. Here we’re creating (or finding) a user via from_omniauth method and storing a user token to a cookie to return this session on browser reload.
$ rails g controller sessions
app/controllers/sessions_controller.rb:
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
cookies.permanent[:auth_token] = user.auth_token
redirect_to root_url
end
def destroy
cookies.delete(:auth_token)
redirect_to root_url
end
def index
end
end
The startpage of the app will stay here for a while, so we use index method here.
7. Add method current_user to our ApplicationController to have access to our logged in user.
app/controllers/application_controller.rb:
helper_method :current_user
def current_user
@current_user ||= User.find_by_auth_token(cookies[:auth_token]) if cookies[:auth_token]
end
8. Add the login link on the startpage or show the current user name if we are already logged in.
app/views/sessions/index.html.erb:
<% if current_user %>
<%= current_user.name %>
<%= link_to "Log out", logout_path, method: :delete %>
<% else %>
<%= link_to "Auth with Coub", "/auth/coub" %>
<% end %>
The /auth/coub path will be handled with the omniauth-oauth2 gem; it will redirect us to the Coub authorisation page.
9. Add routes:
config/routes.rb:
Rails.application.routes.draw do
root "sessions#index"
get "/auth/:provider/callback" => "sessions#create"
delete "logout" => "sessions#destroy"
end
Now we can open http://memegenerator.dev and check authorisation. It should look like this:
All good. Now we have a user with a token that can make API calls to Coub.
API calls
Having a token we can make API calls to Coub. These are simple HTTP requests, like in a browser. GET calls you can test directly in browser. Each call has its own parameters and it should contain access_token field that we have got during authorisation.
For example, to like a coub you should make this request:
POST http://coub.com/api/v2/likes?id=[coub_id]&channel_id=[channel_id]&access_token=[users_token]
Some API calls you can make even without access_token. Coub info, for example (you can only get public coubs info without a token). This is a simple GET request, you can just open this link in browser:
GET http://coub.com/api/v2/coubs/4yp6r
All these API requests are well documented here: http://coub.com/dev/docs/Coub+API/Overview
Client
It’s not very convenient to make each requests from scratch, so we’re going to make a client method for User model that could be used for making API calls:
app/models/user.rb:
def client
@client ||= Faraday.new(:url => "http://coub.com/api/v2/", :params => {:access_token => auth_token})
end
Gemfile:
gem "faraday"
$ bundle install
We’ll use Faraday, a Ruby HTTP client.
Let’s start a console and play with the API:
$ rails c
> user = User.first
> user.client.get "coubs/4yp6r"
Responses from server are in JSON format, so if we want to read server response, we should parse it by standard JSON library:
> coub_info = JSON.parse(user.client.get("coubs/4yp6r").body)
> coub_info["id"]
=> 9090841Great! We have a coub ID, let’s like it:
> user.client.post "likes?id=#{coub_info["id"]}"Generating video
We have a sample video, a scene from a movie, now we need to add a text to it. We’ll use use ffmpeg, a terminal app that lets you do literally anything with video.
You can install it on Mac via Homebrew, we need it with freetytpe flag to work with text:
$ brew install ffmpeg --with-freetype
Adding text via ffmpeg looks like this:
$ ffmpeg -i template.mp4 -vf "drawtext=enable='between(t,1,3)':text=Blah:fontfile=PFDinTextCondPro-XBlack.ttf:fontsize=40:fontcolor=white:x=(w-tw)/2:y=(h*0.9-th)" output.mp4
This string means:
- ffmpeg -i template.mp4 -vf: take video from template.mp4 file. You can download this template file here.
2. drawtext=enable: add text
3. between(t,1,3): from the first to the third second
4. fontfile=PFDinTextCondPro-XBlack.ttf: use this TTF font file
5. text=Blah: write Blah text
6. fontsize=40: font size 40
7. fontcolor=white: white color
8. x=(w-tw)/2:y=(h*0.9-th): the text position is in the bottom center (w and h are the video width and height, tw and th are text block width and height)
9. output.mp4: write the result to this file
To add three pieces of text, we can use the drawtext three times divided by comma:
$ ffmpeg -i template.mp4 -vf "drawtext=enable='between(t,1,2)':text=Text1:fontfile=PFDinTextCondPro-XBlack.ttf:fontsize=40:fontcolor=white:x=(w-tw)/2:y=(h*0.9-th), drawtext=enable='between(t,3,5)':text=Text2:fontfile=PFDinTextCondPro-XBlack.ttf:fontsize=40:fontcolor=white:x=(w-tw)/2:y=(h*0.9-th), drawtext=enable='between(t,6.3,8)':text=Text3:fontfile=PFDinTextCondPro-XBlack.ttf:fontsize=40:fontcolor=white:x=(w-tw)/2:y=(h*0.9-th)" output.mp4
Place the template.mp4 and font file (any TTF file from your fonts directory) to the tmp directory and try this on command line.
Uploading a video
Ok, we have the video, now we need to make a coub from it.
Uploading video via Coub API can be done in three steps:
- Initialise upload by the request POST coubs/init_upload, and we receive the coub id and permalink in the response.
$ rails c
> user = User.first
> init_response = JSON.parse(user.client.post("coubs/init_upload").body)
> coub_id = init_response["id"]
> permalink = init_response["id"]
2. Upload video by the request POST coubs/:id/upload_video. The file is transfered in the request body. You should also add Content-Type to the headers, in our case it is video/mp4:
> user.client.post do |r|
r.url "coubs/#{coub_id}/upload_video"
r.headers["Content-Type"] = "video/mp4"
r.body = File.open("tmp/output.mp4", "r").read
end
If you need to add a soundtrack to the coub, you can do it by the additional request coubs/:id/upload_audio. We don’t need it now, so we’ll skip this step.
3. Finalize the coub creation by the request POST coubs/:id/finalize_upload, we should send title, privacy settings, tags and sound settings in this request:
> user.client.post "coubs/#{coub_id}/finalize_upload", title: "Test coub", original_visibility_type: "private", tags: "tag1, tag2, tag3", sound_enabled: trueAfter finalizing, the coub will be processed for a few minutes: video will be encoded in different versions for different platforms, it will generate images, previews and do a lot of other resource-greedy processes. You can check the progress of this with the request GET coubs/:id/finalize_status. It will return something like this: { percent_done: 20, done: false}.
> user.client.get "coubs/#{coub_id}/finalize_status"Ok, we have tested everything in the console, now we need to bind it to the application.
Coub model
- Let’s create a coub model:
$ rails g model coub user:belongs_to title:string visibility_type:string tags:string permalink:string coub_id:string text1:string text2:string text3:string
$ rake db:migrate
2. The generate_video_file method will generate video from the template and the text in the fields text1, text2, text3. we’ll place the Video template and font in the assets directory. The resulting video will be placed in the tmp folder.
app/models/coub.rb:
def escape_ffmpeg_text(text)
text.to_s.gsub("'", "\\\\\\\\\\\\\\\\\\\\\\\\'").gsub(":", "\\\\\\\\\\\\\\\\:").mb_chars.upcase # Crazy ffmpeg escaping
end
def ffmpeg_drawtext(text, from, to)
font_file = File.join(Rails.root, "app", "assets", "fonts", "PFDinTextCondPro-XBlack.ttf")
"drawtext=enable='between(t,#{from},#{to})':text=#{escape_ffmpeg_text(text)}:fontfile=#{font_file}:fontsize=40:fontcolor=white:x=(w-tw)/2:y=(h*0.9-th)"
end
def generate_video_file
self.video_file = File.join(Rails.root, "tmp", "output-#{Time.now.to_i}.mp4")
template_file = File.join(Rails.root, "app", "assets", "videos", "template.mp4")
`ffmpeg -i #{template_file} -vf \"#{ffmpeg_drawtext(text1, 1, 2)}, #{ffmpeg_drawtext(text2, 3, 5)}, #{ffmpeg_drawtext(text3, 6.3, 8)}\" #{video_file}`
return video_file
end
3. Add the upload_video method, which will upload the video file to Coub in three steps:
app/models/coub.rb:
def upload_video
self.title ||= text2
self.visibility_type ||= "private"
self.tags ||= ""
init_response = JSON.parse(client.post("coubs/init_upload").body)
self.coub_id = init_response["id"]
self.permalink = init_response["permalink"]
save
client.post do |r|
r.url "coubs/#{coub_id}/upload_video"
r.headers["Content-Type"] = "video/mp4"
r.body = File.open(video_file, "r").read
end
client.post "coubs/#{coub_id}/finalize_upload",
title: title,
original_visibility_type: visibility_type,
tags: tags,
sound_enabled: true
enddef generate_and_upload_video
generate_video_file
upload_video
end
4. Connect Coub and User models and add the client proxy method for Coub:
app/models/coub.rb:
belongs_to :user
def client
@client ||= user.client
end
app/models/user.rb:
has_many :coubs
5. Method url will return the coub’s URL using its permalink:
app/models/coub.rb:
def url
"http://coub.com/view/#{permalink}"
end
Let’s test to make sure everything’s working:
$ rails c
> coub = Coub.new
> coub.user = User.first
> coub.text1 = 'Text 1'
> coub.text2 = 'Text 2'
> coub.text3 = 'Text 3'
> coub.tags = 'tag1, tag2, tag3'
> coub.visibility_type = 'unlisted'
> coub.generate_and_upload_video
> coub.url
=> "http://coub.com/view/5dbyc"
You can open this URL and see the blue screen with the message “Your coub is being processed”.
Now we need to make a controller for this:
1. Create Coubs controller. It will have only two methods: index (the new startpage instead of sessions#index) and create. After the coub creation we will automatically redirect to its url.
$ rails g controller coubs
app/controllers/coubs_controller.rb:
class CoubsController < ApplicationController
def index
end
def create
@coub = current_user.coubs.create(coub_params)
@coub.generate_and_upload_video
redirect_to @coub.url
end
private
def coub_params
params.require(:coub).permit(:text1, :text2, :text3, :visibility_type, :tags)
end
end
2. Move index.html.erb from sessions to coubs and add form to it:
app/views/coubs/index.html.erb:
<% if current_user %>
<p>You’re logged in as <%= current_user.name %>. <%= link_to "Log out", logout_path, method: :delete %></p>
<%= form_for Coub.new, url: {action: "create"} do |f| %>
<%= f.text_field :text1, placeholder: "To me", maxlength: 30, size: 50 %><br />
<%= f.text_field :text2, placeholder: "Your Coub API", maxlength: 30, size: 50 %><br />
<%= f.text_field :text3, placeholder: "Is a piece of shit", maxlength: 30, size: 50 %><br />
<%= f.select :visibility_type, options_for_select(["public", "friends", "unlisted", "private"]) %><br />
<%= f.text_field :tags, placeholder: "first tag, second tag" %><br />
<%= f.submit "Create Coub" %>
<% end %>
<% else %>
<p>Please <a href="/auth/coub">log in via Coub</a>.</p>
<% end %>
Now we can check everything in our browser:
This tutorial covers only the base principles of the Coub API. This code is not for production; you need a lot more for a real application: validations, error handling, working with the server responses, tests, design interface, and a whole lot more. But this is a good start to writing an app for Coub.
The working demo of this app you can find here: http://fantozzi.dev2.workisfun.ru, it is almost the same, but video generating and uploading was made asynchronously by delayed_job.
You can find the source code of this app on GitHub: https://github.com/igorgladkoborodov/memegenerator/
If you have any questions or feedback on this tutorial or Coub API, please email me at igor@coub.com.