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
FROM python:3.10-slim
RUN pip install pydub
RUN python3 -m venv /app/.venv
RUN /app/.venv/bin/pip install pydub
WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
COPY --from=builder /app/ffmpeg /app/ffmpeg

View File

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

View File

@ -83,7 +83,7 @@ class Config:
search_prefix: str = os.getenv(
"XIAOMUSIC_SEARCH", "bilisearch:"
) # "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(
"XIAOMUSIC_ACTIVE_CMD", "play,set_random_play,playlocal,play_music_list,stop"
)
@ -136,6 +136,9 @@ class Config:
remove_id3tag: bool = (
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)) # 下一首歌延迟播放秒数
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>
<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>
</select>

View File

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

View File

@ -45,6 +45,8 @@ from xiaomusic.utils import (
traverse_music_directory,
)
from xiaomusic.convert_to_mp3 import Convert_To_MP3
class XiaoMusic:
def __init__(self, config: Config):
@ -64,6 +66,12 @@ class XiaoMusic:
self.devices = {} # key 为 did
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()
@ -107,6 +115,7 @@ class XiaoMusic:
self.exclude_dirs = set(self.config.exclude_dirs.split(","))
self.music_path_depth = self.config.music_path_depth
self.remove_id3tag = self.config.remove_id3tag
self.convert_to_mp3 = self.config.convert_to_mp3
def update_devices(self):
self.device_id_did = {} # key 为 device_id
@ -318,6 +327,23 @@ class XiaoMusic:
return filename
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):
if name not in self.all_music:
@ -381,15 +407,37 @@ class XiaoMusic:
else:
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("\\", "/")
if filename.startswith(self.config.music_path):
filename = filename[len(self.config.music_path) :]
filename = filename[len(self.config.music_path):]
if filename.startswith("/"):
filename = filename[1:]
self.log.info(f"get_music_url local music. name:{name}, filename:{filename}")
encoded_name = urllib.parse.quote(filename)
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):
self.all_music = {}
@ -404,7 +452,7 @@ class XiaoMusic:
if len(files) == 0:
continue
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(
self.download_path
):