diff --git a/calibre-plugin/dialog/advanced_search.py b/calibre-plugin/dialog/advanced_search.py index 3410a57..80dd04e 100644 --- a/calibre-plugin/dialog/advanced_search.py +++ b/calibre-plugin/dialog/advanced_search.py @@ -7,7 +7,6 @@ # See https://github.com/ping/libby-calibre-plugin for more # information # -import copy from threading import Lock from typing import Dict, List @@ -15,22 +14,18 @@ from qt.core import ( QAbstractItemView, QButtonGroup, - QCursor, QFormLayout, QGridLayout, QHBoxLayout, - QIcon, QLineEdit, - QMenu, QRadioButton, QThread, QWidget, Qt, ) -from .base import BaseDialogMixin +from .base_search import SearchBaseDialog from .widgets import DefaultQPushButton, DefaultQTableView -from .. import DEMO_MODE from ..compat import ( QHeaderView_ResizeMode_ResizeToContents, QHeaderView_ResizeMode_Stretch, @@ -38,20 +33,17 @@ ) from ..config import ( BorrowActions, - MAX_SEARCH_LIBRARIES, PREFS, PreferenceKeys, SearchMode, ) -from ..libby import LibbyClient, LibbyFormats +from ..libby import LibbyFormats from ..models import ( LibbySearchModel, LibbySearchSortFilterModel, - get_media_title, - truncate_for_display, ) from ..overdrive import LibraryMediaSearchParams -from ..utils import PluginImages, obfuscate_name +from ..utils import PluginImages from ..workers import OverDriveLibraryMediaSearchWorker # noinspection PyUnreachableCode @@ -61,7 +53,7 @@ load_translations() -class AdvancedSearchDialogMixin(BaseDialogMixin): +class AdvancedSearchDialogMixin(SearchBaseDialog): def __init__(self, *args): super().__init__(*args) @@ -266,214 +258,18 @@ def hold_removed_advsearch(self, hold: Dict): self.adv_search_results_view.selectionModel().clearSelection() def adv_search_results_view_selection_model_selectionchanged(self): - selection_model = self.adv_search_results_view.selectionModel() - if not selection_model.hasSelection(): - # selection cleared - self.adv_search_borrow_btn.borrow_menu = None - self.adv_search_borrow_btn.setMenu(None) - self.adv_hold_btn.borrow_menu = None - self.adv_hold_btn.setMenu(None) - return - - indices = selection_model.selectedRows() - media = indices[-1].data(Qt.UserRole) - self.status_bar.showMessage(get_media_title(media, include_subtitle=True), 3000) - - borrow_action_default_is_borrow = PREFS[ - PreferenceKeys.LAST_BORROW_ACTION - ] == BorrowActions.BORROW or not hasattr(self, "download_loan") - - available_sites = self.get_available_sites(media, self.adv_search_model) - - borrow_sites = [ - s - for s in available_sites - if s.get("isAvailable") or s.get("luckyDayAvailableCopies") - ] - hold_sites = [ - s - for s in available_sites - if not (s.get("isAvailable") or s.get("luckyDayAvailableCopies")) - ] - if borrow_sites: - borrow_menu = QMenu() - borrow_menu.setToolTipsVisible(True) - for site in borrow_sites: - cards = self.adv_search_model.get_cards_for_library_key( - site["advantageKey"] - ) - for card in cards: - card_action = borrow_menu.addAction( - QIcon(self.get_card_pixmap(site["__library"])), - truncate_for_display( - f'{card["advantageKey"]}: {card["cardName"] or ""}', - font=borrow_menu.font(), - ), - ) - if not LibbyClient.can_borrow(card): - card_action.setToolTip( - self._wrap_for_rich_text( - "
".join( - [ - f'{site["__library"]["name"]}', - _("This card is out of loans."), - ] - ) - ) - ) - card_action.setEnabled(False) - continue - - if self.adv_search_model.has_loan(media["id"], card["cardId"]): - card_action.setToolTip( - self._wrap_for_rich_text( - "
".join( - [ - f'{site["__library"]["name"]}', - _("You already have a loan for this title."), - ] - ) - ) - ) - card_action.setEnabled(False) - continue - - card_action.setToolTip(self._borrow_tooltip(media, site)) - media_for_borrow = copy.deepcopy(media) - media_for_borrow["cardId"] = card["cardId"] - card_action.triggered.connect( - # this is from the holds tab - lambda checked, m=media_for_borrow, s=site: self.borrow_hold( - m, - availability=s, - do_download=not borrow_action_default_is_borrow, - ) - ) - self.adv_search_borrow_btn.setEnabled(True) - self.adv_search_borrow_btn.borrow_menu = borrow_menu - self.adv_search_borrow_btn.setMenu(borrow_menu) - else: - self.adv_search_borrow_btn.borrow_menu = None - self.adv_search_borrow_btn.setMenu(None) - self.adv_search_borrow_btn.setEnabled(False) - - if hold_sites: - hold_menu = QMenu() - hold_menu.setToolTipsVisible(True) - for site in hold_sites: - cards = self.adv_search_model.get_cards_for_library_key( - site["advantageKey"] - ) - for card in cards: - card_action = hold_menu.addAction( - QIcon(self.get_card_pixmap(site["__library"])), - truncate_for_display( - f'{card["advantageKey"]}: {card["cardName"] or ""}', - font=hold_menu.font(), - ), - ) - if not LibbyClient.can_place_hold(card): - card_action.setToolTip( - self._wrap_for_rich_text( - "
".join( - [ - f'{site["__library"]["name"]}', - _("This card is out of holds."), - ] - ) - ) - ) - card_action.setEnabled(False) - continue - if self.adv_search_model.has_hold(media["id"], card["cardId"]): - card_action.setToolTip( - self._wrap_for_rich_text( - "
".join( - [ - f'{site["__library"]["name"]}', - _("You already have a hold for this title."), - ] - ) - ) - ) - card_action.setEnabled(False) - continue - - card_action.setToolTip(self._hold_tooltip(media, site)) - card_action.triggered.connect( - lambda checked, m=media, c=card: self.create_hold(m, c) - ) - self.adv_hold_btn.setEnabled(True) - self.adv_hold_btn.hold_menu = hold_menu - self.adv_hold_btn.setMenu(hold_menu) - else: - self.adv_hold_btn.borrow_menu = None - self.adv_hold_btn.setMenu(None) - self.adv_hold_btn.setEnabled(False) + self.view_selection_model_selectionchanged( + self.adv_search_borrow_btn, + self.adv_hold_btn, + self.adv_search_results_view, + self.adv_search_model, + ) def adv_search_results_view_context_menu_requested(self, pos): - selection_model = self.adv_search_results_view.selectionModel() - if not selection_model.hasSelection(): - return - mi = self.adv_search_results_view.indexAt(pos) - media = mi.data(Qt.UserRole) - - menu = QMenu(self) - menu.setToolTipsVisible(True) - available_sites = self.get_available_sites(media, self.adv_search_model) - view_in_libby_menu = QMenu(_("View in Libby")) - view_in_libby_menu.setIcon(self.resources[PluginImages.ExternalLink]) - view_in_libby_menu.setToolTipsVisible(True) - for site in available_sites: - _card = site["__card"] - library = site["__library"] - libby_action = view_in_libby_menu.addAction( - QIcon(self.get_card_pixmap(site["__library"])), - _card["advantageKey"] - if not DEMO_MODE - else obfuscate_name(_card["advantageKey"]), - ) - libby_action.setToolTip(library["name"]) - libby_action.triggered.connect( - lambda checked, c=_card: self.view_in_libby_action_triggered( - [mi], self.adv_search_model, c - ) - ) - menu.addMenu(view_in_libby_menu) - view_in_overdrive_menu = QMenu(_("View in OverDrive")) - view_in_overdrive_menu.setIcon(self.resources[PluginImages.ExternalLink]) - view_in_overdrive_menu.setToolTipsVisible(True) - for site in available_sites: - _card = site["__card"] - library = site["__library"] - overdrive_action = view_in_overdrive_menu.addAction( - QIcon(self.get_card_pixmap(site["__library"])), - _card["advantageKey"] - if not DEMO_MODE - else obfuscate_name(_card["advantageKey"]), - ) - overdrive_action.setToolTip(library["name"]) - overdrive_action.triggered.connect( - lambda checked, c=_card: self.view_in_overdrive_action_triggered( - [mi], self.adv_search_model, c - ) - ) - menu.addMenu(view_in_overdrive_menu) - - selected_search = self.adv_search_results_view.indexAt(pos).data(Qt.UserRole) - # view book details - self.add_view_book_details_menu_action(menu, selected_search) - # copy share link - self.add_copy_share_link_menu_action(menu, selected_search) - # find calibre matches - self.add_find_library_match_menu_action(menu, selected_search) - # search for author - self.add_search_for_title_menu_action( - menu, selected_search, search_for_author=True + self.view_context_menu_requested( + pos, self.adv_search_results_view, self.adv_search_model ) - menu.exec(QCursor.pos()) - def _adv_reset_borrow_hold_buttons(self): self.adv_search_borrow_btn.borrow_menu = None self.adv_search_borrow_btn.setMenu(None) @@ -532,18 +328,13 @@ def adv_search_btn_clicked(self): self.adv_search_btn.setText(_c("Searching...")) self.adv_search_btn.setEnabled(False) self.setCursor(Qt.WaitCursor) - all_library_keys = self.adv_search_model.library_keys() - library_keys = [ - lib - for lib in all_library_keys - if lib in PREFS[PreferenceKeys.SEARCH_LIBRARIES] - ] - if not library_keys: - library_keys = all_library_keys - - library_keys = library_keys[:MAX_SEARCH_LIBRARIES] + library_keys = self.adv_search_model.limited_library_keys() self.status_bar.showMessage( - _("Searching across {n} libraries...").format(n=len(library_keys)) + ngettext( + "Searching across {n} library...", + "Searching across {n} libraries...", + len(library_keys), + ).format(n=len(library_keys)) ) self._lib_search_threads = [] self._lib_search_result_sets = {} diff --git a/calibre-plugin/dialog/base_search.py b/calibre-plugin/dialog/base_search.py new file mode 100644 index 0000000..a9aaca6 --- /dev/null +++ b/calibre-plugin/dialog/base_search.py @@ -0,0 +1,283 @@ +# +# Copyright (C) 2023 github.com/ping +# +# This file is part of the OverDrive Libby Plugin by ping +# OverDrive Libby Plugin for calibre / libby-calibre-plugin +# +# See https://github.com/ping/libby-calibre-plugin for more +# information +# +import copy + +from qt.core import Qt, QMenu, QIcon, QCursor + +from .base import BaseDialogMixin +from .. import DEMO_MODE +from ..compat import _c +from ..config import PREFS, PreferenceKeys, BorrowActions +from ..libby import LibbyClient +from ..models import get_media_title, truncate_for_display +from ..utils import PluginImages, obfuscate_name + +# noinspection PyUnreachableCode +if False: + load_translations = _ = ngettext = lambda x=None, y=None, z=None: x + +load_translations() + + +class SearchBaseDialog(BaseDialogMixin): + def __init__(self, *args): + super().__init__(*args) + + def _wrap_for_rich_text(self, txt): + return f"

{txt}

" + + def _borrow_tooltip(self, media, site_availability): + available_copies = site_availability.get( + "luckyDayAvailableCopies", 0 + ) + site_availability.get("availableCopies", 0) + owned_copies = site_availability.get( + "luckyDayOwnedCopies", 0 + ) + site_availability.get("ownedCopies", 0) + texts = [f'{site_availability["__library"]["name"]}'] + if available_copies: + texts.append( + ngettext( + "{n} copy available.", + "{n} copies available.", + available_copies, + ).format(n=available_copies) + ) + if owned_copies: + texts.append( + ngettext("{n} copy owned.", "{n} copies owned.", owned_copies).format( + n=owned_copies + ) + ) + return self._wrap_for_rich_text("
".join(texts)) + + def _hold_tooltip(self, media, site_availability): + owned_copies = site_availability.get("ownedCopies", 0) + texts = [ + f'{site_availability["__library"]["name"]}', + _("Estimated wait days: {n}.").format( + n=site_availability.get("estimatedWaitDays", 0) or _c("Unknown") + ), + _("You will be number {n} in line.").format( + n=site_availability.get("holdsCount", 0) + 1 + ), + ngettext("{n} copy ordered.", "{n} copies ordered.", owned_copies).format( + n=owned_copies + ) + if media.get("isPreReleaseTitle", False) + else ngettext( + "{n} copy in use.", "{n} copies in use.", owned_copies + ).format(n=owned_copies), + ] + return self._wrap_for_rich_text("
".join(texts)) + + def view_selection_model_selectionchanged(self, borrow_btn, hold_btn, view, model): + selection_model = view.selectionModel() + if not selection_model.hasSelection(): + # selection cleared + borrow_btn.borrow_menu = None + borrow_btn.setMenu(None) + hold_btn.borrow_menu = None + hold_btn.setMenu(None) + return + + indices = selection_model.selectedRows() + media = indices[-1].data(Qt.UserRole) + self.status_bar.showMessage(get_media_title(media, include_subtitle=True), 3000) + + borrow_action_default_is_borrow = PREFS[ + PreferenceKeys.LAST_BORROW_ACTION + ] == BorrowActions.BORROW or not hasattr(self, "download_loan") + + available_sites = self.get_available_sites(media, model) + + borrow_sites = [ + s + for s in available_sites + if s.get("isAvailable") or s.get("luckyDayAvailableCopies") + ] + hold_sites = [ + s + for s in available_sites + if not (s.get("isAvailable") or s.get("luckyDayAvailableCopies")) + ] + if borrow_sites: + borrow_menu = QMenu() + borrow_menu.setToolTipsVisible(True) + for site in borrow_sites: + cards = model.get_cards_for_library_key(site["advantageKey"]) + for card in cards: + card_action = borrow_menu.addAction( + QIcon(self.get_card_pixmap(site["__library"])), + truncate_for_display( + f'{card["advantageKey"]}: {card["cardName"] or ""}', + font=borrow_menu.font(), + ), + ) + if not LibbyClient.can_borrow(card): + card_action.setToolTip( + self._wrap_for_rich_text( + "
".join( + [ + f'{site["__library"]["name"]}', + _("This card is out of loans."), + ] + ) + ) + ) + card_action.setEnabled(False) + continue + + if model.has_loan(media["id"], card["cardId"]): + card_action.setToolTip( + self._wrap_for_rich_text( + "
".join( + [ + f'{site["__library"]["name"]}', + _("You already have a loan for this title."), + ] + ) + ) + ) + card_action.setEnabled(False) + continue + + card_action.setToolTip(self._borrow_tooltip(media, site)) + media_for_borrow = copy.deepcopy(media) + media_for_borrow["cardId"] = card["cardId"] + card_action.triggered.connect( + # this is from the holds tab + lambda checked, m=media_for_borrow, s=site: self.borrow_hold( + m, + availability=s, + do_download=not borrow_action_default_is_borrow, + ) + ) + borrow_btn.setEnabled(True) + borrow_btn.borrow_menu = borrow_menu + borrow_btn.setMenu(borrow_menu) + else: + borrow_btn.borrow_menu = None + borrow_btn.setMenu(None) + borrow_btn.setEnabled(False) + + if hold_sites: + hold_menu = QMenu() + hold_menu.setToolTipsVisible(True) + for site in hold_sites: + cards = model.get_cards_for_library_key(site["advantageKey"]) + for card in cards: + card_action = hold_menu.addAction( + QIcon(self.get_card_pixmap(site["__library"])), + truncate_for_display( + f'{card["advantageKey"]}: {card["cardName"] or ""}', + font=hold_menu.font(), + ), + ) + if not LibbyClient.can_place_hold(card): + card_action.setToolTip( + self._wrap_for_rich_text( + "
".join( + [ + f'{site["__library"]["name"]}', + _("This card is out of holds."), + ] + ) + ) + ) + card_action.setEnabled(False) + continue + if model.has_hold(media["id"], card["cardId"]): + card_action.setToolTip( + self._wrap_for_rich_text( + "
".join( + [ + f'{site["__library"]["name"]}', + _("You already have a hold for this title."), + ] + ) + ) + ) + card_action.setEnabled(False) + continue + + card_action.setToolTip(self._hold_tooltip(media, site)) + card_action.triggered.connect( + lambda checked, m=media, c=card: self.create_hold(m, c) + ) + hold_btn.setEnabled(True) + hold_btn.hold_menu = hold_menu + hold_btn.setMenu(hold_menu) + else: + hold_btn.borrow_menu = None + hold_btn.setMenu(None) + hold_btn.setEnabled(False) + + def view_context_menu_requested(self, pos, view, model): + selection_model = view.selectionModel() + if not selection_model.hasSelection(): + return + mi = view.indexAt(pos) + media = mi.data(Qt.UserRole) + + menu = QMenu(self) + menu.setToolTipsVisible(True) + available_sites = self.get_available_sites(media, model) + view_in_libby_menu = QMenu(_("View in Libby")) + view_in_libby_menu.setIcon(self.resources[PluginImages.ExternalLink]) + view_in_libby_menu.setToolTipsVisible(True) + for site in available_sites: + _card = site["__card"] + library = site["__library"] + libby_action = view_in_libby_menu.addAction( + QIcon(self.get_card_pixmap(site["__library"])), + _card["advantageKey"] + if not DEMO_MODE + else obfuscate_name(_card["advantageKey"]), + ) + libby_action.setToolTip(library["name"]) + libby_action.triggered.connect( + lambda checked, c=_card: self.view_in_libby_action_triggered( + [mi], model, c + ) + ) + menu.addMenu(view_in_libby_menu) + view_in_overdrive_menu = QMenu(_("View in OverDrive")) + view_in_overdrive_menu.setIcon(self.resources[PluginImages.ExternalLink]) + view_in_overdrive_menu.setToolTipsVisible(True) + for site in available_sites: + _card = site["__card"] + library = site["__library"] + overdrive_action = view_in_overdrive_menu.addAction( + QIcon(self.get_card_pixmap(site["__library"])), + _card["advantageKey"] + if not DEMO_MODE + else obfuscate_name(_card["advantageKey"]), + ) + overdrive_action.setToolTip(library["name"]) + overdrive_action.triggered.connect( + lambda checked, c=_card: self.view_in_overdrive_action_triggered( + [mi], model, c + ) + ) + menu.addMenu(view_in_overdrive_menu) + + selected_search = view.indexAt(pos).data(Qt.UserRole) + # view book details + self.add_view_book_details_menu_action(menu, selected_search) + # copy share link + self.add_copy_share_link_menu_action(menu, selected_search) + # find calibre matches + self.add_find_library_match_menu_action(menu, selected_search) + # search for author + self.add_search_for_title_menu_action( + menu, selected_search, search_for_author=True + ) + + menu.exec(QCursor.pos()) diff --git a/calibre-plugin/dialog/search.py b/calibre-plugin/dialog/search.py index 274d2ec..3379109 100644 --- a/calibre-plugin/dialog/search.py +++ b/calibre-plugin/dialog/search.py @@ -7,25 +7,20 @@ # See https://github.com/ping/libby-calibre-plugin for more # information # -import copy from typing import Dict, List from calibre.constants import DEBUG from qt.core import ( QAbstractItemView, - QCursor, QGridLayout, - QIcon, QLineEdit, - QMenu, QThread, QWidget, Qt, ) -from .base import BaseDialogMixin +from .base_search import SearchBaseDialog from .widgets import DefaultQPushButton, DefaultQTableView -from .. import DEMO_MODE from ..compat import ( QHeaderView_ResizeMode_ResizeToContents, QHeaderView_ResizeMode_Stretch, @@ -33,29 +28,26 @@ ) from ..config import ( BorrowActions, - MAX_SEARCH_LIBRARIES, PREFS, PreferenceKeys, SearchMode, ) -from ..libby import LibbyClient, LibbyFormats +from ..libby import LibbyFormats from ..models import ( LibbySearchModel, LibbySearchSortFilterModel, - get_media_title, - truncate_for_display, ) -from ..utils import PluginImages, obfuscate_name +from ..utils import PluginImages from ..workers import OverDriveMediaSearchWorker # noinspection PyUnreachableCode if False: - load_translations = _ = ngettext = lambda x=None, y=None: x + load_translations = _ = ngettext = lambda x=None, y=None, z=None: x load_translations() -class SearchDialogMixin(BaseDialogMixin): +class SearchDialogMixin(SearchBaseDialog): def __init__(self, *args): super().__init__(*args) self._search_thread = QThread() @@ -149,9 +141,9 @@ def __init__(self, *args): search_widget.layout.addWidget( self.search_borrow_btn, widget_row_pos, self.view_hspan - 1 ) - self.hold_btn = DefaultQPushButton(_("Place Hold"), None, self) + self.search_hold_btn = DefaultQPushButton(_("Place Hold"), None, self) search_widget.layout.addWidget( - self.hold_btn, widget_row_pos, self.view_hspan - 2 + self.search_hold_btn, widget_row_pos, self.view_hspan - 2 ) # set last 2 col's min width (buttons) for i in (1, 2): @@ -217,269 +209,26 @@ def search_for(self, text: str): self.search_btn.setFocus(Qt.OtherFocusReason) self.search_btn.animateClick() - def _wrap_for_rich_text(self, txt): - return f"

{txt}

" - - def _borrow_tooltip(self, media, site_availability): - available_copies = site_availability.get( - "luckyDayAvailableCopies", 0 - ) + site_availability.get("availableCopies", 0) - owned_copies = site_availability.get( - "luckyDayOwnedCopies", 0 - ) + site_availability.get("ownedCopies", 0) - texts = [f'{site_availability["__library"]["name"]}'] - if available_copies: - texts.append( - ngettext( - "{n} copy available.", - "{n} copies available.", - available_copies, - ).format(n=available_copies) - ) - if owned_copies: - texts.append( - ngettext("{n} copy owned.", "{n} copies owned.", owned_copies).format( - n=owned_copies - ) - ) - return self._wrap_for_rich_text("
".join(texts)) - - def _hold_tooltip(self, media, site_availability): - owned_copies = site_availability.get("ownedCopies", 0) - texts = [ - f'{site_availability["__library"]["name"]}', - _("Estimated wait days: {n}.").format( - n=site_availability.get("estimatedWaitDays", 0) or _c("Unknown") - ), - _("You will be number {n} in line.").format( - n=site_availability.get("holdsCount", 0) + 1 - ), - ngettext("{n} copy ordered.", "{n} copies ordered.", owned_copies).format( - n=owned_copies - ) - if media.get("isPreReleaseTitle", False) - else ngettext( - "{n} copy in use.", "{n} copies in use.", owned_copies - ).format(n=owned_copies), - ] - return self._wrap_for_rich_text("
".join(texts)) - def search_results_view_selection_model_selectionchanged(self): - selection_model = self.search_results_view.selectionModel() - if not selection_model.hasSelection(): - # selection cleared - self.search_borrow_btn.borrow_menu = None - self.search_borrow_btn.setMenu(None) - self.hold_btn.borrow_menu = None - self.hold_btn.setMenu(None) - return - - indices = selection_model.selectedRows() - media = indices[-1].data(Qt.UserRole) - self.status_bar.showMessage(get_media_title(media, include_subtitle=True), 3000) - - borrow_action_default_is_borrow = PREFS[ - PreferenceKeys.LAST_BORROW_ACTION - ] == BorrowActions.BORROW or not hasattr(self, "download_loan") - - available_sites = self.get_available_sites(media, self.search_model) - - borrow_sites = [ - s - for s in available_sites - if s.get("isAvailable") or s.get("luckyDayAvailableCopies") - ] - hold_sites = [ - s - for s in available_sites - if not (s.get("isAvailable") or s.get("luckyDayAvailableCopies")) - ] - if borrow_sites: - borrow_menu = QMenu() - borrow_menu.setToolTipsVisible(True) - for site in borrow_sites: - cards = self.search_model.get_cards_for_library_key( - site["advantageKey"] - ) - for card in cards: - card_action = borrow_menu.addAction( - QIcon(self.get_card_pixmap(site["__library"])), - truncate_for_display( - f'{card["advantageKey"]}: {card["cardName"] or ""}', - font=borrow_menu.font(), - ), - ) - if not LibbyClient.can_borrow(card): - card_action.setToolTip( - self._wrap_for_rich_text( - "
".join( - [ - f'{site["__library"]["name"]}', - _("This card is out of loans."), - ] - ) - ) - ) - card_action.setEnabled(False) - continue - - if self.search_model.has_loan(media["id"], card["cardId"]): - card_action.setToolTip( - self._wrap_for_rich_text( - "
".join( - [ - f'{site["__library"]["name"]}', - _("You already have a loan for this title."), - ] - ) - ) - ) - card_action.setEnabled(False) - continue - - card_action.setToolTip(self._borrow_tooltip(media, site)) - media_for_borrow = copy.deepcopy(media) - media_for_borrow["cardId"] = card["cardId"] - card_action.triggered.connect( - # this is from the holds tab - lambda checked, m=media_for_borrow, s=site: self.borrow_hold( - m, - availability=s, - do_download=not borrow_action_default_is_borrow, - ) - ) - self.search_borrow_btn.setEnabled(True) - self.search_borrow_btn.borrow_menu = borrow_menu - self.search_borrow_btn.setMenu(borrow_menu) - else: - self.search_borrow_btn.borrow_menu = None - self.search_borrow_btn.setMenu(None) - self.search_borrow_btn.setEnabled(False) - - if hold_sites: - hold_menu = QMenu() - hold_menu.setToolTipsVisible(True) - for site in hold_sites: - cards = self.search_model.get_cards_for_library_key( - site["advantageKey"] - ) - for card in cards: - card_action = hold_menu.addAction( - QIcon(self.get_card_pixmap(site["__library"])), - truncate_for_display( - f'{card["advantageKey"]}: {card["cardName"] or ""}', - font=hold_menu.font(), - ), - ) - if not LibbyClient.can_place_hold(card): - card_action.setToolTip( - self._wrap_for_rich_text( - "
".join( - [ - f'{site["__library"]["name"]}', - _("This card is out of holds."), - ] - ) - ) - ) - card_action.setEnabled(False) - continue - if self.search_model.has_hold(media["id"], card["cardId"]): - card_action.setToolTip( - self._wrap_for_rich_text( - "
".join( - [ - f'{site["__library"]["name"]}', - _("You already have a hold for this title."), - ] - ) - ) - ) - card_action.setEnabled(False) - continue - - card_action.setToolTip(self._hold_tooltip(media, site)) - card_action.triggered.connect( - lambda checked, m=media, c=card: self.create_hold(m, c) - ) - self.hold_btn.setEnabled(True) - self.hold_btn.hold_menu = hold_menu - self.hold_btn.setMenu(hold_menu) - else: - self.hold_btn.borrow_menu = None - self.hold_btn.setMenu(None) - self.hold_btn.setEnabled(False) + self.view_selection_model_selectionchanged( + self.search_borrow_btn, + self.search_hold_btn, + self.search_results_view, + self.search_model, + ) def search_results_view_context_menu_requested(self, pos): - selection_model = self.search_results_view.selectionModel() - if not selection_model.hasSelection(): - return - mi = self.search_results_view.indexAt(pos) - media = mi.data(Qt.UserRole) - - menu = QMenu(self) - menu.setToolTipsVisible(True) - available_sites = self.get_available_sites(media, self.search_model) - view_in_libby_menu = QMenu(_("View in Libby")) - view_in_libby_menu.setIcon(self.resources[PluginImages.ExternalLink]) - view_in_libby_menu.setToolTipsVisible(True) - for site in available_sites: - _card = site["__card"] - library = site["__library"] - libby_action = view_in_libby_menu.addAction( - QIcon(self.get_card_pixmap(site["__library"])), - _card["advantageKey"] - if not DEMO_MODE - else obfuscate_name(_card["advantageKey"]), - ) - libby_action.setToolTip(library["name"]) - libby_action.triggered.connect( - lambda checked, c=_card: self.view_in_libby_action_triggered( - [mi], self.search_model, c - ) - ) - menu.addMenu(view_in_libby_menu) - view_in_overdrive_menu = QMenu(_("View in OverDrive")) - view_in_overdrive_menu.setIcon(self.resources[PluginImages.ExternalLink]) - view_in_overdrive_menu.setToolTipsVisible(True) - for site in available_sites: - _card = site["__card"] - library = site["__library"] - overdrive_action = view_in_overdrive_menu.addAction( - QIcon(self.get_card_pixmap(site["__library"])), - _card["advantageKey"] - if not DEMO_MODE - else obfuscate_name(_card["advantageKey"]), - ) - overdrive_action.setToolTip(library["name"]) - overdrive_action.triggered.connect( - lambda checked, c=_card: self.view_in_overdrive_action_triggered( - [mi], self.search_model, c - ) - ) - menu.addMenu(view_in_overdrive_menu) - - selected_search = self.search_results_view.indexAt(pos).data(Qt.UserRole) - # view book details - self.add_view_book_details_menu_action(menu, selected_search) - # copy share link - self.add_copy_share_link_menu_action(menu, selected_search) - # find calibre matches - self.add_find_library_match_menu_action(menu, selected_search) - # search for author - self.add_search_for_title_menu_action( - menu, selected_search, search_for_author=True + self.view_context_menu_requested( + pos, self.search_results_view, self.search_model ) - menu.exec(QCursor.pos()) - def _reset_borrow_hold_buttons(self): self.search_borrow_btn.borrow_menu = None self.search_borrow_btn.setMenu(None) self.search_borrow_btn.setEnabled(True) - self.hold_btn.hold_menu = None - self.hold_btn.setMenu(None) - self.hold_btn.setEnabled(True) + self.search_hold_btn.hold_menu = None + self.search_hold_btn.setMenu(None) + self.search_hold_btn.setEnabled(True) def search_btn_clicked(self): self.search_model.sync({"search_results": []}) @@ -492,19 +241,10 @@ def search_btn_clicked(self): self.search_btn.setText(_c("Searching...")) self.search_btn.setEnabled(False) self.setCursor(Qt.WaitCursor) - all_library_keys = self.search_model.library_keys() - library_keys = [ - lib - for lib in all_library_keys - if lib in PREFS[PreferenceKeys.SEARCH_LIBRARIES] - ] - if not library_keys: - library_keys = all_library_keys - self._search_thread = self._get_search_thread( self.overdrive_client, search_query, - library_keys[:MAX_SEARCH_LIBRARIES], + self.search_model.limited_library_keys(), PREFS[PreferenceKeys.SEARCH_RESULTS_MAX], ) self._search_thread.start() diff --git a/calibre-plugin/models.py b/calibre-plugin/models.py index f8d9a45..7c53c57 100644 --- a/calibre-plugin/models.py +++ b/calibre-plugin/models.py @@ -26,7 +26,7 @@ from . import DEMO_MODE from .compat import QColor_fromString, _c -from .config import PREFS, PreferenceKeys +from .config import MAX_SEARCH_LIBRARIES, PREFS, PreferenceKeys from .libby import LibbyClient from .libby.client import LibbyFormats, LibbyMediaTypes from .overdrive import OverDriveClient @@ -169,6 +169,21 @@ def removeRows(self, row, count, _): self.endRemoveRows() return True + def library_keys(self) -> List[str]: + return list(set([c["advantageKey"] for c in self._cards])) + + def limited_library_keys(self) -> List[str]: + all_library_keys = self.library_keys() + library_keys = [ + lib + for lib in all_library_keys + if lib in PREFS[PreferenceKeys.SEARCH_LIBRARIES] + ] + if not library_keys: + library_keys = all_library_keys + + return library_keys[:MAX_SEARCH_LIBRARIES] + def sync(self, synced_state: Optional[Dict] = None): if not synced_state: synced_state = {}