Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:X0F:branches:multimedia
trackma
trackma_PR688.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File trackma_PR688.patch of Package trackma
From b74380d70e7b7ebfcc1b79eda3dfb798a3dc8d5e Mon Sep 17 00:00:00 2001 From: v-fox <virtuousfox@gmail.com> Date: Tue, 2 Apr 2024 22:05:36 +0500 Subject: [PATCH] PR688 --- trackma/data.py | 6 +- trackma/engine.py | 4 +- trackma/lib/lib.py | 2 +- trackma/lib/libanilist.py | 4 +- trackma/lib/libkitsu.py | 10 +- trackma/lib/libmal.py | 4 +- trackma/lib/libshikimori.py | 6 +- trackma/lib/libvndb.py | 6 +- trackma/ui/gtk/data/searchwindow.ui | 203 ++++++++++++++++++++++------ trackma/ui/gtk/mainview.py | 1 - trackma/ui/gtk/searchwindow.py | 101 +++++++++++--- 11 files changed, 268 insertions(+), 79 deletions(-) diff --git a/trackma/data.py b/trackma/data.py index 7204543..18d67b3 100644 --- a/trackma/data.py +++ b/trackma/data.py @@ -213,12 +213,12 @@ class Data: """Get list from memory""" return self.showlist - def search(self, criteria, method): + def search(self, criteria, method, page): # Tell API to search - results = self.api.search(criteria, method) + results = self.api.search(criteria, method, page or 1) self.api.logout() if results: - return results + return results if page else results[0] raise utils.DataError('No results.') diff --git a/trackma/engine.py b/trackma/engine.py index a0dba2e..b682416 100644 --- a/trackma/engine.py +++ b/trackma/engine.py @@ -492,7 +492,7 @@ class Engine: return None - def search(self, criteria, method=utils.SearchMethod.KW): + def search(self, criteria, method=utils.SearchMethod.KW, page=None): """ Request a remote list of shows matching the criteria and returns it as a list of show dictionaries. @@ -502,7 +502,7 @@ class Engine: raise utils.EngineError( 'Search method not supported by API or mediatype.') - return self.data_handler.search(criteria, method) + return self.data_handler.search(criteria, method, page) def add_show(self, show, status=None): """ diff --git a/trackma/lib/lib.py b/trackma/lib/lib.py index af7d76c..1ed81fb 100644 --- a/trackma/lib/lib.py +++ b/trackma/lib/lib.py @@ -138,7 +138,7 @@ class lib: """ raise NotImplementedError - def search(self, criteria, method): + def search(self, criteria, method, page): """ Called when the data handler needs a detailed list of shows from the remote server. It should return a list of show dictionaries with the additional 'extra' key (which is a list of tuples) diff --git a/trackma/lib/libanilist.py b/trackma/lib/libanilist.py index ed5bb87..8331e73 100644 --- a/trackma/lib/libanilist.py +++ b/trackma/lib/libanilist.py @@ -374,7 +374,7 @@ fragment mediaListEntry on MediaList { variables = {'id': item['my_id']} self._request(query, variables) - def search(self, criteria, method): + def search(self, criteria, method, page): self.check_credentials() self.msg.info("Searching for {}...".format(criteria)) @@ -415,7 +415,7 @@ fragment mediaListEntry on MediaList { infolist.append(self._parse_info(media)) self._emit_signal('show_info_changed', infolist) - return infolist + return (infolist, len(infolist), 1, 1) def request_info(self, itemlist): self.check_credentials() diff --git a/trackma/lib/libkitsu.py b/trackma/lib/libkitsu.py index 430d690..ac3e202 100644 --- a/trackma/lib/libkitsu.py +++ b/trackma/lib/libkitsu.py @@ -452,18 +452,22 @@ class libkitsu(lib): except urllib.error.URLError as e: raise utils.APIError('Error deleting: ' + str(e.reason)) - def search(self, query, method): + def search(self, query, method, page): self.msg.info("Searching for %s..." % query) + item_per_page = 20 values = { "filter[text]": query, - "page[limit]": 20, + "page[limit]": item_per_page, + "page[offset]": (page - 1) * item_per_page } try: data = self._request('GET', self.prefix + "/" + self.mediatype, get=values) shows = json.loads(data) + pages = int(shows['meta']['count'] / item_per_page) + \ + 1 if shows['meta']['count'] % item_per_page != 0 else 0 infolist = [] for media in shows['data']: @@ -475,7 +479,7 @@ class libkitsu(lib): if not infolist: raise utils.APIError('No results.') - return infolist + return (infolist, shows['meta']['count'], page, pages,) except urllib.error.HTTPError as e: raise utils.APIError('Error searching: ' + str(e.code)) except urllib.error.URLError as e: diff --git a/trackma/lib/libmal.py b/trackma/lib/libmal.py index 2ecada8..b387294 100644 --- a/trackma/lib/libmal.py +++ b/trackma/lib/libmal.py @@ -310,7 +310,7 @@ class libmal(lib): self.msg.info("Deleting item %s..." % item['title']) data = self._request('DELETE', self.query_url + '/%s/%d/my_list_status' % (self.mediatype, item['id']), auth=True) - def search(self, criteria, method): + def search(self, criteria, method, page): self.check_credentials() self.msg.info("Searching for {}...".format(criteria)) @@ -335,7 +335,7 @@ class libmal(lib): results.append(self._parse_info(item['node'])) self._emit_signal('show_info_changed', results) - return results + return (results, len(results), 1, 1) def request_info(self, itemlist): self.check_credentials() diff --git a/trackma/lib/libshikimori.py b/trackma/lib/libshikimori.py index 82d5b7e..b636a81 100644 --- a/trackma/lib/libshikimori.py +++ b/trackma/lib/libshikimori.py @@ -282,7 +282,7 @@ class libshikimori(lib): data = self._request( "DELETE", self.api_url + "/user_rates/{}".format(item['my_id']), auth=True) - def search(self, criteria, method): + def search(self, criteria, method, page): self.check_credentials() self.msg.info("Searching for {}...".format(criteria)) @@ -293,7 +293,7 @@ class libshikimori(lib): except ValueError: # An empty document, without any JSON, is returned # when there are no results. - return [] + return ([], 0, 0, 0) showlist = [] @@ -314,7 +314,7 @@ class libshikimori(lib): showlist.append(show) - return showlist + return (showlist, len(showlist), 1, 1) def request_info(self, itemlist): self.check_credentials() diff --git a/trackma/lib/libvndb.py b/trackma/lib/libvndb.py index e30bca0..27cc640 100644 --- a/trackma/lib/libvndb.py +++ b/trackma/lib/libvndb.py @@ -321,14 +321,14 @@ class libvndb(lib): if name != 'ok': raise utils.APIError("Invalid response (%s)" % name) - def search(self, criteria, method): + def search(self, criteria, method, page): self.check_credentials() results = list() self.msg.info('Searching for %s...' % criteria) (name, data) = self._sendcmd('get vn basic,details (search ~ "%s")' % criteria, - {'page': 1, + {'page': page, 'results': self.pagesize_details, }) @@ -345,7 +345,7 @@ class libvndb(lib): if not results: raise utils.APIError('No results.') - return results + return (results, len(results), 1, 1) def logout(self): self.msg.info('Disconnecting...') diff --git a/trackma/ui/gtk/data/searchwindow.ui b/trackma/ui/gtk/data/searchwindow.ui index 2231eb4..8e2c5d3 100644 --- a/trackma/ui/gtk/data/searchwindow.ui +++ b/trackma/ui/gtk/data/searchwindow.ui @@ -3,56 +3,32 @@ <interface> <requires lib="gtk+" version="3.20"/> <template class="SearchWindow" parent="GtkWindow"> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="modal">True</property> - <property name="default_width">800</property> - <property name="default_height">400</property> - <property name="type_hint">dialog</property> - <child type="titlebar"> - <object class="GtkHeaderBar" id="headerbar"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="title" translatable="yes">Search</property> - <property name="show_close_button">True</property> - <child> - <object class="GtkButton" id="btn_add_show"> - <property name="label" translatable="yes">Add</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <signal name="clicked" handler="_on_btn_add_show_clicked" swapped="no"/> - <style> - <class name="suggested-action"/> - </style> - </object> - <packing> - <property name="pack_type">end</property> - </packing> - </child> - </object> - </child> + <property name="default-width">800</property> + <property name="default-height">400</property> + <property name="type-hint">dialog</property> <child> <object class="GtkBox"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="orientation">vertical</property> <child> <object class="GtkBox"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="halign">center</property> <property name="valign">center</property> - <property name="margin_top">6</property> - <property name="margin_bottom">6</property> + <property name="margin-top">6</property> + <property name="margin-bottom">6</property> <property name="spacing">9</property> <child> <object class="GtkSearchEntry" id="search_entry"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="primary_icon_name">edit-find-symbolic</property> - <property name="primary_icon_activatable">False</property> - <property name="primary_icon_sensitive">False</property> + <property name="can-focus">True</property> + <property name="primary-icon-name">edit-find-symbolic</property> + <property name="primary-icon-activatable">False</property> + <property name="primary-icon-sensitive">False</property> <signal name="search-changed" handler="_on_search_entry_search_changed" swapped="no"/> </object> <packing> @@ -64,7 +40,7 @@ <child> <object class="GtkSpinner" id="progress_spinner"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="halign">center</property> <property name="valign">center</property> </object> @@ -84,7 +60,7 @@ <child> <object class="GtkSeparator"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> </object> <packing> <property name="expand">False</property> @@ -95,16 +71,16 @@ <child> <object class="GtkPaned" id="search_paned"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <child> <object class="GtkScrolledWindow"> <property name="visible">True</property> - <property name="can_focus">True</property> + <property name="can-focus">True</property> <child> <object class="GtkViewport" id="shows_viewport"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="shadow_type">none</property> + <property name="can-focus">False</property> + <property name="shadow-type">none</property> <child> <placeholder/> </child> @@ -119,7 +95,7 @@ <child> <object class="GtkBox" id="show_info_container"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <child> <placeholder/> </child> @@ -138,5 +114,146 @@ </child> </object> </child> + <child type="titlebar"> + <object class="GtkHeaderBar" id="headerbar"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="title" translatable="yes">Search</property> + <property name="show-close-button">True</property> + <child> + <object class="GtkButton" id="btn_add_show"> + <property name="label" translatable="yes">Add</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <signal name="clicked" handler="_on_btn_add_show_clicked" swapped="no"/> + <style> + <class name="suggested-action"/> + </style> + </object> + <packing> + <property name="pack-type">end</property> + </packing> + </child> + <child> + <object class="GtkButtonBox" id="paginator"> + <property name="can-focus">False</property> + <property name="no-show-all">True</property> + <property name="layout-style">start</property> + <child type="center"> + <object class="GtkButton" id="next_page"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <signal name="clicked" handler="_on_next_page_clicked" swapped="no"/> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">go-next</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + <property name="non-homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkButton" id="prev_page"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <signal name="clicked" handler="_on_prev_page_clicked" swapped="no"/> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">go-previous</property> + <property name="icon_size">2</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + <property name="non-homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkButton" id="select_page"> + <property name="label" translatable="yes">-</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <signal name="clicked" handler="_on_select_page_clicked" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + <property name="non-homogeneous">True</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + </child> </template> + <object class="GtkPopover" id="page_popover"> + <property name="can-focus">False</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="margin-start">8</property> + <property name="margin-end">8</property> + <property name="margin-top">8</property> + <property name="margin-bottom">8</property> + <property name="spacing">6</property> + <child> + <object class="GtkEntry" id="page_entry"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <signal name="activate" handler="_on_page_change" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="entry_done"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <signal name="clicked" handler="_on_page_change" swapped="no"/> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">object-select-symbolic</property> + </object> + </child> + <style> + <class name="suggested-action"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> </interface> diff --git a/trackma/ui/gtk/mainview.py b/trackma/ui/gtk/mainview.py index 0a7897b..7ee784a 100644 --- a/trackma/ui/gtk/mainview.py +++ b/trackma/ui/gtk/mainview.py @@ -534,7 +534,6 @@ class MainView(Gtk.Box): self.emit('show-action', event_type, data) def get_current_status(self): - print(self._engine.mediainfo['statuses']) return self._current_page.status if self._current_page.status is not None else self._engine.mediainfo['statuses'][-1] def get_selected_show(self): diff --git a/trackma/ui/gtk/searchwindow.py b/trackma/ui/gtk/searchwindow.py index a7c81e9..d6aea4d 100644 --- a/trackma/ui/gtk/searchwindow.py +++ b/trackma/ui/gtk/searchwindow.py @@ -25,18 +25,21 @@ from trackma.ui.gtk.showinfobox import ShowInfoBox class SearchThread(threading.Thread): - def __init__(self, engine, search_text, callback): + def __init__(self, engine, query, callback): threading.Thread.__init__(self) self._entries = [] self._error = None self._engine = engine - self._search_text = search_text + if isinstance(query, (tuple,)): + self._search_text, self._page = query + else: + self._search_text, self._page = (query, 1) self._callback = callback self._stop_request = threading.Event() def run(self): try: - self._entries = self._engine.search(self._search_text) + self._entries = self._engine.search(self._search_text, page=self._page) except utils.TrackmaError as e: self._entries = [] self._error = e @@ -63,11 +66,23 @@ class SearchWindow(Gtk.Window): show_info_container = Gtk.Template.Child() progress_spinner = Gtk.Template.Child() headerbar = Gtk.Template.Child() + search_entry = Gtk.Template.Child() + + # pagination + paginator = Gtk.Template.Child() + next_page = Gtk.Template.Child() + prev_page = Gtk.Template.Child() + select_page = Gtk.Template.Child() + page_popover = Gtk.Template.Child() + page_entry = Gtk.Template.Child() def __init__(self, engine, colors, current_status, transient_for=None): Gtk.Window.__init__(self, transient_for=transient_for) self.init_template() self._entries = [] + self._pages = 1 + self._page = 1 + self._results = 0 self._selected_show = None self._showdict = None @@ -91,46 +106,58 @@ class SearchWindow(Gtk.Window): @Gtk.Template.Callback() def _on_search_entry_search_changed(self, search_entry): - search_text = search_entry.get_text().strip() + self._search(1) + + def _search(self, page): + text = self.search_entry.get_text().strip() self.progress_spinner.start() - if search_text == "": + + if text == "": if self._search_thread: self._search_thread.stop() self._search_finish() - else: - self._search(search_text) - self.progress_spinner.start() + return - def _search(self, text): if self._search_thread: self._search_thread.stop() self.headerbar.set_subtitle("Searching: \"%s\"" % text) self._search_thread = SearchThread(self._engine, - text, + (text, page), self._search_finish_idle) self._search_thread.start() def _search_finish(self): self.headerbar.set_subtitle( - "%s result%s." % ((len(self._entries), 's') - if len(self._entries) > 0 - else ('No', '') - ) + "%s result%s." % ((self._results, 's') if self._results > 0 + else ('No', '')) ) self.progress_spinner.stop() def _search_finish_idle(self, entries, error): - self._entries = entries + if isinstance(entries, (tuple,)): + self._entries, self._results, self._page, self._pages = entries + else: + self._entries, self._results, self._page, self._pages = ( + entries, len(entries), 1, 1 + ) + self._showdict = dict() self._search_finish() self.showlist.append_start() - for show in entries: + for show in self._entries: self._showdict[show['id']] = show self.showlist.append(show) self.showlist.append_finish() self.btn_add_show.set_sensitive(False) + self.paginator.props.visible = self._pages > 1 + self.select_page.props.sensitive = self._pages > 1 + + if self.paginator.props.visible: + self.prev_page.props.sensitive = self._page > 1 + self.next_page.props.sensitive = self._page < self._pages + self.select_page.props.label = '{} / {}'.format(self._page, self._pages) if error: self.emit('search-error', error) @@ -142,6 +169,48 @@ class SearchWindow(Gtk.Window): if show is not None: self._add_show(show) + @Gtk.Template.Callback() + def _on_prev_page_clicked(self, btn): + self._search(self._page - 1) + + @Gtk.Template.Callback() + def _on_next_page_clicked(self, btn): + self._search(self._page + 1) + + @Gtk.Template.Callback() + def _on_select_page_clicked(self, btn): + popover = self.page_popover.props + entry = self.page_entry.props + popover.relative_to = btn + popover.position = Gtk.PositionType.BOTTOM + + entry.text = str(self._page) + entry.placeholder_text = "1 <= page <= {}".format(self._pages) + (entry.secondary_icon_tooltip_text, + entry.secondary_icon_name) = ('', '') + + self.page_popover.show() + self.page_entry.grab_focus() + + @Gtk.Template.Callback() + def _on_page_change(self, *args): + props = self.page_entry.props + + if not props.text.isdigit(): + props.secondary_icon_name = 'dialog-error' + props.secondary_icon_tooltip_text = 'Not a number.' + return False + elif not 1 <= int(self.page_entry.props.text) <= self._pages: + props.secondary_icon_name = 'dialog-error' + props.secondary_icon_tooltip_text = 'Not in range 1 <= page <= {}'.format(self._pages) + return False + elif props.secondary_icon_name: + props.secondary_icon_name = '' + props.secondary_icon_tooltip_text = '' + + self.page_popover.hide() + self._search(int(props.text)) + def _get_full_selected_show(self): for item in self._entries: if item['id'] == self._selected_show: -- 2.44.0
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor