Esempio n. 1
0
    def __init__(self, playlist=None, **kwargs):
        self._position = 0  # seconds
        self._volume = 100  # (0, 100)
        self._playlist = Playlist() if playlist is None else playlist
        self._state = State.stopped
        self._duration = None

        self._current_media = None

        #: player position changed signal
        self.position_changed = Signal()

        #: player state changed signal
        self.state_changed = Signal()

        #: current media finished signal
        self.media_finished = Signal()

        #: duration changed signal
        self.duration_changed = Signal()

        #: media about to change: (old_media, media)
        self.media_about_to_changed = Signal()
        #: media changed signal
        self.media_changed = Signal()

        #: volume changed signal: (int)
        self.volume_changed = Signal()

        self._playlist.song_changed_v2.connect(self._on_song_changed)
        self.media_finished.connect(self._on_media_finished)
Esempio n. 2
0
class TestPlaylist(TestCase):
    def setUp(self):
        self.s1 = FakeSongModel()
        self.s2 = FakeSongModel()
        self.playlist = Playlist(app_mock)
        self.playlist.add(self.s1)
        self.playlist.add(self.s2)

    def tearDown(self):
        self.playlist.clear()

    def test_add(self):
        self.playlist.add(self.s1)
        self.assertEqual(len(self.playlist), 2)

    def test_getitem(self):
        self.assertEqual(self.playlist[1], self.s2)

    def test_mark_as_bad(self):
        self.assertEqual(self.playlist.next_song, self.s1)
        self.playlist.mark_as_bad(self.s1)
        self.assertEqual(self.playlist.next_song, self.s2)

    def test_list(self):
        self.assertIn(self.s1, self.playlist.list())
Esempio n. 3
0
    def __init__(self, playlist=None, **kwargs):
        self._position = 0  # seconds
        self._volume = 100  # (0, 100)
        self._playlist = Playlist() if playlist is None else playlist
        self._state = State.stopped
        self._duration = None

        self._current_media = None

        #: player position changed signal
        self.position_changed = Signal()

        #: player state changed signal
        self.state_changed = Signal()

        #: current song finished signal
        self.song_finished = Signal()

        #: duration changed signal
        self.duration_changed = Signal()

        #: media changed signal
        self.media_changed = Signal()

        #: volume changed signal: (int)
        self.volume_changed = Signal()
Esempio n. 4
0
class AbstractPlayer(metaclass=ABCMeta):
    """Player abstrace base class"""
    def __init__(self, playlist=None, **kwargs):
        self._position = 0  # seconds
        self._volume = 100  # (0, 100)
        self._playlist = Playlist() if playlist is None else playlist
        self._state = State.stopped
        self._duration = None

        self._current_media = None

        #: player position changed signal
        self.position_changed = Signal()

        #: player state changed signal
        self.state_changed = Signal()

        #: current media finished signal
        self.media_finished = Signal()

        #: duration changed signal
        self.duration_changed = Signal()

        #: media about to change: (old_media, media)
        self.media_about_to_changed = Signal()
        #: media changed signal
        self.media_changed = Signal()

        #: volume changed signal: (int)
        self.volume_changed = Signal()

        self._playlist.song_changed_v2.connect(self._on_song_changed)
        self.media_finished.connect(self._on_media_finished)

    @property
    def state(self):
        """Player state

        :rtype: State
        """
        return self._state

    @state.setter
    def state(self, value):
        """set player state, emit state changed signal

        outer object should not set state directly,
        use ``pause`` / ``resume`` / ``stop`` / ``play`` method instead.
        """
        self._state = value
        self.state_changed.emit(value)

    @property
    def current_media(self):
        return self._current_media

    @property
    def current_song(self):
        """alias of playlist.current_song"""
        return self._playlist.current_song

    @property
    def playlist(self):
        """player playlist

        :return: :class:`.Playlist`
        """
        return self._playlist

    @property
    def position(self):
        """player position, the units is seconds"""
        return self._position

    @position.setter
    def position(self, position):
        """set player position, the units is seconds"""

    @property
    def volume(self):
        return self._volume

    @volume.setter
    def volume(self, value):
        value = 0 if value < 0 else value
        value = 100 if value > 100 else value
        self._volume = value
        self.volume_changed.emit(value)

    @property
    def duration(self):
        """player media duration, the units is seconds"""
        return self._duration

    @duration.setter
    def duration(self, value):
        if value is not None and value != self._duration:
            self._duration = value
            self.duration_changed.emit(value)

    @abstractmethod
    def play(self, url, video=True):
        """play media

        :param url: a local file absolute path, or a http url that refers to a
            media file
        :param video: show video or not
        """

    @abstractmethod
    def set_play_range(self, start=None, end=None):
        pass

    def load_song(self, song):
        """加载歌曲

        如果目标歌曲与当前歌曲不相同,则修改播放列表当前歌曲,
        播放列表会发出 song_changed 信号,player 监听到信号后调用 play 方法,
        到那时才会真正的播放新的歌曲。如果和当前播放歌曲相同,则忽略。

        .. note::

            调用方不应该直接调用 playlist.current_song = song 来切换歌曲
        """
        if song is not None and song != self.current_song:
            self._playlist.current_song = song

    def play_song(self, song):
        """加载并播放指定歌曲"""
        self.load_song(song)
        self.resume()

    def play_songs(self, songs):
        """(alpha) play list of songs"""
        self.playlist.init_from(songs)
        self.playlist.next()
        self.resume()

    @abstractmethod
    def resume(self):
        """play playback"""

    @abstractmethod
    def pause(self):
        """pause player"""

    @abstractmethod
    def toggle(self):
        """toggle player state"""

    @abstractmethod
    def stop(self):
        """stop player"""

    @abstractmethod
    def shutdown(self):
        """shutdown player, do some clean up here"""

    def _on_song_changed(self, song, media):
        """播放列表 current_song 发生变化后的回调

        判断变化后的歌曲是否有效的,有效则播放,否则将它标记为无效歌曲。
        如果变化后的歌曲是 None,则停止播放。
        """
        if song is not None:
            if media is None:
                self._playlist.mark_as_bad(song)
                self._playlist.next()
            else:
                self.play(media)
        else:
            self.stop()

    def _on_media_finished(self):
        if self._playlist.playback_mode == PlaybackMode.one_loop:
            self.playlist.current_song = self.playlist.current_song
        else:
            self._playlist.next()
Esempio n. 5
0
 def setUp(self):
     self.s1 = FakeSongModel()
     self.s2 = FakeSongModel()
     self.playlist = Playlist()
     self.playlist.add(self.s1)
     self.playlist.add(self.s2)
Esempio n. 6
0
class TestPlaylist(TestCase):
    def setUp(self):
        self.s1 = FakeSongModel()
        self.s2 = FakeSongModel()
        self.playlist = Playlist()
        self.playlist.add(self.s1)
        self.playlist.add(self.s2)

    def tearDown(self):
        self.playlist.clear()

    def test_add(self):
        self.playlist.add(self.s1)
        self.assertEqual(len(self.playlist), 2)

    @mock.patch.object(MpvPlayer, 'play')
    def test_remove_current_song(self, mock_play):
        s3 = FakeSongModel()
        self.playlist.add(s3)
        self.playlist.current_song = self.s2
        self.playlist.remove(self.s2)
        self.assertEqual(self.playlist.current_song, s3)
        self.assertEqual(len(self.playlist), 2)

    def test_remove(self):
        self.playlist.remove(self.s1)
        self.assertEqual(len(self.playlist), 1)

    @mock.patch.object(MpvPlayer, 'play')
    def test_remove_2(self, mock_play):
        """播放器正在播放,移除一首歌"""
        self.playlist.current_song = self.s2
        self.playlist.remove(self.s1)
        self.assertEqual(self.playlist.current_song, self.s2)
        self.assertEqual(len(self.playlist), 1)

    def test_remove_3(self):
        """移除一首不存在的歌"""
        self.playlist.remove(FakeSongModel())
        self.assertEqual(len(self.playlist), 2)

    def test_remove_4(self):
        """移除一首被标记为无效的歌曲"""
        self.playlist.mark_as_bad(self.s2)
        self.assertEqual(len(self.playlist._bad_songs), 1)
        self.playlist.remove(self.s2)
        self.assertEqual(len(self.playlist), 1)
        self.assertEqual(len(self.playlist._bad_songs), 0)

    def test_getitem(self):
        self.assertEqual(self.playlist[1], self.s2)

    def test_mark_as_bad(self):
        self.assertEqual(self.playlist.next_song, self.s1)
        self.playlist.mark_as_bad(self.s1)
        self.assertEqual(self.playlist.next_song, self.s2)

    def test_list(self):
        self.assertIn(self.s1, self.playlist.list())

    def test_set_current_song(self):
        """将一首不存在于播放列表的歌曲设置为当前播放歌曲"""
        s3 = FakeSongModel()
        self.playlist.current_song = s3
        self.assertIn(s3, self.playlist)

    def test_next_song(self):
        self.playlist.playback_mode = PlaybackMode.sequential
        self.playlist.current_song = self.s2
        self.assertIsNone(self.playlist.next_song)
        self.playlist.playback_mode = PlaybackMode.random
        self.playlist.next_song  # assert no exception

    def test_previous_song(self):
        self.assertEqual(self.playlist.previous_song, self.s2)
        self.playlist.current_song = self.s2
        self.assertEqual(self.playlist.previous_song, self.s1)
Esempio n. 7
0
 def setUp(self):
     self.player = MpvPlayer(Playlist(app_mock))
     self.player.volume = 0