玩式草子─ソフトウェアとたわむれる日々

第58回 「らじる★らじる」をもう一度(その3)

この記事を読むのに必要な時間:およそ 7 分

前回NHKの「Now On Air情報」⁠以下NOA情報)から入手した番組情報を,SQLite3を用いてデータベースに記録するためのスクリプトを紹介しました。手元では,このスクリプトを「らじる★らじる」録音用スクリプトから起動し,録音した番組の情報を自動収集して,日夜増えていく番組情報データベースをほくそ笑みながら眺めています。

 $ sqlite3 radiru_titles.sql3
 SQLite version 3.8.0.2 2013-09-03 17:11:13
 Enter ".help" for instructions
 Enter SQL statements terminated with a ";"
 
 sqlite> select oid,* from titles where date like '2014-05-24%';
 1567|2014-05-24-17-00_奥の細道~名句でたどるみちのくの旅.mp3|2014-
 05-24 17:00:00|001netr20|古典講読「奥の細道~名句でたどるみちのくの
 旅~」(8)|和洋女子大学副学長…佐藤勝明
 ...
 1570|2014-05-24-21-00_私の日本語辞典「能をささえることば」_004.mp3|
 2014-05-24 21:00:00|001netr20|私の日本語辞典「能をささえることば」
 (4)|法政大学名誉教授、能楽研究家…西野春雄, 【アナウンサー】秋山和平
 1571|2014-05-24-21-00_クラシックの迷宮‐私の試聴室~ヴァインベルクの
 作品~.mp3|2014-05-24 21:00:00|001netfm0|クラシックの迷宮 -私の試聴
 室~ヴァインベルクの作品~-|
 
 sqlite> select * from contents where id=1571;
 1571|片山杜秀
 - 私の試聴室~ヴァインベルクの作品~ -
 1571|「バイオリンと弦楽のための小協奏曲 作品42から 第3楽章」
 ヴァインベルク作曲
 (4分39秒)
 (バイオリン)ギドン・クレーメル
 ....

録音したMP3ファイルにどういう内容が入っているかは,この番組情報データベースを調べればわかるようにはなったものの,MP3ファイルは別のファイルサーバに移動することもありますし,Nexus 7や携帯MP3プレイヤーなどに移して外部に持ち出すこともあります。そのような場合,楽曲情報データベースを調べないと収録曲目等がわからないのは不便です。

何かいい方法はないかな,と考えているうちに,MP3ファイルにはID3タグとよばれるタイトルや演奏者の情報を埋めこむ機能があったことを思いだしました。PythonにはMP3ファイルのID3タグを操作するためのeyeD3というモジュールが開発されていたはずです。そこで,このモジュールを使って,録音したMP3ファイルに番組情報を直接記録してみることにしました。

ID3タグについて

実際のコード等を紹介するまえに,少しID3タグについておさらいしておきましょう。

ID3タグはMP3ファイルにタイトルやアーティスト名を埋めこむために広く利用されている機能なものの,もともとのMP3形式に定められていた仕様ではなく,MP3形式が普及していくにつれ「デファクト・スタンダード」的に広まっていった仕様です。

wikipediaによると,最初にID3タグを使ったソフトウェアは1996年に公開された「Studio3」だったそうです。その後,Winampなどのメディアプレイヤーが対応したことでID3タグは広く普及することになりました。

最初に定義されたID3タグはID3v1と呼ばれ,128バイトの固定長のデータがMP3ファイルの末尾に添付される形式になっています。128バイトのデータは,曲名,アーティスト名,アルバム名とコメント欄に30バイトづつ割り振られ,残りは,日付や楽曲ジャンル,トラック番号などに使われています。このタグは構造が単純なこともあって携帯型メディアプレイヤーなどにも広く採用され,ほとんどのMP3プレイヤーで利用できるようになっています。

一方,ID3v1では曲名やコメントとして記録できるのは30バイトに限られており,30バイトというと日本語では15文字なので,あまり長いタイトルやアルバム名は記録できません。また,文字コードに関する規定もなかったため,Windows環境で作られたShift-JISのID3タグが,UNIX/Linux用のメディアプレイヤーでは文字バケするなど,不便な点がありました。

そこで新たに開発されたのがID3v2と呼ばれる形式です。この形式では,従来はMP3ファイルの末尾に置かれていた固定長の楽曲情報が,可変長のフレームに収めてMP3ファイルの先頭に置かれるように変更されています。可変長のフレームは256MBの上限まで複数個置くことが可能で,歌詞を収めたり,ジャケット写真などの画像データを埋めこむことも可能になりました。また,文字コードもUnicodeで記録するように定められたので,環境による互換性の問題も少なくなりました。

前述のように,元々ID3タグは,あるソフトウェアメーカが自社のソフトウェア用に開発した機能だったものの,デファクト・スタンダードとして普及した結果,最近ではid3.orgという組織が仕様を定義するようになりました。

しかしながら,⁠ID3v2」にはすでに「ID3v2.2」⁠ID3v2.3」⁠ID3v2.4」の3つのバージョンが存在し,バージョン間での下位互換性は保証されない(⁠ID3v2.3」のみに対応しているメディアプレイヤーでは「ID3v2.4」のタグは読めない)といった問題が生じています。その結果,最新の仕様は「ID3v2.4」なものの,メディアプレイヤー等では「ID3v2.3」が最も広く利用されているようです。

PythonのeyeD3モジュール

前述のように,⁠ID3タグ」は元々は特定のソフトウェア用に開発された機能だったものの,MP3ファイルに楽曲情報を記録できる便利さから急速に普及して,現在ではMP3ファイルの標準機能のように利用されています。Plamo Linuxでも,EasyTagというID3タグを編集するためのソフトウェアを用意していますし,JuKAmarokといったメディアプレイヤーからもタグ情報を編集することができます。

図1 EasyTagによるID3タグの編集

図1 EasyTagによるID3タグの編集

図2 メディアプレイヤーJuKによるID3タグの編集

図2 メディアプレイヤーJuKによるID3タグの編集

これらタグ編集機能を使えば,録音したMP3ファイルにタイトル名等の情報を手書きしていくことは可能なものの,一日何本も自動録音しているファイルに一々手動でタグを書き込んでゆくのも面倒です。そこで注目したのがeyeD3です。

eyeD3はID3タグを操作するために開発されたPython用のモジュールで,PythonスクリプトからMP3ファイルのID3タグを操作するためのインターフェイスを提供します。また,eyeD3というコマンド(実体はPythonスクリプトを起動するためのラッパー)も用意されており,コマンドラインからID3タグを操作することもできる便利なツールになっています。

 $ eyeD3 02.銀座カンカン娘.mp3 
 02.銀座カンカン娘.mp3	[ 3.92 MB ]
 -------------------------------------------------------------------------------
 Time: 02:51	MPEG1, Layer III	[ 192 kb/s @ 44100 Hz - Joint stereo ]
 -------------------------------------------------------------------------------
 ID3 v2.3:
 title: 銀座カンカン娘
 artist: 遊佐未森
 album: スヰート檸檬
 recording date: 2008
 track: 2		genre: JPop (id 146)
 OTHER Image: [Size: 7453 bytes] [Type: image/jpeg]
 Description: 
 
 -------------------------------------------------------------------------------

さて,本来,ID3タグはCDから切り出した1つのトラックのMP3ファイルに,曲名やアーティスト名,アルバム名などを記録するように設計されています。一方,この連載で扱ってきた「らじる★らじる」の場合,1つの番組が1つのMP3ファイルになるので,複数の曲名やアーティスト名などをID3タグに書き込むことになります。そのあたりを考慮して,ダウンロードできる番組情報を以下のような形でID3タグに対応させることにしました。

ID3タグ名番組情報
albumファイル名
title番組名(title)
artist出演者(act)
date放送日(date)
comment番組内容(contents)

これらの情報は番組の放送中ならばNOA情報からダウンロードできます。しかし,録音中のMP3ファイルにタグを書き込むことはできないので,ID3タグの書き込みは録音終了後に行うことにします。その場合,NOA情報は次の番組に変っているので,必要な情報は手元に保存しているデータベースから取り出すことになります。

そのあたりを考慮して,録音したファイル名をキーに,番組情報データベースを検索して,必要な情報を集める処理を書いてみました。

 62  filepath = sys.argv[1]
 63  file = os.path.basename(filepath)
 64  ufile = file.decode('euc-jp')
 65 
 66  query = u"select oid,title,date,ch,act from titles where filename='{}';".format(ufile)
 67  (oid, title, date, channel, act) = query_db(query)[0]

番組情報データベースに記録しているのはファイル名だけなので,パス名の部分は取りのぞき(63行目)⁠UTF-8形式でデータを保存しているSQLite3用にファイル名を変換して(64行目)⁠そのファイル名を用いた検索リクエストを作って,query_db()関数に投げます。

データベースを検索するquery_db()関数はこういう風にしてみました。実際に検索しているのは30行目から33行目で,それ以外はデータベースファイルが見つからなかったり,指定されたファイル名が登録されていなかった場合のエラー終了処理です。

 23  def query_db(query):
 24      dbdir = config['DB_dir']
 25      dbname = dbdir + '/radiru_titles.sql3'
 26      if os.access(dbname, os.R_OK) == False:
 27          print("cannot find title DB({})".format(dbname))
 28          sys.exit(1)
 29  
 30      connection = sqlite3.connect(dbname)
 31      cursor = connection.cursor()
 32      c = cursor.execute(query)
 33      res = c.fetchall()
 34      if len(res) == 0:
 35          print("DB doesn't have entry for id: {} ".format(query))
 36          print("please check title DB:{}".format(dbname))
 37          sys.exit(1)
 38      else:
 39          return(res)

前回紹介したように,番組情報データベースは項目数が決まっているtitlesテーブルと,番組内容を複数行に記録したcontentsテーブルから構成されています。曲目リストなどはcontentsテーブルから取り出す必要があるので,titlesテーブルから得たoidをキーにcontentsテーブルを検索する処理を書きました。

 41  def query_contents(oid):
 42      query = u"select * from contents where id={}".format(oid)
 43      t = query_db(query)
 44      return t
 45  

contentsテーブルから取り出した曲目リストは,(id, 曲目)というタプルが並んだリストになっているので,曲目の部分を順に取りだして,1つの文字列につないでいきます。

 69      res = query_contents(oid)
 70      contents = ''
 71      for i in res:
 72          (id, l) = i
 73          contents = contents + l
 74  

ID3タグの"album"に記録するファイル名は,拡張子や日付の情報を省いた形にしてみました。

 75      album_parts = ufile.rstrip('.mp3').split('_')
 76      album = album_parts[1]

以上で必要な情報が揃ったので,それらを"tag"という辞書型のデータ構造にまとめて,set_id3tag()関数に渡してID3タグを書き込みます。

 77      tag = {'title':title, 'album':album, 'act':act, 'date':date, 'ch':channel, 'contents':contents}
 78      set_id3tag(filepath, tag)

set_id3tag()関数では,書き込み先のMP3ファイル名と,書き込みたいタグ情報を受けとり,eyeD3モジュールを使ってそれらを結びつけ,save()メソッドで保存します。

 46  def set_id3tag(file, tag):
 47      mp3file = file
 48  
 49      new_tag = eyed3.id3.Tag()
 50      new_tag.file_info = eyed3.id3.FileInfo(mp3file)
 51      new_tag.title = tag['title']
 52      new_tag.album = tag['album']
 53      new_tag.artist = tag['act']
 54      new_tag.release_date = tag['date']
 55      if len(tag['contents']) > 0:
 56          new_tag.comments.set(tag['contents'])
 57  
 58      new_tag.save()

なお,今回利用した eyeD3 はバージョン 0.7.3 で,以前から広く使われていた0.6.x系とはAPIが大きく異なっており,このコードは0.6.x系では動きません。

昨年末に公開したPlamo-5.2では,eyeD3パッケージは収録しているものの,バージョンは0.6.18だったので,そのままではこのスクリプトは動きません。eyeD3-0.7.3は後述する筆者のホームページで公開しています。

著者プロフィール

こじまみつひろ

Plamo Linuxとりまとめ役。もともとは人類学的にハッカー文化を研究しようとしていたものの,いつの間にかミイラ取りがミイラになってOSSの世界にどっぷりと漬かってしまいました。最近は田舎に隠棲して半農半自営な生活をしながらソフトウェアと戯れています。

URLhttp://www.linet.gr.jp/~kojima/Plamo/index.html

コメント

コメントの記入