Skip to content

Commit

Permalink
player: use mv as standby (configurable) (#796)
Browse files Browse the repository at this point in the history
  • Loading branch information
cosven committed Feb 16, 2024
1 parent 3b64331 commit 92a1723
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 21 deletions.
4 changes: 4 additions & 0 deletions feeluown/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ def create_config() -> Config:
config.deffield('VIDEO_SELECT_POLICY', default='hd<>')
config.deffield('ALLOW_LAN_CONNECT', type_=bool, default=False, desc='是否可以从局域网连接服务器')
config.deffield('PROVIDERS_STANDBY', type_=list, default=None, desc='')
# TODO(cosven): maybe
# 1. when it is set to 2, find standby from other providers first.
# 2. when it is set to 3, play it's MV model instead of using MV's media.
config.deffield('ENABLE_MV_AS_STANDBY', type_=int, default=1, desc='MV 作为备用资源')
config.deffield('ENABLE_TRAY', type_=bool, default=True, desc='启用系统托盘')
config.deffield('NOTIFY_ON_TRACK_CHANGED', type_=bool, default=False,
desc='切换歌曲时显示桌面通知')
Expand Down
50 changes: 29 additions & 21 deletions feeluown/player/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ async def a_set_current_song(self, song):
return
except Exception as e: # noqa
# When the exception is unknown, we mark the song as bad.
self._app.show_msg(f'prepare media failed due to unknown error: {e}')
self._app.show_msg(f'获取歌曲链接失败: {e}')
logger.exception('prepare media failed due to unknown error, '
'so we mark the song as a bad one')
self.mark_as_bad(song)
Expand All @@ -519,7 +519,13 @@ async def a_set_current_song(self, song):
if self.mode is PlaylistMode.fm:
run_afn(self.a_next)
return
target_song, media = await self.find_and_use_standby(song)
if self._app.config.ENABLE_MV_AS_STANDBY:
self._app.show_msg('尝试获取音乐视频的播放资源...')
media = await self._prepare_mv_media(song)
if media:
self._app.show_msg('使用音乐视频作为其播放资源 ✅')
else:
target_song, media = await self.find_and_use_standby(song)

metadata = await self._prepare_metadata_for_song(target_song)
self.pure_set_current_song(target_song, media, metadata)
Expand All @@ -538,27 +544,25 @@ async def a_set_current_song_children(self, song):
return

async def find_and_use_standby(self, song):
self._app.show_msg(f'{song} is invalid, try to find standby')
logger.info(f'try to find standby for {song}')
self._app.show_msg(f'{song} 无可用的播放资源, 尝试寻找备用歌曲...')
logger.info(f'try to find standby from other providers for {song}')
standby_candidates = await self._app.library.a_list_song_standby_v2(
song,
self.audio_select_policy
)
if standby_candidates:
standby, media = standby_candidates[0]
msg = f'song standby was found in {standby.source} ✅'
logger.info(msg)
self._app.show_msg(msg)
logger.info(f'song standby was found in {standby.source} ✅')
self._app.show_msg(f'在 {standby.source} 平台找到 {song} 的备用歌曲 ✅')
# Insert the standby song after the song
if song in self._songs and standby not in self._songs:
index = self._songs.index(song)
self._songs.insert(index + 1, standby)
self.songs_added.emit(index + 1, 1)
return standby, media

msg = 'song standby not found'
logger.info(msg)
self._app.show_msg(msg)
logger.info(f'{song} song standby not found')
self._app.show_msg(f'未找到 {song} 的备用歌曲')
return song, None

def pure_set_current_song(self, song, media, metadata=None):
Expand Down Expand Up @@ -651,22 +655,26 @@ async def _prepare_media(self, song):
task_spec = self._app.task_mgr.get_or_create('prepare-media')
task_spec.disable_default_cb()
if self.watch_mode is True:
try:
mv_media = await task_spec.bind_blocking_io(
self._app.library.song_prepare_mv_media,
song,
self._app.config.VIDEO_SELECT_POLICY)
except MediaNotFound:
mv_media = None
self._app.show_msg('No mv found')
except Exception as e: # noqa
mv_media = None
self._app.show_msg(f'Prepare mv media failed: {e}')
mv_media = await task_spec.bind_coro(self._prepare_mv_media(song))
if mv_media:
return mv_media
self._app.show_msg('未找到可用的歌曲视频资源')
return await task_spec.bind_blocking_io(
self._app.library.song_prepare_media, song, self.audio_select_policy)

async def _prepare_mv_media(self, song) -> Optional[Media]:
try:
mv_media = await run_fn(
self._app.library.song_prepare_mv_media,
song,
self._app.config.VIDEO_SELECT_POLICY)
except MediaNotFound:
mv_media = None
except Exception as e: # noqa
mv_media = None
logger.exception(f'fail to get {song} mv: {e}')
return mv_media

def set_current_model(self, model):
"""
.. versionadded: 3.7.13
Expand Down
22 changes: 22 additions & 0 deletions tests/player/test_playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def pl(app_mock, song, song1):
"""
pl: [song, song1], current_song: song
"""
app_mock.config.ENABLE_MV_AS_STANDBY = 0
playlist = Playlist(app_mock)
playlist.add(song)
playlist.add(song1)
Expand Down Expand Up @@ -138,6 +139,26 @@ async def test_set_current_song_with_bad_song_2(
mock_pure_set_current_song.assert_called_once_with(song2, SONG2_URL, sentinal)


@pytest.mark.asyncio
async def test_set_current_song_with_bad_song_3(
mocker, song2, app_mock,
pl_prepare_media_none,):
"""song has mv and the mv has valid media, should use mv's media as the media"""
media = object()
metadata = object()

mock_pure_set_current_song = mocker.patch.object(Playlist, 'pure_set_current_song')
mock_prepare_mv_media = mocker.patch.object(Playlist, '_prepare_mv_media',
return_value=media)
mocker.patch.object(Playlist, '_prepare_metadata_for_song', return_value=metadata)

app_mock.config.ENABLE_MV_AS_STANDBY = 1
pl = Playlist(app_mock)
await pl.a_set_current_song(song2)
mock_prepare_mv_media.assert_called_once_with(song2)
mock_pure_set_current_song.assert_called_once_with(song2, media, metadata)


def test_pure_set_current_song(
mocker, song, song2, pl):
# Current song index is 0
Expand Down Expand Up @@ -285,6 +306,7 @@ async def test_play_next_bad_song(app_mock, song, song1, mocker):
mocker.patch.object(Playlist, '_prepare_media', side_effect=Exception())
mock_mark_as_bad = mocker.patch.object(Playlist, 'mark_as_bad')

app_mock.config.ENABLE_MV_AS_STANDBY = 0
pl = Playlist(app_mock)
pl.add(song)
pl.add(song1)
Expand Down

0 comments on commit 92a1723

Please sign in to comment.