def show_song(song, uri_length=None, brief=False, fetch=False): """以一行文字的方式显示一首歌的信息 :param uri_length: 控制 song uri 的长度 :param brief: 是否只显示简要信息 :param fetch: 是否在线请求未初始化的属性 """ artists_name = song.artists_name if fetch else song.artists_name_display song_title = song.title if fetch else song.title_display title = _fit_text(song_title or '', 18, filling=False) album_name = song.album_name if fetch else song.album_name_display song_uri = reverse(song) if uri_length is not None: song_uri = _fit_text(song_uri, uri_length) if brief: artists_name = _fit_text(artists_name, 20, filling=False) s = '{song}\t# {title} - {artists_name}'.format( song=song_uri, title=title, artists_name=artists_name) return s # XXX: 这个操作可能会产生网络请求 album_uri = reverse(song.album) artists_uri = ','.join(reverse(artist) for artist in song.artists) msgs = ( 'provider: {}'.format(song.source), ' uri: {}'.format(song_uri), ' title: {}'.format(song.title), 'duration: {}'.format(song.duration), ' url: {}'.format(song.url), ' artists: {}\t# {}'.format(artists_uri, artists_name), ' album: {}\t# {}'.format(album_uri, album_name), ) return '\n'.join(msgs)
def _goto(self, model=None, uri=None): """真正的跳转逻辑""" if model is None: try: model = resolve(uri) except ResolveFailed: model = None else: uri = reverse(model) if not uri.startswith('fuo://'): uri = 'fuo://' + uri with self._app.create_action('-> {}'.format(uri)) as action: if model is not None: self._render_model(model) else: try: self.router.dispatch(uri, {'app': self._app}) except NotFound: action.failed('not found.'.format(uri)) return self._last_uri = self.current_uri if model is not None: self.current_uri = reverse(model) else: self.current_uri = uri
def fetchMore(self, _=QModelIndex()): expect_len = 10 try: items = list(itertools.islice(self.reader, expect_len)) except ProviderIOError: logger.exception('fetch more items failed') return acture_len = len(items) colors = [ random.choice(list(COLORS.values())) for _ in range(0, acture_len) ] if acture_len < expect_len: self._maybe_has_more = False begin = len(self.items) self.beginInsertRows(QModelIndex(), begin, begin + acture_len - 1) self.items.extend(items) self.colors.extend(colors) self.endInsertRows() for item in items: aio.create_task( self.fetch_image(item, self._fetch_image_callback(item), uid=reverse(item) + '/cover'))
def show_album(album, brief=False): if brief: return '{uri}\t# {name} - {artists_name}'.format( uri=reverse(album), name=album.name, artists_name=album.artists_name) msgs = [ 'provider: {}'.format(album.source), 'identifier: {}'.format(album.identifier), 'name: {}'.format(album.name), ] if album.artists is not None: artists = album.artists artists_id = ','.join([str(artist.identifier) for artist in artists]) artists_name = ','.join([artist.name for artist in artists]) msgs_artists = [ 'artists: {}\t#{}'.format(artists_id, artists_name) ] msgs += msgs_artists msgs_songs_header = ['songs::'] msgs_songs = ['\t' + each for each in show_songs(album.songs).split('\n')] msgs += msgs_songs_header msgs += msgs_songs return '\n'.join(msgs)
def fetchMore(self, _=QModelIndex()): expect_len = 10 albums = list(itertools.islice(self.albums_g, expect_len)) acture_len = len(albums) colors = [random.choice(list(COLORS.values())) for _ in range(0, acture_len)] if acture_len < expect_len: self._maybe_has_more = False begin = len(self.albums) self.beginInsertRows(QModelIndex(), begin, begin + acture_len - 1) self.albums.extend(albums) self.colors.extend(colors) self.endInsertRows() # FIXME: since album.cover may trigger web request, # this may block the UI for album in albums: cover = album.cover if cover: # check if cover url is valid # FIXME: check if cover is a media object if not isinstance(cover, str): cover = cover.url self.fetch_image(cover, self._fetch_image_callback(album), uid=reverse(album) + '/cover')
def add(self, song): if song not in self.models: line = reverse(song, as_line=True) with open(self.fpath, 'r+', encoding='utf-8') as f: content = f.read() f.seek(0, 0) f.write(line + '\n' + content) self.models.insert(0, song) return True
def cb(future): if not future.done(): return content = future.result() img = QImage() img.loadFromData(content) pixmap = QPixmap(img) uri = reverse(album) self.pixmaps[uri] = pixmap
def cb(content): img = QImage() img.loadFromData(content) pixmap = QPixmap(img) uri = reverse(album) self.pixmaps[uri] = pixmap row = self.albums.index(album) top_left = self.createIndex(row, 0) bottom_right = self.createIndex(row, 0) self.dataChanged.emit(top_left, bottom_right)
def show_artist(artist, brief=False): if brief: return '{uri}\t# {name}'.format(uri=reverse(artist), name=artist.name) msgs = [ 'provider: {}'.format(artist.source), 'identifier: {}'.format(artist.identifier), 'name: {}'.format(artist.name), ] if artist.songs: songs_header = ['songs::'] songs = ['\t' + each for each in show_songs(artist.songs).split('\n')] msgs += songs_header msgs += songs return '\n'.join(msgs)
def remove(self, model): if model in self.models: url = reverse(model) with open(self.fpath, 'r+', encoding='utf-8') as f: lines = [] for line in f: if line.startswith(url): continue lines.append(line) f.seek(0) f.write(''.join(lines)) f.truncate() # 确保最后写入一个换行符,让文件更加美观 if lines and not lines[-1].endswith('\n'): f.write('\n') self.models.remove(model) return True
async def show_album(self, album): meta_widget = self.collection_body.meta_widget meta_widget.clear() meta_widget.title = album.name_display meta_widget.creator = album.artists_name_display songs = await async_run(lambda: album.songs) meta_widget.songs_count = len(songs) reader = RandomSequentialReader.from_list(songs) model = SongListModel(reader) self.collection_body.song_list_view.show() self.collection_body.song_list_view.setModel(model) meta_widget.desc = await async_run(lambda: album.desc) meta_widget.title = await async_run(lambda: album.name) meta_widget.creator = await async_run(lambda: album.artists_name) cover = await async_run(lambda: album.cover) if cover: aio.create_task(self.show_cover(cover, reverse(album, '/cover')))
def data(self, index, role): offset = index.row() if not index.isValid() or offset >= len(self.albums): return None album = self.albums[offset] if role == Qt.DecorationRole: uri = reverse(album) pixmap = self.pixmaps.get(uri) if pixmap is not None: return pixmap color_str = self.colors[offset] color = QColor(color_str) color.setAlphaF(0.8) return color elif role == Qt.DisplayRole: return album.name_display elif role == Qt.UserRole: return album return None
def add(self, model): """add model to collection :param model: :class:`fuocore.models.BaseModel` :return: True means succeed, False means failed """ if (self.type == CollectionType.sys_song and model.meta.model_type != ModelType.song) or \ (self.type == CollectionType.sys_album and model.meta.model_type != ModelType.album): return False if model not in self.models: line = reverse(model, as_line=True) with open(self.fpath, 'r+', encoding='utf-8') as f: content = f.read() f.seek(0, 0) f.write(line + '\n' + content) self.models.insert(0, model) return True
def fetchMore(self, _=QModelIndex()): expect_len = 10 albums = list(itertools.islice(self.albums_g, expect_len)) acture_len = len(albums) colors = [ random.choice(list(COLORS.values())) for _ in range(0, acture_len) ] if acture_len < expect_len: self._maybe_has_more = False begin = len(self.albums) self.beginInsertRows(QModelIndex(), begin, begin + acture_len - 1) self.albums.extend(albums) self.colors.extend(colors) self.endInsertRows() for album in albums: aio.create_task( self.fetch_image(album, self._fetch_image_callback(album), uid=reverse(album) + '/cover'))
def data(self, index, role): offset = index.row() if not index.isValid() or offset >= len(self.items): return None item = self.items[offset] if role == Qt.DecorationRole: uri = reverse(item) pixmap = self.pixmaps.get(uri) if pixmap is not None: return pixmap color_str = self.colors[offset] color = QColor(color_str) color.setAlphaF(0.8) return color elif role == Qt.DisplayRole: return item.name_display elif role == Qt.UserRole: return item elif role == Qt.WhatsThisRole: return self.source_name_map[item.source] return None
def add(self, model): """add model to collection :param model: :class:`fuocore.models.BaseModel` :return: True means succeed, False means failed """ if (self.type == CollectionType.sys_song and model.meta.model_type != ModelType.song) or \ (self.type == CollectionType.sys_album and model.meta.model_type != ModelType.album): return False if model not in self.models: line = reverse(model, as_line=True) with open(self.fpath, 'r+', encoding='utf-8') as f: content = f.read() parts = content.split(TOML_DELIMLF, maxsplit=2) body = parts[-1] self._write_metadata_if_needed(f) f.seek(0, 0) f.write(f'{line}\n{body}') self.models.insert(0, model) return True
def remove(self, model): if model in self.models: url = reverse(model) with open(self.fpath, 'r+', encoding='utf-8') as f: content = f.read() parts = content.split(TOML_DELIMLF, maxsplit=2) body = parts[-1] lines = [] for line in body.split('\n'): if line.startswith(url): continue if line: lines.append(line) f.seek(0) self._write_metadata_if_needed(f) f.write('\n'.join(lines)) f.truncate() # 确保最后写入一个换行符,让文件更加美观 if lines and not lines[-1].endswith('\n'): f.write('\n') self.models.remove(model) return True
def show_songs(songs): uri_length = max((len(reverse(song)) for song in songs)) if songs else None return '\n'.join( [show_song(song, uri_length=uri_length, brief=True) for song in songs])
def remove(self, song_uri): # FIXME: a little bit tricky for song in self.playlist.list(): if reverse(song) == song_uri: self.playlist.remove(song) break