Ejemplo n.º 1
0
def test_playing_field_equality(prop, value1, value2):
    playing1 = Playing(**{prop: value1})
    playing2 = Playing(**{prop: value2})
    playing3 = Playing(**{prop: value2})

    assert playing1 == playing1
    assert playing1 != playing2
    assert playing2 == playing3
Ejemplo n.º 2
0
    async def _perform_update(expected_updates, expected_protocol):
        dmap_pusher.post_update(
            Playing(MediaType.Music,
                    DeviceState.Idle,
                    title=f"dmap_{expected_updates}"))
        mrp_pusher.post_update(
            Playing(MediaType.Music,
                    DeviceState.Idle,
                    title=f"mrp_{expected_updates}"))

        await until(lambda: listener.no_of_updates == expected_updates)
        assert listener.last_update.title == f"{expected_protocol}_{expected_updates}"
Ejemplo n.º 3
0
def test_playing_generate_same_hash():
    playing = Playing(title="title", artist="artist", album="album", total_time=123)
    assert (
        "538df531d1715629fdd87affd0c5957bcbf54cd89180778071e6535b7df4e22c"
        == playing.hash
    )

    playing2 = Playing(title="dummy", artist="test", album="none", total_time=321)

    assert (
        "80045c05d18382f33a5369fd5cdfc6ae42c3eb418125f638d7a31ab173b01ade"
        == playing2.hash
    )
Ejemplo n.º 4
0
    async def playing(self) -> Playing:
        """Return what is currently playing."""
        if self._state_manager.metadata is None:
            return Playing(device_state=const.DeviceState.Idle,
                           media_type=const.MediaType.Unknown)

        metadata = self._state_manager.metadata
        return Playing(
            device_state=const.DeviceState.Playing,
            media_type=const.MediaType.Music,
            title=metadata.title,
            artist=metadata.artist,
            album=metadata.album,
        )
Ejemplo n.º 5
0
    async def playing(self) -> Playing:
        """Return what is currently playing."""
        if self._playback_manager.playback_info is None:
            return Playing(device_state=const.DeviceState.Idle,
                           media_type=const.MediaType.Unknown)

        metadata = self._playback_manager.playback_info.metadata
        total_time = int(metadata.duration) if metadata.duration else None
        return Playing(
            device_state=const.DeviceState.Playing,
            media_type=const.MediaType.Music,
            title=metadata.title,
            artist=metadata.artist,
            album=metadata.album,
            position=int(self._playback_manager.playback_info.position),
            total_time=total_time,
        )
Ejemplo n.º 6
0
def test_playing_title_artist_album_genre():
    out = str(
        Playing(title="mytitle", artist="myartist", album="myalbum", genre="mygenre")
    )
    assert "mytitle" in out
    assert "myartist" in out
    assert "myalbum" in out
    assert "mygenre" in out
Ejemplo n.º 7
0
async def dispatch_device_state(mrp_state_dispatcher,
                                state,
                                protocol=Protocol.MRP):
    event = asyncio.Event()

    # Add a listener last in the last and make it set an asyncio.Event. That way we
    # can synchronize and know that all other listeners have been called.
    mrp_state_dispatcher.listen_to(UpdatedState.Playing,
                                   lambda message: event.set())
    mrp_state_dispatcher.dispatch(UpdatedState.Playing,
                                  Playing(MediaType.Unknown, state))

    await event.wait()
Ejemplo n.º 8
0
def test_post_ignore_duplicate_update(event_loop, updates):
    listener = MagicMock()
    playing = Playing()

    async def _post_updates(repeats: int):
        updater = PushUpdaterDummy(event_loop)
        updater.listener = listener
        for _ in range(repeats):
            updater.post_update(playing)

    event_loop.run_until_complete(_post_updates(updates))

    assert listener.playstatus_update.call_count == 1
    listener.playstatus_update.assert_called_once_with(ANY, playing)
Ejemplo n.º 9
0
def test_playing_basic_fields():
    out = str(
        Playing(
            title="mytitle",
            artist="myartist",
            album="myalbum",
            genre="mygenre",
            series_name="myseries",
            season_number=1245,
            episode_number=2468,
        ))
    assert "mytitle" in out
    assert "myartist" in out
    assert "myalbum" in out
    assert "mygenre" in out
    assert "myseries" in out
    assert "1245" in out
    assert "2468" in out
Ejemplo n.º 10
0
def test_post_ignore_duplicate_update(event_loop, state_dispatcher, updates):
    listener = MagicMock()
    playing = Playing()

    def _state_changed(message):
        assert message.protocol == Protocol.MRP
        assert message.state == UpdatedState.Playing
        listener.state_updated(message.value)

    async def _post_updates(repeats: int):
        updater = PushUpdaterDummy(state_dispatcher)
        updater.listener = listener
        state_dispatcher.listen_to(UpdatedState.Playing, _state_changed)
        for _ in range(repeats):
            updater.post_update(playing)

    event_loop.run_until_complete(_post_updates(updates))

    assert listener.playstatus_update.call_count == 1
    listener.playstatus_update.assert_called_once_with(ANY, playing)
    listener.state_updated.assert_called_once_with(playing)
Ejemplo n.º 11
0
def build_playing_instance(state: PlayerState) -> Playing:
    """Build a Playing instance from play state."""
    def media_type() -> MediaType:
        """Type of media is currently playing, e.g. video, music."""
        if state.metadata:
            media_type = state.metadata.mediaType
            if media_type == cim.Audio:
                return MediaType.Music
            if media_type == cim.Video:
                return MediaType.Video

        return MediaType.Unknown

    def device_state() -> DeviceState:
        """Device state, e.g. playing or paused."""
        return {
            None: DeviceState.Idle,
            PlaybackState.Playing: DeviceState.Playing,
            PlaybackState.Paused: DeviceState.Paused,
            PlaybackState.Stopped: DeviceState.Stopped,
            PlaybackState.Interrupted: DeviceState.Loading,
            PlaybackState.Seeking: DeviceState.Seeking,
        }.get(state.playback_state, DeviceState.Paused)

    def title() -> Optional[str]:
        """Title of the current media, e.g. movie or song name."""
        return state.metadata_field("title")

    def artist() -> Optional[str]:
        """Artist of the currently playing song."""
        return state.metadata_field("trackArtistName")

    def album() -> Optional[str]:
        """Album of the currently playing song."""
        return state.metadata_field("albumName")

    def genre() -> Optional[str]:
        """Genre of the currently playing song."""
        return state.metadata_field("genre")

    def total_time() -> Optional[int]:
        """Total play time in seconds."""
        duration = state.metadata_field("duration")
        if duration is None or math.isnan(duration):
            return None
        return int(duration)

    def position() -> Optional[int]:
        """Position in the playing media (seconds)."""
        elapsed_timestamp = state.metadata_field("elapsedTimeTimestamp")

        # If we don't have reference time, we can't do anything
        if not elapsed_timestamp:
            return None

        elapsed_time: int = state.metadata_field("elapsedTime") or 0
        diff = (datetime.datetime.now() -
                _cocoa_to_timestamp(elapsed_timestamp)).total_seconds()

        if device_state() == DeviceState.Playing:
            return int(elapsed_time + diff)
        return int(elapsed_time)

    def shuffle() -> ShuffleState:
        """If shuffle is enabled or not."""
        info = state.command_info(CommandInfo_pb2.ChangeShuffleMode)
        if info is None:
            return ShuffleState.Off
        if info.shuffleMode == protobuf.ShuffleMode.Off:
            return ShuffleState.Off
        if info.shuffleMode == protobuf.ShuffleMode.Albums:
            return ShuffleState.Albums

        return ShuffleState.Songs

    def repeat() -> RepeatState:
        """Repeat mode."""
        info = state.command_info(CommandInfo_pb2.ChangeRepeatMode)
        if info is None:
            return RepeatState.Off
        if info.repeatMode == protobuf.RepeatMode.One:
            return RepeatState.Track
        if info.repeatMode == protobuf.RepeatMode.All:
            return RepeatState.All

        return RepeatState.Off

    def hash() -> str:
        """Create a unique hash for what is currently playing."""
        return state.item_identifier

    return Playing(
        media_type=media_type(),
        device_state=device_state(),
        title=title(),
        artist=artist(),
        album=album(),
        genre=genre(),
        total_time=total_time(),
        position=position(),
        shuffle=shuffle(),
        repeat=repeat(),
        hash=hash(),
    )
Ejemplo n.º 12
0
def test_playing_only_total_time():
    assert "5678" in str(Playing(total_time=5678))
Ejemplo n.º 13
0
def build_playing_instance(playstatus) -> Playing:
    """Build a Playing instance from play state."""
    def _get_time_in_seconds(tag) -> int:
        time = parser.first(playstatus, "cmst", tag)
        return daap.ms_to_s(time)

    def media_type() -> MediaType:
        """Type of media is currently playing, e.g. video, music."""
        state = parser.first(playstatus, "cmst", "caps")
        if not state:
            return MediaType.Unknown

        mediakind = parser.first(playstatus, "cmst", "cmmk")
        if mediakind is not None:
            return daap.media_kind(mediakind)

        # Fallback: if artist or album exists we assume music (not present
        # for video)
        if artist() or album():
            return MediaType.Music

        return MediaType.Video

    def device_state() -> DeviceState:
        """Device state, e.g. playing or paused."""
        state = parser.first(playstatus, "cmst", "caps")
        return daap.playstate(state)

    def title() -> Optional[str]:
        """Title of the current media, e.g. movie or song name."""
        return parser.first(playstatus, "cmst", "cann")

    def artist() -> Optional[str]:
        """Arist of the currently playing song."""
        return parser.first(playstatus, "cmst", "cana")

    def album() -> Optional[str]:
        """Album of the currently playing song."""
        return parser.first(playstatus, "cmst", "canl")

    def genre() -> Optional[str]:
        """Genre of the currently playing song."""
        return parser.first(playstatus, "cmst", "cang")

    def total_time() -> Optional[int]:
        """Total play time in seconds."""
        return _get_time_in_seconds("cast")

    def position() -> Optional[int]:
        """Position in the playing media (seconds)."""
        total = total_time()
        remaining_time = _get_time_in_seconds("cant")
        if not total or not remaining_time:
            return None
        return total - remaining_time

    def shuffle() -> Optional[ShuffleState]:
        """If shuffle is enabled or not."""
        state = parser.first(playstatus, "cmst", "cash")
        if state is None or state == 0:
            return ShuffleState.Off

        # DMAP does not support the "albums" state and will always report
        # "songs" if shuffle is active
        return ShuffleState.Songs

    def repeat() -> Optional[RepeatState]:
        """Repeat mode."""
        state = parser.first(playstatus, "cmst", "carp")
        if state is None:
            return RepeatState.Off
        return RepeatState(state)

    return Playing(
        media_type=media_type(),
        device_state=device_state(),
        title=title(),
        artist=artist(),
        album=album(),
        genre=genre(),
        total_time=total_time(),
        position=position(),
        shuffle=shuffle(),
        repeat=repeat(),
    )
Ejemplo n.º 14
0
def test_playing_position_force_in_range(position, total_time, expected):
    assert Playing(position=position,
                   total_time=total_time).position == expected
Ejemplo n.º 15
0
def test_playing_media_type_and_playstate():
    out = str(
        Playing(media_type=MediaType.Video, device_state=DeviceState.Playing))
    assert convert.media_type_str(MediaType.Video) in out
    assert convert.device_state_str(DeviceState.Playing) in out
Ejemplo n.º 16
0
def test_playing_init_field_values(prop, value1, value2):
    playing = Playing(**{prop: value1})
    assert getattr(playing, prop) == value1
Ejemplo n.º 17
0
def test_playing_eq_ensure_member_count():
    # Fail if a property is added or removed to interface, just as a reminder to
    # update equality comparison
    assert len(Playing().__dict__) == 14
Ejemplo n.º 18
0
def test_playing_custom_hash():
    playing = Playing(hash="dummy")
    assert playing.hash == "dummy"
Ejemplo n.º 19
0
def test_playing_shuffle_and_repeat():
    out = str(Playing(shuffle=ShuffleState.Songs, repeat=RepeatState.Track))
    assert "Shuffle: Songs" in out
    assert "Repeat: Track" in out
Ejemplo n.º 20
0
def test_playing_both_position_and_total_time():
    out = str(Playing(position=1234, total_time=5678))
    assert "1234/5678" in out
Ejemplo n.º 21
0
def test_playing_only_position():
    assert "1234" in str(Playing(position=1234))