Skip to content

Commit

Permalink
remove ProviderNotFound error
Browse files Browse the repository at this point in the history
  • Loading branch information
cosven committed Jan 17, 2024
1 parent bb77ce1 commit cae82b2
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 161 deletions.
15 changes: 11 additions & 4 deletions feeluown/excs.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,24 @@ class ProviderAlreadyRegistered(LibraryException):
pass


class ProviderNotFound(ResourceNotFound):
pass


class ModelNotFound(ResourceNotFound):
"""Model is not found
For example, a model identifier is invalid.
.. versionadded:: 3.7.7
"""
class Reason(Enum):
"""
.. versionadded:: v4.0
"""
not_found = 'not_found'
# The provider does implement corresponding protocol.
not_supported = 'not_supported'

def __init__(self, *args, reason=Reason.not_found, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.reason = reason


class NotSupported(LibraryException):
Expand Down
13 changes: 7 additions & 6 deletions feeluown/gui/components/avatar.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from PyQt5.QtWidgets import QMenu, QAction
from PyQt5.QtGui import QPainter, QIcon, QPalette, QContextMenuEvent

from feeluown.library import NoUserLoggedIn, UserModel
from feeluown.library import UserModel, SupportsCurrentUser
from feeluown.library import reverse
from feeluown.utils.aio import run_afn, run_fn
from feeluown.gui.provider_ui import UISupportsLoginOrGoHome, ProviderUiItem, \
Expand Down Expand Up @@ -106,13 +106,14 @@ async def show_provider_current_user(self):
if user is not None:
self.setToolTip(f'{user.name} ({item.text})')

async def _show_provider_current_user(self, name):
async def _show_provider_current_user(self, source):
self.setToolTip('')
self._avatar_drawer = None
try:
user = await run_fn(self._app.library.provider_get_current_user, name)
except NoUserLoggedIn:
user = None
provider = self._app.library.get(source)
assert provider is not None
user = None
if isinstance(provider, SupportsCurrentUser):
user = await run_fn(provider.get_current_user_or_none, source)

if user is None:
return None
Expand Down
23 changes: 5 additions & 18 deletions feeluown/gui/pages/song_explore.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QVBoxLayout, \
QSizePolicy, QScrollArea, QFrame

from feeluown.excs import ProviderNotFound, ResourceNotFound
from feeluown.excs import ResourceNotFound
from feeluown.library import (
SupportsSongHotComments, SupportsSongSimilar, SupportsSongWebUrl,
NotSupported, ModelFlags
Expand All @@ -30,6 +30,7 @@
from feeluown.gui.widgets.song_minicard_list import (
SongMiniCardListDelegate, SongMiniCardListModel, SongMiniCardListView
)
from .template import render_error_message

if TYPE_CHECKING:
from feeluown.app.gui_app import GuiApp
Expand All @@ -50,12 +51,9 @@ async def render(req, **kwargs): # pylint: disable=too-many-locals,too-many-bra
app: GuiApp = req.ctx['app']
song = req.ctx['model']

try:
provider = app.library.get_or_raise(song.source)
except ProviderNotFound as e:
err_view = InlineErrorMessageView()
err_view.show_msg(f'无法展示歌曲详情:{repr(e)}')
app.ui.right_panel.set_body(err_view)
provider = app.library.get(song.source)
if provider is None:
await render_error_message(app, f'没有相应的资源提供方 {song.source}')
return

# TODO: Initialize the view with song object, and it should reduce
Expand Down Expand Up @@ -354,14 +352,3 @@ def resizeEvent(self, e: QResizeEvent) -> None:
self._left_con.setMaximumWidth(
self.width() - margin_h - self._left_right_spacing - self._right_con.width())
return super().resizeEvent(e)


class InlineErrorMessageView(QLabel):
"""Error message view
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setAlignment(Qt.AlignCenter)

def show_msg(self, msg):
self.setText(msg)
4 changes: 2 additions & 2 deletions feeluown/gui/uimain/sidebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,14 +274,14 @@ def show_pool(self):
def _remove_playlist(self, playlist):

async def do():
provider = self._app.library.get_or_raise(playlist.source)
provider = self._app.library.get(playlist.source)
if isinstance(provider, SupportsPlaylistDelete):
ok = await aio.run_fn(provider.playlist_delete, playlist.identifier)
self._app.show_msg(f"删除歌单 {playlist.name} {'成功' if ok else '失败'}")
if ok is True:
self._app.pl_uimgr.model.remove(playlist)
else:
self._app.show_msg(f'资源提供方({provider.identifier})不支持删除歌单')
self._app.show_msg(f'资源提供方({playlist.source})不支持删除歌单')

box = QMessageBox(QMessageBox.Warning, '提示', f"确认删除歌单 '{playlist.name}' 吗?",
QMessageBox.Yes | QMessageBox.No, self)
Expand Down
1 change: 0 additions & 1 deletion feeluown/library/excs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
ResourceNotFound,
# FIXME: ProviderAlreadyExists should be renamed to ProviderAlreadyRegistered
ProviderAlreadyRegistered as ProviderAlreadyExists,
ProviderNotFound,
ModelNotFound,
NotSupported,
NoUserLoggedIn,
Expand Down
156 changes: 31 additions & 125 deletions feeluown/library/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,28 @@
import logging
import warnings
from functools import partial
from typing import Optional, Union, TypeVar
from typing import Optional, TypeVar

from feeluown.media import Media
from feeluown.utils.aio import run_fn, as_completed
from feeluown.utils.dispatch import Signal
from .base import SearchType, ModelType
from .provider import AbstractProvider, ProviderV2
from .provider import Provider
from .excs import (
NotSupported, MediaNotFound, NoUserLoggedIn, ProviderAlreadyExists,
ProviderNotFound, ModelNotFound, ResourceNotFound
NotSupported, MediaNotFound, ProviderAlreadyExists,
ModelNotFound, ResourceNotFound
)
from .flags import Flags as PF
from .models import (
ModelFlags as MF, BriefSongModel, UserModel,
ModelFlags as MF, BriefSongModel,
)
from .model_protocol import (
BriefVideoProtocol, ModelProtocol, BriefSongProtocol, SongProtocol, UserProtocol,
BriefVideoProtocol, ModelProtocol, BriefSongProtocol, SongProtocol,
LyricProtocol, VideoProtocol, BriefAlbumProtocol, BriefArtistProtocol
)
from .model_state import ModelState
from .provider_protocol import (
check_flag as check_flag_impl,
SupportsCurrentUser,
SupportsSongLyric, SupportsSongMV, SupportsSongMultiQuality,
SupportsVideoMultiQuality,
)
Expand Down Expand Up @@ -127,11 +126,7 @@ def err_provider_not_support_flag(pid, model_type, op):


class Library:
"""音乐库,管理资源提供方以及资源
.. versionchanged:: 4.0
Never raise ProviderNotFound error.
"""
"""Resource entrypoints."""

def __init__(self, providers_standby=None):
"""
Expand All @@ -150,7 +145,7 @@ def register(self, provider):
:raises ProviderAlreadyExists:
:raises ValueError:
"""
if not isinstance(provider, AbstractProvider):
if not isinstance(provider, Provider):
raise ValueError('invalid provider instance')
for _provider in self._providers:
if _provider.identifier == provider.identifier:
Expand All @@ -162,15 +157,15 @@ def deregister(self, provider) -> bool:
"""deregister provider
.. versionchanged:: 4.0
Do not raise ProviderNotFound anymore, return False instead.
Do not raise exception anymore, return False instead.
"""
if provider in self._providers:
self._providers.remove(provider)
self.provider_removed.emit(provider)
return True
return False

def get(self, identifier):
def get(self, identifier) -> Optional[Provider]:
"""通过资源提供方唯一标识获取提供方实例"""
for provider in self._providers:
if provider.identifier == identifier:
Expand Down Expand Up @@ -293,28 +288,6 @@ async def prepare_media(standby, policy):
return song_media_list
return song_media_list

#
# methods for v2
#

# provider common

def get_or_raise(self, identifier) -> Union[AbstractProvider, ProviderV2]:
"""
:raises ProviderNotFound:
"""
provider = self.get(identifier)
if provider is None:
raise ProviderNotFound(f'provider {identifier} not found')
return provider

def getv2_or_raise(self, identifier):
provider = self.get_or_raise(identifier)
# You should ensure the provider is v2 first. For example, if check_flags
# returns true, the provider must be a v2 instance.
assert isinstance(provider, ProviderV2), 'provider must be v2'
return provider

def check_flags(self, source: str, model_type: ModelType, flags: PF) -> bool:
"""Check if a provider satisfies the specific ability for a model type
Expand Down Expand Up @@ -387,10 +360,6 @@ def song_get_lyric(self, song: BriefSongModel) -> Optional[LyricProtocol]:
if isinstance(provider, SupportsSongLyric):
return provider.song_get_lyric(song)

def song_get_web_url(self, song: BriefSongProtocol) -> str:
provider = self.getv2_or_raise(song.source)
return provider.song_get_web_url(song)

# --------
# Album
# --------
Expand Down Expand Up @@ -424,25 +393,10 @@ def model_get(self, pid, mtype, mid):
:raise NotSupported: provider has not .get for this model type
:raise ResourceNotFound: model does not exist
"""
provider = self.get_or_raise(pid)
model = None
try_v1way = True
if isinstance(provider, ProviderV2):
if provider.use_model_v2(mtype):
if self.check_flags(pid, mtype, PF.get):
try_v1way = False
model = provider.model_get(mtype, mid)

# Try to use the ModelV1.get API to get the model.
if try_v1way and isinstance(provider, AbstractProvider):
try:
model_cls = provider.get_model_cls(mtype)
model = model_cls.get(mid)
except AttributeError:
pass
if model is None:
raise ModelNotFound
return model
provider = self.get(pid)
if provider is None:
raise ModelNotFound(f'provider:{pid} not found')
return provider.model_get(mtype, mid)

def model_get_cover(self, model):
"""Get the cover url of model
Expand All @@ -469,36 +423,30 @@ def _model_upgrade(self, model):
:raises NotSupported: provider does't impl SupportGetProtocol for the model type
:raises ModelNotFound: the model does not exist
:raises ProviderNotFound: the provider does not exist
Note you may catch ResourceNotFound exception to simplify your code.
.. versionchanged:: 3.8.11
Raise ModelNotFound if the model does not exist.
Before ModelCannotUpgrade was raised.
"""
# Return model directly if it is already a normal model.
# Return model directly if it is already a normal(upgraded) model.
if MF.normal in model.meta.flags:
return model

provider = self.getv2_or_raise(model.source)
model_type = ModelType(model.meta.model_type)
is_support = check_flag_impl(provider, model_type, PF.get)
if is_support:
try:
upgraded_model = provider.model_get(model_type, model.identifier)
except ModelNotFound:
provider = self.get(model.source)
if provider is None:
raise ModelNotFound(f'provider:{model.source} not found')
try:
upgraded_model = provider.model_get(model_type, model.identifier)
except ModelNotFound as e:
if e.reason is ModelNotFound.Reason.not_found:
model.state = ModelState.not_exists
raise
else:
# Provider should raise ModelNotFound when the mode does not exist.
# Some providers does not follow the protocol, and they may return None.
# Keep this logic to keep backward compatibility.
if upgraded_model is None:
model.state = ModelState.not_exists
raise ModelNotFound(f'provider:{provider} return an empty model')
return upgraded_model
raise NotSupported
elif e.reason is ModelNotFound.Reason.not_supported:
model.state = ModelState.cant_upgrade
raise
return upgraded_model

# --------
# Video
Expand All @@ -511,53 +459,11 @@ def video_prepare_media(self, video: BriefVideoProtocol, policy) -> Media:
:param video: either a v1 MvModel or a v2 (Brief)VideoModel.
"""
provider = self.get_or_raise(video.source)
# provider MUST has multi_quality flag for video
assert isinstance(provider, SupportsVideoMultiQuality)
media, _ = provider.video_select_media(video, policy)
provider = self.get(video.source)
if isinstance(provider, SupportsVideoMultiQuality):
media, _ = provider.video_select_media(video, policy)
else:
raise MediaNotFound('provider or video not found')
if not media:
raise MediaNotFound('provider returns empty media')
return media

# --------
# Provider
# --------
def provider_has_current_user(self, source: str) -> bool:
"""Check if a provider has a logged in user
No IO operation is triggered.
.. versionadded:: 3.7.6
"""
provider = self.get_or_raise(source)
if isinstance(provider, SupportsCurrentUser):
return provider.has_current_user()

try:
user_v1 = getattr(provider, '_user')
except AttributeError:
logger.warn("We can't determine if the provider has a current user")
return False
else:
return user_v1 is not None

def provider_get_current_user(self, source: str) -> UserProtocol:
"""Get provider current logged in user
:raises NotSupported:
:raises ProviderNotFound:
:raises NoUserLoggedIn:
.. versionadded:: 3.7.6
"""
provider = self.get_or_raise(source)
if isinstance(provider, SupportsCurrentUser):
return provider.get_current_user()

user_v1 = getattr(provider, '_user', None)
if user_v1 is None:
raise NoUserLoggedIn
return UserModel(identifier=user_v1.identifier,
source=source,
name=user_v1.name_display,
avatar_url='')
Loading

0 comments on commit cae82b2

Please sign in to comment.