Esempio n. 1
0
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)
Esempio n. 2
0
 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
Esempio n. 3
0
    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'))
Esempio n. 4
0
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)
Esempio n. 5
0
    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')
Esempio n. 6
0
 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
Esempio n. 7
0
 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
Esempio n. 8
0
 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)
Esempio n. 9
0
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)
Esempio n. 10
0
 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
Esempio n. 11
0
 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')))
Esempio n. 12
0
 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
Esempio n. 13
0
    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
Esempio n. 14
0
    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'))
Esempio n. 15
0
 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
Esempio n. 16
0
    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
Esempio n. 17
0
    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
Esempio n. 18
0
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])
Esempio n. 19
0
 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