とあるエロサイトの改修

エロサイトをつくったことで著名な僕ですが、その中でも動かし続けている「とある18禁のエロサイト」があります。国内外の「ほぼアダルト専用」動画共有サイトの更新を反映し、AV女優やタグ別に自動的に分類されることが特徴です。それにより閲覧者は様々な性癖に基づいた映像を更新順に閲覧することが出来ます。

YourAVHost

さてこのエロサイト。自動で動画リンクを収集するので長らく放置していましたが、ちょいと最近テコ入れしました。ってことで、何をしたかをザックリ紹介します。

デッドリンク/ビデオのチェック

共有サイト上の動画をアグリゲートしているのですが、その中で、サイトに掲載したはいいが「後ほど消された」リンクが存在します。すると動画が見れないばかりかサムネイルまでが表示されなくて残念なことになります。そこで定期的にデッドリンクもしくはデッドビデオを検出し本サイト上では表示させない仕組みを導入しました。動画が消えていることを判定するロジックは以下です。

  • 動画に紐づくサムネイルの画像のレスポンスコードが400以上である
  • もしくは画像のContent-Typeが image という文字列に前方一致しない

共有サイトで配信しているパーマリンクのページの状態で管理する方法もありますが、こちらの方が正確に出ます。

Twitterを情報ソースに

今まで日本内でウケるエロサイトのリンクを収集するために Blog検索 を情報ソース取得の手段にしていましたが、Twiterのツイートをその対象にすることでターゲットを広げました。

  • エロにまつわる検索キーワードでTwitter検索
  • その際、日本語のツイートを指定
  • ツイートに含まれる Blog というか エログ にアクセス
  • そこに動画共有サイトへのリンクがあればそれを解析
  • ツイートと記事の情報を基にメタデータを作成

という具合です。

DMMフィードから新規女優を取得

今までAV女優の辞書としてWikipediaの「AV女優」という項目を参考としていましたが、アップデートが大変なので、もう一つ違う辞書生成の方法を採択。DMMのエロ動画の新着を配信するRSSフィードから女優名をピックアップし、データベースへ格納。クロールする時の辞書に利用するという手順です。これで新人女優までもカバー出来るでしょう。

embedタグの見直し

動画共有サイトではembedタグが発行されていて、自サイトへ映像を取り込むことが出来ます。そのembedタグを最新のものにしました。iframeを採用しているところが多く、これにより、iOSなどモバイル環境でも動画再生が可能になりました。未対応なのですがモバイルに特化したビューをつくることも検討しています。


情報をどうインターネット上から取得し、アグリゲートし、表現するかを考えるこの作業はとても楽しいです。まだ効果はハッキリと出ていませんが、他にやりたいこともあるのでちょこちょこ改修していきたいと思います。

このエントリーをはてなブックマークに追加 39 リアクション

Perlによる速習プランもしくは「Webアプリエンジニア養成読本」番外編

先日共著で出版し、本日出版記念のイベントが行われる「Webアプリエンジニア養成読本」。基礎知識から運用まで一気通貫でWebアプリ周辺の教本を目指してアレンジしました。

Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)
和田 裕介 石田 絢一 (uzulla) すがわら まさのり 斎藤 祐一郎
技術評論社
売り上げランキング: 285


目玉となるアプリケーションのプログラミングを行う第2章では、諸々の関係上、PHPとRubyのみの実装を扱っています。執筆時期の最後の方で「2ページ空きが出来たからなんか書いて!」と言われ悪あがき的にPerlの紹介を書きましたがそれではさすがにページ数が足りません。そこで今回は「PHP/Rubyによる速習プラン」に追加する形で「Perlによる速習プラン」をお届けいたします。

とは言ってもPerlとはなんぞや?から入ると書くのが大変ですので、その辺りは「初めてのPerl」や「続・初めてのPerl」などで補ってください。今回は最も肝心なところの例として、Ruby編のサンプルアプリをつくる件、をPerlで実装してみたコードを紹介します。該当部分の作者であるすがさんによると…

はてなブックマークには及びませんが、素敵なWebアプリケーションを作成してきましょう。

ということでShioriというブックマークWebアプリをつくります。

投稿フォーム

既にあがっているコードはこちら。


仕様

分かりやすいようにすがさんの書いた箇所を今回つくるアプリなりにアレンジしつつ列挙します。まず、ユースケースは以下の2つ。

  • URLを登録出来る
  • 登録したURLの一覧を参照出来る

次にデータベースの構造はbookmarkテーブルひとつとなります。MySQL/InnoDBでの開発運用を見据えて、SQL文で書くとこんな具合になりました。

CREATE TABLE bookmark (
    id INT UNSIGNED AUTO_INCREMENT,
    url TEXT NOT NULL,
    created_at DATETIME NOT NULL,
    updated_at DATETIME NOT NULL,
    PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET 'utf8' engine=InnoDB;

URLのエンドポイントは以下の3つです。

  • GET / => 登録されたURLの一覧が見れる
  • GET /new => URLを登録するフォーム画面
  • POST /create => 上記フォームがPOSTする先

Mojoliciousによる実装

Perl編ってことでWAFは「Mojolicious」を使います。Amon2でもやり方はそんな変わらないはずです。

まずMojolicious付属のmojoコマンドで雛形をつくります。

$ mojo generatge app Shiori::Web

ディレクトリとファイルが生成されますが、これはそのまま使わず一部カスタムして利用します。最終的なディレクトリ構造は以下の通りです。完全僕の趣味が出てます。

.
├── etc # SQLのスキーマファイルなど入れる
├── lib
│   └── Shiori
│       ├── DB # これから説明するTeng::*を継承したモジュール群
│       ├── Model # ロジック
│       └── Web # Webに関係するもの
│           └── Controller # コントローラー
├── log # Mojoliciousのログ、レポジトリには入れない
├── script # 自動生成されたスクリプトがあるが使わない
├── t # テストコード
└── templates
    ├── layouts # テンプレートの大枠
    └── root # 個別テンプレート

.psgiの作成

.psgiファイルをつくっておくとPlack::Middleware::*が使えたりして何かと便利なのでつくります。

use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/lib";
use Mojo::Server::PSGI;
use Plack::Builder;
use Shiori::Web;

my $psgi = Mojo::Server::PSGI->new(app => Shiori::Web->new);
my $app = $psgi->to_psgi_app;

builder {
    # ここでPlack::Middleware::*を使う記述をする
    $app;
};

これをshiori_web.psgiという名前に保存してplackupするとよいでしょう。

$ plackup -R templates,lib -p 5000 shiori_web.psgi

設定ファイルのロード

Mojoliciousにも設定ファイルをロードする機能はありますが、これをそのまま使うとMojolicious依存が強すぎちゃうんで独自でつくりましょう。環境変数PLACK_ENVを覗いてその状態によりロードするファイルを分岐させています。Config::PLを使ったコードをlib/Shiori.pmに書いてます。

package Shiori;
use strict;
use warnings;
use Config::PL;

our $VERSION = '0.01';
my $config;

sub _load_config {
    my $mode = $ENV{PLACK_ENV} || 'development';
    my $filename = "config_${mode}.pl";
    return config_do $filename;
}

sub config {
    return $config if $config;
    $config = Shiori->_load_config();
    return $config;
}

1;

この設定ファイルがしっかりロードさせれているか?をテストコード書いて確かめましょう。実際に扱うconfig_development.pl が存在しつつハッシュリファレンスを返せないととテストが通らないという、ちょっとよろしくない実装になってますが、分かりやすいので紹介します。t/config.tがこちら。

use strict;
use Test::More;

BEGIN {
    use_ok('Shiori');
}

subtest 'load_config' => sub {
    $ENV{PLACK_ENV} = 'development';
    my $config = Shiori->config();
    ok $config;
    isa_ok $config, 'HASH';
};

done_testing();

テストコードはこのように通常t/ディレクトリに置いて、prove -lコマンドで実行します。

$ prove -l t/config.t

Tengでデータベースを扱う

O/R MapperにはTengを利用してみましょう。lib/Shiori/DB.pm及びlib/Shiori/DB/Schema.pmを以下のように実装します。

package Shiori::DB;
use parent 'Teng';
__PACKAGE__->load_plugin('Pager'); # 後ほどページャーをつくるのでPluginをロード
1;

Shiori::DB::Schemaは日付を扱うフィールドに対し、更新時DateTime型を与えられる、もしくは参照時にDateTime型で取得可能にするためにinflatedeflateの設定をしています。

package Shiori::DB::Schema;
use Teng::Schema::Declare;
use DateTime::Format::MySQL;

table {
    name 'bookmark';
    pk 'id';
    columns qw/id url created_at updated_at/;
    inflate qr/.+_at/ => sub {
        my $value = shift;
        return DateTime::Format::MySQL->parse_datetime($value);
    };
    deflate qr/.+_at/ => sub {
        my $value = shift;
        return DateTime::Format::MySQL->format_datetime($value);
    };
};

1;

ここまでくれば…

my $db = Shiori::DB->new( connect_info => [ 'dbi:mysql:shiori:localhost', 'root', undef ] );
my $bookmark = $db->single('bookmark', { id => 1 });
print $bookmark->url;

のような操作を行うことでデータベースを操作可能です。

モデルをつくる

上記のShiori::DBを直接コントローラーで処理してもいいのですが、後の拡張性を考えた場合にもう一層モデルをかませた方がいいでしょう。ユースケースを元に

  • create => パラメータを元にブックマークを作成する
  • entries => パラメータを元にブックマーク一覧を返却する

という二つのメソッドを持つモデルにします。この実装ではcreateメソッドで引数の検証を行います。コントローラーでも使うことが出来るFormValidator::Liteを利用してバリデーション。エラーだった場合とDBへのInsertが成功した場合で返却されるハッシュリファレンスの構造を変えることで呼び出し側が判断出来るようにしています。ちなみにこちらもMojolicious依存を避けるためにMojo::Baseは使わずにMouseなクラスにしています。

package Shiori::Model::Bookmark;
use Mouse;
use Shiori;
use Shiori::DB;
use DateTime;
use FormValidator::Lite;

FormValidator::Lite->load_constraints(qw/URL/);

has 'connect_info' => (
    is      => 'ro',
    isa     => 'ArrayRef',
    default => sub {
       return Shiori->config->{connect_info};
    }
);
has 'db' => ( is => 'ro', isa => 'Shiori::DB', lazy_build => 1 );

sub _build_db {
    my $self = shift;
    Shiori::DB->new(
        connect_info => $self->connect_info()
    );
}

sub create {
    my ($self, $args) = @_;
    my $validator = FormValidator::Lite->new($args);
    $validator->load_function_message('en');
    $validator->set_param_message( url => 'URL' );
    my $res = $validator->check(
        url => [qw/NOT_NULL HTTP_URL/],
    );
    if($validator->has_error) {
        my $messages = $validator->get_error_messages();
        return { error => { messages => $messages } };
    }
    my $now = $self->now;
    my $bookmark = $self->db->insert('bookmark', {
        url => $args->{url},
        created_at => $now,
        updated_at => $now
    });
    return { success => { bookmark => $bookmark } };
}

sub entries {
    my ($self, $args) = @_;
    my $limit = $args->{limit} || 10;
    my $page = $args->{page} || 1;
    my ( $entries, $pager ) = $self->db->search_with_pager( 'bookmark', {},
        { 
            page => $page,
            rows => $limit,
            order_by => 'id DESC'
        }
    );
    if(wantarray) {
        return ($entries, $pager);
    }else{
        return $entries;
    }
}

sub now {
    DateTime->now( time_zone => 'Asia/Tokyo' );
}

__PACKAGE__->meta->make_immutable();

さて、こちらもテストしていきましょう。DBを扱うのでローカルなどに立ててるテスト用のサーバーにアクセスさせてもいいですが、その都度追加したレコードを削除しなくてはいけなかったり扱いが面倒です。そこでTest::mysqldを使い専用のテンポラリなMySQLサーバをつくり出しそれを参照させます。

use strict;
use Test::More;
use FindBin;
use lib "$FindBin::Bin/lib";
use DBI;
use SQL::SplitStatement;
use Path::Tiny;
use Test::mysqld;
use Shiori::Model::Bookmark;

 subtest 'bookmark' => sub {
    my $mysqld = Test::mysqld->new(
        my_cnf => {
            'skip-networking' => '',
        }
    ) or die $Test::mysqld::errstr;
    my $dbh = DBI->connect($mysqld->dsn, 'root', undef);
    my $schema_file = path('etc', 'shiori_schema.sql');
    my $schema_sql  = $schema_file->slurp();
    my $initial_sql = <<"SQL";
USE test;
$schema_sql
SQL
    my $splitter = SQL::SplitStatement->new(
        keep_terminator      => 1,
        keep_comments        => 0,
        keep_empty_statement => 0,
    );
    for ( $splitter->split($initial_sql) ) {
        $dbh->do($_) or die($dbh->errstr);
    }
    my $dsn = $mysqld->dsn();
    ok $dsn;

    my $model = Shiori::Model::Bookmark->new(connect_info => [ $dsn, 'root', undef ]);
    ok $model;
    my $res = $model->create({ url => 'htt://example.jp/' });
    ok $res->{error};
    $res = $model->create({ url => 'http://example.jp/' });
    ok $res->{success};
    isa_ok $res->{success}{bookmark}, 'Shiori::DB::Row::Bookmark';

    my ($entries, $pager) = $model->entries({ page => 1 , limit => 1 });
    ok $entries;
    isa_ok $pager, 'Data::Page::NoTotalEntries';
};

done_testing();

Web.pmとコントローラをつくる

モデルが出来ればあとはすんなりいくでしょう。lib/Shiori/Web.pmを以下のように変更します。Mojoliciousの機能であるhelperでモデルの呼び出しを可能にしています。コントローラー内で$self->modelメソッドが使えるようになるのです。

package Shiori::Web;
use Mojo::Base 'Mojolicious';
use Shiori::Model::Bookmark;

sub startup {
    my $self = shift;

    my $model = Shiori::Model::Bookmark->new();
    $self->helper(
        model => sub {
            return $model;
        }
    );

    my $r = $self->routes;
    $r->namespaces([qw/Shiori::Web::Controller/]);
    $r->get('/')->to('root#index');
    $r->get('/new')->to('root#post');
    $r->post('/create')->to('root#create');
}

1;

まだブックマークが登録されていないと思いますが、トップページのコントローラに該当する部分はこうでしょう。

sub index {
    my $self = shift;
    my $page = $self->param('page') || 1;
    my ($entries, $pager) = $self->model->entries({ page => $page, limit => 10 });
    $self->stash->{entries} = $entries;
    $self->stash->{pager} = $pager;
    $self->render();
}

対応するテンプレートは以下のとおりです。Mojo::Templateを使っているのでPerlが直接書けちゃいます。

% title 'Top';
% layout 'default';

<h1>URL LIST</h1>
<p>
  <a href="/new"><button type="button" class="btn btn-default">NEW POST</button></a>
</p>

<table class="table table-striped table-bordered table-hover">
  <thead>
    <tr>
      <th>ID</th>
      <th>URL</th>
      <th>DATE</th>
    </tr>
  </thead>
  <tbody>
% for my $entry (@$entries) {
      <tr>
        <td><%= $entry->id %></td>
        <td><a href="<%= $entry->url %>" target="_blank"><%= $entry->url %></a></td>
        <td><%= $entry->created_at->ymd('/') %> <%= $entry->created_at->hms(':') %></td>
      </tr>
% }
  </tbody>
</table>

<ul class="pagination">
% if (my $prev_page = $pager->prev_page) {
  <li><a href="/?page=<%= $prev_page %>">&laquo;</a></li>
% }
  <li><a href="#"><%= $pager->current_page %></a></li>
% if (my $next_page = $pager->next_page) {
  <li><a href="/?page=<%= $next_page %>">&raquo;</a></li>
% }
</ul>

投稿用フォームを表示する/newというパスに対応するコントローラーの/postメソッドはこれだけでよいです。

sub post {
    my $self = shift;
    $self->stash->{messages} = undef;
    $self->render();
}

諸事情でstashmessagesという変数にundefを渡しております。テンプレートは以下のとおり。エラーが出た際の表示も担っています。

% title 'POST URL';
% layout 'default';

 <h1>POST URL</h1>

% if ($messages) {
  <div class="alert alert-danger">
    <ul>
% for my $message (@$messages) {
      <li><%= $message %></li>
% }
    </ul>
  </div>
% }

<form method="post" action="/create">
  <div class="form-group">
    <input name="csrf_token" value="<%= csrf_token %>" type="hidden" />
    <label for="url">URL</label>
    <input type="text" class="form-control" id="url" name="url" placeholder="Enter URL" />
  </div>
  <button type="submit" class="btn btn-default">Submit</button>
</form>

いよいよ、投稿のエンドポイントPOST /createに対するコントローラーメソッドは以下です。モデルからエラーが来た場合に適切に処理しています。

sub create {
    my $self = shift;
    my $validation = $self->validation();
    if( $validation->csrf_protect->has_error() ){
        return $self->render_not_found();
    }
    my $res = $self->model->create({ url => $self->param('url') });
    if( $res->{error} ) {
        $self->stash->{messages} = $res->{error}{messages};
        return $self->render('root/new');
    }
    $self->redirect_to('/');
}

完成

これまで紹介した個別のテンプレートファイルの外枠となるべきtemplates/layouts/default.html.epにてCSSフレームワークのBootstrapを読みこませれば完成です。

<!DOCTYPE html>
<html>
  <head>
    <title>Shiori - <%= title %></title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
  </head>
  <body>
    <div class="navbar navbar-inverse">
      <div class="container">
        <a class="navbar-brand" href="/">Shiori</a>
      </div>
    </div>
    <div class="container">
      <%= content %>
    </div>
  </body>
</html>

ではplackupでアプリを起動し「http://localhost:5000/」などにアクセスしてみましょう!

Shiori

出来ましたね!

まとめ

コードばかりでかつ駆け足になっちゃいましたが、分からないところは適宜ドキュメントやWeb上のリソースを見てください!また、

  • Mojoliciousでいいのか?
  • DateTimeでいいのか?
  • 画像やJSなど静的ファイルはどこに置くのか?
  • subtestの粒度が荒いんだけどどうすれば?
  • Test::mysqldを毎スコープごとに立ち上げるの辛い
  • テーブルが増えた時Join的なのはどうするのか?
  • 本番サーバーで運用するには?

などの課題があると思うのでその点も意識しつつ、徐々にノウハウを貯めていければよいでしょう。今回のサンプルを含め、つくり方は色々なんで皆さんなりのWebアプリのつくり方を身につけてくださいね!そんでもって「Webアプリエンジニア養成読本」もよろしく!

Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)
和田 裕介 石田 絢一 (uzulla) すがわら まさのり 斎藤 祐一郎
技術評論社
売り上げランキング: 285


このエントリーをはてなブックマークに追加 9 リアクション

みんなで「Webアプリエンジニア養成読本」を書きました!

共著で「 Webアプリエンジニア養成読本 」という書き下ろしのムック本を執筆しました。来る 3月11日 にAmazonや全国書店にて発売いたします。出版社は以前の単著「Webサービスのつくり方」その他でもお世話になっている技術評論社さんです。

Webアプリケーションは、プログラム、データベース、サーバなどを含むさまざまなレイヤから構成されるシステムであり、またそのしくみも日々進歩し続けていることから、新人エンジニア、経験のないエンジニアには、覚えるべきことが多く全体像の理解も難しい側面があります。 本書では、こうしたWebアプリケーション開発の基礎を、前提知識、開発、デプロイ、運用の各フェーズに分けて解説し、全体像の体系的な理解を促すものとして刊行します。最前線の現場で活躍する著者勢が各々の得意分野について書いているので、実践的な内容に仕上がりました。 プログラミング言語としてはPHP、Ruby、一部Perlを取り上げ、それぞれ動作可能なサンプルアプリケーションをつくりながら、最新の技術トレンドを踏まえた開発手法を紹介。また、見落としがちなWebを配信するための本番環境の構築。そして運用に関してもピックアップしています。

Webアプリエンジニア養成読本

さて、個人的にも興味深かったので事の発端や執筆過程を紹介してみましょう。

まず、去年の11月に技術評論社、略して 技評 の編集者、細谷さんとなんとなく本の企画について話していました。時期的に「その当時の来年」つまりは「今年の」3月〜4月に「Webアプリの技術にフォーカスした基礎知識から運用までもカバーする入門書」を「新人向け」に出せるんじゃないかとまとまります。ただ、僕一人では知識的にもなによりリソース面でも難しいと、細谷さんには正直に伝えてあったので「共著はいかが?」となりました。

うむ、共著か…

ひとりで文章を書くのは苦では無いのですが、複数人で書くとなると状況が変わると思い、ためらいつつも細谷さんに僕以外の担当分の著者をアサインしてもらうように頼みました。それがとある日の夜…

その後細谷さんと分かれ、ふと。考えてみたら、身近に一緒に楽しく書いてくれそうな人達。いや。「書きたいけれどウズウズしてそうな人達」がいるではないか!!と、その瞬間、Facebookメッセージで個別に連絡をし、3人からOKを承諾。細谷さんとの打ち合わせのその日のうちに共同執筆者が確定していました。素晴らしいスピード感。その3人こそが彼ら。

  • ウズラさん / 実践部分のPHP編を担当してもらおう
  • すがさん / Ruby編を担当してもらおう
  • こえむ / インフラと運用を担当してもらおう

僕を含めてこの4人は去年の年末で 7回目 を迎えた「 ハチイチ忘年会 」で幹事をやったりして親しくなった仲です。生で出会う機会は少ないけれども同じWeb系のエンジニアということで、それぞれお互いを意識していたはず。あまりにも身内的に本を書くのはよく無いけれども、この4人ならば、社会性を帯びたままいい本が書けるんじゃないかと企んだのです。いい具合に専門分野が微妙に違うので配分もピッタリだし、何よりも今まで書籍関係の執筆経験の無いウズラさんとこえむが経験を踏んでもらえれば幸いと感じていました。

ハチイチ忘年会の様子。

ハチイチ忘年会の様子

みんなで書くぞ!というGOが出たのも早かったですが、執筆もいいペースで進みました。2013年11月26日にキックオフの打ち合わせ兼飲み会をみんなで行い、方針を固めます。

キックオフ打ち合わせ兼飲み会

原稿は Markdown で書いて GitHubプライベートレポジトリ で全て管理する方針を取りました。naoyaさんのようにPull Requestを活用する手もありますが、編集者とのやりとりは最後の段階であるPDFになったゲラが対象だったりするので、特に意識せずに、シンプルな運用にします。

  • masterブランチ にガンガン push してよね
  • commitメッセージ は分かりやすいように日本語でいいよね
  • Issue も活用。例えば各担当ごとに Issue 立ててそこで意見を投げ合う

などですね。

Issues

また、雑誌スタイルであるムック本の体裁なので、実物の本になった時のイメージがつきにくいと思い、Markdownのプレビューソフト「 App::mookview 」なるものをつくったりしました。

コマンドラインの引数で指定したMarkdownテキストをザックリとムック本っぽくHTMLページにして配信してくれます。当然編集を当したあとの原稿との差が出てしまいますが、雰囲気をつかむのに役に立ちました。

App::mookview

そんなこんなで、ちょいと編集間際にコンテンツ量の調整で手間取りましたが、そこそこいい具合に執筆、脱稿、校正、入稿まで進んで今に至ります。

一度だけやった執筆ハッカソンの様子。

執筆ハッカソン

結果、楽しく4人で本を書きましたよーって報告と宣伝。また、3月20日に池袋ジュンク堂でイベントをやるので興味があれば是非!というお話でした。立ち読みでいいので、お手にとっていただければ幸い!!


Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)
和田 裕介 石田 絢一 (uzulla) すがわら まさのり 斎藤 祐一郎
技術評論社
売り上げランキング: 1,087
このエントリーをはてなブックマークに追加 10 リアクション

Podcast「だんごゆっけの平和な話」を始めました #dandyfm

仲良しな「だんごさん」と俺「ゆっけさん」が緩くお話をするPodcast「 だんごゆっけの平和な話 」をはじめました!既に dango and yukke を文字ったドメイン「dandy.fm」を取得し、フィードの出力やサイトのホスティング、iTunes Storeへの登録を行いました。

Podcast

カテゴリ的には「テクノロジー」の「Techニュース」となっておりますが、おそらくは、Web関係のテクノロジーや話題をベースとしつつも!、それとは全く関係のない僕らの価値観をだべることになるかと思います。例えば「#001」の回では最後に「どこまでが味噌汁か!?」という話で盛り上がりました。僕ら自身、実験的に行っている面もあるので、温かい目で見守ってくれると助かります。

ちなみに、半日くらいでシステムが出来上がりましたので、参考のため、その手順を列挙しておきます。

  1. だんごさんと役割分担をする
  2. ドメイン「dandy.fm」を取る
  3. ハッシュタグをその時点で「#dandyfm」に決める
  4. Tumblrで新しいBlogを立ち上げ、だんごさんをインバイトする
  5. dandy.fmのCNAMEをTumblrのものに向ける
  6. コンテンツ配信についてmiyagawaさんのRebuild等を参考に調査
  7. アップロードが簡便などの理由で、富豪的にS3+CloudFrontでいくことにする
  8. S3に専用のBucketを作成
  9. AWSのIAMでだんごさん向けのアカウントをつくる
  10. S3のパーミッション周りをごにょごにょ設定する
  11. CloudFrontと連携させる
  12. 配信用のサブドメインをCloudFrontへ向ける
  13. フィード配信も同じく調査
  14. お手軽にTumblr+FeedBurnerで行うことにする
  15. FeedBurnerの「マイ・ブランド」を使うためにサブドメインのCNAME設定
  16. Tumblrのフィードを拾ってFeedBurnerでPodcastを配信出来るか待つ
  17. その間にTumblrサイト本体のテーマやスタイルを調整
  18. iTunes Storeに登録
  19. 反映を諸々待つ
  20. CloudFrontのキャッシュが効いているか?などを確認
  21. 今ココ

って具合です。

いざ聴いていただいた方の感想を知ると「そこに感心してくれるのか!」という思わぬ発見点があって面白いですね^^ 最低でも2週間に1回くらいは更新するかと思うので、よろしくお願いしますっ!

追記

miyagawaさんからこのようなツイート頂きました。

ファイルサイズもそうだけど、音がそもそも小さかったりと、録音したものをそのまま使っていたので、ポストプロダクションを完全にRebuildの方法を模倣して音声ファイルを加工してみました。

  1. Audacity でノイズ除去を行う
  2. Levelatorでノーマライズ
  3. LAME MP3 Encoderでmp3化

LAMEはコマンドで以下のように打ちました。

$ lame -m m -b 64 dandyfm-ep000.output.aiff dandyfm-ep000.mp3

確かにファイルサイズが半分になったし、音も問題ないです!miyagawaさんありがとうございます。

Tags: podcast,
このエントリーをはてなブックマークに追加 10 リアクション

AWS S3 の認証リクエストをつくる「その 1」

PerlのAWS関係のライブラリについて。

ファイルのアップロードなどS3の操作に関して、現在Net::Amazon::S3を使っているのですが、深遠な理由からかもっさりしているのと、バージョンアップが頻繁ではないので、それとはまた別コンセプト+別実装でライブラリを書きたい気分。当然Net::Amazon::S3に関して挙動としては正しい動きをしておりますのでそれはそれで使うといいかと。以前から考えているのはAWSの各種サービスに対して透過的に使える署名作成と認証のためのフレームワーク。S3はちょいと特殊なのですが、大抵のサービスは

  • Signature Version 2
  • Signature Version 4

とこの2つの方式で署名をつくってHTTPリクエストヘッダに埋め込んで認証をかけます。現在CPANに上がっているNet::Amazon::* や Net::AWS::* 系のモジュールは個別のサービスに特化しているので、それぞれで認証を実装していてるケースが見受けられて、これ、どうせだったら認証だけを面倒見る汎用プログラムがあっていいかとも考えています。確かにnodeとかの実装で似たようなコンセプトのライブラリがあったので多言語の実装も参考にする。

で、昔つくろうと思って挫折というか放棄したので、ちょっとずつ、まずはスクリプトレベルで書いています。

まずはよく使うS3の署名作成をしようと、リクエストのAuthorizationヘッダに埋め込むためのSignatureを生成するロジックを書いてみます。ちなみに公式な仕様を含むドキュメントは以下のページで見れます。

このページには例として

  • AWSAccessKeyId => AKIAIOSFODNN7EXAMPLE
  • AWSSecretAccessKey => wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

であること。そして検証用に

GET /photos/puppy.jpg HTTP/1.1
Host: johnsmith.s3.amazonaws.com
Date: Tue, 27 Mar 2007 19:36:42 +0000

というリクエストを例としています。Authorizationヘッダに書くべきSignature「bWq2s1WEIj+Ydj0vQ697zp+IXMU=」も掲載されています。サンプルを元にPerlスクリプトでSignatureの値を出して、果たして当たっている結果になるかを書いてみました。こんなの!

use strict;
use warnings;
use Test::More;
use Digest::HMAC_SHA1;

# Yet another script to generate the AWS-S3 signature

my $access_key = 'AKIAIOSFODNN7EXAMPLE'; #XXX not used
my $secret_key = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY';

my $string_to_sign = "GET\n\n\nTue, 27 Mar 2007 19:36:42 +0000\n/johnsmith/photos/puppy.jpg";
my $hmac = Digest::HMAC_SHA1->new($secret_key);
$hmac->add($string_to_sign);
my $signature = $hmac->b64digest . '=';

ok $signature;
is $signature, 'bWq2s1WEIj+Ydj0vQ697zp+IXMU=';

done_testing();

ハッシュ化とBase64によるエンコードが必要なんだなぁ〜、具体的な実装はこれで決まりだねぇ〜 って感じでひとまず手元のスクリプトが出した値がドキュメントにある値と等しかったので安心。この先やることがまだあるので、進捗あれば書きますしそのうちライブラリにするかも。

Tags: aws perl,
このエントリーをはてなブックマークに追加 4 リアクション

JUST MAKE IT - 僕がつくる時に思うこと

年末に引き続きまたまた北海道に来ました!

HOKKAIDO アイデアソン&ハッカソン2014セミナー で発表した資料でございます!

https://speakerdeck.com/yusukebe/just-make-it

このエントリーをはてなブックマークに追加

このエントリーをはてなブックマークに追加 7 リアクション