import re from PySide6 import QtCore, QtGui, QtWidgets from tidalapi import Album, Mix, Playlist, Track, UserPlaylist, Video from tidalapi.artist import Artist from tidalapi.media import Quality from tidal_dl_ng.helper.gui import HumanProxyModel def get_table_data( item: QtWidgets.QTreeWidgetItem, column: int ) -> Track | Video | Album | Artist | Mix | Playlist | UserPlaylist: result: Track | Video | Album | Artist = item.data(column, QtCore.Qt.ItemDataRole.UserRole) return result def get_table_text(item: QtWidgets.QTreeWidgetItem, column: int) -> str: result: str = item.text(column) return result def get_results_media_item( index: QtCore.QModelIndex, proxy: HumanProxyModel, model: QtGui.QStandardItemModel ) -> Track | Video | Album | Artist | Playlist | Mix: # Switch column to "obj" column and map proxy data to our model. item: QtGui.QStandardItem = model.itemFromIndex(proxy.mapToSource(index.siblingAtColumn(1))) result: Track | Video | Album | Artist = item.data(QtCore.Qt.ItemDataRole.UserRole) return result def get_user_list_media_item(item: QtWidgets.QTreeWidgetItem) -> Mix | Playlist | UserPlaylist: result: Mix | Playlist | UserPlaylist = get_table_data(item, 1) return result def get_queue_download_media( item: QtWidgets.QTreeWidgetItem, ) -> Mix | Playlist | UserPlaylist | Track | Video | Album | Artist: result: Mix | Playlist | UserPlaylist | Track | Video | Album | Artist = get_table_data(item, 1) return result def get_queue_download_quality( item: QtWidgets.QTreeWidgetItem, ) -> Quality: result: Quality = get_table_text(item, 4) return result def set_table_data( item: QtWidgets.QTreeWidgetItem, data: Track | Video | Album | Artist | Mix | Playlist | UserPlaylist, column: int ): item.setData(column, QtCore.Qt.ItemDataRole.UserRole, data) def set_results_media(item: QtWidgets.QTreeWidgetItem, media: Track | Video | Album | Artist): set_table_data(item, media, 1) def set_user_list_media( item: QtWidgets.QTreeWidgetItem, media: Track | Video | Album | Artist | Mix | Playlist | UserPlaylist ): set_table_data(item, media, 1) def set_queue_download_media( item: QtWidgets.QTreeWidgetItem, media: Mix | Playlist | UserPlaylist | Track | Video | Album | Artist ): set_table_data(item, media, 1) class FilterHeader(QtWidgets.QHeaderView): filter_activated = QtCore.Signal() def __init__(self, parent): super().__init__(QtCore.Qt.Horizontal, parent) self._editors = [] self._padding = 4 self.setStretchLastSection(True) self.setSectionResizeMode(QtWidgets.QHeaderView.Stretch) self.setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) self.setSortIndicatorShown(False) self.setSectionsMovable(True) self.sectionResized.connect(self.adjust_positions) parent.horizontalScrollBar().valueChanged.connect(self.adjust_positions) def set_filter_boxes(self, count): while self._editors: editor = self._editors.pop() editor.deleteLater() for _ in range(count): editor = QtWidgets.QLineEdit(self.parent()) editor.setPlaceholderText("Filter") editor.setClearButtonEnabled(True) editor.returnPressed.connect(self.filter_activated.emit) self._editors.append(editor) self.adjust_positions() def sizeHint(self): size = super().sizeHint() if self._editors: height = self._editors[0].sizeHint().height() size.setHeight(size.height() + height + self._padding) return size def updateGeometries(self): if self._editors: height = self._editors[0].sizeHint().height() self.setViewportMargins(0, 0, 0, height + self._padding) else: self.setViewportMargins(0, 0, 0, 0) super().updateGeometries() self.adjust_positions() def adjust_positions(self): for index, editor in enumerate(self._editors): height = editor.sizeHint().height() editor.move(self.sectionPosition(index) - self.offset() + 2, height + (self._padding // 2)) editor.resize(self.sectionSize(index), height) def filter_text(self, index) -> str: if 0 <= index < len(self._editors): return self._editors[index].text() return "" def set_filter_text(self, index, text): if 0 <= index < len(self._editors): self._editors[index].setText(text) def clear_filters(self): for editor in self._editors: editor.clear() class HumanProxyModel(QtCore.QSortFilterProxyModel): def _human_key(self, key): parts = re.split(r"(\d*\.\d+|\d+)", key) return tuple((e.swapcase() if i % 2 == 0 else float(e)) for i, e in enumerate(parts)) def lessThan(self, source_left, source_right): data_left = source_left.data() data_right = source_right.data() if isinstance(data_left, str) and isinstance(data_right, str): return self._human_key(data_left) < self._human_key(data_right) return super().lessThan(source_left, source_right) @property def filters(self): if not hasattr(self, "_filters"): self._filters = [] return self._filters @filters.setter def filters(self, filters): print("filters() called.") self._filters = filters self.invalidateFilter() def filterAcceptsRow(self, sourceRow, sourceParent) -> bool: for i, text in self.filters: if 0 <= i < self.columnCount(): ix = self.sourceModel().index(sourceRow, i, sourceParent) data = ix.data() if str(data) in text: return False return True