コード例 #1
0
ファイル: test_player.py プロジェクト: Tastyep/Pi-OpenCast
    def test_toggle_pause_twice(self):
        video_id = IdentityService.id_video("source")
        self.player.play(video_id)
        self.player.release_events()

        self.player.toggle_pause()
        self.player.toggle_pause()
        self.assertEqual(PlayerState.PLAYING, self.player.state)
        self.expect_events(
            self.player,
            Evt.PlayerStateUpdated,
            Evt.PlayerStateUpdated,
        )
コード例 #2
0
ファイル: event_expecter.py プロジェクト: Tastyep/Pi-OpenCast
    def from_event(self, evt_cls, model_id, *args, **kwargs):
        cmd_id = IdentityService.random()

        for cls, handler_data in self.evt_to_handler.items():
            self.evt_dispatcher.observe_result(cmd_id,
                                               {cls: handler_data.functor})

        evt = evt_cls(cmd_id, model_id, *args, **kwargs)
        self.evt_dispatcher.dispatch(evt)

        for cls, handler_data in self.evt_to_handler.items():
            handler_data.functor.assert_called_once_with(
                cls(cmd_id, *handler_data.attrs, **handler_data.dict_attrs))
コード例 #3
0
ファイル: test_video.py プロジェクト: Tastyep/Pi-OpenCast
 def test_deleting_to_aborted(self):
     cmd = Cmd.CreateVideo(
         IdentityService.random(), self.video.id, "source", collection_id=None
     )
     error = OperationError(cmd, "")
     self.workflow.to_DELETING(error)
     cmd = self.expect_dispatch(Cmd.DeleteVideo, self.video.id)
     self.raise_event(
         Evt.VideoDeleted,
         cmd.id,
         self.video.id,
     )
     self.assertTrue(self.workflow.is_ABORTED())
コード例 #4
0
    async def test_event_listening(self):
        async with self.client.ws_connect("/api/events") as ws:
            playlist_id = IdentityService.id_playlist()
            cmd_id = IdentityService.id_command(PlaylistCmd.CreatePlaylist,
                                                playlist_id)
            created_evt = PlaylistEvt.PlaylistCreated(
                cmd_id,
                playlist_id,
                "name",
                [],
                False,
            )
            self.evt_dispatcher.dispatch(created_evt)
            await self.expect_ws_events(ws, [created_evt])

            update_cmd_id = IdentityService.id_command(
                PlaylistCmd.UpdatePlaylistContent, playlist_id)
            updated_evt = PlaylistEvt.PlaylistContentUpdated(
                update_cmd_id, playlist_id, [])
            self.evt_dispatcher.dispatch(created_evt)
            self.evt_dispatcher.dispatch(updated_evt)
            await self.expect_ws_events(ws, [created_evt, updated_evt])
コード例 #5
0
    def test_add_video_updates_artist_thumbnail(self):
        video_id_1 = IdentityService.id_video("source")
        video_id_2 = IdentityService.id_video("source2")
        artist_id = IdentityService.id_artist("artist")
        self.data_producer.video("source2", artist_id=artist_id).populate(
            self.data_facade)
        self.data_producer.artist(artist_id, "artist", thumbnail=None).video(
            "source", artist_id=artist_id).populate(self.data_facade)

        metadata = {
            "title": "title",
            "duration": 300,
            "source_protocol": "http",
            "artist": "artist",
            "album": "album",
            "thumbnail": "thumbnail_url",
        }
        self.downloader.download_metadata.return_value = metadata

        deezer_data = [{"artist": {"picture_medium": "picture"}}]
        self.deezer.search.return_value = deezer_data

        self.evt_expecter.expect(Evt.ArtistVideosUpdated, artist_id,
                                 [video_id_1, video_id_2]).expect(
                                     Evt.ArtistThumbnailUpdated, artist_id,
                                     "picture").from_event(
                                         VideoEvt.VideoCreated,
                                         video_id_2,
                                         "source2",
                                         None,
                                         artist_id,
                                         None,
                                         "title",
                                         300,
                                         "http",
                                         "thumbnail",
                                         VideoState.CREATED,
                                     )
コード例 #6
0
ファイル: test_video.py プロジェクト: Tastyep/Pi-OpenCast
 def test_retrieving_to_parsing(self):
     event = Evt.VideoCreated(
         IdentityService.random(),
         *self.video.to_tuple(),
         IdentityService.id_artist("artist"),
         IdentityService.id_album("album"),
         "title",
         300,
         "http",
         "thumbnail",
         VideoState.CREATED,
     )
     self.workflow.to_RETRIEVING(event)
     cmd = self.expect_dispatch(
         Cmd.RetrieveVideo, self.video.id, settings["downloader.output_directory"]
     )
     self.raise_event(
         Evt.VideoRetrieved,
         cmd.id,
         self.video.id,
         f"{settings['downloader.output_directory']}/video.mp4",
     )
     self.assertTrue(self.workflow.is_PARSING())
コード例 #7
0
    def test_creating_player_to_aborted(self, identityMock):
        player_id = IdentityService.id_player()
        identityMock.id_player.return_value = player_id

        self.workflow.to_CREATING_PLAYER()
        createPlaylistId = IdentityService.id_command(
            PlaylistCmd.CreatePlaylist, HOME_PLAYLIST.id)
        createPlayerId = IdentityService.id_command(PlayerCmd.CreatePlayer,
                                                    player_id)
        expected_cmds = [
            PlaylistCmd.CreatePlaylist(
                createPlaylistId,
                HOME_PLAYLIST.id,
                HOME_PLAYLIST.name,
                [],
                True,
            ),
            PlayerCmd.CreatePlayer(createPlayerId, player_id,
                                   HOME_PLAYLIST.id),
        ]
        self.expect_dispatch_l(expected_cmds)
        self.raise_error(expected_cmds[-1])
        self.assertTrue(self.workflow.is_ABORTED())
コード例 #8
0
    def test_list_containing(self):
        videos = [IdentityService.id_video(f"source{i}") for i in range(4)]

        albums = [
            Album(IdentityService.random(), "album_1", videos[:2]),
            Album(IdentityService.random(), "album_2", videos[2:]),
            Album(
                IdentityService.random(),
                "album_3",
                [videos[0], videos[2]],
            ),
        ]
        for album in albums:
            self.repo.create(album)

        result = self.repo.list_containing(videos[0])
        self.assertEqual([albums[0], albums[2]], result)

        result = self.repo.list_containing(videos[3])
        self.assertEqual([albums[1]], result)

        result = self.repo.list_containing(IdentityService.random())
        self.assertEqual([], result)
コード例 #9
0
ファイル: test_artist.py プロジェクト: Tastyep/Pi-OpenCast
    def test_list_containing(self):
        videos = [IdentityService.id_video(f"source{i}") for i in range(4)]

        artists = [
            Artist(IdentityService.random(), "artist_1", videos[:2]),
            Artist(IdentityService.random(), "artist_2", videos[2:]),
            Artist(
                IdentityService.random(),
                "artist_3",
                [videos[0], videos[2]],
            ),
        ]
        for artist in artists:
            self.repo.create(artist)

        result = self.repo.list_containing(videos[0])
        self.assertEqual([artists[0], artists[2]], result)

        result = self.repo.list_containing(videos[3])
        self.assertEqual([artists[1]], result)

        result = self.repo.list_containing(IdentityService.random())
        self.assertEqual([], result)
コード例 #10
0
 def on_enter_CREATING_PLAYER(self):
     cmd = make_cmd(
         PlaylistCmd.CreatePlaylist,
         HOME_PLAYLIST.id,
         HOME_PLAYLIST.name,
         generated=True,
     )
     self._cmd_dispatcher.dispatch(cmd)
     self._observe_dispatch(
         PlayerEvt.PlayerCreated,
         PlayerCmd.CreatePlayer,
         IdentityService.id_player(),
         HOME_PLAYLIST.id,
     )
コード例 #11
0
ファイル: __init__.py プロジェクト: Tastyep/Pi-OpenCast
def make_cmd(cmd_cls, model_id, *args, **kwargs):
    """Command factory method.

    Args:
        cmd_cls: The class of the command.
        model_id: The ID of the related model.
        *args: Variable length argument list, forwarded to the command's constructor
        **kwargs: Arbitrary keyword arguments, forwarded to the command's constructor

    Returns:
        Command: The created command.
    """
    cmd_id = IdentityService.id_command(cmd_cls, model_id)
    return cmd_cls(cmd_id, model_id, *args, **kwargs)
コード例 #12
0
ファイル: player.py プロジェクト: Tastyep/Pi-OpenCast
 def on_enter_QUEUEING(self, _):
     video = self.videos.pop()
     workflow_id = IdentityService.id_workflow(QueueVideoWorkflow, video.id)
     workflow = self._factory.make_queue_video_workflow(
         workflow_id,
         self._app_facade,
         self._data_facade,
         video,
         self.playlist_id,
         queue_front=True,
     )
     self._observe_start(
         workflow,
     )
コード例 #13
0
    def test_retrieve_video_download_success(self):
        video_id = IdentityService.id_video("source")
        video_title = "video_title"
        self.data_producer.video("source", title=video_title).populate(self.data_facade)

        def dispatch_downloaded(op_id, *args):
            self.app_facade.evt_dispatcher.dispatch(DownloadSuccess(op_id))

        self.downloader.download_video.side_effect = dispatch_downloaded
        output_dir = settings["downloader.output_directory"]
        location = str(Path(output_dir) / f"{video_title}.mp4")
        self.evt_expecter.expect(VideoEvt.VideoRetrieved, video_id, location).from_(
            Cmd.RetrieveVideo, video_id, output_dir
        )
コード例 #14
0
    async def test_play_not_queued(self):
        video_id = IdentityService.id_video("source")
        self.data_producer.video("source").populate(self.data_facade)

        resp = await self.client.post(
            "/api/player/play",
            params={"id": str(video_id)},
        )
        body = await resp.json()
        self.assertEqual(403, resp.status)
        self.assertEqual({
            "message": "the video is not queued",
            "details": {}
        }, body)
コード例 #15
0
ファイル: event_expecter.py プロジェクト: Tastyep/Pi-OpenCast
    def from_(self, cmd_cls, model_id, *args, **kwargs):
        cmd_id = IdentityService.id_command(cmd_cls, model_id)

        for evt_cls, handler_data in self.evt_to_handler.items():
            self.evt_dispatcher.observe_result(cmd_id,
                                               {evt_cls: handler_data.functor})

        cmd = cmd_cls(cmd_id, model_id, *args, **kwargs)
        self.cmd_dispatcher.dispatch(cmd)

        for evt_cls, handler_data in self.evt_to_handler.items():
            handler_data.functor.assert_called_once_with(
                evt_cls(cmd_id, *handler_data.attrs,
                        **handler_data.dict_attrs))
コード例 #16
0
ファイル: test_album.py プロジェクト: Tastyep/Pi-OpenCast
    def test_add_video_updates_album(self):
        video_id_1 = IdentityService.id_video("source")
        video_id_2 = IdentityService.id_video("source2")
        album_id = IdentityService.id_album("album")
        self.data_producer.video("source2",
                                 album_id=album_id).populate(self.data_facade)
        self.data_producer.album(album_id, "album").video(
            "source", album_id=album_id).populate(self.data_facade)

        self.evt_expecter.expect(Evt.AlbumVideosUpdated, album_id,
                                 [video_id_1, video_id_2]).from_event(
                                     VideoEvt.VideoCreated,
                                     video_id_2,
                                     "source2",
                                     None,
                                     None,
                                     album_id,
                                     "title",
                                     300,
                                     "http",
                                     "thumbnail",
                                     VideoState.CREATED,
                                 )
コード例 #17
0
    async def test_play_error(self):
        self.data_producer.select(
            Player, IdentityService.id_player()).video("source").populate(
                self.data_facade)
        video_id = IdentityService.id_video("source")
        self.expect_and_error(
            make_cmd(PlayerCmd.PlayVideo, self.player_id, video_id),
            error="Error message",
        )

        resp = await self.client.post(
            "/api/player/play",
            params={"id": str(video_id)},
        )
        body = await resp.json()
        self.assertEqual(500, resp.status)
        self.assertEqual(
            {
                "message": "Error message",
                "details": {},
            },
            body,
        )
コード例 #18
0
    def test_retrieve_video_from_stream_error(self):
        video_id = IdentityService.id_video("http://url")
        title = "title"
        self.data_producer.video(
            "http://url", title=title, source_protocol="m3u8"
        ).populate(self.data_facade)

        metadata = {}
        self.downloader.download_metadata.return_value = metadata

        output_dir = settings["downloader.output_directory"]
        self.evt_expecter.expect(
            OperationError, "Unavailable stream URL", {"title": title}
        ).from_(Cmd.RetrieveVideo, video_id, output_dir)
コード例 #19
0
ファイル: test_video.py プロジェクト: Tastyep/Pi-OpenCast
 def test_parsing_to_sub_fetching(self):
     event = Evt.VideoRetrieved(
         IdentityService.random(),
         self.video.id,
         f"{settings['downloader.output_directory']}/video.mp4",
     )
     self.workflow.to_PARSING(event)
     cmd = self.expect_dispatch(Cmd.ParseVideo, self.video.id)
     self.raise_event(
         Evt.VideoParsed,
         cmd.id,
         self.video.id,
         {},
     )
     self.assertTrue(self.workflow.is_SUB_RETRIEVING())
コード例 #20
0
    def test_delete_video(self):
        self.data_producer.player().video("source").video("source2").play(
            "source"
        ).populate(self.data_facade)

        player = self.player_repo.get_player()
        video_id = IdentityService.id_video("source")
        self.evt_expecter.expect(VideoEvt.VideoDeleted, video_id).expect(
            PlayerEvt.PlayerStateUpdated,
            player.id,
            PlayerState.PLAYING,
            PlayerState.STOPPED,
        ).expect(PlayerEvt.PlayerVideoUpdated, player.id, video_id, None,).expect(
            PlaylistEvt.PlaylistContentUpdated,
            player.queue,
            [IdentityService.id_video("source2")],
        ).from_(
            Cmd.DeleteVideo, video_id
        )

        other_video_id = IdentityService.id_video("source2")
        self.assertListEqual(
            [self.video_repo.get(other_video_id)], self.video_repo.list()
        )
コード例 #21
0
ファイル: test_player.py プロジェクト: Tastyep/Pi-OpenCast
    def test_queue_no_merge_past_index(self):
        collection_id = IdentityService.random()
        self.data_producer.player().video("source1", collection_id=collection_id).video(
            "source2"
        ).play("source2").parent_producer().video(
            "source3", collection_id=collection_id
        ).populate(
            self.data_facade
        )

        queue = self.playlist_repo.get(self.queue_id)
        videos = self.video_repo.list()
        queue.ids = self.service.queue(queue, videos[2].id, front=True)
        expected = [videos[0].id, videos[1].id, videos[2].id]
        self.assertListEqual(expected, queue.ids)
コード例 #22
0
    def test_retrieve_video_from_stream_success(self):
        video_id = IdentityService.id_video("http://url")
        self.data_producer.video("http://url", source_protocol="m3u8").populate(
            self.data_facade
        )

        metadata = {
            "url": "http://stream-url.m3u8",
        }
        self.downloader.download_metadata.return_value = metadata

        output_dir = settings["downloader.output_directory"]
        self.evt_expecter.expect(
            VideoEvt.VideoRetrieved, video_id, metadata["url"]
        ).from_(Cmd.RetrieveVideo, video_id, output_dir)
コード例 #23
0
ファイル: test_video.py プロジェクト: Tastyep/Pi-OpenCast
 def test_finalizing_to_completed(self):
     event = Evt.VideoSubtitleFetched(
         IdentityService.random(), self.video.id, Path()
     )
     self.workflow.to_FINALIZING(event)
     cmd = self.expect_dispatch(Cmd.SetVideoReady, self.video.id)
     self.raise_event(
         Evt.VideoStateUpdated,
         cmd.id,
         self.video.id,
         VideoState.CREATED,
         VideoState.READY,
         timedelta(),
         None,
     )
コード例 #24
0
    def test_retrieve_video_download_error(self):
        video_id = IdentityService.id_video("source")
        video_title = "video_title"
        self.data_producer.video("source", title=video_title).populate(self.data_facade)

        def dispatch_error(op_id, *args):
            self.app_facade.evt_dispatcher.dispatch(
                DownloadError(op_id, "Download error")
            )

        self.downloader.download_video.side_effect = dispatch_error
        output_dir = settings["downloader.output_directory"]
        self.evt_expecter.expect(OperationError, "Download error").from_(
            Cmd.RetrieveVideo, video_id, output_dir
        )
コード例 #25
0
ファイル: test_video.py プロジェクト: Tastyep/Pi-OpenCast
 def test_parsing_to_finalizing(self):
     settings["subtitle.enabled"] = False
     event = Evt.VideoRetrieved(
         IdentityService.random(),
         self.video.id,
         f"{settings['downloader.output_directory']}/video.mp4",
     )
     self.workflow.to_PARSING(event)
     cmd = self.expect_dispatch(Cmd.ParseVideo, self.video.id)
     self.raise_event(
         Evt.VideoParsed,
         cmd.id,
         self.video.id,
         {},
     )
     self.assertTrue(self.workflow.is_FINALIZING())
コード例 #26
0
ファイル: test_player.py プロジェクト: Tastyep/Pi-OpenCast
    def test_next_loop_last_album_no_album(self):
        collection_id = IdentityService.random()
        self.data_producer.player().video(
            "source1", collection_id=collection_id, state=VideoState.READY
        ).video("source2", state=VideoState.READY).video(
            "source3", state=VideoState.READY
        ).populate(
            self.data_facade
        )

        videos = self.video_repo.list()
        expected = videos[2].id
        self.assertEqual(
            expected,
            self.service.next_video(self.queue_id, videos[2].id, loop_last="album"),
        )
コード例 #27
0
    def test_fetch_video_subtitle(self):
        self.data_producer.video("source", location="/tmp/source.mp4").populate(
            self.data_facade
        )

        # Load from disk
        self.file_service.list_directory.return_value = []

        # Download from source
        source_subtitle = "/tmp/source.vtt"
        self.downloader.download_subtitle.return_value = source_subtitle

        video_id = IdentityService.id_video("source")
        subtitle_language = settings["subtitle.language"]
        self.evt_expecter.expect(
            VideoEvt.VideoSubtitleFetched, video_id, Path(source_subtitle)
        ).from_(Cmd.FetchVideoSubtitle, video_id, subtitle_language)
コード例 #28
0
    def test_parse_video(self):
        self.data_producer.video("source", location="/tmp/source.mp4").populate(
            self.data_facade
        )

        streams = [
            (0, "video", None),
            (1, "audio", None),
            (2, "subtitle", "subtitle_lang"),
        ]
        self.video_parser.parse_streams.return_value = streams

        expected = [Stream(*stream) for stream in streams]
        video_id = IdentityService.id_video("source")
        self.evt_expecter.expect(VideoEvt.VideoParsed, video_id, expected).from_(
            Cmd.ParseVideo, video_id
        )
コード例 #29
0
    async def test_update_error(self):
        req_body = {
            "name": "test_playlist",
            "ids": [str(IdentityService.random())]
        }
        playlist = self.playlist_repo.get(self.playlist_id)
        self.expect_and_raise_l([
            {
                "cmd":
                make_cmd(PlaylistCmd.RenamePlaylist, playlist.id,
                         req_body["name"]),
                "evt":
                PlaylistEvt.PlaylistRenamed,
                "args": {
                    "name": req_body["name"]
                },
            },
            {
                "cmd":
                make_cmd(PlaylistCmd.UpdatePlaylistContent, playlist.id,
                         req_body["ids"]),
                "evt":
                OperationError,
                "args": {
                    "error": "Error message"
                },
            },
        ])

        resp = await self.client.patch(f"/api/playlists/{playlist.id}",
                                       json=req_body)
        body = await resp.json()
        self.assertEqual(500, resp.status)
        self.assertEqual(
            {
                "message": "Error message",
                "details": {},
            },
            body,
        )
コード例 #30
0
    async def create(self, req):
        data = await req.json()
        channel = self._io_factory.make_janus_channel()

        def on_success(evt):
            playlist = self._playlist_repo.get(evt.model_id)
            channel.send(self._ok(playlist))

        def on_error(evt):
            channel.send(self._internal_error(evt.error, evt.details))

        self._observe_dispatch(
            {
                PlaylistEvt.PlaylistCreated: on_success,
                OperationError: on_error
            },
            Cmd.CreatePlaylist,
            IdentityService.id_playlist(),
            **data,
        )

        return await channel.receive()