• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

feeluown / FeelUOwn / 3607006942

pending completion
3607006942

push

github

GitHub
gui: new search page (#627)

155 of 155 new or added lines in 6 files covered. (100.0%)

7619 of 12598 relevant lines covered (60.48%)

0.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

25.0
/feeluown/gui/pages/search.py
1
from PyQt5.QtWidgets import QFrame, QVBoxLayout
1✔
2

3
from feeluown.models import SearchType
1✔
4
from feeluown.gui.page_containers.table import TableContainer, Renderer
1✔
5
from feeluown.gui.page_containers.scroll_area import ScrollArea
1✔
6
from feeluown.gui.widgets.imglist import ImgListView
1✔
7
from feeluown.gui.widgets.songs import SongsTableView, ColumnsMode
1✔
8
from feeluown.gui.base_renderer import TabBarRendererMixin
1✔
9
from feeluown.gui.helpers import BgTransparentMixin
1✔
10
from feeluown.gui.widgets.magicbox import KeySourceIn, KeyType
1✔
11
from feeluown.gui.widgets.header import LargeHeader, MidHeader
1✔
12
from feeluown.gui.widgets.accordion import Accordion
1✔
13
from feeluown.utils.reader import create_reader
1✔
14

15
Tabs = [('歌曲', SearchType.so),
1✔
16
        ('专辑', SearchType.al),
17
        ('歌手', SearchType.ar),
18
        ('歌单', SearchType.pl),
19
        ('视频', SearchType.vi)]
20

21

22
async def render(req, **kwargs):  # pylint: disable=too-many-locals
1✔
23
    """/search handler
24

25
    :type app: feeluown.app.App
26
    """
27
    q = req.query.get('q', '')
×
28
    if not q:
×
29
        return
×
30

31
    tab_index = int(req.query.get('tab_index', 0))
×
32
    source_in = req.query.get('source_in', None)
×
33
    if source_in is not None:
×
34
        source_in = source_in.split(',')
×
35
    else:
36
        source_in = None
×
37

38
    app = req.ctx['app']
×
39

40
    body = Body()
×
41
    view = View(app, q)
×
42
    body.setWidget(view)
×
43
    app.ui.right_panel.set_body(body)
×
44

45
    search_type = Tabs[tab_index][1]
×
46

47
    is_first = True  # Is first search result.
×
48
    async for result in app.library.a_search(
×
49
            q, type_in=search_type, source_in=source_in):
50
        if result is not None:
×
51
            table_container = TableContainer(app, view.accordion)
×
52

53
            # HACK: set fixed row for tables.
54
            # pylint: disable=protected-access
55
            for table in table_container._tables:
×
56
                if isinstance(table, ImgListView):
×
57
                    table._fixed_row_count = 2
×
58
                    table.img_min_width = 100
×
59
                elif isinstance(table, SongsTableView):
×
60
                    table._fixed_row_count = 8
×
61
                    table._row_height = table.verticalHeader().defaultSectionSize()
×
62

63
            table_container.layout().setContentsMargins(0, 0, 0, 0)
×
64

65
            renderer = SearchResultRenderer(q, tab_index, source_in=source_in)
×
66
            await table_container.set_renderer(renderer)
×
67

68
            _, search_type, attrname, show_handler = renderer.tabs[tab_index]
×
69
            objects = getattr(result, attrname) or []
×
70
            if search_type is SearchType.so:
×
71
                show_handler(create_reader(objects), columns_mode=ColumnsMode.playlist)
×
72
            else:
73
                show_handler(create_reader(objects))
×
74

75
            if is_first is False:
×
76
                table_container.hide()
×
77

78
            renderer.meta_widget.hide()
×
79
            renderer.toolbar.hide()
×
80

81
            provider = app.library.get(result.source)
×
82
            provider_name = result.source if provider is None else provider.name
×
83
            view.accordion.add_section(MidHeader(provider_name), table_container, 6, 12)
×
84
            is_first = False
×
85

86

87
class SearchResultRenderer(Renderer, TabBarRendererMixin):
1✔
88
    def __init__(self, q, tab_index, source_in=None):
1✔
89
        self.q = q
×
90
        self.tab_index = tab_index
×
91
        self.source_in = source_in
×
92

93
        self.tabs = [
×
94
            (*Tabs[0], 'songs', self.show_songs),
95
            (*Tabs[1], 'albums', self.show_albums),
96
            (*Tabs[2], 'artists', self.show_artists),
97
            (*Tabs[3], 'playlists', self.show_playlists),
98
            (*Tabs[4], 'videos', self.show_videos),
99
        ]
100

101
    async def render(self):
1✔
102
        self.render_tab_bar()
×
103

104
    def render_by_tab_index(self, tab_index):
1✔
105
        search_type = self.tabs[tab_index][1]
×
106
        self._app.browser.local_storage[KeyType] = search_type.value
×
107
        query = {'q': self.q, 'tab_index': tab_index}
×
108
        source_in = self._app.browser.local_storage.get(KeySourceIn, None)
×
109
        if source_in is not None:
×
110
            query['source_in'] = source_in
×
111
        self._app.browser.goto(page='/search', query=query)
×
112

113

114
class Body(ScrollArea):
1✔
115
    def fillable_bg_height(self):
1✔
116
        """Implement VFillableBg protocol"""
117
        return self.widget().height() - self.widget().accordion.height()
×
118

119

120
class View(QFrame, BgTransparentMixin):
1✔
121
    def __init__(self, app, q):
1✔
122
        super().__init__()
×
123

124
        self._app = app
×
125

126
        self.title = LargeHeader(f'搜索“{q}”')
×
127
        self.accordion = Accordion()
×
128

129
        self._layout = QVBoxLayout(self)
×
130
        self._layout.setContentsMargins(20, 0, 20, 0)
×
131
        self._layout.setSpacing(0)
×
132
        self._layout.addSpacing(30)
×
133
        self._layout.addWidget(self.title)
×
134
        self._layout.addSpacing(10)
×
135
        self._layout.addWidget(self.accordion)
×
136
        self._layout.addStretch(0)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc