経緯
数年前にiPodからソニーのwalkmanに乗り換えました。
walkmanでは歌詞表示することができ、歌詞の1行1行に時間情報を記入することで、歌や英会話にあわせてスクロール表示してくれます。歌の練習に使ったり、英会話を学習するのにとても便利です。
ただし、MP3/MP4ファイル内の歌詞は表示してくれなくて、歌詞専用のLRCファイルに歌詞テキストを保存しておく必要があります。
これまでは、ソニーが提供するソフトウェア「Media Go」で、iPod用にiTunesで保存していた楽曲を再生したりwalkmanに転送したり、そして iTunesで1曲1曲プロパティを開いて歌詞テキストをコピーしては Media Go 側にペーストして、歌詞の時間編集をしてきました。
この転送作業が面倒になり、MP3/MP4ファイルから歌詞テキストを抜き出し、LRCファイルに保存するPythonスクリプトを作りました。
単に歌詞を表示したり、ディレクトリ指定で全ファイルのLRCファイルをまとめて作ったりできます。
動作環境
Python3 と mutagen モジュールが必要です。
私はWindows環境で使っていて、日本の楽曲は日本語ファイル名が多くて、日本語をうまく処理するのに Python3 が必要でした。
また、MP3/MP4から 歌詞情報を読み出すのに mutagen モジュールをpipでインストールして使っています。
mutagenモジュールのインストール
$ pip3 install mutagen
または
$ python3 -m pip install mutagen
または
$ sudo python3 -m pip install mutagen
使い方
ヘルプ表示 (help)
$ lyrics.py -h
usage: lyrics.py [-h] [-l] [-s] [-c] [-r] file [file ...]
positional arguments:
file file or directory name
optional arguments:
-h, --help show this help message and exit
-l, --list show file names containing lyrics (default)
-s, --show show lyrics
-c, --create create "*.lrc" lyrics files if it does not exist
-r, --replace create or replace "*.lrc" lyrics files
歌詞情報を含むファイル名一覧表示 (list)
$ lyrics.py -l .
歌詞の表示 (show)
$ lyrics.py -s "1-01 Just The Way You Are.mp3"
$ lyrics.py -s "Billy Joel"
LRCファイルへの保存 (create: 存在しない場合のみ)
$ lyrics.py -c "Billy Joel"
LRCファイルへの保存 (replace: 存在する場合は上書き、時間情報が消えるので要注意)
$ lyrics.py -r "Billy Joel"
ソースコード
github
https://github.com/shiracamus/lyrics_converter
Pythonスクリプト
# !/usr/bin/env python3
#
# Lyrics converter
#
# version: 2017.4.18
# author: @shiracamus
# github: https://github.com/shiracamus/lyrics_converter
#
# Need to install mutagen module
# $ python3 -m pip install mutagen
# or
# $ sudo python3 -m pip install mutagen
#
import os
import sys
import argparse
from mutagen.id3 import ID3, ID3NoHeaderError
from mutagen.mp4 import MP4, MP4StreamInfoError
def read_lyrics_in_mp3(path):
try:
tags = ID3(path)
for key in tags:
if key.startswith('USLT'):
return tags[key].text
except ID3NoHeaderError:
pass
return None
def read_lyrics_in_mp4(path):
try:
tags = MP4(path).tags
if '\xa9lyr' in tags:
return tags['\xa9lyr'][0]
except MP4StreamInfoError:
pass
return None
def read_lyrics(path):
return (read_lyrics_in_mp3(path) or
read_lyrics_in_mp4(path) or
None)
class Lyrics:
def __init__(self, path):
self.path = path
self.path_base, self.path_ext = os.path.splitext(path)
self.text = read_lyrics(path)
self.exists = bool(self.text)
def __repr__(self):
return '<base="%s", ext="%s", lyrics=%s>' % (self.path_base,
self.path_ext,
self.exists)
class LRC:
ext = '.lrc'
def __init__(self, path):
self.path = path
self.exists = os.path.exists(self.path)
def save(self, text):
with open(self.path, 'w') as f:
f.write(text)
self.exists = True
def show_filename(lyrics):
print(lyrics.path)
def show_lyrics(lyrics):
print('=' * 30)
print(lyrics.path)
print('-' * 30)
print(lyrics.text)
print()
def save_lrc(lyrics, replace=False):
lrc = LRC(lyrics.path_base + LRC.ext)
if not lrc.exists or replace:
lrc.save(lyrics.text)
print('Saved "%s"' % lrc.path)
else:
print('Already exists "%s"' % lrc.path)
def pathes(files_and_directories):
for file_or_directory in files_and_directories:
if os.path.isfile(file_or_directory):
yield file_or_directory
elif os.path.isdir(file_or_directory):
for root, dirs, files in os.walk(file_or_directory):
for filename in files:
yield os.path.join(root, filename)
def parse_args():
p = argparse.ArgumentParser()
p.add_argument('-l', '--list', action='store_true',
help='show file names containing lyrics (default)')
p.add_argument('-s', '--show', action='store_true',
help='show lyrics')
p.add_argument('-c', '--create', action='store_true',
help='create "*.lrc" lyrics files if it does not exist')
p.add_argument('-r', '--replace', action='store_true',
help='create or replace "*.lrc" lyrics files')
p.add_argument('file', nargs='+', default='.',
help='file or directory name')
args = p.parse_args()
if not (args.show or args.create or args.replace):
args.list = True
return args
def main(args):
for path in pathes(args.file):
lyrics = Lyrics(path)
if not lyrics.text:
continue
if args.list:
show_filename(lyrics)
if args.show:
show_lyrics(lyrics)
if args.create or args.replace:
save_lrc(lyrics, replace=args.replace)
if __name__ == '__main__':
main(parse_args())
今後
ソース内コメントは順次追加します。
MP3/MP4以外も扱えるようにしたいので、処理方法を知っていたら是非教えてください。
GUIラッパーも作ってみたいですね。
時間情報も自動で作れたら最高ですが、楽曲の中から音声認識して歌詞を作り出すライブラリなんてまだないですよね?