From 8f232e17555b87e0c83996dfa6dd4c02fddf7fdb Mon Sep 17 00:00:00 2001 From: cosven Date: Tue, 4 Feb 2025 21:10:18 +0800 Subject: [PATCH] show search error --- feeluown/gui/components/search.py | 20 ++++++++++++-- feeluown/gui/widgets/labels.py | 9 ++++-- feeluown/library/library.py | 46 +++++++++++++++++++++++-------- feeluown/library/models.py | 2 ++ 4 files changed, 60 insertions(+), 17 deletions(-) diff --git a/feeluown/gui/components/search.py b/feeluown/gui/components/search.py index d186efe19..89698a37a 100644 --- a/feeluown/gui/components/search.py +++ b/feeluown/gui/components/search.py @@ -78,7 +78,7 @@ def __init__(self, app, **kwargs): self._layout.addStretch(0) async def search_and_render(self, q, search_type, source_in): - # pylint: disable=too-many-locals + # pylint: disable=too-many-locals,too-many-statements view = self app = self._app @@ -88,9 +88,19 @@ async def search_and_render(self, q, search_type, source_in): succeed = 0 start = datetime.now() is_first = True # Is first search result. - view.hint.show_msg('正在搜索...') + if source_in is not None: + source_count = len(source_in) + else: + source_count = len(app.library.list()) + hint_msgs = [f'正在搜索 {source_count} 个资源提供方...'] + view.hint.show_msg('\n'.join(hint_msgs)) async for result in app.library.a_search( q, type_in=search_type, source_in=source_in): + if result.err_msg: + hint_msgs.append(f'搜索 {result.source} 的资源出错:{result.err_msg}') + view.hint.show_msg('\n'.join(hint_msgs)) + continue + table_container = TableContainer(app, view.accordion) table_container.layout().setContentsMargins(0, 0, 0, 0) @@ -112,6 +122,8 @@ async def search_and_render(self, q, search_type, source_in): _, search_type, attrname, show_handler = renderer.tabs[tab_index] objects = getattr(result, attrname) or [] if not objects: # Result is empty. + hint_msgs.append(f'搜索 {result.source} 资源,提供方返回空') + view.hint.show_msg('\n'.join(hint_msgs)) continue succeed += 1 @@ -130,7 +142,9 @@ async def search_and_render(self, q, search_type, source_in): renderer.toolbar.hide() is_first = False time_cost = (datetime.now() - start).total_seconds() - view.hint.show_msg(f'搜索完成,共有 {succeed} 个有效的结果,花费 {time_cost:.2f}s') + hint_msgs.pop(0) + hint_msgs.insert(0, f'搜索完成,共有 {succeed} 个有效的结果,花费 {time_cost:.2f}s') + view.hint.show_msg('\n'.join(hint_msgs)) class SearchResultRenderer(Renderer, TabBarRendererMixin): diff --git a/feeluown/gui/widgets/labels.py b/feeluown/gui/widgets/labels.py index 8c8da6203..9d1ecc407 100644 --- a/feeluown/gui/widgets/labels.py +++ b/feeluown/gui/widgets/labels.py @@ -1,4 +1,5 @@ from PyQt5.QtCore import QTime, Qt +from PyQt5.QtGui import QPalette, QColor from PyQt5.QtWidgets import QLabel, QSizePolicy from feeluown.utils.utils import parse_ms @@ -83,7 +84,7 @@ class MessageLabel(QLabel): def __init__(self, text='', level=None, *args, **kwargs): super().__init__(*args, **kwargs) - self.setTextFormat(Qt.RichText) + self.setWordWrap(True) self.show_msg(text, level) def show_msg(self, text, level=None): @@ -96,4 +97,8 @@ def show_msg(self, text, level=None): else: hint = '️' color = SOLARIZED_COLORS['blue'] - self.setText(f"{hint}{text}") + palette = self.palette() + palette.setColor(QPalette.Text, QColor(color)) + palette.setColor(QPalette.WindowText, QColor(color)) + self.setPalette(palette) + self.setText(f"{hint}{text}") diff --git a/feeluown/library/library.py b/feeluown/library/library.py index fcdfd50e5..bb02ae812 100644 --- a/feeluown/library/library.py +++ b/feeluown/library/library.py @@ -2,7 +2,6 @@ import logging import warnings from collections import Counter -from functools import partial from typing import Optional, TypeVar, List, TYPE_CHECKING from feeluown.media import Media @@ -16,7 +15,7 @@ ) from feeluown.library.flags import Flags as PF from feeluown.library.models import ( - ModelFlags as MF, BaseModel, + ModelFlags as MF, BaseModel, SimpleSearchResult, BriefVideoModel, BriefSongModel, SongModel, LyricModel, VideoModel, BriefAlbumModel, BriefArtistModel ) @@ -135,7 +134,7 @@ def search(self, keyword, type_in=None, source_in=None, **kwargs): yield result async def a_search(self, keyword, source_in=None, timeout=None, - type_in=None, + type_in=None, return_err=False, **_): """async version of search @@ -143,22 +142,45 @@ async def a_search(self, keyword, source_in=None, timeout=None, """ type_in = SearchType.batch_parse(type_in) if type_in else [SearchType.so] + # Wrap the search function to associate the result with source. + def wrap_search(pvd, kw, t): + def search(): + try: + res = pvd.search(kw, type_=t) + except Exception as e: # noqa + e._source_ = pvd.identifier + raise e + # When a provider does not implement search method, it returns None. + if res is not None and ( + res.songs or res.albums or + res.artists or res.videos or res.playlists + ): + return res + return SimpleSearchResult( + q=keyword, source=pvd.identifier, err_msg='结果为空') + return search + fs = [] # future list for provider in self._filter(identifier_in=source_in): for type_ in type_in: - future = run_fn(partial(provider.search, keyword, type_=type_)) + future = run_fn(wrap_search(provider, keyword, type_)) fs.append(future) - - for future in as_completed(fs, timeout=timeout): + for task_ in as_completed(fs, timeout=timeout): try: - result = await future - except: # noqa + result = await task_ + except Exception as e: # noqa logger.exception('search task failed') - continue - else: - # When a provider does not implement search method, it returns None. - if result is not None: + result = SimpleSearchResult( + q=keyword, + source=e._source_, # noqa + err_msg=str(e), + ) + if return_err: yield result + else: + continue + else: + yield result async def a_song_prepare_media_no_exc(self, standby, policy): media = None diff --git a/feeluown/library/models.py b/feeluown/library/models.py index a65bf3983..a2cd5f118 100644 --- a/feeluown/library/models.py +++ b/feeluown/library/models.py @@ -450,6 +450,8 @@ class SimpleSearchResult(_BaseModel): artists: List[TArtist] = [] playlists: List[TPlaylist] = [] videos: List[TVideo] = [] + source: str = '' + err_msg: str = '' _type_modelcls_mapping = {