Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

player: add timeout when fetching song metadata #853

Merged
merged 2 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion feeluown/library/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@ def song_prepare_media(self, song: BriefSongModel, policy) -> Media:
song_web_url = provider.song_get_web_url(song)
logger.info(f'use ytdl to get media for {song_web_url}')
media = self.ytdl.select_audio(song_web_url, policy, source=song.source)
found = media is not None
logger.debug(f'ytdl select audio for {song_web_url} finished, '
f'found: {found}')
if not media:
raise MediaNotFound('provider returns empty media')
return media
Expand Down Expand Up @@ -425,7 +428,6 @@ def video_prepare_media(self, video: BriefVideoModel, policy) -> Media:
:param video: either a v1 MvModel or a v2 (Brief)VideoModel.
"""
provider = self.get(video.source)

try:
if isinstance(provider, SupportsVideoMultiQuality):
media, _ = provider.video_select_media(video, policy)
Expand All @@ -440,6 +442,11 @@ def video_prepare_media(self, video: BriefVideoModel, policy) -> Media:
media = self.ytdl.select_video(video_web_url,
policy,
source=video.source)
found = media is not None
logger.debug(f'ytdl select video for {video_web_url} finished, '
f'found: {found}')
else:
media = None
if not media:
raise
return media
20 changes: 13 additions & 7 deletions feeluown/player/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,26 +600,32 @@ async def _prepare_metadata_for_song(self, song):
MetadataFields.album: song.album_name_display or '',
})
try:
song = await aio.run_fn(self._app.library.song_upgrade, song)
song: SongModel = await aio.wait_for(
aio.run_fn(self._app.library.song_upgrade, song),
timeout=1,
)
except ResourceNotFound:
return metadata
except: # noqa
logger.exception(f"fetching song's meta failed, song:'{song.title_display}'")
return metadata

artwork = ''
released = ''
if song.album is not None:
artwork = song.pic_url
released = song.date
if not (artwork and released) and song.album is not None:
try:
album = await aio.run_fn(self._app.library.album_upgrade, song.album)
album = await aio.wait_for(
aio.run_fn(self._app.library.album_upgrade, song.album),
timeout=1
)
except ResourceNotFound:
pass
except: # noqa
logger.warning(
f"fetching song's album meta failed, song:{song.title_display}")
else:
artwork = album.cover
released = album.released
artwork = album.cover or artwork
released = album.released or released
# For model v1, the cover can be a Media object.
# For example, in fuo_local plugin, the album.cover is a Media
# object with url set to fuo://local/songs/{identifier}/data/cover.
Expand Down
3 changes: 3 additions & 0 deletions feeluown/utils/aio.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
#: gather is an alias of `asyncio.gather`.
gather = asyncio.gather

#: wait_for is an alias of `asyncio.wait_for`
wait_for = asyncio.wait_for


def run_in_executor(executor, func, *args):
"""alias for loop.run_in_executor"""
Expand Down
10 changes: 6 additions & 4 deletions tests/player/test_playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,13 +355,15 @@ def test_playlist_next_should_call_set_current_song(app_mock, mocker, song):


@pytest.mark.asyncio
async def test_playlist_prepare_metadata_for_song(app_mock, pl, ekaf_brief_song0):
async def test_playlist_prepare_metadata_for_song(
app_mock, library, pl, ekaf_brief_song0, mocker):
class Album:
cover = Media('fuo://')
released = '2018-01-01'

app_mock.library = library
album = Album()
f = asyncio.Future()
f.set_result(album)
app_mock.library.album_upgrade.return_value = album
mocker.patch.object(library, 'album_upgrade', return_value=album)
# app_mock.library.album_upgrade.return_value = album
# When cover is a media object, prepare_metadata should also succeed.
await pl._prepare_metadata_for_song(ekaf_brief_song0)
Loading