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: show some message(progress) after user play a model #862

Merged
merged 2 commits into from
Aug 6, 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
18 changes: 14 additions & 4 deletions feeluown/gui/components/line_song.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from PyQt5.QtGui import QPainter, QPalette, QColor
from PyQt5.QtWidgets import QLabel, QSizePolicy, QMenu, QVBoxLayout, QWidget

from feeluown.player import PlaylistPlayModelStage
from feeluown.library import fmt_artists_names
from feeluown.gui.components import SongMenuInitializer
from feeluown.gui.helpers import elided_text
Expand Down Expand Up @@ -35,8 +36,8 @@ def __init__(self, app: 'GuiApp', parent=None):
self._app.player.metadata_changed.connect(
self.on_metadata_changed, aioqueue=True
)
self._app.playlist.play_model_handling.connect(
self.on_play_model_handling, aioqueue=True
self._app.playlist.play_model_stage_changed.connect(
self.on_play_model_stage_changed, aioqueue=True
)

def on_metadata_changed(self, metadata):
Expand All @@ -53,8 +54,17 @@ def on_metadata_changed(self, metadata):
text += f" • {','.join(artists)}"
self.setText(text)

def on_play_model_handling(self):
self.setText('正在加载歌曲...')
def on_play_model_stage_changed(self, stage):
if stage == PlaylistPlayModelStage.prepare_media:
self.setText('正在获取歌曲播放链接...')
elif stage == PlaylistPlayModelStage.find_standby_by_mv:
self.setText('正在获取音乐的视频播放链接...')
elif stage == PlaylistPlayModelStage.find_standby:
self.setText('尝试寻找备用播放链接...')
elif stage == PlaylistPlayModelStage.prepare_metadata:
self.setText('尝试获取完整的歌曲元信息...')
elif stage == PlaylistPlayModelStage.load_media:
self.setText('正在加载歌曲资源...')

def change_text_position(self):
if not self.parent().isVisible(): # type: ignore
Expand Down
2 changes: 0 additions & 2 deletions feeluown/library/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,6 @@ def song_prepare_media(self, song: BriefSongModel, policy) -> Media:
if not media:
if self.ytdl is not None and isinstance(provider, SupportsSongWebUrl):
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, '
Expand Down Expand Up @@ -438,7 +437,6 @@ def video_prepare_media(self, video: BriefVideoModel, policy) -> Media:
except MediaNotFound:
if self.ytdl is not None and isinstance(provider, SupportsVideoWebUrl):
video_web_url = provider.video_get_web_url(video)
logger.info(f'use ytdl to get media for {video_web_url}')
media = self.ytdl.select_video(video_web_url,
policy,
source=video.source)
Expand Down
6 changes: 5 additions & 1 deletion feeluown/library/ytdl.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def __init__(self, rules: Optional[List[Any]] = None):
# TODO: valid rules
self._rules = rules or []

def match_rule(self, _, source=''):
def match_rule(self, url, source=''):
"""
Currently, only 'match_source' is supported. Maybe support 'match_url'
in the future.
Expand All @@ -39,6 +39,10 @@ def match_rule(self, _, source=''):
for rule in self._rules:
if rule['name'] == 'match_source' and source == rule['pattern']:
matched_rule = rule
if matched_rule:
logger.info(f"ytdl rule matched for {url}")
else:
logger.info(f"no ytdl rule matched for {url}")
return matched_rule

def select_audio(self, url, _: Optional[str] = None, source='') -> Optional[Media]:
Expand Down
4 changes: 3 additions & 1 deletion feeluown/player/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .metadata import MetadataFields, Metadata
from .playlist import PlaybackMode, PlaylistRepeatMode, PlaylistShuffleMode
from .playlist import PlaybackMode, PlaylistRepeatMode, PlaylistShuffleMode, \
PlaylistPlayModelStage
from .base_player import State
from .mpvplayer import MpvPlayer as Player
from .playlist import PlaylistMode, Playlist
Expand All @@ -15,6 +16,7 @@
'PlaybackMode',
'PlaylistRepeatMode',
'PlaylistShuffleMode',
'PlaylistPlayModelStage',
'State',

'FM',
Expand Down
24 changes: 22 additions & 2 deletions feeluown/player/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ class PlaylistMode(IntEnum):
fm = 1 #: FM mode


class PlaylistPlayModelStage(IntEnum):
prepare_media = 10
find_standby_by_mv = 20
find_standby = 30
prepare_metadata = 40
load_media = 50


class Playlist:
def __init__(self, app: 'App', songs=None, playback_mode=PlaybackMode.loop,
audio_select_policy='hq<>'):
Expand Down Expand Up @@ -134,6 +142,9 @@ def __init__(self, app: 'App', songs=None, playback_mode=PlaybackMode.loop,
# .. versionadded:: 3.9.0
# The *play_model_handling* signal.
self.play_model_handling = Signal()
# .. versionadded:: 4.1.7
# The *play_model_handling* signal.
self.play_model_stage_changed = Signal()

self._app.player.media_finished.connect(self._on_media_finished)
self.song_changed.connect(self._on_song_changed)
Expand Down Expand Up @@ -493,6 +504,7 @@ async def a_set_current_song(self, song):
target_song = song # The song to be set.
media = None # The corresponding media to be set.
try:
self.play_model_stage_changed.emit(PlaylistPlayModelStage.prepare_media)
media = await self._prepare_media(song)
except MediaNotFound as e:
if e.reason is MediaNotFound.Reason.check_children:
Expand All @@ -513,12 +525,14 @@ async def a_set_current_song(self, song):
# The song has no media, try to find and use standby unless it is in fm mode.
if media is None:
if self._app.config.ENABLE_MV_AS_STANDBY:
self._app.show_msg('尝试获取音乐视频的播放资源...')
self.play_model_stage_changed.emit(
PlaylistPlayModelStage.find_standby_by_mv)
media = await self._prepare_mv_media(song)

if media:
self._app.show_msg('使用音乐视频作为其播放资源 ✅')
else:
self._app.show_msg('未找到可用的音乐视频资源 🙁')
# if mode is fm mode, do not find standby song, just skip the song.
if self.mode is PlaylistMode.fm:
self.mark_as_bad(song)
Expand All @@ -527,9 +541,14 @@ async def a_set_current_song(self, song):

logger.info(f"no media found for {song}, mark it as bad")
self.mark_as_bad(song)
self.play_model_stage_changed.emit(PlaylistPlayModelStage.find_standby)
target_song, media = await self.find_and_use_standby(song)

metadata = await self._metadata_mgr.prepare_for_song(target_song)
metadata = None
if media is not None:
self.play_model_stage_changed.emit(PlaylistPlayModelStage.prepare_metadata)
metadata = await self._metadata_mgr.prepare_for_song(target_song)
self.play_model_stage_changed.emit(PlaylistPlayModelStage.load_media)
self.pure_set_current_song(target_song, media, metadata)

async def a_set_current_song_children(self, song):
Expand Down Expand Up @@ -582,6 +601,7 @@ def pure_set_current_song(self, song, media, metadata=None):

if song is not None:
if media is None:
self._app.show_msg("没找到可用的播放链接,播放下一首...")
run_afn(self.a_next)
else:
# Note that the value of model v1 {}_display may be None.
Expand Down
4 changes: 1 addition & 3 deletions tests/player/test_playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,11 @@ async def test_set_current_song_with_bad_song_1(
pl_list_standby_return_empty):
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(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
# Since there is no standby song, the media should be None
mock_pure_set_current_song.assert_called_once_with(song2, None, sentinal)
mock_pure_set_current_song.assert_called_once_with(song2, None, None)


@pytest.mark.asyncio
Expand Down
Loading