feat: Add feature as requested in issue #143

This commit is contained in:
Hi-Jiajun 2024-08-24 09:11:14 +00:00 committed by 涵曦
parent 4e532d298d
commit 4ab3c5cbee
7 changed files with 129 additions and 8 deletions

View File

@ -12,6 +12,9 @@ COPY install_dependencies.sh .
RUN bash install_dependencies.sh RUN bash install_dependencies.sh
FROM python:3.10-slim FROM python:3.10-slim
RUN pip install pydub
RUN python3 -m venv /app/.venv
RUN /app/.venv/bin/pip install pydub
WORKDIR /app WORKDIR /app
COPY --from=builder /app/.venv /app/.venv COPY --from=builder /app/.venv /app/.venv
COPY --from=builder /app/ffmpeg /app/ffmpeg COPY --from=builder /app/ffmpeg /app/ffmpeg

View File

@ -12,7 +12,7 @@
"public_port": 0, "public_port": 0,
"proxy": null, "proxy": null,
"search_prefix": "bilisearch:", "search_prefix": "bilisearch:",
"ffmpeg_location": "./ffmpeg/bin", "ffmpeg_location": "./ffmpeg/bin/ffmpeg",
"active_cmd": "play,set_random_play,playlocal,play_music_list,stop", "active_cmd": "play,set_random_play,playlocal,play_music_list,stop",
"exclude_dirs": "@eaDir", "exclude_dirs": "@eaDir",
"music_path_depth": 10, "music_path_depth": 10,
@ -77,5 +77,6 @@
}, },
"enable_force_stop": false, "enable_force_stop": false,
"devices": {}, "devices": {},
"group_list": "" "group_list": "",
"convert_to_mp3": false
} }

View File

@ -83,7 +83,7 @@ class Config:
search_prefix: str = os.getenv( search_prefix: str = os.getenv(
"XIAOMUSIC_SEARCH", "bilisearch:" "XIAOMUSIC_SEARCH", "bilisearch:"
) # "bilisearch:" or "ytsearch:" ) # "bilisearch:" or "ytsearch:"
ffmpeg_location: str = os.getenv("XIAOMUSIC_FFMPEG_LOCATION", "./ffmpeg/bin") ffmpeg_location: str = os.getenv("XIAOMUSIC_FFMPEG_LOCATION", "./ffmpeg/bin/ffmpeg")
active_cmd: str = os.getenv( active_cmd: str = os.getenv(
"XIAOMUSIC_ACTIVE_CMD", "play,set_random_play,playlocal,play_music_list,stop" "XIAOMUSIC_ACTIVE_CMD", "play,set_random_play,playlocal,play_music_list,stop"
) )
@ -136,6 +136,9 @@ class Config:
remove_id3tag: bool = ( remove_id3tag: bool = (
os.getenv("XIAOMUSIC_REMOVE_ID3TAG", "false").lower() == "true" os.getenv("XIAOMUSIC_REMOVE_ID3TAG", "false").lower() == "true"
) )
convert_to_mp3: bool = (
os.getenv("CONVERT_TO_MP3", "false").lower() == "true"
)
delay_sec: int = int(os.getenv("XIAOMUSIC_DELAY_SEC", 3)) # 下一首歌延迟播放秒数 delay_sec: int = int(os.getenv("XIAOMUSIC_DELAY_SEC", 3)) # 下一首歌延迟播放秒数
def append_keyword(self, keys, action): def append_keyword(self, keys, action):

View File

@ -0,0 +1,59 @@
# convert_to_mp3.py
import os
import subprocess
import tempfile
from pydub import AudioSegment
from pydub.playback import play
from xiaomusic.config import Config
class Convert_To_MP3:
def __init__(self, config: Config):
self.config = config
self.music_path = self.config.music_path
self.ffmpeg_location = self.config.ffmpeg_location
@staticmethod
def convert_to_mp3(input_file: str, ffmpeg_location: str, music_path: str) -> str:
"""
Convert the music file to MP3 format and return the path of the temporary MP3 file.
"""
# 指定临时文件的目录为 music_path 目录下的 tmp 文件夹
temp_dir = os.path.join(music_path, 'tmp')
if not os.path.exists(temp_dir):
os.makedirs(temp_dir) # 确保目录存在
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp3', dir=temp_dir)
temp_file.close()
temp_file_path = temp_file.name
command = [
ffmpeg_location,
'-i', input_file,
'-f', 'mp3',
'-y',
temp_file_path
]
try:
subprocess.run(command, check=True)
except subprocess.CalledProcessError as e:
print(f"Error during conversion: {e}")
return None
return temp_file_path
@classmethod
def convert_and_play(cls, input_file: str, ffmpeg_location: str):
"""
将音乐文件转码为 MP3 格式播放然后不删除临时文件依赖于 xiaomusic 启动时的清理逻辑
"""
temp_mp3_file = cls.convert_to_mp3(input_file, ffmpeg_location, cls.music_path)
if temp_mp3_file:
try:
# 假设 xiaomusic_playmusic 是一个播放 MP3 文件的函数
cls.xiaomusic.xiaomusic_playmusic(temp_mp3_file)
finally:
# 此处不再删除临时文件,依赖 xiaomusic 的清理逻辑
pass
else:
print("Conversion failed")

View File

@ -98,7 +98,13 @@ var vConsole = new window.VConsole();
<label for="remove_id3tag">去除MP3 ID3v2和填充减少播放前延迟:</label> <label for="remove_id3tag">去除MP3 ID3v2和填充减少播放前延迟:</label>
<select id="remove_id3tag"> <select id="remove_id3tag">
<option value="true" >true</option> <option value="true">true</option>
<option value="false" selected>false</option>
</select>
<label for="convert_to_mp3">转换为MP3</label>
<select id="convert_to_mp3">
<option value="true">true</option>
<option value="false" selected>false</option> <option value="false" selected>false</option>
</select> </select>

View File

@ -12,6 +12,7 @@ import re
import shutil import shutil
import string import string
import tempfile import tempfile
import subprocess
from collections.abc import AsyncIterator from collections.abc import AsyncIterator
from http.cookies import SimpleCookie from http.cookies import SimpleCookie
from urllib.parse import urlparse from urllib.parse import urlparse

View File

@ -45,6 +45,8 @@ from xiaomusic.utils import (
traverse_music_directory, traverse_music_directory,
) )
from xiaomusic.convert_to_mp3 import Convert_To_MP3
class XiaoMusic: class XiaoMusic:
def __init__(self, config: Config): def __init__(self, config: Config):
@ -64,6 +66,12 @@ class XiaoMusic:
self.devices = {} # key 为 did self.devices = {} # key 为 did
self.running_task = [] self.running_task = []
# 在程序启动时调用清理函数
self.cleanup_old_temp_files()
self.convert_to_mp3 = self.config.convert_to_mp3
self.ffmpeg_location = self.config.ffmpeg_location
self.music_path = self.config.music_path
# 初始化配置 # 初始化配置
self.init_config() self.init_config()
@ -107,6 +115,7 @@ class XiaoMusic:
self.exclude_dirs = set(self.config.exclude_dirs.split(",")) self.exclude_dirs = set(self.config.exclude_dirs.split(","))
self.music_path_depth = self.config.music_path_depth self.music_path_depth = self.config.music_path_depth
self.remove_id3tag = self.config.remove_id3tag self.remove_id3tag = self.config.remove_id3tag
self.convert_to_mp3 = self.config.convert_to_mp3
def update_devices(self): def update_devices(self):
self.device_id_did = {} # key 为 device_id self.device_id_did = {} # key 为 device_id
@ -318,6 +327,23 @@ class XiaoMusic:
return filename return filename
return "" return ""
def cleanup_old_temp_files(self):
"""
清理在 /tmp 目录下旧的临时 MP3 文件
"""
temp_dir = '/tmp' # 临时文件存储的目录
file_ext = '.mp3' # 临时文件的扩展名
try:
for filename in os.listdir(temp_dir):
if filename.endswith(file_ext):
file_path = os.path.join(temp_dir, filename)
# 如果文件是超过一天的旧文件,则删除它
if (time.time() - os.path.getmtime(file_path)) > 86400:
os.remove(file_path)
self.log.info(f"Deleted old temporary file: {file_path}")
except Exception as e:
self.log.error(f"Failed to cleanup old temp files: {e}")
# 判断本地音乐是否存在,网络歌曲不判断 # 判断本地音乐是否存在,网络歌曲不判断
def is_music_exist(self, name): def is_music_exist(self, name):
if name not in self.all_music: if name not in self.all_music:
@ -381,15 +407,37 @@ class XiaoMusic:
else: else:
self.log.info("No ID3 tag remove needed") self.log.info("No ID3 tag remove needed")
# 如果开启了MP3转换功能且文件不是MP3格式则进行转换
if self.convert_to_mp3 and not is_mp3(filename):
self.log.info(f"convert_to_mp3 is enabled. Checking file: {filename}")
temp_mp3_file = self.convert_file_to_mp3(filename)
if temp_mp3_file:
# 转换成功后修改文件名为music_path/tmp下的相对路径
relative_path = os.path.relpath(temp_mp3_file, self.config.music_path)
self.log.info(f"Converted file: {temp_mp3_file} to {relative_path}")
filename = relative_path
else:
self.log.warning(f"Failed to convert file to MP3 format: {filename}")
return "" # 转换失败返回空字符串表示无法获取播放URL
# 构造音乐文件的URL
filename = filename.replace("\\", "/") filename = filename.replace("\\", "/")
if filename.startswith(self.config.music_path): if filename.startswith(self.config.music_path):
filename = filename[len(self.config.music_path) :] filename = filename[len(self.config.music_path):]
if filename.startswith("/"): if filename.startswith("/"):
filename = filename[1:] filename = filename[1:]
self.log.info(f"get_music_url local music. name:{name}, filename:{filename}")
encoded_name = urllib.parse.quote(filename) encoded_name = urllib.parse.quote(filename)
return f"http://{self.hostname}:{self.public_port}/music/{encoded_name}" return f"http://{self.hostname}:{self.public_port}/music/{encoded_name}"
def convert_file_to_mp3(self, input_file):
"""
Convert the file to MP3 format using convert_to_mp3.py.
"""
# 创建 Convert_To_MP3 类的实例,只传递 config 对象
converter = Convert_To_MP3(self.config)
# 调用静态方法 convert_to_mp3并传递所需的文件路径和 ffmpeg 位置
return converter.convert_to_mp3(input_file, self.ffmpeg_location, self.music_path)
# 获取目录下所有歌曲,生成随机播放列表 # 获取目录下所有歌曲,生成随机播放列表
def _gen_all_music_list(self): def _gen_all_music_list(self):
self.all_music = {} self.all_music = {}
@ -404,7 +452,7 @@ class XiaoMusic:
if len(files) == 0: if len(files) == 0:
continue continue
if dir_name == os.path.basename(self.music_path): if dir_name == os.path.basename(self.music_path):
dir_name = "其他" dir_name = "默认"
if self.music_path != self.download_path and dir_name == os.path.basename( if self.music_path != self.download_path and dir_name == os.path.basename(
self.download_path self.download_path
): ):