Beispiel #1
0
    def q_media_mapping(self):
        """fetch media info and save it in q_media_mapping"""
        q_media_mapping = {}
        if True:
            # 注:self.quality_suffix 这里可能会触发一次网络请求
            for idx, (q, t, b, s) in enumerate(self.quality_suffix):
                url = self._api.get_song_url_v2(self.mid, self.media_id, t)
                if url:
                    q_media_mapping[q] = Media(url, bitrate=b, format=s)
                    # 一般来说,高品质有权限 低品质也会有权限,减少网络请求。
                    # 这里把值设置为 UNFETCHED_MEDIA,作为一个标记。
                    for i in range(idx + 1, len(self.quality_suffix)):
                        q_media_mapping[self.quality_suffix[i][0]] = UNFETCHED_MEDIA
                    break
        else:
            q_urls_mapping = self._api.get_song_url(self.mid)
            q_bitrate_mapping = {'shq': 1000,
                                 'hq': 800,
                                 'sq': 500,
                                 'lq': 64}
            q_media_mapping = {}
            for quality, url in q_urls_mapping.items():
                bitrate = q_bitrate_mapping[quality]
                format = url.split('?')[0].split('.')[-1]
                q_media_mapping[quality] = Media(url, bitrate=bitrate, format=format)

        self.q_media_mapping = q_media_mapping
        return q_media_mapping
Beispiel #2
0
 def get_media(self, quality):
     logger.info(quality)
     if quality == 'lq':
         return Media(self.url,
                      format=KuwoApi.FORMATS_BRS[quality],
                      bitrate=KuwoApi.FORMATS_RATES[quality] // 1000)
     if self._media.get(str(self.identifier)) and self._media.get(str(self.identifier)).get(quality)[0] is not None \
             and self._media.get(str(self.identifier)).get(quality)[1] > time.time():
         return self._media.get(str(self.identifier)).get(quality)[0]
     data = self._api.get_song_url_mobi(self.identifier, quality)
     bitrate = 0
     for d in data.split():
         if 'bitrate=' in d:
             bitrate = int(d.split('=')[-1])
         if 'url=' in d:
             if bitrate == 0:
                 bitrate = int(KuwoApi.FORMATS_RATES[quality] // 1000)
             media = Media(d.split('=')[-1],
                           format=KuwoApi.FORMATS_BRS[quality],
                           bitrate=bitrate)
             self._media[str(self.identifier)] = {}
             self._media[str(
                 self.identifier)][quality] = (media,
                                               int(time.time()) + 60 * 10)
             return media or None
     return None
Beispiel #3
0
 def get_media(self, quality):
     if quality == 'lq':
         return Media(self.url,
                      format=MiguApi.QUALITIES[quality][1],
                      bitrate=MiguApi.QUALITIES[quality][0])
     data_song = self._api.get_song(self.identifier, quality=quality)
     url = data_song.get('data', {}).get('url')
     return Media(url, format=MiguApi.QUALITIES[quality][1], bitrate=MiguApi.QUALITIES[quality][0])\
         if url is not None else None
 def to_q_media_mapping(cls, lfiles):
     q_media_mapping = {}
     if lfiles:
         q_q_mapping = {
             's': 'shq',
             'h': 'hq',
             'l': 'sq',
             'f': 'lq',
             'e': 'lq',
         }
         for lfile in filter(lambda lfile: lfile['url'], lfiles):
             url = lfile['url']
             quality = lfile['quality']
             format = lfile['format']
             # url example: http://m720.xiami.net/...
             try:
                 bitrate = int(urlparse(url).netloc.split('.')[0][1:])
             except:
                 bitrate = None
             if quality not in q_q_mapping:
                 field = 'lq'
                 logger.warning('unknown quality {}'.format(quality))
             else:
                 field = q_q_mapping[quality]
             q_media_mapping[field] = Media(url,
                                            format=format,
                                            bitrate=bitrate)
     return q_media_mapping
 def _refresh_url(self):
     """刷新获取 url,失败的时候返回空而不是 None"""
     # FIXME: move q_media_mapping fetch logic to somewhere else
     songs = self._api.weapi_songs_url([int(self.identifier)], 999000)
     if songs and songs[0]['url']:
         self.url = songs[0]['url']
     else:
         self.url = ''
     self.q_media_mapping = {}
     if songs and songs[0]['url']:
         media = Media(songs[0]['url'],
                       format=songs[0]['type'],
                       bitrate=songs[0]['br'] // 1000)
         if songs[0]['br'] > 320000:
             self.q_media_mapping = {
                 'shq': media,
                 'hq': None,
                 'sq': None,
                 'lq': None
             }
         if songs[0]['br'] == 320000:
             self.q_media_mapping = {'hq': media, 'sq': None, 'lq': None}
         if songs[0]['br'] == 192000:
             self.q_media_mapping = {'sq': media, 'lq': None}
         if songs[0]['br'] == 128000:
             self.q_media_mapping = {'lq': media}
     self.expired_at = int(time.time()) + 60 * 20 * 1
Beispiel #6
0
    def play(self, media, video=True):
        # NOTE - API DESGIN: we should return None, see
        # QMediaPlayer API reference for more details.

        logger.debug("Player will play: '%s'", media)

        if video:
            # FIXME: for some property, we need to set via setattr, however,
            #  some need to be set via _mpv_set_property_string
            self._mpv.handle.vid = b'auto'
            # it seems that ytdl will auto choose the default format
            #  if we set ytdl-format to ''
            _mpv_set_property_string(self._mpv.handle, b'ytdl-format', b'')
        else:
            # set vid to no and ytdl-format to bestaudio/best
            # see https://mpv.io/manual/stable/#options-vid for more details
            self._mpv.handle.vid = b'no'
            _mpv_set_property_string(self._mpv.handle, b'ytdl-format',
                                     b'bestaudio/best')

        if isinstance(media, Media):
            media = media
        else:  # media is a url
            media = Media(media)
        url = media.url

        # Clear playlist before play next song,
        # otherwise, mpv will seek to the last position and play.
        self._mpv.playlist_clear()
        self._mpv.play(url)
        self._mpv.pause = False
        self.state = State.playing
        self._current_media = media
        # TODO: we will emit a media object
        self.media_changed.emit(media)
Beispiel #7
0
 def callback(future):
     try:
         media, quality = future.result()
     except Exception as e:
         logger.exception('prepare media data failed')
     else:
         media = Media(media) if media else None
         done_cb(media)
Beispiel #8
0
 def prepare_media(self, song):
     """prepare media data
     """
     if song.meta.support_multi_quality:
         media, quality = song.select_media(self.audio_select_policy)
     else:
         media = song.url  # maybe a empty string
     return Media(media) if media else None
Beispiel #9
0
    def after_scan(self):
        """
        歌曲扫描完成后,对信息进行一些加工,比如
        1. 给专辑歌曲排序
        2. 给专辑和歌手加封面
        """
        def sort_album_func(album):
            if album.songs:
                return (album.songs[0].date is not None, album.songs[0].date)
            return (False, '0')

        for album in self._albums.values():
            try:
                album.songs.sort(key=lambda x: (int(x.disc.split('/')[0]), int(x.track.split('/')[0])))
                if album.name != 'Unknown':
                    cover_data, _ = read_audio_cover(album.songs[0].url)
                    if cover_data:
                        cover = Media(reverse(album.songs[0], '/cover/data'),
                                      type_=MediaType.image)
                    else:
                        cover = None
                    album.cover = cover
            except:  # noqa
                logger.exception('Sort album songs failed.')

        for artist in self._artists.values():
            if artist.albums:
                artist.albums.sort(key=sort_album_func, reverse=True)
                artist.cover = artist.albums[0].cover
            if artist.contributed_albums:
                artist.contributed_albums.sort(key=sort_album_func, reverse=True)
            if artist.songs:
                # sort artist songs
                artist.songs.sort(key=lambda x: x.title)
                # use song cover as artist cover
                # https://github.com/feeluown/feeluown-local/pull/3/files#r362126996
                songs_with_unknown_album = [song for song in artist.songs
                                            if song.album_name == 'Unknown']
                for song in sorted(songs_with_unknown_album,
                                   key=lambda x: (x.date is not None, x.date),
                                   reverse=True):
                    if read_audio_cover(song.url)[0]:
                        artist.cover = Media(reverse(song, '/cover/data'),
                                             type_=MediaType.image)
                        break
Beispiel #10
0
 def prepare_media(self, song, done_cb=None):
     if song.meta.support_multi_quality:
         media, quality = song.select_media('hq<>')
     else:
         media = song.url
     media = Media(media) if media else None
     if done_cb is not None:
         done_cb(media)
     return media
Beispiel #11
0
 async def show_cover(self, cover, cover_uid):
     cover = Media(cover, MediaType.image)
     url = cover.url
     app = self._app
     content = await app.img_mgr.get(url, cover_uid)
     img = QImage()
     img.loadFromData(content)
     pixmap = QPixmap(img)
     if not pixmap.isNull():
         self.meta_widget.set_cover_pixmap(pixmap)
Beispiel #12
0
 def from_youtubedl_output(cls, data):
     duration = data['duration']
     url = data['url']
     media = Media(url,
                   http_headers={'Referer': 'https://www.bilibili.com/'})
     title = data['title']
     return cls(identifier=data['id'],
                duration=duration,
                url=media,
                title=title)
Beispiel #13
0
 def from_youtubedl_output(cls, data):
     url = data['url']
     media = Media(url,
                   http_headers={'Referer': 'https://www.bilibili.com/'})
     return cls(identifier=data['id'],
                duration=data['duration'],
                cover=data['thumbnail'],
                description=data['description'],
                media=media,
                title=data['title'],
                stage=ModelStage.gotten)
Beispiel #14
0
 def q_media_mapping(self):
     """fetch media info and save it in q_media_mapping"""
     q_urls_mapping = self._api.get_song_url(self.mid)
     q_bitrate_mapping = {'shq': 1000,
                          'hq': 800,
                          'sq': 500,
                          'lq': 64}
     q_media_mapping = {}
     for quality, url in q_urls_mapping.items():
         bitrate = q_bitrate_mapping[quality]
         q_media_mapping[quality] = Media(url, bitrate=bitrate)
     self.q_media_mapping = q_media_mapping
     return q_media_mapping
Beispiel #15
0
 def get_media(self, quality):
     media = self.q_media_mapping.get(quality)
     if media is UNFETCHED_MEDIA:
         for (q, t, b, s) in self.quality_suffix:
             if quality == q:
                 url = self._api.get_song_url_v2(self.mid, self.media_id, t)
                 if url:
                     media = Media(url, bitrate=b, format=s)
                     self.q_media_mapping[quality] = media
                 else:
                     media = None
                     self.q_media_mapping[quality] = None
                 break
         else:
             media = None
     return media
Beispiel #16
0
    def play(self, media, video=True):
        logger.debug("Player will play: '%s'", media)
        if isinstance(media, Media):
            media = media
        else:  # media is a url
            media = Media(media)
        self._set_http_headers(media.http_headers)
        url = media.url

        # Clear playlist before play next song,
        # otherwise, mpv will seek to the last position and play.
        self.media_about_to_changed.emit(self._current_media, media)
        self._mpv.playlist_clear()
        self._mpv.play(url)
        self._current_media = media
        self.media_changed.emit(media)
Beispiel #17
0
 async def show_cover(self, cover, cover_uid, as_background=False):
     cover = Media(cover, MediaType.image)
     url = cover.url
     app = self._app
     content = await app.img_mgr.get(url, cover_uid)
     img = QImage()
     img.loadFromData(content)
     pixmap = QPixmap(img)
     if not pixmap.isNull():
         if as_background:
             self.meta_widget.set_cover_pixmap(None)
             self._app.ui.right_panel.show_background_image(pixmap)
         else:
             self._app.ui.right_panel.show_background_image(None)
             self.meta_widget.set_cover_pixmap(pixmap)
         self._app.ui.table_container.updateGeometry()
Beispiel #18
0
    def play(self, media, video=True):
        logger.debug("Player will play: '%s'", media)
        if isinstance(media, Media):
            media = media
        else:  # media is a url
            media = Media(media)
        self._set_http_headers(media.http_headers)
        url = media.url

        # Clear playlist before play next song,
        # otherwise, mpv will seek to the last position and play.
        self._mpv.playlist_clear()
        self._mpv.play(url)
        self._mpv.pause = False
        self.state = State.playing
        self._current_media = media
        # TODO: we will emit a media object
        self.media_changed.emit(media)
Beispiel #19
0
 async def show_cover(self, cover, cover_uid, as_background=False):
     cover = Media(cover, MediaType.image)
     url = cover.url
     app = self._app
     content = await app.img_mgr.get(url, cover_uid)
     img = QImage()
     img.loadFromData(content)
     pixmap = QPixmap(img)
     if not pixmap.isNull():
         # TODO: currently, background image is shown with dark
         # overlay. When we are using light theme, the text is also
         # in dark color, so we only show background image in dark theme
         if as_background and self._app.theme_mgr.theme == 'dark':
             self.meta_widget.set_cover_pixmap(None)
             self._app.ui.right_panel.show_background_image(pixmap)
         else:
             self._app.ui.right_panel.show_background_image(None)
             self.meta_widget.set_cover_pixmap(pixmap)
         self._app.ui.table_container.updateGeometry()
 def get_media(self, quality):
     if self.is_expired:
         self._refresh_url()
     media = self.q_media_mapping.get(quality)
     if media is None:
         q_bitrate_mapping = {
             'shq': 999000,
             'hq': 320000,
             'sq': 192000,
             'lq': 128000,
         }
         bitrate = q_bitrate_mapping[quality]
         songs = self._api.weapi_songs_url([int(self.identifier)], bitrate)
         if songs and songs[0]['url']:
             media = Media(songs[0]['url'],
                           format=songs[0]['type'],
                           bitrate=songs[0]['br'] // 1000)
             self.q_media_mapping[quality] = media
         else:
             self.q_media_mapping[quality] = ''
     return self.q_media_mapping.get(quality)
Beispiel #21
0
def test_media_copy(media):
    media2 = Media(media)
    assert media2.http_headers == media.http_headers
Beispiel #22
0
def media():
    return Media('zzz://xxx.yyy', http_headers={'referer': 'http://xxx.yyy'})
Beispiel #23
0
 def test_play_media_with_http_headers(self, mock_set_option_string):
     media = Media('http://xxx', http_headers={'referer': 'http://xxx'})
     self.player.play(media)
     assert mock_set_option_string.called
     self.player.stop()