1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/pth2000-LrcMusicPlayer

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
song.py 9.3 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Nagisa Отправлено 22.05.2023 14:20 2522aac
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))

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/pth2000-LrcMusicPlayer.git
git@api.gitlife.ru:oschina-mirror/pth2000-LrcMusicPlayer.git
oschina-mirror
pth2000-LrcMusicPlayer
pth2000-LrcMusicPlayer
master