奇声ラッシュというPodcastで配信されている番組を時々聞いています。ネット上に音源があり、放送時間帯というものもなく、好きなときに聞けて便利です。

元々、FM福岡で放送されていた番組の後続番組のようですが、ネットでの配送で地域を問わずに聴取できるので福岡の者であれば分かるような場所や物事を簡単に注釈を付けるというような配慮があります。2008年から続いているようですが、番組のことを知ったのは三年弱前の2013年だったのでそれ以前の放送が五年分以上あります。存在も知らなかった番組開始時期からの番組からちまちまと聞いていますが、この番組は一回の放送がだいたい15~20分の三つのパートに分かれていて、一つのパートを聞き終えるたびに次のパートを聞く操作をするのが面倒くさいときがあります。そこでいくつかのパートをまとめて聞くようにできないかと考えてみました。

MP3ファイルのダウンロード

Podcastの方では第361回パート1以降しか聞けませんが、アーカイブのページに年別にページが分かれていて、年を選んで下のAll pagesをクリックすればその年の全ての放送の一覧が表示されます。MP3ファイルにたどり着くにはその一覧のどれかをクリックして表示されたページの「ファイルをダウンロード」のところをクリックしなければなりません。しかし、これを全てのファイルに関してはやってられません。そこでファイル名を見てみることにしました。

一覧とその先のページでリンクされたMP3ファイルは次のようになっています。

[ 2008年5月 8日 ] 第2回 パート1[] 
http://fmfukuoka.co.jp/kisei/podcast/kisei_080508_1.mp3
ファイル名は年月日とパートから構成されているようです。これは一覧からMP3ファイルへの直接リンクを作成できそうです。

初めは一覧のテキストの文字列をエディタで置換を繰り返してMP3ファイルのURLを作ってみましたが、それを数年分行うのは面倒と思い、スクリプトを作ってみました。(後述)2008年のファイルはこれでほぼダウンロードできましたが、一部、ダウンロードされていないものがあります。一覧の文字列は意図したとおりにURLに変換できているので、どのファイルが存在しないのかを確認することにしました。

使っているWaterfoxではLinkCheckerという便利なアドオンが使えます。これをインストールすると閲覧しているページのリンク先が有効か無効かが色付けされて分かります。これを使ってリンクを確認することにしました。そのために、リンクを示すHTMLファイルを出力できるようにスクリプトを作りなおしました。出力されたHTMLファイルをWaterfoxで開き、右クリックメニューからCheck Page Linksを選びリンクの確認を行います。すると、次のように無効なリンクが見つかりました。(赤いのが無効なリンク)

link2008

これらのファイルだけは仕方なく日付からブログのページを辿って個別にダウンロードしました。同じスクリプトを使って2009年のファイルもダウンロードしてみると、かなりの数のファイルがダウンロードされていませんでした。これも先ほどと同じくリンクを確認してみると次のとおり、約半分のリンクが無効になっていました。 link2009

調べてみると2009年6月15日からファイル名の書式が次のように変わっていました。

http://fmfukuoka.co.jp/kisei/podcast/kisei_090615_58.mp3

従来のようにパートの番号ではなく回数がファイル名に含まれるようになっています。これに対応するためにまた6月15日前後で出力を分けるようにスクリプトを書き直したところ、次のようにリンクを正しく辿ることが出来るようになりました。(それでも一部不整合はありますが)

2009-2

以降のファイルはそれで対応できましたが、ところどころ不整合は発生してしまいます。少数なのでそこは手作業でダウンロードすることで対応しました。

MP3ファイルの結合

音声ファイルを結合するためには特別なソフトウェアが必要だろうと思い、C_vineというフリーウェアをダウンロードして使って複数のMP3ファイルをまとめて聞くことができるのを確認しましたが、その後、驚くべきことを知りました。

C_vineはGUIなのでマウス操作が必要です。それをコマンドラインで出来ないかと何気なく調べていたら「Linuxでmp3をお手軽に結合する」というページを見つけました。通常はテキストファイルの内容を表示するのにUNIXで使っているcatコマンドですが、C言語でstrcat()という関数があるように連結(concatenate)するコマンドということは知っていました。これをバイナリファイルのMP3にそのまま適用できました。

例えば第6回の三つのパートを一つにまとめるには

cat k*0618* > 0618.mp3
のようにすれば簡単に行なえます。zshでは"<"でファイルをコンソールに出力できるのでcatを使わずに
< k*0618* > 0618.mp3
でも同様のことが出来ます。

試しにWindowsのコマンドプロンプトで下記のように"copy /b"でファイルをまとめてみたらcatと同様にMP3ファイルを結合することができました。MP3以外の音声ファイルは試してませんが、MP3ファイルであればWindows標準で結合できるということを知りました。

oopy /b k*081209* 081209.mp3

スクリプト出力スクリプト

次がやっつけの拙作スクリプトです。これをkiseigeturl.plというファイルで保存したとします。
#!/bin/perl -w
# 奇声ラッシュのアーカイブ一覧からMP3ファイルをダウンロードするスクリプトを出力

use warnings;
use strict;
use Getopt::Long;

GetOptions(
    'h' => \my $html,	       # HTMLで出力
);

my $urlbase = "http://fmfukuoka.co.jp/kisei/podcast/kisei_";

open(FILE, "< $ARGV[0]") or die("指定ファイルエラー\n");

while (my $line = ) {	# 読み込み
    next unless ($line);	# 空行は飛ばす
    $line =~ s/\x0D//g;		# CRの削除
    $line =~ s/\x0A//g;		# LFの削除
    $line =~ s/\s+$//;		# 末尾の空白文字の削除

    print "# $line\n" unless ($html);

    # 回数の全角を半角に変換
    $line =~ s/0/0/g;
    $line =~ s/1/1/g;
    $line =~ s/2/2/g;
    $line =~ s/3/3/g;
    $line =~ s/4/4/g;
    $line =~ s/5/5/g;
    $line =~ s/6/6/g;
    $line =~ s/7/7/g;
    $line =~ s/8/8/g;
    $line =~ s/9/9/g;
    # ずっとエレガントな方法があるみたいやけどうまくいかんのでこれで済ます

    $line =~ s/^\[ 20//;	# 先頭の"[ 20"の削除
    $line =~ s/\[.*\]$//;	# 末尾の"[]"の削除

    my @elem = split(/年|月|日|第|回|]| | パート/, $line); # 要素を分離

    if ($#elem < 7) {		# 書式が異なる回は飛ばす
	print "# Skipped\n";
	next;
    }

    my @id = ();
    my $n = 0;
    for (my $i = 0; $i <= $#elem; $i++) {
	next unless ($elem[$i]); # 内容がないものは飛ばす
	$id[$n++] = $elem[$i];	# 内容があるもののみ格納
    }

    unless ($id[3] =~ /\d/) {	# 回が書かれていないものは飛ばす
	print "# Skipped\n";
	next;
    }

    my $url;
    my $tail = $id[4];		# パートを指定
    my $date = $id[0] * 10000 + $id[1] * 100 + $id[2]; # 日付を数値化
    # 09/06/15以後は回数を指定
    $tail = $id[3] if ($date >= 90615);
    # 15/11/05より後は回数とパートを指定
    $tail = "$id[3]" . "_" . "$id[4]"  if ($date > 151105) && $id[4];
    # 2016年はパート不要
    $tail = $id[3] if ($date > 160000);

    unless ($tail =~ /\d/) {	# 数字がなければ飛ばす
	print "# Skipped\n";
	next;
    }

    $url = sprintf("%s%02d%02d%02d_%s.mp3",
		   $urlbase, $id[0], $id[1], $id[2], $tail);

    if ($html) {		# ファイル名にリンクを付ける
	my $link = $url;
	$link =~ s/$urlbase//;
	$link =~ s/\.mp3//;
	print "<a href=\"$url\">$link</a>\n";
    }
    else {
	print "wget $url\n";
    }
}

これに対して例えば奇声ラッシュ: 2008年アーカイブから次のようなテキストファイルをコピーしてarchlist.txtとして保存したとします。

[ 2008年6月 4日 ] 第4回 パート2[]
[ 2008年6月 4日 ] 第4回 パート1[]
[ 2008年5月21日 ] 第3回 パート2[]
[ 2008年5月21日 ] 第3回 パート1[]
[ 2008年5月 8日 ] 第2回 パート2[]
[ 2008年5月 8日 ] 第2回 パート1[] 
[ 2008年5月 1日 ] 一回目なので、小手調べ[] 
これに対して例えば
kiseigeturl.pl archlist.txt > get2008.sh
とすれば、get2008.shが次のような内容で出力されます。
# [ 2008年6月 4日 ] 第4回 パート2[]
wget http://fmfukuoka.co.jp/kisei/podcast/kisei_080604_2.mp3
# [ 2008年6月 4日 ] 第4回 パート1[]
wget http://fmfukuoka.co.jp/kisei/podcast/kisei_080604_1.mp3
# [ 2008年5月21日 ] 第3回 パート2[]
wget http://fmfukuoka.co.jp/kisei/podcast/kisei_080521_2.mp3
# [ 2008年5月21日 ] 第3回 パート1[]
wget http://fmfukuoka.co.jp/kisei/podcast/kisei_080521_1.mp3
# [ 2008年5月 8日 ] 第2回 パート2[]
wget http://fmfukuoka.co.jp/kisei/podcast/kisei_080508_2.mp3
# [ 2008年5月 8日 ] 第2回 パート1[]
wget http://fmfukuoka.co.jp/kisei/podcast/kisei_080508_1.mp3
# [ 2008年5月 1日 ] 一回目なので、小手調べ[]
# Skipped
ファイル名に解析できなかったものは"# Skipped"と出力します。この出力したファイルに対して
sh get2008.sh

とすれば次々とMP3ファイルをダウンロードできます。Skippedとなっているものは個別にリンクを辿ってダウンロードするしかありません。後は上記のとおり適当にファイルを結合すれば継続して聞くことができます。

パート単位でまとめてみると約40~50分程度になり、ちまちまファイルを一つずつ再生する手間を省けるようになりました。

生成したリンクで辿れなかったもの

機械的に生成したリンクでほぼファイルを取得することができたのですが、不整合で取得できないファイルがありました。必要な記述がなかったものは仕方ないとして、どのようなものがあるのか好奇心で調べてみました。主にファイル名の日付が異なるものでしたが、違うディレクトリに格納されているものもありました。以下のとおりです。
2008年
[ 2008年11月19日 ] 第28回 パート3[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_081118_3.mp3
日付違い
[ 2008年11月12日 ] 第27回 パート3[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_081111_3.mp3
日付違い
[ 2008年11月12日 ] 第27回 パート2[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_081111_2.mp3
日付違い
[ 2008年11月12日 ] 第27回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_081111_1.mp3
日付違い
[ 2008年5月 1日 ] 一回目なので、小手調べ[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_080501.mp3

2009年
[ 2009年12月16日 ] 番外編(その2)[]
http://fmfukuoka.co.jp/kisei/podcast/MaNa_2.mp3
[ 2009年12月16日 ] 番外編(その1)[]
http://fmfukuoka.co.jp/kisei/podcast/MaNa_1.mp3
[ 2009年12月 7日 ] 第82回 パート3[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_091204_82.mp3
日付違い
[ 2009年10月 1日 ] 第73回 パート2[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_090930_73.mp3
日付違い
[ 2009年9月 8日 ] 第70回 パート1[] 
http://fmfukuoka.co.jp/kisei/podcast/kisei_090907_70.mp3
日付違い
[ 2009年8月28日 ] 第68回 パート3[]
http://fmfukuoka.co.jp/kisei/poccast/kisei_090828_68.mp3
ディレクトリ違い poccast

2010年
[ 2010年9月16日 ] 第123回 パート2[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_100915_123.mp3
日付違い
[ 2010年6月28日 ] 第112回 パート1[] 
http://fmfukuoka.co.jp/kisei/kisei_100628_112.mp3
ディレクトリ違い podcast外
[ 2010年4月 9日 ] 第100回 パート3[]
http://fmfukuoka.co.jp/kisei/images/kisei_100409_100.mp3
ディレクトリ違い images
[ 2010年2月10日 ] 第92回 パート2[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_1002010_92.mp3
日付違い 20/10
[ 2010年2月 4日 ] 第91回 パート2[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_100203_91.mp3
日付違い
[ 2010年1月25日 ] 第89回 パート3[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_100122_89.mp3
日付違い
[ 2010年1月19日 ] 第89回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_100118_89.mp3
日付違い
[ 2010年1月 7日 ] 第87回 パート3[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_100107_3_87.mp3
パートあり
[ 2010年1月 7日 ] 第87回 パート2[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_100107_2_87.mp3
 パートあり
[ 2010年1月 7日 ] 第87回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_100107_1_87.mp3
 パートあり

2011年
[ 2011年12月27日 ] 第188回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_111226_188.mp3
日付違い
[ 2011年12月23日 ] 第187回 パート2[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_111221_187.mp3
日付違い
[ 2011年12月23日 ] 第187回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_111219_187.mp3
日付違い
[ 2011年9月 8日 ] 第172回 パート2[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_110907_172.mp3
日付違い
[ 2011年9月 6日 ] 第172回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_110905_172.mp3
日付違い
[ 2011年8月23日 ] 第170回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_110822_170.mp3
日付違い
[ 2011年6月27日 ] 第162回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_110628_162.mp3
日付違い
[ 2011年6月18日 ] 第160回 パート3[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_110617_160.mp3
日付違い
[ 2011年4月 4日 ] 第149回 パート3[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_110401_149.mp3
日付違い
[ 2011年1月 6日 ] 第139回 パート2[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_110105_139.mp3
日付違い
[ 2011年1月 6日 ] 第139回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_110103_139.mp3
日付違い

2012年
[ 2012年12月31日 ] 第241回 パート3[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_121231_241_3.mp3
パートあり
[ 2012年12月31日 ] 第241回 パート2[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_121231_241_2.mp3
パートあり
[ 2012年12月31日 ] 第241回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_121231_241_1.mp3
パートあり
[ 2012年7月11日 ] 第216回 パート2[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_1207011_216.mp3
日付違い  07011
[ 2012年3月15日 ] 第199回 パート3[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_120316_199.mp3
日付違い
[ 2012年2月14日 ] 第195回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_120213_195.mp3
日付違い

2013年
無し

2014年
[ 2014年1月 7日 ] 第294回 パート1[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_140106_294.mp3
日付違い

2015年
[ 2015年8月20日 ] 第365回[]
http://fmfukuoka.co.jp/kisei/podcast/kisei_150819_365.mp3
日付違い
[ 2015年1月 9日 ] 第346回 パート3[]
http://fmfukuoka.co.jp/kisei/kisei_150109_346.mp3
ディレクトリ違い podcast外

2016年
無し

2016年2月11日現在