#!/usr/bin/python # coding: euc-jp # Requires: CDDB.py http://cddb-py.sourceforge.net/ # Requires: eyeD3 http://eyed3.nicfit.net/ import CDDB, DiscID import string import codecs import sys, os import re import subprocess import struct from array import array import eyeD3 from threading import Thread OUT_DIR = "/share03/music/Music" BROKEN_CHARSET = "cp949" # 韓国語 #BROKEN_CHARSET = "cp874" # タイ語 DIR_NAME = "%(artist)s - %(album)s" FILE_NAME = "%(track_no)02d_%(track)s" #FILE_NAME = "%(artist)s - %(track)s" CDROM = "/dev/cdrom" MP3_COMMAND = ["gogo", "-silent"] CDDB.proto = 6 def escape_filename(filename): filename = re.sub(r'[\\/:*?"<>|]', '_', filename) filename = re.sub(r'[. ]+$', '', filename) return filename def track2filename(i, info, ext): track_info = {} track_info = dict(category=info["category"], artist=info["artist"], album=info["album"], year=info["year"], track_no=i + 1, track=info["tracks"][i]) filename = FILE_NAME % track_info filename = "%s.%s" % (escape_filename(filename), ext) return filename.encode("utf-8") def fix_broken_korean(str): """古い韓国などのCDのcddbを取得すると、euc-krの「c1f6」が「c381c3b6」 に文字化けしている。これは、c1/f6 を1バイトずつutf-8に変換 した結果であるので、c381/c3b6 を c1/f6 に変換し、更に、これを euc-krからutf-8に変換する。(pythonでは書きにくい・・・)""" b_str = array("B", str) for c in b_str: if c >= 0x80: # 最初に出てくる非asciiな文字が0xc0 <= 0xc3の場合のみ # ハングルなどの文字化けパターンとして処理する if 0xc0 <= c <= 0xc3: break else: return str # U+0080...U+07FF のUTF-8 -> Unicode変換を行う # 但し、今回は0x80...0xFFの範囲のみとして処理する new_array = array("B") prev = 0 in_utf = False for c in b_str: if 0xc0 <= c <= 0xc3: prev = (c & 3) in_utf = True else: if in_utf: new_array.append((c & 0x3f) | (prev << 6)) in_utf = False else: new_array.append(c) try: return new_array.tostring().decode(BROKEN_CHARSET).encode("utf-8") # ヨーロッパ系言語? except UnicodeDecodeError: return str def get_cddb(): cdrom = DiscID.open() disc_id = DiscID.disc_id(cdrom) (query_status, query_info) = CDDB.query(disc_id) if query_status != 200: return None query_info["title"] = fix_broken_korean(query_info["title"]).decode("utf-8") (artist, album) = map(string.strip, query_info["title"].split(" / ", 1)) (read_status, read_info) = CDDB.read(query_info['category'], query_info['disc_id']) track_titles = [] for i in range(disc_id[1]): title = fix_broken_korean(read_info["TTITLE%s" % (i)]).decode("utf-8") track_titles.append(title) info = dict(category=query_info["category"], artist=artist, album=album, tracks=track_titles, disc_id=disc_id, year=read_info["DYEAR"] ) return info def rip_cd(i, info): track = info["tracks"][i] print ("Ripping: %02d %s ..." % (i+1, track)) cmd = ("cdda2wav", "-D", CDROM, "-H", "-q", "-t", "%s" % (i + 1), track2filename(i, info, "wav")) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.wait() print ("Ripping: %02d %s (Done)" % (i+1, track)) def wav2mp3(i, info, remove=True): track = info["tracks"][i] print (" Converting: %02d %s ..." % (i+1, track)) cmd = MP3_COMMAND + [track2filename(i, info, "wav")] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.wait() mp3_filename = track2filename(i, info, "mp3") # os.chown(mp3_filename, 0, 512) # os.chmod(mp3_filename, 0664) if remove: _remove_wav(i, info) print (" Converting: %02d %s (Done)" % (i+1, track)) def _remove_wav(i, info): os.remove(track2filename(i, info, "wav")) def set_id3(info, dir): tracks = info["tracks"] tracks_len = len(tracks) for i in range(tracks_len): track = tracks[i] filepath = track2filename(i, info, "mp3") tag = eyeD3.Tag() tag.link(filepath) tag.header.setVersion(eyeD3.ID3_V2_3); tag.setTextEncoding(eyeD3.UTF_16_ENCODING); tag.setAlbum(info["album"]) tag.setArtist(info["artist"]) tag.setTitle(track) tag.setGenre(info["category"]) tag.setDate(info["year"]) tag.setTrackNum((i+1, tracks_len)) tag.update() def close_cdrom(): subprocess.call(("eject", "-t")) def open_cdrom(): subprocess.call(("eject",)) def main(): sys.stdout = codecs.getwriter('utf_8')(sys.stdout) close_cdrom() info = get_cddb() if info == None: print "[ERROR] Could not get CDDB." open_cdrom() sys.exit(2) print "%s / %s / %s / %s" % (info["artist"], info["album"], info["category"], info["year"]) dir = DIR_NAME % info dir = os.path.join(OUT_DIR, escape_filename(dir)).encode("utf-8") try: os.mkdir(dir) except OSError, e: if e.errno == 17: # File exists print "[ERROR] Directory already exists." open_cdrom() sys.exit(1) except: raise # os.chown(dir, 0, 512) # os.chmod(dir, 04775) os.chdir(dir) open("disc_id", "w").write("%s\n" % info["disc_id"]) tracks = info["tracks"] for i in range(len(tracks)): print ("%02d.%s" % (i+1, tracks[i])) threads = [] for i in range(len(tracks)): rip_cd(i, info) # convert wave to mp3 in background th = Thread(target=wav2mp3, args=(i, info)) threads.append(th) th.start() open_cdrom() for th in threads: th.join() set_id3(info, dir) print "Done." if __name__ == '__main__': main()