仕事でテストデータ作るのが面倒だったのでw ツールを作成してみました.
何がしたいかというと,.mp3ファイルがたくさん欲しかったので,IDv3タグを変更してIdv3タグ的に別のアーティスト・アルバムにしたかったのです.
IDv3タグをいじれるライブラリを探したらmutagenというのを発見したので,
早速ダウンロードして使ってみました.
作ったソースはこんな感じです. Python慣れてないから,変なことしてるかも知れないけど・・・
#エラー処理とかはちゃんとやってません.自分で使うのがメインだし.
#!/usr/bin/python import os, sys, time import threading sys.path.append('mutagen') from mutagen.mp3 import MP3 from mutagen.easyid3 import EasyID3 import mutagen.id3 class ExcuteWriting(threading.Thread): def __init__(self, tagData): threading.Thread.__init__(self) self.tagData = tagData def run(self): target_dir = self.tagData.result_dir + "/" + "artist_" + str(self.tagData.artist) create_dir(target_dir) for album in range(int(self.tagData.album_max)): for track in range(int(self.tagData.track_max)): filename = target_dir + "/" + "album_" + str(album) + "_" + str(track + 1) + ".mp3" print "Creating %s...\n" % filename self.copy_file(self.tagData.base_file, filename) self.write_mp3_tag(filename, album) def write_mp3_tag(self, filename, album): m = MP3(filename, ID3=EasyID3) m.add_tags(ID3=EasyID3) m['title'] = "Title" + str(self.tagData.artist) m['artist'] = str(self.tagData.artist) m['album'] = str(album) m.save() m.pprint() # print(m.pprint()) # Read base file and copy it. def copy_file(self, old_name, new_name): try: fr = open(old_name, "rb") fw = open(new_name, "wb") fw.write(fr.read()) fr.close() fw.close() except : print "file read/write error occured\n" class TagData: def __init__(self, base_file, artist, album_max, track_max, result_dir): self.base_file = base_file self.artist = artist self.album_max = album_max self.track_max = track_max self.result_dir = result_dir def create_dir(ret_dir): # if we do have result dir, we won't create it. if (os.access(ret_dir, os.F_OK) == False): os.mkdir(ret_dir) if __name__ == "__main__": # At first, we have to check command line arguments. if (len(sys.argv) != 6): print "usage: id3writer [base file] [output directory] [n artist] [n album] [n tracks]\n" exit (0) base_file = sys.argv[1] ret_dir = sys.argv[2] artist_max = sys.argv[3] album_max = sys.argv[4] track_max = sys.argv[5] # Do we have result dir? create_dir(ret_dir) for artist_num in range (int(artist_max)): # Max thread is 10. if (threading.activeCount() > 10): while threading.activeCount() > 10: time.sleep(0.5) tagData = TagData(base_file, artist_num, album_max, track_max, ret_dir) worker = ExcuteWriting(tagData) worker.start()
大量にデータ作るのにシーケンシャルにやってたら時間かかりそうなので,スレッドも初めて使ってみました.
このプログラムが行うのは,1アルバム辺りNトラックあるアルバムをM個,Kアーティスト分作成するという内容です.
例えば,1アルバムに2トラック・1アーティストに2アルバムというのを2アーティスト分作るとこんな感じです.
[masami@moonlight:~/experiment/id3writer]% ./id3write.py base.mp3 result 2 2 2 Creating result/artist_0/album_0_1.mp3... Creating result/artist_1/album_0_1.mp3... Creating result/artist_0/album_0_2.mp3... Creating result/artist_1/album_0_2.mp3... Creating result/artist_0/album_1_1.mp3... Creating result/artist_1/album_1_1.mp3... Creating result/artist_0/album_1_2.mp3... Creating result/artist_1/album_1_2.mp3... [masami@moonlight:~/experiment/id3writer]% ls -ls result/* result/artist_0: 合計 2360 4 drwxr-xr-x 2 masami masami 4096 Jan 9 00:19 ./ 4 drwxr-xr-x 4 masami masami 4096 Jan 9 00:19 ../ 588 -rw-r--r-- 1 masami masami 598595 Jan 9 00:19 album_0_1.mp3 588 -rw-r--r-- 1 masami masami 598595 Jan 9 00:19 album_0_2.mp3 588 -rw-r--r-- 1 masami masami 598595 Jan 9 00:19 album_1_1.mp3 588 -rw-r--r-- 1 masami masami 598595 Jan 9 00:19 album_1_2.mp3 result/artist_1: 合計 2360 4 drwxr-xr-x 2 masami masami 4096 Jan 9 00:19 ./ 4 drwxr-xr-x 4 masami masami 4096 Jan 9 00:19 ../ 588 -rw-r--r-- 1 masami masami 598595 Jan 9 00:19 album_0_1.mp3 588 -rw-r--r-- 1 masami masami 598595 Jan 9 00:19 album_0_2.mp3 588 -rw-r--r-- 1 masami masami 598595 Jan 9 00:19 album_1_1.mp3 588 -rw-r--r-- 1 masami masami 598595 Jan 9 00:19 album_1_2.mp3 [masami@moonlight:~/experiment/id3writer]%
スレッドを使うときは"threading.Thread"を継承する.継承の仕方はクラス名の後に(threading.Thread)を付けると.
class ExcuteWriting(threading.Thread):
コンストラクタからthreading.Threadの__init__を呼んであげる必要があります.
def __init__(self, tagData): threading.Thread.__init__(self)
そうしたら,スレッド動かすだけなので,
クラスのインスタンスを生成して,startメソッドを呼べばOKです.
worker = ExcuteWriting(tagData) worker.start()
start()からExcuteWritingのrunメソッドが呼ばれるので,runメソッドは必須です.
今回は使ってないですけど、class localってTLSみたいなもんですかね?
リファレンスによるとクラスに対して使う感じのようです.
foo = threading.local()
foo.x = 10
あと,無制限にスレッド作るのもなんなので,threading.activeCount()というメソッドを使って,
動いているスレッド数は最大10個までという感じにしました.
if (threading.activeCount() > 10): while threading.activeCount() > 10: time.sleep(0.5)
スレッド周りは大体こんな感じです.
実際にIDv3タグを弄るところはこんな感じになってます.
MP3クラスのインスタンスを作って・・・
m = MP3(filename, ID3=EasyID3) m.add_tags(ID3=EasyID3)
タグに対して値をセットします.
trackを試したら"無い"と怒られました(つд⊂)エーン
m['title'] = "Title" + str(self.tagData.artist) m['artist'] = str(self.tagData.artist) m['album'] = str(album)
最後にセーブすれば完了です.
m.save()
スレッド使ったり,IDv3タグ弄るライブラリ使っても,たったこれだけのコードで必要な処理ができるので
Python便利です!