예제 #1
0
    def test_purging_videos_to_completed_with_deletion(self, path_cls_mock):
        path_inst = path_cls_mock.return_value
        path_inst.exists.return_value = False

        video1 = Mock()
        video1.id = IdentityService.id_video("mock1")
        video1.streamable.return_value = False

        video2 = Mock()
        video2.id = IdentityService.id_video("mock2")
        video2.location = None

        self.video_repo.list.return_value = [video1, video2]
        self.workflow.to_PURGING_VIDEOS()

        cmd = self.expect_dispatch(VideoCmd.DeleteVideo, video2.id)
        self.raise_event(
            VideoEvt.VideoDeleted,
            cmd.id,
            video2.id,
        )

        self.assertTrue(self.workflow.is_PURGING_VIDEOS())
        cmd = self.expect_dispatch(VideoCmd.DeleteVideo, video1.id)
        self.raise_event(
            VideoEvt.VideoDeleted,
            cmd.id,
            video1.id,
        )
        self.assertTrue(self.workflow.is_COMPLETED())
예제 #2
0
    async def queue(self, req):
        source = req.query["url"]
        video_id = IdentityService.id_video(source)
        playlist_id = self._player_repo.get_player().queue

        if self._source_service.is_playlist(source):
            collection_id = IdentityService.random()
            self._evt_dispatcher.dispatch(
                Notification(
                    collection_id,
                    NotifLevel.INFO,
                    "unfolding playlist",
                    {"source": source},
                ))

            loop = asyncio.get_running_loop()
            with ThreadPoolExecutor() as pool:
                sources = await loop.run_in_executor(
                    pool, partial(self._source_service.unfold, source))
                if not sources:
                    return self._internal_error(
                        "Could not unfold the playlist URL")

                self._evt_dispatcher.dispatch(
                    Notification(
                        collection_id,
                        NotifLevel.INFO,
                        "downloading playlist",
                        {
                            "source": source,
                            "count": f"{len(sources)} media"
                        },
                    ))
                videos = [
                    Video(IdentityService.id_video(source), source,
                          collection_id) for source in sources
                ]

                workflow_id = IdentityService.id_workflow(
                    QueuePlaylistWorkflow, video_id)
                self._start_workflow(
                    QueuePlaylistWorkflow,
                    workflow_id,
                    self._data_facade,
                    videos,
                    playlist_id,
                )
                return self._no_content()

        video = Video(video_id, source, collection_id=None)
        self._start_workflow(
            QueueVideoWorkflow,
            video_id,
            self._data_facade,
            video,
            playlist_id,
            queue_front=False,
        )

        return self._no_content()
예제 #3
0
 def test_remove(self):
     self.playlist.ids = [
         IdentityService.id_video("source1"),
         IdentityService.id_video("source2"),
     ]
     self.playlist.release_events()
     self.playlist.remove(IdentityService.id_video("source1"))
     self.expect_events(self.playlist, Evt.PlaylistContentUpdated)
예제 #4
0
    def test_delete_video_updates_artist(self):
        artist_id = IdentityService.id_artist("artist")
        video_id_1 = IdentityService.id_video("source1")
        video_id_2 = IdentityService.id_video("source2")
        self.data_producer.artist(artist_id, "artist").video(
            "source1", artist_id=artist_id).video(
                "source2", artist_id=artist_id).populate(self.data_facade)

        self.evt_expecter.expect(Evt.ArtistVideosUpdated, artist_id,
                                 [video_id_2]).from_event(
                                     VideoEvt.VideoDeleted,
                                     video_id_1,
                                 )
예제 #5
0
    def test_toggle_player_state(self, datetime_mock):
        now = datetime.now()
        datetime_mock.now.return_value = now

        self.data_producer.player().video("source").play("source").populate(
            self.data_facade)

        video_id = IdentityService.id_video("source")
        self.evt_expecter.expect(
            Evt.PlayerStateUpdated,
            self.player_id,
            PlayerState.PLAYING,
            PlayerState.PAUSED,
        ).expect(
            VideoEvt.VideoStateUpdated,
            video_id,
            VideoState.PLAYING,
            VideoState.PAUSED,
            timedelta(),
            now,
        ).from_(Cmd.TogglePlayerState, self.player_id)
        self.evt_expecter.expect(
            Evt.PlayerStateUpdated,
            self.player_id,
            PlayerState.PAUSED,
            PlayerState.PLAYING,
        ).expect(
            VideoEvt.VideoStateUpdated,
            video_id,
            VideoState.PAUSED,
            VideoState.PLAYING,
            timedelta(),
            now,
        ).from_(Cmd.TogglePlayerState, self.player_id)
예제 #6
0
    def test_seek_video(self):
        video_id = IdentityService.id_video("source")
        self.player.play(video_id)
        self.player.release_events()

        self.player.seek_video(100)
        self.expect_events(self.player, Evt.VideoSeeked)
예제 #7
0
    def test_create_artist_no_deezer_data(self):
        video_id = IdentityService.id_video("source")
        artist_id = IdentityService.id_artist("artist")
        self.data_producer.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
        self.deezer.search.return_value = []

        self.evt_expecter.expect(Evt.ArtistCreated, artist_id, "artist",
                                 [video_id], None).from_event(
                                     VideoEvt.VideoCreated,
                                     video_id,
                                     "source",
                                     None,
                                     artist_id,
                                     None,
                                     "title",
                                     300,
                                     "http",
                                     "thumbnail_url",
                                     VideoState.CREATED,
                                 )
예제 #8
0
    def test_create_video(self):
        source = "source"
        video_id = IdentityService.id_video(source)
        collection_id = IdentityService.random()

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

        attrs = deepcopy(metadata)
        attrs["artist_id"] = IdentityService.id_artist(attrs.pop("artist"))
        attrs["album_id"] = IdentityService.id_album(attrs.pop("album"))
        self.evt_expecter.expect(
            VideoEvt.VideoCreated,
            video_id,
            source,
            collection_id,
            **attrs,
            state=VideoState.CREATED,
        ).from_(Cmd.CreateVideo, video_id, source, collection_id)
예제 #9
0
 def test_construction(self):
     content = [IdentityService.id_video("")]
     playlist = Playlist(None, "name", content)
     self.assertEqual("name", playlist.name)
     self.assertEqual(content, playlist.ids)
     self.assertEqual(False, playlist.generated)
     self.expect_events(playlist, Evt.PlaylistCreated)
예제 #10
0
    def test_create_disk_video(self, path_cls_mock):
        path_inst = path_cls_mock.return_value
        source = "source"
        video_id = IdentityService.id_video(source)
        collection_id = None

        path_inst.is_file.return_value = True
        metadata = {
            "artist": None,
            "album": None,
            "title": "test_title",
            "duration": None,
            "source_protocol": None,
            "thumbnail": None,
        }
        path_inst.stem = metadata["title"]
        attrs = deepcopy(metadata)
        attrs["artist_id"] = None
        attrs["album_id"] = None
        del attrs["artist"]
        del attrs["album"]

        self.evt_expecter.expect(
            VideoEvt.VideoCreated,
            video_id,
            source,
            collection_id,
            **attrs,
            state=VideoState.CREATED,
        ).from_(Cmd.CreateVideo, video_id, source, collection_id)
예제 #11
0
    def setUp(self):
        super(QueueVideoWorkflowTest, self).setUp()
        self.video_repo = self.data_facade.video_repo
        self.playlist_repo = self.data_facade.playlist_repo

        self.video_repo.exists.return_value = True

        self.player_playlist = Mock()
        self.player_playlist.ids = []
        self.playlist_repo.get.return_value = self.player_playlist

        self.player_id = IdentityService.id_player()
        self.player_playlist_id = IdentityService.id_playlist()

        player = Mock()
        player.queue = self.player_playlist_id
        self.data_facade.player_repo.get_player.return_value = player

        self.video = Video(
            IdentityService.id_video("source"),
            "source",
            collection_id=None,
        )
        self.workflow = self.make_workflow(
            QueueVideoWorkflow,
            self.video,
            self.player_playlist_id,
            queue_front=False,
        )
예제 #12
0
    def test_creating_player_to_purging_videos(self, identityMock):
        player_id = IdentityService.id_player()
        identityMock.id_player.return_value = player_id
        video = Video(IdentityService.id_video("source"),
                      "source",
                      location="unknown")
        self.video_repo.list.return_value = [video]

        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_event(
            PlayerEvt.PlayerCreated,
            createPlayerId,
            player_id,
            HOME_PLAYLIST.id,
            PlayerState.STOPPED,
            True,
            0,
            70,
        )
        self.assertTrue(self.workflow.is_PURGING_VIDEOS())
예제 #13
0
    async def test_stop(self):
        self.data_producer.player().video("source").play("source").populate(
            self.data_facade)
        video_id = IdentityService.id_video("source")
        self.expect_and_raise(
            make_cmd(PlayerCmd.StopPlayer, self.player_id),
            [
                {
                    "type": PlayerEvt.PlayerStateUpdated,
                    "args": {
                        "old_state": PlayerState.PLAYING,
                        "new_state": PlayerState.STOPPED,
                    },
                },
                {
                    "type": PlayerEvt.PlayerVideoUpdated,
                    "args": {
                        "old_video_id": video_id,
                        "new_video_id": None,
                    },
                },
            ],
        )

        resp = await self.client.post("/api/player/stop")
        body = await resp.json()
        player = self.data_facade.player_repo.get_player()
        self.assertEqual(200, resp.status)
        self.assertEqual(player.to_dict(), body)
예제 #14
0
    def test_stop_player(self, datetime_mock):
        now = datetime.now()
        datetime_mock.now.return_value = now

        self.data_producer.player().video("source").play("source").populate(
            self.data_facade)

        video_id = IdentityService.id_video("source")
        self.evt_expecter.expect(
            Evt.PlayerStateUpdated,
            self.player_id,
            PlayerState.PLAYING,
            PlayerState.STOPPED,
        ).expect(
            Evt.PlayerVideoUpdated,
            self.player_id,
            video_id,
            None,
        ).expect(
            VideoEvt.VideoStateUpdated,
            video_id,
            VideoState.PLAYING,
            VideoState.READY,
            timedelta(),
            now,
        ).from_(Cmd.StopPlayer, self.player_id)
예제 #15
0
 def test_remove_unknown(self):
     video_id = IdentityService.id_video("source1")
     with self.assertRaises(DomainError) as ctx:
         self.album.remove(video_id)
     self.assertEqual(
         "video not in album",
         str(ctx.exception),
     )
예제 #16
0
    def setUp(self):
        self.evt_dispatcher = Mock()
        self.manager = WorkflowManager(self.evt_dispatcher)

        video_id = IdentityService.id_video("source")
        self.workflow = Mock()
        self.queue_workflow_id = IdentityService.id_workflow(
            QueueVideoWorkflow, video_id)
예제 #17
0
    async def test_play_video_not_found(self):
        video_id = IdentityService.id_video("source")

        resp = await self.client.post(
            "/api/player/play",
            params={"id": str(video_id)},
        )
        self.assertEqual(404, resp.status)
예제 #18
0
    def test_toggle_pause(self):
        video_id = IdentityService.id_video("source")
        self.player.play(video_id)
        self.player.release_events()

        self.player.toggle_pause()
        self.assertEqual(PlayerState.PAUSED, self.player.state)
        self.expect_events(self.player, Evt.PlayerStateUpdated)
예제 #19
0
 def play(self, source: str):
     video_id = IdentityService.id_video(source)
     player = self._population.last(Player)
     player.play(video_id)
     video = self._population.find(Video, video_id)
     video.state = VideoState.READY
     video.start()
     return self
예제 #20
0
 def make_test_workflow(self, video_count=2):
     collection_id = IdentityService.random()
     sources = [f"src{i}" for i in range(video_count)]
     videos = [
         Video(IdentityService.id_video(source), source, collection_id)
         for source in sources
     ]
     return self.make_workflow(StreamPlaylistWorkflow, videos,
                               self.player_playlist_id)
예제 #21
0
    def test_retrieve_video_from_disk_success(self, path_cls_mock):
        video_id = IdentityService.id_video("/tmp/video.mp4")
        self.data_producer.video("/tmp/video.mp4").populate(self.data_facade)

        path_cls_mock.return_value.is_file.return_value = True
        output_dir = settings["downloader.output_directory"]
        self.evt_expecter.expect(
            VideoEvt.VideoRetrieved, video_id, "/tmp/video.mp4"
        ).from_(Cmd.RetrieveVideo, video_id, output_dir)
예제 #22
0
    def test_next_invalid_video(self):
        self.data_producer.player().video("source1", state=VideoState.READY).video(
            "source2", state=VideoState.READY
        ).populate(self.data_facade)

        video_id = IdentityService.id_video("source3")
        self.assertEqual(
            None, self.service.next_video(self.queue_id, video_id, loop_last="track")
        )
예제 #23
0
    def test_init_to_purging_videos(self):
        self.player_repo.exists.return_value = True

        video = Video(IdentityService.id_video("source"),
                      "source",
                      location="unknown")
        self.video_repo.list.return_value = [video]

        self.workflow.start()
        self.assertTrue(self.workflow.is_PURGING_VIDEOS())
예제 #24
0
    def test_update_playlist_content(self):
        playlist_id = IdentityService.id_playlist()
        self.data_producer.playlist(playlist_id, "name",
                                    []).populate(self.data_facade)

        playlist = self.playlist_repo.get(playlist_id)
        new_content = [IdentityService.id_video("source")]
        self.evt_expecter.expect(Evt.PlaylistContentUpdated, playlist.id,
                                 new_content).from_(Cmd.UpdatePlaylistContent,
                                                    playlist.id, new_content)
예제 #25
0
    def test_create_video_missing_metadata(self):
        source = "source"
        video_id = IdentityService.id_video(source)

        metadata = None
        self.downloader.download_metadata.return_value = metadata

        self.evt_expecter.expect(
            OperationError, "no media found", {"source": source}
        ).from_(Cmd.CreateVideo, video_id, source, collection_id=None)
예제 #26
0
 def test_play(self):
     video_id = IdentityService.id_video("source")
     self.player.play(video_id)
     self.assertEqual(PlayerState.PLAYING, self.player.state)
     self.assertEqual(video_id, self.player.video_id)
     self.expect_events(
         self.player,
         Evt.PlayerVideoUpdated,
         Evt.PlayerStateUpdated,
     )
예제 #27
0
    def test_delete_video_deletes_album(self):
        album_id = IdentityService.id_album("album")
        video_id = IdentityService.id_video("source")
        self.data_producer.album(album_id, "album").video(
            "source", album_id=album_id).populate(self.data_facade)

        self.evt_expecter.expect(Evt.AlbumDeleted, album_id, []).from_event(
            VideoEvt.VideoDeleted,
            video_id,
        )
예제 #28
0
    def test_download_video_error(self):
        self.ydl.download.side_effect = RuntimeError("error")

        op_id = IdentityService.random()
        video_id = IdentityService.id_video("url")
        self.downloader.download_video(op_id, video_id, "url",
                                       "/tmp/media.mp4")

        self.dispatcher.dispatch.assert_called_with(
            DownloadError(op_id, "error"))
예제 #29
0
    def test_download_video(self, path_cls):
        path_mock = path_cls.return_value
        path_mock.exists.return_value = True

        op_id = IdentityService.random()
        video_id = IdentityService.id_video("url")
        self.downloader.download_video(op_id, video_id, "url",
                                       "/tmp/media.mp4")

        self.dispatcher.dispatch.assert_called_with(DownloadSuccess(op_id))
예제 #30
0
    def test_download_video_missing(self, path_cls):
        path_mock = path_cls.return_value
        path_mock.exists.return_value = False

        op_id = IdentityService.random()
        video_id = IdentityService.id_video("url")
        self.downloader.download_video(op_id, video_id, "url",
                                       "/tmp/media.mp4")

        self.dispatcher.dispatch.assert_called_with(
            DownloadError(op_id, "video path points to non existent file"))