Skip to content

Commit

Permalink
gui: add fav page (#744)
Browse files Browse the repository at this point in the history
1. add fav page
2. introduce several protocols
    - SupportsCurrentUserFavSongsReader
    - SupportsCurrentUserFavAlbumsReader
    - SupportsCurrentUserFavArtistsReader
    - SupportsCurrentUserFavPlaylistsReader
    - SupportsCurrentUserFavVideosReader
  • Loading branch information
cosven authored Jan 6, 2024
1 parent 73349c0 commit 2e7de10
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 44 deletions.
10 changes: 10 additions & 0 deletions feeluown/gui/base_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from abc import abstractmethod
from typing import runtime_checkable, Protocol

from feeluown.models import ModelType
from feeluown.gui.widgets.tabbar import Tab, TabBar


Expand Down Expand Up @@ -67,6 +68,15 @@ def render_tab_bar(self):
def render_by_tab_index(self, tab_index):
raise NotImplementedError

def default_tabs(self):
return [
('歌曲', ModelType.song, self.show_songs),
('专辑', ModelType.album, self.show_albums),
('歌手', ModelType.artist, self.show_artists),
('歌单', ModelType.playlist, self.show_playlists),
('视频', ModelType.video, self.show_videos)
]


@runtime_checkable
class VFillableBg(Protocol):
Expand Down
2 changes: 2 additions & 0 deletions feeluown/gui/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ def initialize(self):
from feeluown.gui.pages.recommendation import render as render_rec
from feeluown.gui.pages.recommendation_daily_songs import \
render as render_rec_daily_songs
from feeluown.gui.pages.my_fav import render as render_my_fav

model_prefix = f'{MODEL_PAGE_PREFIX}<provider>'

Expand All @@ -214,6 +215,7 @@ async def dummy_render(req, *args, **kwargs):
('/search', render_search),
('/rec', render_rec),
('/rec/daily_songs', render_rec_daily_songs),
('/my_fav', render_my_fav),
]
for url, renderer in urlpatterns:
self.route(url)(renderer)
31 changes: 31 additions & 0 deletions feeluown/gui/drawers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import math
from typing import Optional

from PyQt5.QtCore import Qt, QRect, QPoint, QPointF
Expand Down Expand Up @@ -255,3 +256,33 @@ def paint(self, painter: QPainter):
painter.drawLine(self.p3, self.p4)
painter.drawLine(self.p4, self.p5)
painter.drawLine(self.p4, self.p6)


class StarIconDrawer:
def __init__(self, length, padding):

radius_outer = (length - 2*padding)//2
length_half = length // 2
radius_inner = radius_outer // 2
center = QPointF(length_half, length_half)
angle = math.pi / 2

self._star_polygon = QPolygonF()
for _ in range(5):
outer_point = center + QPointF(
radius_outer * math.cos(angle),
-radius_outer * math.sin(angle)
)
self._star_polygon.append(outer_point)
inner_point = center + QPointF(
radius_inner * math.cos(angle + math.pi/5),
-radius_inner * math.sin(angle + math.pi/5)
)
self._star_polygon.append(inner_point)
angle += 2 * math.pi / 5

def paint(self, painter: QPainter):
pen = painter.pen()
pen.setWidthF(1.5)
painter.setPen(pen)
painter.drawPolygon(self._star_polygon)
8 changes: 1 addition & 7 deletions feeluown/gui/pages/coll_mixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,7 @@ def __init__(self, app, tab_index, coll: Collection):
self._app = app
self._coll = coll
self.tab_index = tab_index
self.tabs = [
('歌曲', ModelType.song, self.show_songs),
('专辑', ModelType.album, self.show_albums),
('歌手', ModelType.artist, self.show_artists),
('歌单', ModelType.playlist, self.show_playlists),
('视频', ModelType.video, self.show_videos)
]
self.tabs = self.default_tabs()

async def render(self):
coll = self._coll
Expand Down
83 changes: 83 additions & 0 deletions feeluown/gui/pages/my_fav.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from feeluown.app.gui_app import GuiApp
from feeluown.models import ModelType
from feeluown.utils.aio import run_fn
from feeluown.gui.page_containers.table import Renderer
from feeluown.gui.base_renderer import TabBarRendererMixin
from feeluown.library import (
SupportsCurrentUserFavSongsReader,
SupportsCurrentUserFavAlbumsReader,
SupportsCurrentUserFavArtistsReader,
SupportsCurrentUserFavPlaylistsReader,
SupportsCurrentUserFavVideosReader,
)
from feeluown.utils.reader import create_reader
from .template import render_error_message


async def render(req, **kwargs):
app: GuiApp = req.ctx['app']
ui = app.ui
tab_index = int(req.query.get('tab_index', 0))
pvd_ui = app.current_pvd_ui_mgr.get()
if pvd_ui is None:
return await render_error_message(app, '当前资源提供方未知,无法浏览该页面')

ui.right_panel.set_body(ui.right_panel.scrollarea)
table_container = ui.right_panel.table_container
renderer = MyFavRenderer(app, tab_index, pvd_ui.provider)
await table_container.set_renderer(renderer)


class MyFavRenderer(Renderer, TabBarRendererMixin):
def __init__(self, app, tab_index, provider):
self._app = app
self._provider = provider
self.tab_index = tab_index
self.tabs = self.default_tabs()

async def render(self):
self.meta_widget.show()
self.meta_widget.title = '我的收藏'
self.render_tab_bar()
await self.render_models()

async def render_models(self):
# pylint: disable=too-many-branches
_, mtype, show_handler = self.tabs[self.tab_index]
err = ''
reader = create_reader([])
if mtype is ModelType.song:
if isinstance(self._provider, SupportsCurrentUserFavSongsReader):
reader = await run_fn(self._provider.current_user_fav_create_songs_rd)
else:
err = '收藏的歌曲'
elif mtype is ModelType.album:
if isinstance(self._provider, SupportsCurrentUserFavAlbumsReader):
reader = await run_fn(self._provider.current_user_fav_create_albums_rd)
else:
err = '收藏的专辑'
elif mtype is ModelType.artist:
if isinstance(self._provider, SupportsCurrentUserFavArtistsReader):
reader = await run_fn(self._provider.current_user_fav_create_artists_rd)
else:
err = '收藏的歌手'
elif mtype is ModelType.playlist:
if isinstance(self._provider, SupportsCurrentUserFavPlaylistsReader):
reader = await run_fn(self._provider.current_user_fav_create_playlists_rd) # noqa
else:
err = '收藏的歌单'
else:
if isinstance(self._provider, SupportsCurrentUserFavVideosReader):
reader = await run_fn(self._provider.current_user_fav_create_videos_rd)
else:
err = '收藏的视频'
if err:
return await render_error_message(
self._app,
f'当前资源提供方({self._provider.name})不支持获取 {err}'
)
else:
show_handler(reader)

def render_by_tab_index(self, tab_index):
self._app.browser.goto(page='/my_fav', query={'tab_index': tab_index})
15 changes: 13 additions & 2 deletions feeluown/gui/uimain/sidebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
HomeButton,
PlusButton,
TriagleButton,
StarButton,
)

from feeluown.gui.widgets.playlists import PlaylistsView
Expand Down Expand Up @@ -107,6 +108,7 @@ def __init__(self, app: 'GuiApp', parent=None):

self.home_btn = HomeButton(height=30, parent=self)
self.discovery_btn = DiscoveryButton(height=30, padding=0.2, parent=self)
self.fav_btn = StarButton('我的收藏', height=30, parent=self)
self.collections_header = QLabel('本地收藏集', self)
self.collections_header.setToolTip('我们可以在本地建立『收藏集』来收藏自己喜欢的音乐资源\n\n'
'每个收藏集都以一个独立 .fuo 文件的存在,'
Expand Down Expand Up @@ -141,6 +143,7 @@ def __init__(self, app: 'GuiApp', parent=None):
self._top_layout.setContentsMargins(15, 16, 16, 0)
self._top_layout.addWidget(self.home_btn)
self._top_layout.addWidget(self.discovery_btn)
self._top_layout.addWidget(self.fav_btn)
self._sub_layout.setContentsMargins(16, 8, 16, 0)
self._sub_layout.addWidget(self.collections_con)
self._sub_layout.addWidget(self.my_music_con)
Expand All @@ -156,6 +159,7 @@ def __init__(self, app: 'GuiApp', parent=None):
self.playlists_con.hide()
self.my_music_con.hide()
self.discovery_btn.setDisabled(True)
self.fav_btn.setDisabled(True)
self.discovery_btn.setToolTip('当前资源提供方未知')

self.home_btn.clicked.connect(self.show_library)
Expand All @@ -173,6 +177,8 @@ def __init__(self, app: 'GuiApp', parent=None):
self.on_current_pvd_ui_changed)
self.discovery_btn.clicked.connect(
lambda: self._app.browser.goto(page='/rec'))
self.fav_btn.clicked.connect(
lambda: self._app.browser.goto(page='/my_fav'))

def popup_collection_adding_dialog(self):
dialog = QDialog(self)
Expand Down Expand Up @@ -282,5 +288,10 @@ def do():
box.open()

def on_current_pvd_ui_changed(self, pvd_ui, _):
self.discovery_btn.setEnabled(True)
self.discovery_btn.setToolTip(f'点击进入 {pvd_ui.provider.name} 推荐页')
if pvd_ui:
self.discovery_btn.setEnabled(True)
self.fav_btn.setEnabled(True)
self.discovery_btn.setToolTip(f'点击进入 {pvd_ui.provider.name} 推荐页')
else:
self.discovery_btn.setEnabled(False)
self.fav_btn.setEnabled(False)
1 change: 1 addition & 0 deletions feeluown/gui/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
HomeButton, LeftArrowButton, RightArrowButton, SearchButton, SettingsButton,
PlusButton, TriagleButton, DiscoveryButton,
SelfPaintAbstractIconTextButton, CalendarButton, RankButton,
StarButton,
)
12 changes: 11 additions & 1 deletion feeluown/gui/widgets/selfpaint_btn.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from feeluown.gui.drawers import (
HomeIconDrawer, PlusIconDrawer, TriangleIconDrawer, CalendarIconDrawer,
RankIconDrawer,
RankIconDrawer, StarIconDrawer,
)
from feeluown.gui.helpers import darker_or_lighter

Expand Down Expand Up @@ -296,6 +296,15 @@ def draw_icon(self, painter):
self.rank_icon.paint(painter)


class StarButton(SelfPaintAbstractIconTextButton):
def __init__(self, text='收藏', *args, **kwargs):
super().__init__(text, *args, **kwargs)
self.star_icon = StarIconDrawer(self.height(), self._padding)

def draw_icon(self, painter):
self.star_icon.paint(painter)


if __name__ == '__main__':
from feeluown.gui.debug import simple_layout

Expand All @@ -315,3 +324,4 @@ def draw_icon(self, painter):
layout.addWidget(TriagleButton(length=length, direction='up'))
layout.addWidget(CalendarButton(height=length))
layout.addWidget(RankButton(height=length))
layout.addWidget(StarButton(height=length))
74 changes: 40 additions & 34 deletions feeluown/library/provider_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,6 @@
from .flags import Flags as PF


__all__ = (
'SupportsAlbumGet',
'SupportsAlbumSongsReader',

'SupportsArtistAlbumsReader',
'SupportsArtistGet',
'SupportsArtistSongsReader',

'SupportsCurrentUser',

'SupportsPlaylistAddSong',
'SupportsPlaylistGet',
'SupportsPlaylistCreateByName',
'SupportsPlaylistDelete',
'SupportsPlaylistRemoveSong',
'SupportsPlaylistSongsReader',

'SupportsSongGet',
'SupportsSongHotComments',
'SupportsSongLyric',
'SupportsSongMV',
'SupportsSongMultiQuality',
'SupportsSongSimilar',
'SupportsSongWebUrl',

'SupportsVideoGet',
'SupportsVideoMultiQuality',

'SupportsRecListDailySongs',
'SupportsRecListDailyAlbums',
'SupportsRecListDailyPlaylists',
)


ID = str
_FlagProtocolMapping: Dict[Tuple[ModelType, PF], type] = {}

Expand Down Expand Up @@ -349,6 +315,46 @@ def get_current_user(self) -> UserModel:
"""


#
# Protocols for current user favorites/collections
#
@runtime_checkable
class SupportsCurrentUserFavSongsReader(Protocol):
@abstractmethod
def current_user_fav_create_songs_rd(self):
"""
: raises NoUserLoggedIn:
"""


@runtime_checkable
class SupportsCurrentUserFavAlbumsReader(Protocol):
@abstractmethod
def current_user_fav_create_albums_rd(self):
pass


@runtime_checkable
class SupportsCurrentUserFavArtistsReader(Protocol):
@abstractmethod
def current_user_fav_create_artists_rd(self):
pass


@runtime_checkable
class SupportsCurrentUserFavPlaylistsReader(Protocol):
@abstractmethod
def current_user_fav_create_playlists_rd(self):
pass


@runtime_checkable
class SupportsCurrentUserFavVideosReader(Protocol):
@abstractmethod
def current_user_fav_create_videos_rd(self):
pass


#
# Protocols for recommendation.
#
Expand Down

0 comments on commit 2e7de10

Please sign in to comment.