From 609cb4f10f4d15955f250464486414cc2e38161a Mon Sep 17 00:00:00 2001 From: "Gao, Ruiyuan" <905370712@qq.com> Date: Tue, 24 Sep 2024 07:24:33 +0800 Subject: [PATCH] use async task for building tags (#195) * use threading for building tags * change tag cache to use asyncio * sleep 0 is not effective, change to 0.001 --- xiaomusic/xiaomusic.py | 64 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/xiaomusic/xiaomusic.py b/xiaomusic/xiaomusic.py index cc8a66f..e9cc1d2 100644 --- a/xiaomusic/xiaomusic.py +++ b/xiaomusic/xiaomusic.py @@ -73,6 +73,7 @@ class XiaoMusic: self.devices = {} # key 为 did self.running_task = [] self.all_music_tags = {} # 歌曲额外信息 + self._tag_generation_task = False self._extra_index_search = {} # 初始化配置 @@ -440,6 +441,8 @@ class XiaoMusic: # 给前端调用 def refresh_music_tag(self): + if not self.ensure_single_thread_for_tag(): + return filename = self.config.tag_cache_path if filename is not None: # 清空 cache @@ -449,8 +452,8 @@ class XiaoMusic: else: self.log.info("刷新:tag cache 未启用") # TODO: 优化性能? - self._gen_all_music_list() - self.log.debug("刷新:已重建 tag cache") + self.try_gen_all_music_tag() + self.log.info("刷新:已启动重建 tag cache") def try_load_from_tag_cache(self) -> dict: filename = self.config.tag_cache_path @@ -478,10 +481,50 @@ class XiaoMusic: else: self.log.info("保存:tag cache 未启用") + def ensure_single_thread_for_tag(self): + if self._tag_generation_task: + self.log.info(f"tag 更新中,请等待") + return not self._tag_generation_task + + def try_gen_all_music_tag(self, only_items: dict = None): + if self.ensure_single_thread_for_tag(): + loop = asyncio.get_event_loop() + if loop.is_running(): + asyncio.ensure_future(self._gen_all_music_tag(only_items)) + self.log.info("启动后台构建 tag cache") + else: + self.log.info("协程时间循环未启动") + + async def _gen_all_music_tag(self, only_items: dict = None): + self._tag_generation_task = True + if only_items is None: + only_items = self.all_music # 默认更新全部 + + all_music_tags = self.try_load_from_tag_cache() + all_music_tags.update(self.all_music_tags) # 保证最新 + for name, file_or_url in only_items.items(): + await asyncio.sleep(0.001) + if name not in all_music_tags: + try: + if self.is_web_music(name): + # TODO: 网络歌曲获取歌曲额外信息 + pass + elif os.path.exists(file_or_url): + all_music_tags[name] = get_audio_metadata(file_or_url) + else: + self.log.info(f"{name}/{file_or_url} 无法更新 tag") + except BaseException as e: + self.log.info(f"{e} {file_or_url} error {type(file_or_url)}!") + # 全部更新结束后,一次性赋值 + self.all_music_tags = all_music_tags + # 刷新 tag cache + self.try_save_tag_cache() + self._tag_generation_task = False + self.log.info(f"tag 更新完成") + # 获取目录下所有歌曲,生成随机播放列表 def _gen_all_music_list(self): self.all_music = {} - self.all_music_tags = self.try_load_from_tag_cache() all_music_by_dir = {} local_musics = traverse_music_directory( self.music_path, @@ -505,8 +548,6 @@ class XiaoMusic: filename = os.path.basename(file) (name, _) = os.path.splitext(filename) self.all_music[name] = file - if name not in self.all_music_tags: - self.all_music_tags[name] = get_audio_metadata(file) all_music_by_dir[dir_name][name] = True self.log.debug(f"_gen_all_music_list {name}:{dir_name}:{file}") @@ -547,8 +588,8 @@ class XiaoMusic: if not (v.startswith("http") or v.startswith("https")): self._extra_index_search[v] = k - # 刷新 tag cache - self.try_save_tag_cache() + # all_music 更新,重建 tag + self.try_gen_all_music_tag() def _append_custom_play_list(self): if not self.config.custom_play_list_json: @@ -581,9 +622,6 @@ class XiaoMusic: if (not name) or (not url): continue self.all_music[name] = url - # TODO: 网络歌曲获取歌曲额外信息 - # if name not in self.all_music_tags: - # self.all_music_tags[name] = get_audio_metadata(url) one_music_list.append(name) # 处理电台列表 @@ -605,6 +643,7 @@ class XiaoMusic: await asyncio.sleep(3600) async def run_forever(self): + self.try_gen_all_music_tag() # 事件循环开始后调用一次 self.crontab.start() await self.analytics.send_startup_event() analytics_task = asyncio.create_task(self.analytics_task_daily()) @@ -1395,7 +1434,10 @@ class XiaoMusicDevice: # 把下载的音乐加入播放列表 def add_download_music(self, name): - self.xiaomusic.all_music[name] = os.path.join(self.download_path, f"{name}.mp3") + filepath = os.path.join(self.download_path, f"{name}.mp3") + self.xiaomusic.all_music[name] = filepath + # 应该很快,直接运行 + self.xiaomusic._gen_all_music_tag({name: filepath}) if name not in self._play_list: self._play_list.append(name) self.log.info(f"add_download_music add_music {name}")