1 Star 0 Fork 18

Oliver/LrcMusicPlayer

forked from Nagisa/LrcMusicPlayer 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
song.py 9.33 KB
一键复制 编辑 原始数据 按行查看 历史
Nagisa 提交于 2023-05-22 19:20 . 优化罗马音显示效果。
import os
import re
import pykakasi
from mutagen.flac import FLAC
from mutagen.mp3 import MP3
from mutagen.dsdiff import DSDIFF
from mutagen.dsf import DSF
from mutagen.wave import WAVE
from mutagen.monkeysaudio import MonkeysAudio
from mutagen.mp4 import MP4
from mutagen import File
class Song:
# 格式限定
SONG_FORMAT_LIMIT = ['flac', 'mp3', 'wav', 'wave', 'dsf', 'dff', 'dsdiff', 'ape', 'm4a']
def __init__(self, path):
self.path: str = path
# Tag信息
self.title = '暂无'
self.artist = '暂无'
self.album = '暂无'
self.date = '暂无'
self.genre = '暂无'
self.lyrics = ''
self.cover = b''
# 音频信息
self.sample_rate = 0 # 采样率
self.bits_per_sample = 0 # 位深
self.bitrate = 0 # 比特率
self.channels = 0 # 声道
self.length = 0 # 持续时间
self.audio_type = '' # 音频类型
self.is_hr = False # 是否达到Hi-Res
try:
self.load_metadata()
except Exception as e:
print(e)
def load_metadata(self):
"""读取歌曲元数据"""
if not self.path:
return
# 获取文件后缀
filetype = self.path.split('.')[-1].lower()
if filetype == 'flac':
audio = FLAC(self.path)
self.audio_type = 'FLAC'
self.parse_audio_info(audio.info)
self.parse_flac_tag(audio)
elif filetype == 'mp3':
self.audio_type = 'MP3'
audio = MP3(self.path)
self.parse_audio_info(audio.info)
self.parse_id3_tag(audio)
elif filetype in ['wav', 'wave']:
self.audio_type = 'WAV'
audio = WAVE(self.path)
self.parse_audio_info(audio.info)
self.parse_id3_tag(audio)
elif filetype == 'dsf':
self.audio_type = 'DSF'
audio = DSF(self.path)
self.parse_audio_info(audio.info)
self.parse_id3_tag(audio)
elif filetype in ['dsdiff', 'dff']:
self.audio_type = 'DFF'
audio = DSDIFF(self.path)
self.parse_audio_info(audio.info)
self.parse_id3_tag(audio)
elif filetype in ['ape']:
self.audio_type = 'APE'
audio = MonkeysAudio(self.path)
self.parse_audio_info(audio.info)
self.parse_ape_tag(audio)
elif filetype in ['m4a']:
self.audio_type = 'AAC'
audio = MP4(self.path)
self.parse_audio_info(audio.info)
self.parse_mp4_tag(audio)
else:
self.audio_type = 'UNKNOWN'
audio = File(self.path)
self.parse_audio_info(audio.info)
self.parse_id3_tag(audio)
if self.lyrics == '' and os.path.exists(os.path.splitext(self.path)[0] + '.lrc'):
with open(os.path.splitext(self.path)[0] + '.lrc', "r", encoding='utf-8', errors='ignore') as f: # 打开文件
self.lyrics = f.read() # 读取本地歌词
def get_lrc_dict(self):
"""根据时间戳将歌词转为字典"""
if not self.lyrics:
return {}
lrc_list = self.lyrics.splitlines()
# 时间戳正则
func = re.compile("\\[.*?]") # 为符合PEP8规范,反斜杠双写,实际使用一个也可以
# 根据时间戳,转换成字典
lrc_dict = {}
for item in lrc_list:
searched = func.search(item)
if not searched:
continue
lrc_time = searched.group()
time_str_list = lrc_time[1:-1].split(":")
if not time_str_list[0].isdigit():
continue
lrc_time_int = int(time_str_list[0]) * 60000 + int(float(time_str_list[1]) * 1000)
lrc_text = func.sub('', item)
lrc_text = ' '.join(lrc_text.split()) # 去除多余空格
if lrc_dict.get(lrc_time_int):
lrc_dict[lrc_time_int].append(lrc_text)
else:
lrc_dict[lrc_time_int] = [lrc_text]
return lrc_dict
def parse_id3_tag(self, audio):
"""解析 ID3 Tag"""
for item in audio:
# 部分可能携带信息的FrameID
if 'APIC' in item:
self.cover = audio.get(item).data
if 'USLT' in item:
self.lyrics = str(audio.get(item))
if audio.get('TIT2'):
self.title = str(audio.get('TIT2'))
if audio.get('TPE1'):
self.artist = str(audio.get('TPE1'))
if audio.get('TALB'):
self.album = str(audio.get('TALB'))
if audio.get('TDRC'):
self.date = str(audio.get('TDRC'))
if audio.get('TCON'):
self.genre = str(audio.get('TCON'))
def parse_flac_tag(self, audio):
"""解析 FLAC Tag"""
if not audio.get('title') is None:
self.title = audio.get('title')[0]
if not audio.get('artist') is None:
self.artist = audio.get('artist')[0]
if not audio.get('album') is None:
self.album = audio.get('album')[0]
if not audio.get('date') is None:
self.date = audio.get('date')[0]
if not audio.get('genre') is None:
self.genre = audio.get('genre')[0]
if not audio.get('lyrics') is None:
self.lyrics = audio.get('lyrics')[0]
if audio.pictures:
self.cover = audio.pictures[0].data
def parse_mp4_tag(self, audio):
"""解析 MP4 Tag"""
if not audio.get('©nam') is None:
self.title = audio.get('©nam')[0]
if not audio.get('©ART') is None:
self.artist = audio.get('©ART')[0]
if not audio.get('©alb') is None:
self.album = audio.get('©alb')[0]
if not audio.get('©day') is None:
self.date = audio.get('©day')[0]
if not audio.get('©gen') is None:
self.genre = audio.get('©gen')[0]
if not audio.get('©lyr') is None:
self.lyrics = audio.get('©lyr')[0]
if not audio.get('covr') is None:
self.cover = audio.get('covr')[0]
def parse_ape_tag(self, audio):
"""解析 APEv2 Tag"""
if audio.get('COVER ART (FRONT)'):
b_value = audio.get('COVER ART (FRONT)')
if b'\0' in b_value.value:
self.cover = b_value.value.split(b'\0', 1)[-1]
if audio.get('TITLE'):
self.title = str(audio.get('TITLE'))
if audio.get('ARTIST'):
self.artist = str(audio.get('ARTIST'))
if audio.get('ALBUM'):
self.album = str(audio.get('ALBUM'))
if audio.get('YEAR'):
self.date = str(audio.get('YEAR'))
if audio.get('GENRE'):
self.genre = str(audio.get('GENRE'))
for item in audio:
if 'LYRICS' in item:
self.lyrics = str(audio.get(item))
def parse_audio_info(self, audio_info):
"""解析音频信息"""
self.sample_rate = audio_info.sample_rate
self.channels = audio_info.channels
self.length = audio_info.length
if self.audio_type != 'APE':
self.bitrate = audio_info.bitrate
if self.audio_type != 'MP3':
self.bits_per_sample = audio_info.bits_per_sample
if self.audio_type in ['DSF', 'DFF'] and self.bits_per_sample == 1:
self.is_hr = True
elif self.bits_per_sample > 16 and self.sample_rate > 44100:
self.is_hr = True
def __str__(self):
"""重写打印信息"""
if self.audio_type == 'MP3':
return f'采样率:{self.sample_rate}, 比特率:{self.bitrate}, ' \
f'声道:{self.channels}, 持续时间:{self.length},文件类型:{self.audio_type},Hi-Res:{self.is_hr}'
else:
return f'采样率:{self.sample_rate}, 位深:{self.bits_per_sample}, 比特率:{self.bitrate}, ' \
f'声道:{self.channels}, 持续时间:{self.length},文件类型:{self.audio_type},Hi-Res:{self.is_hr}'
@staticmethod
def get_romaji(text):
"""获取罗马音"""
def get_all_str(t):
kks = pykakasi.kakasi()
t = ' '.join(t) # 插入空格
result = kks.convert(t)
_text = ''
for _item in result:
_text += _item['hepburn']
return f' {_text} '
# 匹配日文字符的正则表达式模式,包括平假名、片假名和CJK统一汉字,以及特殊的日文汉字字符"々"和"〇"
pattern = re.compile(r'([\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3005\u3007]+)')
# 将日文字符转换为罗马音,其他部分保持原样
parts = re.split(pattern, text)
processed_parts = [get_all_str(part) if re.match(pattern, part) else part for part in parts]
processed_text = ''.join(processed_parts)
processed_text = ' '.join(processed_text.split()) # 去除多余空格
return processed_text
@staticmethod
def contains_japanese(text):
"""判断日文字符"""
# Unicode范围:平假名[\u3040-\u309F]、片假名[\u30A0-\u30FF]、CJK统一汉字[\u4E00-\u9FFF]、々[\u3005]、〇[\u3007]
pattern = re.compile(r'[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u3005\u3007]')
return bool(re.search(pattern, text))
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/oliver_su/LrcMusicPlayer.git
git@gitee.com:oliver_su/LrcMusicPlayer.git
oliver_su
LrcMusicPlayer
LrcMusicPlayer
master

搜索帮助