支持LRC歌词的终端播放器
2014-10-09 | Liu Yuyang | python
最终效果可以看这里:
来自asciinema的终端录像
缘起
自从降频之后,电脑特别卡……
降频是因为总是自动关机
自动关机据几个月观察是因为cpu过热
cpu过热猜测是因为散热有问题了
散热有问题猜测是该清灰换硅胶了
但三星……非常麻烦,自从去年毕业自己把本拆了一遍后就再懒得拆机了。
于是,想听歌时懒得打开vlc界面,就用命令行版本的cvlc来播放
可是……没有歌词?
某天想干脆自己做个支持lrc的终端播放器算了。
作为一个脚本小子,我只习惯python……
说干就干
谷歌搜寻了下:
- python如何播放mp3音乐
- lrc文件的格式,考虑下如何在终端打印歌词
- vlc有没有python绑定
于是,有了以下版本,仅仅100行的多彩歌词显示终端播放器(使用pygame
来播放音乐):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
|
from pygame import mixer from mutagen.mp3 import MP3 import re import time import sys import random
f_mp3 = sys.argv[1] f_lrc = sys.argv[2]
def lrc2dict(lrc): lrc_dict = {} remove = lambda x: x.strip('[|]') for line in lrc.split('\n'): time_stamps = re.findall(r'\[[^\]]+\]', line) if time_stamps: lyric = line for tplus in time_stamps: lyric = lyric.replace(tplus, '') for tplus in time_stamps: t = remove(tplus) tag_flag = t.split(':')[0] if not tag_flag.isdigit(): continue time_lrc = int(tag_flag) * 60000 time_lrc += int(t.split(':')[1].split('.')[0]) * 1000 try: time_lrc += int(t.split('.')[1]) except: pass time_lrc = time_lrc / 100 * 100 lrc_dict[time_lrc] = lyric return lrc_dict
def print_lrc(mixer, lrc_d, color, wholetime): mixer.music.play() mixer.music.pause() time.sleep(0.001) mixer.music.play() while 1: t = mixer.music.get_pos() / 100 * 100 sys.stdout.write('[' + time.strftime("%M:%S", time.localtime(t / 1000)) + '/' + time.strftime("%M:%S", time.localtime(wholetime)) + '] ') if t in lrc_d: sys.stdout.write(color + lrc_d[t] + '\033[0m') sys.stdout.flush() sys.stdout.write("\033[K") else: sys.stdout.flush() sys.stdout.write('\r') if t < 0: sys.exit(0) time.sleep(0.05)
with open(f_lrc) as f: lrc = f.read()
lrc_d = lrc2dict(lrc)
mixer.init() mixer.music.load(f_mp3)
try: wholetime = MP3(f_mp3).info.length except: wholetime = 0
colors = [ '\x1B[31m', '\x1B[32m', '\x1B[33m', '\x1B[34m', '\x1B[35m', '\x1B[36m', '\x1B[37m' ] color = random.choice(colors) print_lrc(mixer, lrc_d, color, wholetime)
|
那个不断循环查看播放进度打印对应歌词的实现真够丧心病狂的……精确到0.1s算了。
Be aware that MP3 support is limited. On some systems an unsupported format can crash the program, e.g. Debian Linux. Consider using OGG instead.
开始在pygame的文档中看到这句话还没在意后来,播放某个叫in my life
的mp3时发现完全不对劲……而且没有获取音频长度的功能[^1]
后来想想,还是用vlc的py绑定吧。
vlc的py绑定相当赞:
完整覆盖libvlc功能,纯python,支持各个vlc版本和完整的文档。[^2]
但开始我以为pip可以直接下载……后来发现pip搜索到的是macos独有的vlc python wrapper……和这个绑定是两码事……真给跪了……
自行下载vlc.py到当前目录或者扔到python搜索路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
|
import vlc import re import time import sys import random
f_mp3 = sys.argv[1] f_lrc = sys.argv[2]
def lrc2dict(lrc): lrc_dict = {} remove = lambda x: x.strip('[|]') for line in lrc.split('\n'): time_stamps = re.findall(r'\[[^\]]+\]', line) if time_stamps: lyric = line for tplus in time_stamps: lyric = lyric.replace(tplus, '') for tplus in time_stamps: t = remove(tplus) tag_flag = t.split(':')[0] if not tag_flag.isdigit(): continue time_lrc = int(tag_flag) * 60000 time_lrc += int(t.split(':')[1].split('.')[0]) * 1000 try: time_lrc += int(t.split('.')[1]) except: pass time_lrc = time_lrc / 1000 * 1000 lrc_dict[time_lrc] = lyric return lrc_dict
def print_lrc(player, lrc_d, color): player.play() player.pause() time.sleep(0.1) player.play() wholetime = mediaObject.get_duration() / 1000 * 1000 while 1: t = player.get_time() / 1000 * 1000 sys.stdout.write('[' + time.strftime("%M:%S", time.localtime(t / 1000)) + '/' + time.strftime("%M:%S", time.localtime(wholetime / 1000)) + '] ') if t not in lrc_d: sys.stdout.flush() else: sys.stdout.write(color + lrc_d[t] + '\033[0m') sys.stdout.flush() sys.stdout.write("\033[K") sys.stdout.write('\r') if t == wholetime: sys.exit(0) time.sleep(0.05)
with open(f_lrc) as f: lrc = f.read()
lrc_d = lrc2dict(lrc)
vlcInstance = vlc.Instance() player = vlcInstance.media_player_new() mediaObject = vlcInstance.media_new(f_mp3) player.set_media(mediaObject)
colors = [ '\x1B[31m', '\x1B[32m', '\x1B[33m', '\x1B[34m', '\x1B[35m', '\x1B[36m', '\x1B[37m' ] color = random.choice(colors) print_lrc(player, lrc_d, color)
|
奇葩的还是那个丧心病狂的循环,查询播放进度最短间隔只有0.3s。只好把lrc歌词显示精度降到1s了
反正对我是能用了。
也许有空可以让它支持播放列表的,用vlc完全不是问题。
嗯,完工。我擦嘞我竟然一天写了俩!
[^1]: Find the Length of a Song with Pygame
[^2]: Python bindings