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
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()
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
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)
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)
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)
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
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')
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
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()
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)
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
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')
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
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'])
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()
def media(): return Media('zzz://xxx.yyy', http_headers={'referer': 'http://xxx.yyy'})
def test_media_copy(media): media2 = Media(media) assert media2.http_headers == media.http_headers