mirror of
https://github.com/hanxi/xiaomusic.git
synced 2025-12-06 14:52:50 +08:00
feat: 监测音乐文件夹变化更新歌曲列表 (#394)
* feat: 监测音乐文件夹变化更新歌曲列表 * fix:增加预设依赖 * Revert "fix:增加预设依赖" This reverts commit3705424e98. * Revert "feat: 监测音乐文件夹变化更新歌曲列表" This reverts commit939c59b908. * feat: 监测音乐文件夹变化更新歌曲列表 新增环境变量: XIAOMUSIC_ENABLE_FILE_WATCH #控制目录监控开关 XIAOMUSIC_FILE_WATCH_DEBOUNCE #控制刷新目录任务刷新延迟 也可通过网页设置,默认更换关闭 * fix:设置文字描述修正
This commit is contained in:
@@ -8,6 +8,7 @@ authors = [
|
||||
dependencies = [
|
||||
"aiohttp>=3.8.6",
|
||||
"miservice-fork>=2.7.0",
|
||||
"watchdog>=6.0.0",
|
||||
"mutagen>=1.47.0",
|
||||
"yt-dlp[default]>=2024.12.1.232904.dev0",
|
||||
"uvicorn>=0.30.1",
|
||||
|
||||
@@ -169,6 +169,11 @@ class Config:
|
||||
continue_play: bool = (
|
||||
os.getenv("XIAOMUSIC_CONTINUE_PLAY", "false").lower() == "true"
|
||||
)
|
||||
# 目录监控配置
|
||||
enable_file_watch: bool = (
|
||||
os.getenv("XIAOMUSIC_ENABLE_FILE_WATCH", "false").lower() == "true"
|
||||
)
|
||||
file_watch_debounce: int = int(os.getenv("XIAOMUSIC_FILE_WATCH_DEBOUNCE", 10)) # 监控刷新延迟时间(秒)
|
||||
pull_ask_sec: int = int(os.getenv("XIAOMUSIC_PULL_ASK_SEC", "1"))
|
||||
enable_pull_ask: bool = (
|
||||
os.getenv("XIAOMUSIC_ENABLE_PULL_ASK", "true").lower() == "true"
|
||||
|
||||
9
xiaomusic/static/default/setting.html
vendored
9
xiaomusic/static/default/setting.html
vendored
@@ -81,6 +81,15 @@ var vConsole = new window.VConsole();
|
||||
<label for="music_path">音乐目录:</label>
|
||||
<input id="music_path" type="text" value="music" />
|
||||
|
||||
<label for="enable_file_watch">启用目录监控(自动刷新音乐列表):</label>
|
||||
<select id="enable_file_watch">
|
||||
<option value="true">开启</option>
|
||||
<option value="false" selected>关闭</option>
|
||||
</select>
|
||||
|
||||
<label for="file_watch_debounce">刷新列表延迟时间(秒):</label>
|
||||
<input id="file_watch_debounce" type="number" value="10" />
|
||||
|
||||
<label for="download_path">音乐下载目录(必须是music的子目录):</label>
|
||||
<input id="download_path" type="text" value='music/download' />
|
||||
|
||||
|
||||
9
xiaomusic/static/default_past/setting.html
vendored
9
xiaomusic/static/default_past/setting.html
vendored
@@ -72,6 +72,15 @@ var vConsole = new window.VConsole();
|
||||
<label for="music_path">音乐目录:</label>
|
||||
<input id="music_path" type="text" value="music" />
|
||||
|
||||
<label for="enable_file_watch">启用目录监控(自动刷新音乐列表):</label>
|
||||
<select id="enable_file_watch">
|
||||
<option value="true">开启</option>
|
||||
<option value="false" selected>关闭</option>
|
||||
</select>
|
||||
|
||||
<label for="file_watch_debounce">刷新列表延迟时间(秒):</label>
|
||||
<input id="file_watch_debounce" type="number" value="10" />
|
||||
|
||||
<label for="download_path">音乐下载目录(必须是music的子目录):</label>
|
||||
<input id="download_path" type="text" value='music/download' />
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ from logging.handlers import RotatingFileHandler
|
||||
from aiohttp import ClientSession, ClientTimeout
|
||||
from miservice import MiAccount, MiIOService, MiNAService, miio_command
|
||||
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
|
||||
from xiaomusic import __version__
|
||||
from xiaomusic.analytics import Analytics
|
||||
from xiaomusic.config import (
|
||||
@@ -782,11 +785,44 @@ class XiaoMusic:
|
||||
await self.analytics.send_daily_event()
|
||||
await asyncio.sleep(3600)
|
||||
|
||||
def start_file_watch(self):
|
||||
if not self.config.enable_file_watch:
|
||||
self.log.info("目录监控功能已关闭")
|
||||
return
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
except RuntimeError:
|
||||
loop = asyncio.get_event_loop()
|
||||
# 延时配置项 file_watch_debounce
|
||||
self._file_watch_handler = XiaoMusicPathWatch(
|
||||
callback=self._on_file_change,
|
||||
debounce_delay=self.config.file_watch_debounce,
|
||||
loop=loop
|
||||
)
|
||||
# 创建监控 music_path 目录对象
|
||||
self._observer = Observer()
|
||||
self._observer.schedule(self._file_watch_handler, self.music_path, recursive=True)
|
||||
self._observer.start()
|
||||
self.log.info(f"已启动对 {self.music_path} 的目录监控。")
|
||||
|
||||
def _on_file_change(self):
|
||||
self.log.info(f"检测到音乐目录文件变化,正在刷新歌曲列表。")
|
||||
self._gen_all_music_list()
|
||||
|
||||
def stop_file_watch(self):
|
||||
if hasattr(self, "_observer"):
|
||||
self._observer.stop()
|
||||
self._observer.join()
|
||||
self.log.info("已停止目录监控。")
|
||||
|
||||
async def run_forever(self):
|
||||
self.log.info("run_forever start")
|
||||
self.try_gen_all_music_tag() # 事件循环开始后调用一次
|
||||
self.crontab.start()
|
||||
asyncio.create_task(self.analytics.send_startup_event())
|
||||
# 取配置 enable_file_watch 循环开始时调用一次,控制目录监控开关
|
||||
if self.config.enable_file_watch:
|
||||
self.start_file_watch()
|
||||
analytics_task = asyncio.create_task(self.analytics_task_daily())
|
||||
assert (
|
||||
analytics_task is not None
|
||||
@@ -1332,6 +1368,8 @@ class XiaoMusic:
|
||||
self.log.info("save_cur_config ok")
|
||||
|
||||
def update_config_from_setting(self, data):
|
||||
# 保存之前的 enable_file_watch 配置
|
||||
pre_efw = self.config.enable_file_watch
|
||||
# 自动赋值相同字段的配置
|
||||
self.config.update_config(data)
|
||||
|
||||
@@ -1343,6 +1381,15 @@ class XiaoMusic:
|
||||
self.log.info(f"语音控制已启动, 用【{joined_keywords}】开头来控制")
|
||||
self.log.debug(f"key_word_dict: {self.config.key_word_dict}")
|
||||
|
||||
# 检查 enable_file_watch 配置是否发生变化
|
||||
now_efw = self.config.enable_file_watch
|
||||
if pre_efw != now_efw:
|
||||
self.log.info("配置更新:{}目录监控".format("开启" if now_efw else "关闭"))
|
||||
if now_efw:
|
||||
self.start_file_watch()
|
||||
else:
|
||||
self.stop_file_watch()
|
||||
|
||||
# 重新加载计划任务
|
||||
self.crontab.reload_config(self)
|
||||
|
||||
@@ -2097,3 +2144,22 @@ class XiaoMusicDevice:
|
||||
if name in self.xiaomusic.music_list.get("所有电台", []):
|
||||
return "所有电台"
|
||||
return "全部"
|
||||
|
||||
# 目录监控类,使用延迟防抖
|
||||
class XiaoMusicPathWatch(FileSystemEventHandler):
|
||||
def __init__(self, callback, debounce_delay, loop):
|
||||
self.callback = callback
|
||||
self.debounce_delay = debounce_delay
|
||||
self.loop = loop
|
||||
self._debounce_handle = None
|
||||
|
||||
def on_any_event(self, event):
|
||||
self.schedule_callback()
|
||||
|
||||
def schedule_callback(self):
|
||||
def _execute_callback():
|
||||
self._debounce_handle = None
|
||||
self.callback()
|
||||
if self._debounce_handle:
|
||||
self._debounce_handle.cancel()
|
||||
self._debounce_handle = self.loop.call_later(self.debounce_delay, _execute_callback)
|
||||
|
||||
Reference in New Issue
Block a user