feat: tag 信息支持写入到歌曲文件 see #266

This commit is contained in:
涵曦 2024-12-06 22:55:14 +08:00
parent 2c5b7b7f26
commit 48a8bf3325
4 changed files with 140 additions and 2 deletions

View File

@ -168,6 +168,9 @@ class Config:
enable_yt_dlp_cookies: bool = ( enable_yt_dlp_cookies: bool = (
os.getenv("XIAOMUSIC_ENABLE_YT_DLP_COOKIES", "false").lower() == "true" os.getenv("XIAOMUSIC_ENABLE_YT_DLP_COOKIES", "false").lower() == "true"
) )
enable_save_tag: bool = (
os.getenv("XIAOMUSIC_ENABLE_SAVE_TAG", "false").lower() == "true"
)
get_ask_by_mina: bool = ( get_ask_by_mina: bool = (
os.getenv("XIAOMUSIC_GET_ASK_BY_MINA", "false").lower() == "true" os.getenv("XIAOMUSIC_GET_ASK_BY_MINA", "false").lower() == "true"
) )

View File

@ -198,6 +198,12 @@ var vConsole = new window.VConsole();
<option value="false" selected>false</option> <option value="false" selected>false</option>
</select> </select>
<label for="enable_save_tag">启用ID3标签写入文件:</label>
<select id="enable_save_tag">
<option value="true">true</option>
<option value="false" selected>false</option>
</select>
<label for="get_ask_by_mina">特殊型号获取对话记录:</label> <label for="get_ask_by_mina">特殊型号获取对话记录:</label>
<select id="get_ask_by_mina"> <select id="get_ask_by_mina">
<option value="true">true</option> <option value="true">true</option>

View File

@ -27,7 +27,19 @@ import aiohttp
import mutagen import mutagen
from mutagen.asf import ASF from mutagen.asf import ASF
from mutagen.flac import FLAC from mutagen.flac import FLAC
from mutagen.id3 import APIC, ID3, Encoding, TextFrame, TimeStampTextFrame from mutagen.id3 import (
APIC,
ID3,
TALB,
TCON,
TDRC,
TIT2,
TPE1,
USLT,
Encoding,
TextFrame,
TimeStampTextFrame,
)
from mutagen.mp3 import MP3 from mutagen.mp3 import MP3
from mutagen.mp4 import MP4 from mutagen.mp4 import MP4
from mutagen.oggvorbis import OggVorbis from mutagen.oggvorbis import OggVorbis
@ -568,6 +580,16 @@ class Metadata:
picture: str = "" picture: str = ""
lyrics: str = "" lyrics: str = ""
def __init__(self, info=None):
if info:
self.title = info.get("title", "")
self.artist = info.get("artist", "")
self.album = info.get("album", "")
self.year = info.get("year", "")
self.genre = info.get("genre", "")
self.picture = info.get("picture", "")
self.lyrics = info.get("lyrics", "")
def _get_alltag_value(tags, k): def _get_alltag_value(tags, k):
v = tags.getall(k) v = tags.getall(k)
@ -731,6 +753,110 @@ def extract_audio_metadata(file_path, save_root):
return asdict(metadata) return asdict(metadata)
def set_music_tag_to_file(file_path, info):
audio = mutagen.File(file_path, easy=True)
if audio is None:
log.error(f"Unable to open file {file_path}")
return "Unable to open file"
if isinstance(audio, MP3):
_set_mp3_tags(audio, info)
elif isinstance(audio, FLAC):
_set_flac_tags(audio, info)
elif isinstance(audio, MP4):
_set_mp4_tags(audio, info)
elif isinstance(audio, OggVorbis):
_set_ogg_tags(audio, info)
elif isinstance(audio, ASF):
_set_asf_tags(audio, info)
elif isinstance(audio, WAVE):
_set_wave_tags(audio, info)
else:
log.error(f"Unsupported file type for {file_path}")
return "Unsupported file type"
try:
audio.save()
log.info(f"Tags saved successfully to {file_path}")
return "OK"
except Exception as e:
log.exception(f"Error saving tags: {e}")
return "Error saving tags"
def _set_mp3_tags(audio, info):
audio["TIT2"] = TIT2(encoding=3, text=info.title)
audio["TPE1"] = TPE1(encoding=3, text=info.artist)
audio["TALB"] = TALB(encoding=3, text=info.album)
audio["TDRC"] = TDRC(encoding=3, text=info.year)
audio["TCON"] = TCON(encoding=3, text=info.genre)
if info.lyrics:
audio["USLT"] = USLT(encoding=3, lang="eng", text=info.lyrics)
if info.picture:
with open(info.picture, "rb") as img_file:
image_data = img_file.read()
audio["APIC"] = APIC(
encoding=3, mime="image/jpeg", type=3, desc="Cover", data=image_data
)
def _set_flac_tags(audio, info):
audio["TITLE"] = info.title
audio["ARTIST"] = info.artist
audio["ALBUM"] = info.album
audio["DATE"] = info.year
audio["GENRE"] = info.genre
if info.lyrics:
audio["LYRICS"] = info.lyrics
if info.picture:
with open(info.picture, "rb") as img_file:
image_data = img_file.read()
audio.add_picture(image_data)
def _set_mp4_tags(audio, info):
audio["\xa9nam"] = info.title
audio["\xa9ART"] = info.artist
audio["\xa9alb"] = info.album
audio["\xa9day"] = info.year
audio["\xa9gen"] = info.genre
if info.picture:
with open(info.picture, "rb") as img_file:
image_data = img_file.read()
audio["covr"] = [image_data]
def _set_ogg_tags(audio, info):
audio["TITLE"] = info.title
audio["ARTIST"] = info.artist
audio["ALBUM"] = info.album
audio["DATE"] = info.year
audio["GENRE"] = info.genre
if info.lyrics:
audio["LYRICS"] = info.lyrics
if info.picture:
with open(info.picture, "rb") as img_file:
image_data = img_file.read()
audio["metadata_block_picture"] = base64.b64encode(image_data).decode()
def _set_asf_tags(audio, info):
audio["Title"] = info.title
audio["Author"] = info.artist
audio["WM/AlbumTitle"] = info.album
audio["WM/Year"] = info.year
audio["WM/Genre"] = info.genre
if info.picture:
with open(info.picture, "rb") as img_file:
image_data = img_file.read()
audio["WM/Picture"] = image_data
def _set_wave_tags(audio, info):
audio["Title"] = info.title
audio["Artist"] = info.artist
# 下载播放列表 # 下载播放列表
async def download_playlist(config, url, dirname): async def download_playlist(config, url, dirname):
title = f"{dirname}/%(title)s.%(ext)s" title = f"{dirname}/%(title)s.%(ext)s"

View File

@ -52,6 +52,7 @@ from xiaomusic.utils import (
parse_cookie_string, parse_cookie_string,
parse_str_to_dict, parse_str_to_dict,
save_picture_by_base64, save_picture_by_base64,
set_music_tag_to_file,
traverse_music_directory, traverse_music_directory,
try_add_access_control_param, try_add_access_control_param,
) )
@ -477,11 +478,13 @@ class XiaoMusic:
tags["year"] = info.year tags["year"] = info.year
tags["genre"] = info.genre tags["genre"] = info.genre
tags["lyrics"] = info.lyrics tags["lyrics"] = info.lyrics
file_path = self.all_music[name]
if info.picture: if info.picture:
file_path = self.all_music[name]
tags["picture"] = save_picture_by_base64( tags["picture"] = save_picture_by_base64(
info.picture, self.config.picture_cache_path, file_path info.picture, self.config.picture_cache_path, file_path
) )
if self.config.enable_save_tag and (not self.is_web_music(name)):
set_music_tag_to_file(file_path, Metadata(tags))
self.all_music_tags[name] = tags self.all_music_tags[name] = tags
self.try_save_tag_cache() self.try_save_tag_cache()
return "OK" return "OK"