カレーの恩返し

おいしいのでオススメ。

Podcast配信機能をRailsに組み込んでみた

これは呉高専エンジニア勉強会 Advent Calendar 2017の9日目の記事です。

高専OB1年目の@euglena1215です。
今年の12月はすごい勢いで記事書いてます。

とあるラジオのサイトでPodcastを配信したくなったのでどう実現するべきなのかを考えた過程を書いていこうと思います。

コードだけ見たい人はこちら

podcastを配信する仕組みについて

AppleのQ&Aから引用させてもらうと

Can I host my podcast on Apple Podcasts?

No, Apple Podcasts does not host podcasts. You must host media files and RSS feeds on your own web servers or use a third-party host. You can then submit your podcast feed for inclusion in the iTunes Store podcast directory.

What is a podcast feed?

A podcast feed is a URL to an RSS (rich site summary) in XML format. RSS is a technology for announcing updates to a website, and XML is a format for creating content on the Internet.

To publish a podcast on Apple Podcasts, use an Apple Podcast Hosting Partner or create a website that generates a feed conforming to RSS 2.0. When you update the site that hosts your podcast, the RSS feed notifies Apple Podcasts that a new episode is available.
iTunes Connect Resources and Help

とのことです。

ざっくり説明すると Appleではホスティングはしてないよ、何らかの方法でRSSを公開してURLを登録してくれればPodcastとして登録してあげるよ。ってことですね。

ふわっと理解するにはこのページが分かりやすいです。
haruthanatos.wixsite.com

ちなみにTech系Podcastとして有名なrebuildはRSSはこんな感じです。
http://feeds.rebuild.fm/rebuildfm

調査してみる

まずPodcastを配信する方法はなにがあるのかを調べたところ以下を発見しました。

Wordpressプラグイン

wordpressでぽちぽちすればPodcast配信機能を追加できる。

参考:Podcast番組の作り方・配信方法【WordPressで30分でラジオを始める方法】 – ヒーローズジャーニー

Octopod

Octopod is a set of Jekyll templates, helpers and extensions to deliver your podcasts the cool text file lover's way.
https://github.com/pattex/octopod

設定ファイルをYAMLで書くだけでpodcastRSSと良い感じのpodcastのページが表示してくれるwebサーバを立てられる

rebuild.fm のrssには <generator>Jekyll</generator> と書かれてあるのでこれを利用しているっぽい?

dropcaster

With Dropcaster, you simply put the mp3 files into the Public folder of your Dropbox. Then run the Dropcaster script that generates the feed, writing it to a file in your Dropbox, e.g. index.rss. All mp3 files in the Public folder of your Dropbox are already accessible via HTTP, and so will the RSS file. You can then take the RSS file's URL and publish it (again, this is because any file in the Public folder of a Dropbox automatically gets a public, HTTP-accessible URL).
https://github.com/nerab/dropcaster

dropboxにmp3ファイルをアップロードして設定ファイルを作るとrssを自動生成してくれるGem 生成されたrssレンタルサーバーやS3にアップロードすればpodcastをすぐ配信できるようになる

rss(Ruby標準ライブラリ)

生成されるRSSpodcast配信に必要なnamespaceに対応していたので頑張れば書けそう

選択してみる

今回の要件としては

  1. podcastが配信できる
  2. podcast配信以外にも他の機能も組み込みやすい
  3. なるべく簡単に作れる
  4. Rails wayに外れすぎない

wordpressとoctopodはアプリケーションとして完結してしまっていて機能を組み込む余地が少ないのでパス、
dropcasterでdropboxを使わずにRSSを生成することも考えましたがコード読むより作った方が早そうだったのでRuby rssを使って実装することにしました。

設計してみる

擬似コードでやりたいことを表現したらこんな感じです。
日本語で説明するのは苦手なのでやりたいことを察してほしい。。。

# routes.rb
Rails.application.routes.draw do
  # /podcast にGETでアクセスすると
  # PodcastControllerのindexメソッドの結果を返す
  get "/podcast", to: "podcast#index"
end

# podcast_controller.rb
class PodcastController < ApplicationController
  def index
    rss = if RSSがキャッシュに存在する
            cached_rss
         else
            radios = Radio.published # 公開されているラジオ全件取得
            Podcast.Feed.new(radios).generate # podcast用RSSを文字列として出力
         end
    render xml: rss # xml形式のデータとしてrssを出力
  end
end

# radio.rb
class Radio < ApplicationRecord
  # レコードが追加/更新/削除された後実行される
  after_save :update_rss_cache

  def update_rss_cache
    # キャッシュを更新
    cached_rss = Podcast.Feed.new(radios).generate
  end
end

RSSを文字列として出力できるモジュールを実装してしまえばpodcastを配信できるイメージが湧いたので実装に移っていきます。

実装してみる

rssのドキュメントとrebuildのrssをにらめっこします。
そしてできたのが feed.rb, config.rb です。

Configにプロジェクトに依存しているデータを集め、FeedではRSSを生成する処理に専念できるようにしてます。

initializeの引数に取っているradioのschemaはこんな感じです。

# == Schema Information
#
# Table name: radios
#
#  id                     :integer          not null, primary key
#  title                  :string(255)      not null
#  description            :text(65535)      not null
#  mp3(ファイルパス)        :string(255)      not null
#  created_at             :datetime         not null
#  updated_at             :datetime         not null
#  published_at           :datetime
#  duration(再生時間[sec]) :integer          not null 
#  size(バイト数)          :integer          not null
#

複雑なことはしてないのでなんとなく処理の流れはつかめると思います。

PRはこんな感じになりました。
https://github.com/kure-kosen/cho_kure_web/pull/26

テストしてみる

いつか絶対にテストを書くという強い意志は持って日々を過ごしています。

まとめ

少しの実装でPodcast配信機能を実装することができました。
テストはきちんと書きましょう。