Skip to content

Commit

Permalink
refactor naming
Browse files Browse the repository at this point in the history
  • Loading branch information
cosven committed Jul 27, 2024
1 parent 90c01ea commit da4e676
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 90 deletions.
5 changes: 3 additions & 2 deletions feeluown/player/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from .playlist import PlaybackMode, PlaylistRepeatMode, PlaylistShuffleMode
from .base_player import State
from .mpvplayer import MpvPlayer as Player
from .playlist import PlaylistMode, Playlist, MetadataManager
from .playlist import PlaylistMode, Playlist
from .metadata_assembler import MetadataAssembler
from .fm import FM
from .radio import SongRadio
from .lyric import LiveLyric, parse_lyric_text, Line as LyricLine, Lyric
Expand All @@ -24,9 +25,9 @@
'Playlist',
'PlayerPositionDelegate',

'MetadataManager',
'Metadata',
'MetadataFields',
'MetadataAssembler',

'LiveLyric',
'parse_lyric_text',
Expand Down
87 changes: 87 additions & 0 deletions feeluown/player/metadata_assembler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import logging
from typing import TYPE_CHECKING

from feeluown.excs import ResourceNotFound, ModelNotFound
from feeluown.library import reverse, SongModel
from feeluown.player import Metadata, MetadataFields
from feeluown.utils import aio

if TYPE_CHECKING:
from feeluown.app import App

logger = logging.getLogger(__name__)


class MetadataAssembler:
"""Cook and fetch metadata for songs and videos"""
def __init__(self, app: 'App'):
self._app = app

@staticmethod
def cook_basic_metadata_for_song(song):
return Metadata({
MetadataFields.uri: reverse(song),
MetadataFields.source: song.source,
MetadataFields.title: song.title_display or '',
# The song.artists_name should return a list of strings
MetadataFields.artists: [song.artists_name_display or ''],
MetadataFields.album: song.album_name_display or '',
})

async def fetch_from_song(self, song):
empty_result = ('', '', None)
try:
usong: SongModel = await aio.wait_for(
aio.run_fn(self._app.library.song_upgrade, song),
timeout=1,
)
except ResourceNotFound:
return empty_result
except: # noqa
logger.exception(f"fetching song's meta failed, song:'{song.title_display}'")
return empty_result
return usong.pic_url, usong.date, usong.album

async def fetch_from_album(self, album):
empty_result = ('', '')
try:
album = await aio.wait_for(
aio.run_fn(self._app.library.album_upgrade, album),
timeout=1
)
except ResourceNotFound:
return empty_result
except: # noqa
logger.warning(
f"fetching album meta failed, album:{album.name}")
return empty_result
return album.cover, album.released

async def prepare_for_song(self, song):
metadata = self.cook_basic_metadata_for_song(song)

artwork, released, album = await self.fetch_from_song(song)
if not (artwork and released) and album is not None:
album_cover, album_released = await self.fetch_from_album(album)
# Try to use album meta first.
artwork = album_cover or artwork
released = album_released or released
metadata[MetadataFields.artwork] = artwork
metadata[MetadataFields.released] = released

return metadata

async def prepare_for_video(self, video):
metadata = Metadata({
# The value of model v1 title_display may be None.
MetadataFields.title: video.title_display or '',
MetadataFields.source: video.source,
MetadataFields.uri: reverse(video),
})
try:
video = await aio.run_fn(self._app.library.video_upgrade, video)
except ModelNotFound as e:
logger.warning(f"can't get cover of video due to {str(e)}")
else:
metadata[MetadataFields.artwork] = video.cover
return metadata
86 changes: 6 additions & 80 deletions feeluown/player/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@
from enum import IntEnum, Enum
from typing import Optional, TYPE_CHECKING

from feeluown.excs import ModelNotFound, ProviderIOError
from feeluown.excs import ProviderIOError
from feeluown.utils import aio
from feeluown.utils.aio import run_fn, run_afn
from feeluown.utils.dispatch import Signal
from feeluown.utils.utils import DedupList
from feeluown.player import Metadata, MetadataFields
from feeluown.library import (
MediaNotFound, SongModel, ModelType, ResourceNotFound, VideoModel,
MediaNotFound, SongModel, ModelType, VideoModel,
)
from feeluown.media import Media
from feeluown.library import reverse
from .metadata_assembler import MetadataAssembler

if TYPE_CHECKING:
from feeluown.app import App
Expand All @@ -27,7 +26,7 @@ class PlaybackMode(IntEnum):
"""
Playlist playback mode.
.. versiondeprecated:: 3.8.12
.. deprecated:: 3.8.12
Please use PlaylistRepeatMode and PlaylistShuffleMode instead.
"""
one_loop = 0 #: One Loop
Expand Down Expand Up @@ -81,7 +80,7 @@ def __init__(self, app: 'App', songs=None, playback_mode=PlaybackMode.loop,
:param playback_mode: :class:`feeluown.player.PlaybackMode`
"""
self._app = app
self._metadata_mgr = MetadataManager(app)
self._metadata_mgr = MetadataAssembler(app)

#: init playlist mode normal
self._mode = PlaylistMode.normal
Expand Down Expand Up @@ -328,7 +327,7 @@ def _get_good_song(self, base=0, random_=False, direction=1, loop=True):
"""从播放列表中获取一首可以播放的歌曲
:param base: base index
:param random: random strategy or not
:param random_: random strategy or not
:param direction: forward if > 0 else backward
:param loop: regard the song list as a loop
Expand Down Expand Up @@ -676,76 +675,3 @@ def cb(future):
self._app.player.resume()
logger.info(f'play a model ({model}) succeed')
task.add_done_callback(cb)


class MetadataManager:
def __init__(self, app: 'App'):
self._app = app

def _prepare_basic_metadata_for_song(self, song):
return Metadata({
MetadataFields.uri: reverse(song),
MetadataFields.source: song.source,
MetadataFields.title: song.title_display or '',
# The song.artists_name should return a list of strings
MetadataFields.artists: [song.artists_name_display or ''],
MetadataFields.album: song.album_name_display or '',
})

async def fetch_from_song(self, song):
empty_result = ('', '', None)
try:
usong: SongModel = await aio.wait_for(
aio.run_fn(self._app.library.song_upgrade, song),
timeout=1,
)
except ResourceNotFound:
return empty_result
except: # noqa
logger.exception(f"fetching song's meta failed, song:'{song.title_display}'")
return empty_result
return (usong.pic_url, usong.date, usong.album)

async def fetch_from_album(self, album):
empty_result = ('', '')
try:
album = await aio.wait_for(
aio.run_fn(self._app.library.album_upgrade, album),
timeout=1
)
except ResourceNotFound:
return empty_result
except: # noqa
logger.warning(
f"fetching album meta failed, album:{album.name}")
return empty_result
return (album.cover, album.released)

async def prepare_for_song(self, song):
metadata = self._prepare_basic_metadata_for_song(song)

artwork, released, album = await self.fetch_from_song(song)
if not (artwork and released) and album is not None:
album_cover, album_released = await self.fetch_from_album(album)
# Try to use album meta first.
artwork = album_cover or artwork
released = album_released or released
metadata[MetadataFields.artwork] = artwork
metadata[MetadataFields.released] = released

return metadata

async def prepare_for_video(self, video):
metadata = Metadata({
# The value of model v1 title_display may be None.
MetadataFields.title: video.title_display or '',
MetadataFields.source: video.source,
MetadataFields.uri: reverse(video),
})
try:
video = await aio.run_fn(self._app.library.video_upgrade, video)
except ModelNotFound as e:
logger.warning(f"can't get cover of video due to {str(e)}")
else:
metadata[MetadataFields.artwork] = video.cover
return metadata
4 changes: 2 additions & 2 deletions tests/player/test_fm.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest

from feeluown.excs import ProviderIOError
from feeluown.player import Playlist, PlaylistMode, FM, MetadataManager
from feeluown.player import Playlist, PlaylistMode, FM, MetadataAssembler
from feeluown.task import TaskManager


Expand Down Expand Up @@ -76,7 +76,7 @@ async def test_multiple_eof_reached_signal(app_mock, song, mocker):
async def test_reactivate_fm_mode_after_playing_other_songs(
app_mock, song, song1, mocker):

mocker.patch.object(MetadataManager, 'prepare_for_song')
mocker.patch.object(MetadataAssembler, 'prepare_for_song')

def f(*args, **kwargs): return [song1]

Expand Down
12 changes: 6 additions & 6 deletions tests/player/test_playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from feeluown.library.excs import MediaNotFound
from feeluown.player import (
Playlist, PlaylistMode, Player, PlaybackMode,
PlaylistRepeatMode, PlaylistShuffleMode, MetadataManager
PlaylistRepeatMode, PlaylistShuffleMode, MetadataAssembler
)
from feeluown.utils.dispatch import Signal

Expand Down Expand Up @@ -115,7 +115,7 @@ async def test_set_current_song_with_bad_song_1(
mock_pure_set_current_song = mocker.patch.object(Playlist, 'pure_set_current_song')
mock_mark_as_bad = mocker.patch.object(Playlist, 'mark_as_bad')
sentinal = object()
mocker.patch.object(MetadataManager, 'prepare_for_song', return_value=sentinal)
mocker.patch.object(MetadataAssembler, 'prepare_for_song', return_value=sentinal)
await pl.a_set_current_song(song2)
# A song that has no valid media should be marked as bad
assert mock_mark_as_bad.called
Expand All @@ -131,7 +131,7 @@ async def test_set_current_song_with_bad_song_2(
mock_pure_set_current_song = mocker.patch.object(Playlist, 'pure_set_current_song')
mock_mark_as_bad = mocker.patch.object(Playlist, 'mark_as_bad')
sentinal = object()
mocker.patch.object(MetadataManager, 'prepare_for_song', return_value=sentinal)
mocker.patch.object(MetadataAssembler, 'prepare_for_song', return_value=sentinal)
await pl.a_set_current_song(song2)
# A song that has no valid media should be marked as bad
assert mock_mark_as_bad.called
Expand All @@ -149,7 +149,7 @@ async def test_set_current_song_with_bad_song_3(
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(MetadataManager, 'prepare_for_song', return_value=metadata)
mocker.patch.object(MetadataAssembler, 'prepare_for_song', return_value=metadata)

app_mock.config.ENABLE_MV_AS_STANDBY = 1
pl = Playlist(app_mock)
Expand Down Expand Up @@ -180,7 +180,7 @@ async def test_set_an_existing_bad_song_as_current_song(
song1 is bad, standby is [song2]
play song1, song2 should be insert after song1 instead of song
"""
mocker.patch.object(MetadataManager, 'prepare_for_song')
mocker.patch.object(MetadataAssembler, 'prepare_for_song')
await pl.a_set_current_song(song1)
assert pl.list().index(song2) == 2

Expand Down Expand Up @@ -298,7 +298,7 @@ async def test_play_next_bad_song(app_mock, song, song1, mocker):
be marked as bad. Besides, it should try to find standby.
"""
mock_pure_set_current_song = mocker.patch.object(Playlist, 'pure_set_current_song')
mocker.patch.object(MetadataManager, 'prepare_for_song', return_value=object())
mocker.patch.object(MetadataAssembler, 'prepare_for_song', return_value=object())
mock_standby = mocker.patch.object(Playlist,
'find_and_use_standby',
return_value=(song1, None))
Expand Down

0 comments on commit da4e676

Please sign in to comment.