MP3ファイルにはアーティスト名の情報を入れることができます。
そしてエクスプローラーからそのアーティスト名で音楽ファイルを検索することができます。
しかし、フォルダAの中にフォルダBがあり、そのフォルダBの中に音楽ファイルが入っている場合、フォルダB内で検索すると見つかりますすが、フォルダAで検索すると見つかりません。
音楽ファイルが大量にある場合フォルダ1階層だけで管理しているということはほぼありません。
ほとんどの場合2階層以上のフォルダ構造になっているかと思います。
そういう場合、「あのアーティストの曲が聴きたいな・・・」って思った時に見つけ出すことが非常に難しいです。
今回Pythonを使って2階層以上のフォルダ構造になっている場合でも、アーティスト名で音楽ファイルを検索することができるアプリケーションを作ったので、その仕組みをご紹介します。
また、今回作成したアプリケーションは近々フリーで配布しようと考えています。
検索用アプリケーションを作ろうと思ったきっかけ
作成したアプリケーションはこのような感じです。
- 参照ボタンをクリックしてフォルダを選択
- アーティスト名を入力して検索ボタンをクリック
- 結果が表示される
このように非常にシンプルな動作をします。
目的は複数階層になっているフォルダ内から指定したアーティストの曲を探すというものです。
そのため、結果は下の広い部分にそれぞれの曲がどこにあるかファイルパスで表示されます。
1つ1つフォルダに入って探すという手間が省けるので、私としては非常に便利かなと考えてます。
音楽ファイルをアーティスト名別に保存していればこのような問題は起こらないでしょうが、私の場合は時期別にフォルダ分けをしています。
そのときに聴いている曲を10~20曲くらいまとめてフォルダに入れて保存しているのです。
例えばCDを2枚買ったとして、その曲4曲をフォルダに入れる。
またしばらくしてから新たに3枚CDを買って、その6曲を同じフォルダに入れる。
そうするとそのフォルダに10曲入っていることになったので、次CDを買ったときは新しくフォルダを作ってそこに保存、のような方法で保存してます。
このような保存方法をすると、あのときこんな曲聴いてたなーって後から思い出に浸ることができます(笑
でも、この方法だと1つのフォルダ内に複数のアーティストの曲が入っているようになるんですよね。
急にあのアーティストの曲が聴きたい!ってなったときに探し出すのが非常に面倒なため、一括で探し出せるアプリケーションを今回作成しました。
EasyID3を使ってアーティスト名を取得する
さて、ではここからは動作のお話です。
MP3ファイルからアーティスト名を取得するために、mutagenというライブラリを使いました。
導入はpipを使って行います。
pip install mutagen
ライブラリをインストールしたら、実際に使うためimportします。
今回はEasyID3というモジュールを使用します。
from mutagen.easyid3 import EasyID3
これでmutagenのEasyID3モジュールが使えるようになりました。
では、実際にMP3ファイルからアーティスト名を取得してみましょう。
from mutagen.easyid3 import EasyID3
tags = EasyID3(path)
print(tags['artist'])
pathにはMP3ファイルのパスを入れます。
すると、指定したファイルの情報が変数tagsに配列で入ります。
そして、その中で今回はアーティスト名が欲しいので、その中からartistを選択してprintしています。
artistだけでなくalbumでアルバム名を取得することもできます。
これでMP3ファイルからアーティスト名を取得することができるようになりました。
フォルダ内のファイルを全て取得する
EasyID3モジュールを使うことによってMP3ファイルからアーティスト名を取得することができるようになりました。
しかし、フォルダの中には音楽ファイルだけでなくフォルダも入っています。
フォルダをEasyID3で読み込んでも、当然のことながらアーティスト名を取得することができません。
そのため、まずはフォルダ内にあるものが音楽ファイルなのかフォルダなのか判別する必要があります。
そして、音楽ファイルであればアーティスト名を取得して、その音楽ファイルのパスを保存します。
具体的な手順は、
- フォルダ内のファイル・フォルダを全て取得する
- 1つ1つ読み込んで、それがファイルなのかフォルダなのか判別する
- ファイルならEasyID3を使ってアーティスト名を取得
- フォルダなら1から順に同じことを行う
このような流れになります。
そんなときに使えるのが、Python標準のライブラリであるosです。
では、コードはどのようになるか見ていきましょう。
フォルダ内のファイル・フォルダを全て取得する
まず、フォルダ内のファイル・フォルダを全て取得しましょう。
import os
file_list = os.listdir(path)
ファイル一覧を取得するにはisdirを使います。
pathに入れたパスのディレクトリの中身一覧が変数file_listに配列として格納されます。
ファイルなのかフォルダなのか判別する
取得した一覧が変数file_listに格納されているので、それらがファイルなのかフォルダなのか判別していきます。
import os
for value in file_list:
if os.path.isdir(value):
#フォルダの場合の処理
else:
#ファイルの処理
os.path.isdirを使うことで、指定したパスがフォルダかそうでないかを判別させることができます。
for文で変数file_listから1つずつ要素を取り出してvalueに格納します。
例えば、file_listに[AAA.mp3, BBB.mp3, CCC]が入っていたとします。(CCCはフォルダ)
その場合、1回目のループではvalueにはAAA.mp3が格納され、AAA.mp3がファイルかフォルダかif文で判断されるのです。
その後に次はBBB.mp3がvalueに格納されて、といったように処理が繰り返されます。
そして、配列の要素が全て処理し終えたらfor文のループも終了します。
また、if文の中ではフォルダであればTrueが返ってくるのでフォルダの場合の処理、ファイルであればFalseが返ってくるのでelseでファイルの場合の処理をさせます。
ファイルならEasyID3を使ってアーティスト名を取得
先程のファイルかフォルダを判別させて、ファイルだった場合はEasyID3を使ってアーティスト名を取得させます。
今回はアーティスト名を取得するための関数artist_returnを作って、その中で処理させます。
from mutagen.easyid3 import EasyID3
def artist_return(path):
tags = EasyID3(path)
return tags['artist']
ファイルだった場合、そのファイルパスをこの関数に与えてやればアーティスト名を返してくれるという動作になります。
関数より返ってきたアーティスト名と、自分が検索したいアーティスト名が一致すればそのファイルパスを結果に出力というようにすれば、今回したいことが実現できます。
複数階層のフォルダからファイルを取得する
フォルダの中にフォルダがあった場合の処理です。
今回一番のミソとなるのがここです。
1~4までの手順を書きましたが、することと言えばまさに書いた通りのものです。
1~3まではこれまでに書いてきましたので、4での処理は先程のファイルかフォルダか判別した場合にフォルダだった場合、手順1から繰り返すのです。
これをコードにする際、再帰関数と呼ばれるものを使います。
実際にコードを見てみましょう。
先程書いたファイルかフォルダを判別させる処理を関数化させます。
import os
def file_check(file_list, path):
for value in file_list:
if os.path.isdir(path + value):
#フォルダの場合の処理
else:
#ファイルの処理
先程の説明ではわかりやすくするためos.path.isdir(path)と書きましたが、実際はos.path.isdir(path + value)となります。
valueにはファイル名だけが格納されますが、isdirで処理するためにはファイルパスが必要となります。
そのため、対象フォルダのパスが格納されている変数pathにフォルダ内一覧リストに表示されているvalueを組み合わせたpath + valueが引数となってます。
さて、ここのファイルの処理は先程作った関数artist_returnを入れて、アーティスト名を取得します。
import os
from mutagen.easyid3 import EasyID3
def artist_return(path):
tags = EasyID3(path)
return tags['artist']
def file_check(file_list, path):
for value in file_list:
if os.path.isdir(path + value):
#フォルダの場合の処理
else:
artist = artist_return(path + value)
file_list = os.listdir(path) #pathは検索したいフォルダのパス
file_check(file_list, path)
これも関数artist_returnに音楽ファイルのファイルパスとなるpath + valueを引数として持たせて、変数artistにその曲のアーティスト名が格納される動作となります。
では、次に肝心の再帰関数を使ってフォルダ内のフォルダの中身を取得させてみましょう。
import os
from mutagen.easyid3 import EasyID3
def artist_return(path):
tags = EasyID3(path)
return tags['artist']
def file_check(file_list, path):
for value in file_list:
if os.path.isdir(path + value):
file_list2 = os.listdir(path + value)
file_check(file_list2, path + value + '/')
else:
artist = artist_return(path + value)
file_list = os.listdir(path) #pathは検索したいフォルダのパス
file_check(file_list, path)
このようになります。
isdirの結果フォルダと判断された要素を対象にos.listdirを使います。
os.listdirを使うことで、フォルダの中身が全て取得できるので、その中身をfile_list2に格納します。
そして、そのファイル一覧を格納したfile_list2と、そのフォルダのパスであるpath + value + ‘/’を引数に、自分自身であるfile_check関数の処理を行います。
すると、今度は新しく取得したフォルダの中身に対して同じように処理がされます。
もし、その中にまたフォルダがあった場合にも同じように一覧を取得して、その一覧とファイルパスを元にfile_check関数が実行されます。
これが再帰関数です。
こうすることで、フォルダの中にいくつフォルダがあっても、全てのファイルのアーティスト名を取得することができます。
Tkinterを使ってGUIで操作できるようにする
ここまでアプリケーションの中身である処理の部分についてお話してきました。
あとは、処理させるための情報をアプリケーションに与えてやる必要があります。
GUIでそれらの情報をアプリケーションに与えるためにはTkinterを使います。
Tkinterを使って、フォルダのパスを表示させる欄、検索するアーティスト名を入力する欄、参照ボタン、検索ボタン、結果を表示する欄を作って、それぞれの情報をこれまで作ってきた関数に与えてやればOKです。
フォルダのパスを表示させる欄を作ることで、わざわざ変数に格納して関数に渡す必要がなく、get()で直接値を取得することができるようになります。
また、アーティスト名についてもget()で入力した値を取得、EasyID3から取得した値と比較することで検索が実現できます。
この検索する際、
入力値 == EasyID3で取得した値
としてしまうと、アーティスト名が完全一致した場合のみとなってしまいます。
そのため、
EasyID3で取得した値 in 入力値
として、入力値がEasyID3で取得した値と一部合致しているでも結果に表示させた方が使い勝手がよくなるかと思います。
検索条件をラジオボタンを使って、完全一致か部分一致で選択できるようにするとより便利ですね。
これらのTkinterを使ってのGUIアプリケーションの作り方や、フォルダを選択してパスを取得する方法か過去の記事で詳しく解説してますので、よろしければ参考にしてみてください。
最後に
今回全てのコードは書いていませんが、メインとなる部分は全て書いたので、あとは過去の記事に書かれていることと組み合わせればこのアプリケーションを簡単に作ることができます。
ただ、今のままではフォルダを指定してアーティスト名を検索して結果を表示するという最低限の機能しかありませんので、改良が必要です。
- 検索がどれくらい進んでいるかをパーセントで表示する
- 検索が終わったら完了のポップアップを表示させる
- 検索結果をリセットできるリセットボタンを作る
- 検索結果に表示された曲をWindowsMediaPlayerで再生する
今私が考えている改良案です。
これらの機能は最低限実装したいなと考えているので、実装でき次第公開アプリケーションの配布をしようかなと考えてます。
少しでも気になった方は自作するなり、公開を楽しみにしていてもらえると嬉しいです!