Example #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
Example #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}"
Example #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
    )
Example #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,
        )
Example #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,
        )
Example #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
Example #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()
Example #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)
Example #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
Example #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)
Example #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(),
    )
Example #12
0
def test_playing_only_total_time():
    assert "5678" in str(Playing(total_time=5678))
Example #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(),
    )
Example #14
0
def test_playing_position_force_in_range(position, total_time, expected):
    assert Playing(position=position,
                   total_time=total_time).position == expected
Example #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
Example #16
0
def test_playing_init_field_values(prop, value1, value2):
    playing = Playing(**{prop: value1})
    assert getattr(playing, prop) == value1
Example #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
Example #18
0
def test_playing_custom_hash():
    playing = Playing(hash="dummy")
    assert playing.hash == "dummy"
Example #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
Example #20
0
def test_playing_both_position_and_total_time():
    out = str(Playing(position=1234, total_time=5678))
    assert "1234/5678" in out
Example #21
0
def test_playing_only_position():
    assert "1234" in str(Playing(position=1234))