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 song_get_media(self, song, quality):
     q_media_mapping = self._song_get_q_media_mapping(song)
     if quality not in q_media_mapping:
         return None
     song_id = int(song.identifier)
     bitrate, url, format = q_media_mapping.get(quality)
     # None means the url is not fetched, so try to fetch it.
     if url is None:
         songs_data = self.api.weapi_songs_url([song_id], bitrate)
         if songs_data:
             song_data = songs_data[0]
             url = song_data['url']
             actual_bitrate = song_data['br']
             format = song_data['type']
             # Check the url bitrate while it is not empty. Api
             # may return a fallback bitrate when the expected bitrate
             # resource is not valid.
             if url and abs(actual_bitrate - bitrate) >= 10000:
                 logger.warning(f'The actual bitrate is {actual_bitrate} '
                                f'while we want {bitrate}. '
                                f'[song:{song_id}].')
     if url:
         media = Media(url, bitrate=bitrate // 1000, format=format)
         # update value in cache
         q_media_mapping[quality] = (bitrate, url, format)
         return media
     logger.error('This should not happend')
     return None
def test_show_bitrate(qtbot, app_mock, mocker):
    app_mock.player.current_media = Media('http://', bitrate=100)
    mocker.patch.object(PlayerControlPanel, '_update_pms_btn_text')
    w = PlayerControlPanel(app_mock)
    qtbot.addWidget(w)
    metadata = {'title': 'xx'}
    w.on_metadata_changed(metadata)
    assert '100kbps' in w.song_source_label.text()
Beispiel #4
0
 def prepare_media(self, song):
     """prepare media data
     """
     warnings.warn('use library.song_prepare_media please', DeprecationWarning)
     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 #5
0
 async def show_cover(self, cover, cover_uid):
     meta_widget = self.collection_body.meta_widget
     cover = Media(cover, MediaType.image)
     cover = cover.url
     app = self._app
     content = await app.img_mgr.get(cover, cover_uid)
     img = QImage()
     img.loadFromData(content)
     pixmap = QPixmap(img)
     if not pixmap.isNull():
         meta_widget.set_cover_pixmap(pixmap)
Beispiel #6
0
    def async_update_song_props(self, song, media):
        task_spec = self._app.task_mgr.get_or_create('mpris2-update-property')
        task = task_spec.bind_blocking_io(self._update_song_props, song,
                                          Media(media))

        def cb(future):
            try:
                future.result()
            except:  # noqa
                logger.exception('mpris update song props failed')

        task.add_done_callback(cb)
Beispiel #7
0
    def play(self, media, video=True, metadata=None):
        if video is False:
            _mpv_set_property_string(self._mpv.handle, b'vid', b'no')
        else:
            _mpv_set_property_string(self._mpv.handle, b'vid', b'auto')

        self.media_about_to_changed.emit(self._current_media, media)
        if media is None:
            self._stop_mpv()
        else:
            logger.debug("Player will play: '%s'", media)
            if isinstance(media, Media):
                media = media
            else:  # media is a url(string)
                media = Media(media)
            self._set_http_headers(media.http_headers)
            self._stop_mpv()
            if media.manifest is None:
                url = media.url
                # Clear playlist before play next song,
                # otherwise, mpv will seek to the last position and play.
                self._mpv.play(url)
            elif isinstance(media.manifest, VideoAudioManifest):
                video_url = media.manifest.video_url
                audio_url = media.manifest.audio_url

                def add_audio():
                    try:
                        if self.current_media is media:
                            self._mpv.audio_add(audio_url)
                            self.resume()
                    finally:
                        self.media_loaded.disconnect(add_audio)

                if video is True:
                    self._mpv.play(video_url)
                    # It seems we can only add audio after the video is loaded.
                    # TODO: add method connect_once for signal
                    self.media_loaded.connect(add_audio, weak=False)
                else:
                    self._mpv.play(audio_url)
            else:
                assert False, 'Unknown manifest'
        self._current_media = media
        self.media_changed.emit(media)
        if metadata is None:
            self._current_metadata = {}
        else:
            # The metadata may be set by manual or automatic
            metadata['__setby__'] = 'manual'
            self._current_metadata = metadata
        self.metadata_changed.emit(self.current_metadata)
Beispiel #8
0
 def song_prepare_media(self, song: BriefSongProtocol,
                        policy) -> Optional[Media]:
     if song.meta.flags & MF.v2:
         # provider MUST has multi_quality flag for song
         assert self.check_flags_by_model(song, PF.multi_quality)
         provider = self.get_or_raise(song.source)
         media, _ = provider.song_select_media(song, policy)
     else:
         if song.meta.support_multi_quality:
             media, _ = song.select_media(policy)  # type: ignore
         else:
             url = song.url  # type: ignore
             media = Media(url) if url else None
     return media
Beispiel #9
0
    async def download_song(self, song):
        media = await aio.run_fn(self._app.library.song_prepare_media, song,
                                 self._app.playlist.audio_select_policy)
        media = Media(media)
        media_url = media.url
        if media_url:
            if not media_url.startswith('http'):
                # media_url in local provider is filelname
                logger.info(f'download {media_url} has exists')
                return

            ext = guess_media_url_ext(media_url)
            if self._tag_mgr._name_fmts:
                if hasattr(song,
                           'state') and song.state is ModelState.cant_upgrade:
                    # BriefSongModdel
                    tag_obj, cover_url = await aio.run_fn(
                        self._tag_mgr.prepare_tag, song)
                else:
                    song = await aio.run_fn(self._app.library.song_upgrade,
                                            song)
                    album = await aio.run_fn(self._app.library.album_upgrade,
                                             song.album)
                    artists = [
                        await aio.run_fn(self._app.library.artist_upgrade,
                                         artist) for artist in song.artists
                    ]
                    tag_obj, cover_url = await aio.run_fn(
                        self._tag_mgr.prepare_tag, song, album, artists)
                tag_obj, cover_url = await aio.run_fn(
                    self._tag_mgr.prepare_tag, song, album, artists)
                filename = self._tag_mgr.prepare_filename(tag_obj, ext)
            else:
                title = await async_run(lambda: song.title)
                artists_name = await async_run(lambda: song.artists_name)
                filename = cook_filename(title, artists_name, ext)
            is_downloaded = self._mgr.is_file_downloaded(filename)
            if is_downloaded:
                logger.info(f'download {filename} has exists')
                return

            logger.info(f'download {media_url} into {filename}')
            file_path = await self._mgr.get(media_url, filename)

            if self._tag_mgr._name_fmts and file_path and cover_url:
                self._tag_mgr.put_f(filename, file_path, tag_obj, cover_url)
        else:
            # this should not happen, so we log a error msg
            logger.error('url of current song is empty, will not download')
Beispiel #10
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 #11
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 #12
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 #13
0
    def song_prepare_mv_media(self, song: BriefSongProtocol, policy) -> Media:
        """

        .. versionadded:: 3.7.5
        """
        song_v1 = self.cast_model_to_v1(song)
        mv = song_v1.mv
        if mv.meta.support_multi_quality:
            media, _ = mv.select_media(policy)
        else:
            media = mv.media
            if media:
                media = Media(media)
            else:
                media = None
        if not media:
            raise MediaNotFound
        return media
Beispiel #14
0
 async def download_song(self, song):
     title = await async_run(lambda: song.title)
     artists_name = await async_run(lambda: song.artists_name)
     media = await aio.run_in_executor(
         None, self._app.library.song_prepare_media, song,
         self._app.playlist.audio_select_policy)
     media = Media(media)
     media_url = media.url
     if media_url:
         ext = guess_media_url_ext(media_url)
         filename = cook_filename(title, artists_name, ext)
         if self._mgr.is_file_downloaded(filename):
             return
         logger.info(f'download {media_url} into {filename}')
         await self._mgr.get(media_url, filename)
     else:
         # this should not happen, so we log a error msg
         logger.error('url of current song is empty, will not download')
Beispiel #15
0
 def song_prepare_media(self, song: BriefSongProtocol, policy) -> Media:
     provider = self.get(song.source)
     if provider is None:
         raise MediaNotFound(f'provider:{song.source} not found')
     if song.meta.flags & MF.v2:
         # provider MUST has multi_quality flag for song
         assert self.check_flags_by_model(song, PF.multi_quality)
         media, _ = provider.song_select_media(song, policy)
     else:
         if song.meta.support_multi_quality:
             media, _ = song.select_media(policy)  # type: ignore
         else:
             url = song.url  # type: ignore
             if url:
                 media = Media(url)
             else:
                 raise MediaNotFound
     return media
Beispiel #16
0
 def create_model(self, data, **kwargs):
     brs = data.pop('brs')
     q_media_mapping = {}
     for q, url in brs.items():
         media = Media(url, type_=MediaType.video)
         if q == '1080':
             quality = Quality.Video.fhd
         elif q == '720':
             quality = Quality.Video.hd
         elif q == '480':
             quality = Quality.Video.sd
         elif q == '240':
             quality = Quality.Video.ld
         else:
             logger.warning('There exists another quality:%s mv.', q)
             quality = Quality.Video.sd
         q_media_mapping[quality] = media
     data['q_media_mapping'] = q_media_mapping
     data['identifier'] = 'mv_' + str(data['identifier'])
     return create_model(VideoModel, data, ['q_media_mapping'])
Beispiel #17
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()
Beispiel #18
0
def media():
    return Media('zzz://xxx.yyy', http_headers={'referer': 'http://xxx.yyy'})
Beispiel #19
0
def test_media_copy(media):
    media2 = Media(media)
    assert media2.http_headers == media.http_headers