Туторіал по Coub API

Днями ми випустили Coub API. Тепер можна робити програми, дивитися стрічку, лайкати, рекобить, тобто практично все, що можна зробити на сайті, можна робити через API. Але найголовніше — тепер можна з сторонніх додатків через API створювати коби.

У цьому туториале я покажу, як можна зробити найпростіший клієнт коба на Ruby on Rails. Додаток дозволяє залогуватися через ксб та згенерувати такий коб з будь-яким текстом:



Робоча версія цього додатка лежить за адресою fantozzi.dev2.workisfun.uk, код додатки з цього туториала можна подивитися на Гітхабі: github.com/igorgladkoborodov/memegenerator

OAuth
Коб використовує стандартний протокол для авторизації OAuth 2.0. Він використовується в багатьох сервісах, які надають зовнішній API (Фейсбук, наприклад), по ньому дуже багато документації та бібліотек для будь-якої платформи.

Працює авторизація приблизно так: додаток зі своїм унікальним ключем заходить на спеціальну сторінку на coub.com там Коб питає, чи згоден користувач дати додатком доступ. Якщо дозволити, то Коб повертає користувача назад в додаток, і віддає разом із запитом токен користувача, який потім використовується при всіх API-запитах користувача. Те ж саме відбувається, наприклад, при авторизації через Фейсбук або Твіттер.

Ми будемо писати на RoR і для авторизації через OAuth для рейок все вже давно написано, ми будемо використовувати для цього гем omniauth-oauth2 і офіційний кобовский гем omniauth-coub.

Створення програми та авторизація
Створюємо додаток з промовистою назвою memegenerator і прикручуємо його до Pow (або хто чим користується):

$ cd ~/apps/
$ rails new memegenerator
$ ln-s ~/apps/memegenerator/ ~/.pow/memegenerator

Перевіряємо в браузері, що у нас за адресою memegenerator.dev живе порожнє рейковий додаток.

2. Реєструємо наше новий додаток за адресою coub.com/dev/applications/



У полі Website вказуємо урл нашого тестового додатку, в полі Callback URL пишемо memegenerator.dev/auth/coub/callback

Після створення програми Коб дасть нам Application ID Secret, вони нам знадобляться далі:



3. Встановлюємо гем omniauth-coub:

Gemfile:
gem "omniauth-coub"

$ bundle install

4. Додаємо коб у провайдери omniauth:

config/initializers/omniauth.rb:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :coub, ENV["COUB_KEY"], ENV["COUB_SECRET"], scope: "logged_in,create"
end

COUB_KEY і COUB_SECRET — це Application ID Secret з минулого кроку, можна додати їх у ENV змінні або поки для тіста вставити рядки прямо тут, хоча залишати в коді ключі небажано, ну ви розумієте.

Якщо у вас Pow, то додати змінні можна у файлі .powenv докорінно додатки:

.powenv:
export COUB_KEY="[Application ID]"
export COUB_SECRET="[Secret]"

У scope ви можете вказати, якими правами буде мати додаток. Наше додаток потрібно тільки для створення кобов, тому зайвого ми нічого не будемо просити, тільки дозвіл на авторизацію і створення коба: logged_in, create. Повний список режимів доступу можна подивитися в документації до API.

5. Створюємо модель користувача з методом from_omniauth, який створює або знаходить в базі користувача за даними, які передав нам сервер авторизації на Кобе.

Що відбувається в цьому пункті та в парі наступних пунктів, добре пояснено в одному з епізодів RailsCasts.

$ 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 = аутентифікації.credentials.token
user.name = аутентифікації.extra.raw_info.name
user.save!
end
end
end

6. Створюємо контролер сесій. Через нього ми створюємо і видаляємо сесію.

Метод create — це те місце, куди повертається користувач з його токеном і даними авторизації. Тут ми створюємо користувача через метод from_omniauth, який ми написали в попередньому кроці, і зберігаємо його токен, записуємо його в куку, щоб при перезавантаженні браузера можна було повернути цю сесію.

$ 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

Морда додатки хай поки поживе в контролері сесій, тому тут метод index.

7. Щоб мати доступ до поточного користувача, додаємо в ApplicationController метод current_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. Виводимо на морді посилання на логін або показуємо поточного користувача з посиланням на вихід.

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 %>

По шляху /auth/coub гем omniauth-oauth2 перекине на сторінку авторизації на coub.com.

9. Прописуємо роуты:

config/routes.rb:
Rails.application.routes.draw do
root "sessions#index"
get "/auth/:provider/callback" => "sessions#create"
delete "logout" => "sessions#destroy"
end

З авторизацією все. Заходимо на memegenerator.dev, перевіряємо. Має виглядати ось так:



Тепер у нас в базі користувач з токеном, який може робити запити через Coub API.

Запити до API
Маючи токен можна робити запити до API. Це звичайні запити по протоколу HTTP, як в браузері. GET запити можна в браузері ж і тестувати. Кожен запит крім своїх параметрів повинен містити параметр access_token з токеном користувача, який нам до цього видав сервер авторизації.

Наприклад, щоб лайкнути коб, треба виконати приблизно такий запит:
POST http://coub.com/api/v2/likes?id=[coub_id]&channel_id=[channel_id]&access_token=[users_token]

Деякі запити можна робити і без сертифіката, наприклад, інфа про кобе (без сертифіката доступні лише публічні коби). Це GET запит, посилання можна просто відкрити в браузері:
GET http://coub.com/api/v2/coubs/4yp6r

Всі ці запити добре задокументовані, повний список можна подивитися тут: coub.com/dev/docs/Coub+API/Overview

Клієнт
Кожен раз вручну робити запити і додавати токен незручно, тому додамо моделі User метод client, через який будемо робити запити:

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

Запити ми робимо через Faraday, це HTTP клієнт на Ruby.

Запустимо консоль, потестим запити до апі:

$ rails c
> user = User.first
> user.client.get "coubs/4yp6r"

Відповіді надаються у форматі JSON, тому, якщо нам хочеться прочитати, що повернув сервер, треба відповідь розпарсити стандартної JSON бібліотекою:

> coub_info = JSON.parse(user.client.get("coubs/4yp6r").body)
> coub_info["id"]
=> 9090841

У нас є айдишник коба, давайте його лайкнем:

> user.client.post "likes?id=#{coub_info["id"]}"


Генера відео
У нас є відео, кадр з фільму, нам треба на нього три рази покласти текст в різний час. Для роботи з відео через консоль є програма ffmpeg, в ній через консоль можна робити з відео практично що завгодно.

На маці через Homebrew він ставиться ось так:

$ brew install ffmpeg --with-freetype

Накладаємо текст через ffmpeg фільтром drawtext:

$ ffmpeg-i template.mp4-vf "drawtext=enable='between(t,1,2)':text=Blah:fontfile=PFDinTextCondPro-XBlack.ttf:fontsize=40:fontcolor=white:x=(w-tw)/2:y=(h*0.9-th)" output.mp4


Цей рядок означає:

1. ffmpeg-i template.mp4-vf: беремо відео файлу template.mp4. Файл для цього туториала можна завантажити ось тут.

2. drawtext=enable: накладаємо текст

3. between(t,1,2): з першої по другу секунду

4. fontfile=PFDinTextCondPro-XBlack.ttf: використовуємо файл з шрифтом у форматі TTF

5. text=Blah: пишемо текст Blah

6. fontsize=40: розмір шрифту

7. fontcolor=white: білого кольору

8. x=(w-tw)/2:y=(h*0.9-th): положення тексту в центрі внизу (w і h — це розмір відео, tw і th — це розмір блоку з текстом)

9. output.mp4: записуємо всі в цей файл

Щоб написати три тексту, треба просто через кому три drawtext написати:

$ 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

В темпової папці можна скласти файл template.mp4 файл шрифту (можна взяти будь-TTF з папки зі шрифтами) і спробувати в консолі запустити, воно має згенерувати правильне відео.

Закачуємо відео
Відео у нас є, тепер треба з нього зробити коб.

Коб через API закачується в три етапи:

1. Спочатку ініціалізуємо закачування запитом POST coubs/init_upload, у відповіді ми отримуємо id коба і його permalink.
$ 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. Закачуємо відео запитом POST coubs/:id/upload_video. Файл передається в тілі запиту, в хедері Content-Type треба передати 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

Якщо ми хочемо завантажити окремий саундтрек до кобу, то це можна зробити окремим запитом coubs/:id/upload_audio. Нам в цей раз цього не потрібно, тому ми опускаємо цей запит.

3. Финализируем створення коба запитом POST coubs/:id/finalize_upload, в параметрах передаємо тайтл, налаштування приватності, теги, включений звук.

> user.client.post "coubs/#{coub_id}/finalize_upload", title: "Test coub", original_visibility_type: "private", tags: "tag1, tag2, tag3", sound_enabled: true

Після закачування коба, він буде якийсь час процесситься: відео на серверах Коба буде конвертуватися в декілька форматів для різних платформ, будуть генериться прев'юшки і купа всяких таких ресурсномістких речей. Прогрес конвертування можна перевірити GET запитом coubs/:id/finalize_status. Він віддає приблизно такий JSON { percent_done: 20, done: false}.

> user.client.get "coubs/#{coub_id}/finalize_status"

Ок. Ми протестували це в консолі, тепер все це треба зібрати в додаток.

Модель Coub
Створюємо модель Coub:

$ 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. Робимо метод generate_video_file, який геренит відео з відео-шаблону і трьох текстів, що знаходяться в полях text1, text2, text3. Шаблон відео і шрифт кладемо в ассеты. Готове відео кладемо в папку tmp.

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. Робимо метод, який в три етапи закачує відео на Коб:

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
end
def generate_and_upload_video
generate_video_file
upload_video
end

4. Прописуємо, що коб належить користувачу і заодно пишемо метод client для зручного доступу до клієнта через користувача:

app/models/coub.rb:
belongs_to :user
def client
@client ||= user.client
end

5. Метод url віддає урл коба за пермалинку:

app/models/coub.rb:
def url
"http://coub.com/view/#{permalink}"
end

Перевіримо, чи все працює:

$ 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"

З цього урлу можна зайти і подивитися на синій екран «Your coub is being processed».

Залишилося зробити для всього цього контролер:

1. Створюємо контролер coubs. Він буде складатися з двох методів: index (це буде нова морда, замість sessions#index) і create. При створенні коба ми відразу редиректим на нього.

$ rails g controller coubs

app/controllers/coubs_controller.rb:
class CoubsController < ApplicationController
def index
end
def create
@coub = Coub.create(coub_params.merge(:user => current_user))
@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. Перетягуємо index.html.erb з sessions в coubs і прикручуємо туди форму:

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 %>

Все, тепер заходимо в браузер, і перевіряємо, що все працює:

У цьому туториале я описав тільки принцип роботи API. Зрозуміло, для цього додатка потрібно ще багато чого дописати: валідації, перевірки відповідей API, обробки помилок, інтерфейс намалювати, тести. Трохи допиленная версія цього скрипта лежить ось тут http://fantozzi.dev2.workisfun.uk, там все те ж саме, але відео готується і закачується асинхронно через delayed_job.

Исходники цього туториала лежать в гітхабі: https://github.com/igorgladkoborodov/memegenerator/.

Якщо є якісь питання по цьому туториалу або по API, пишіть на igor@coub.com.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.