Skip to content

Commit

Permalink
Merge pull request #193 from Tastyep/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Tastyep committed Jul 22, 2021
2 parents 20e5aee + 7f8648b commit 1ed0713
Show file tree
Hide file tree
Showing 61 changed files with 20,236 additions and 795 deletions.
1 change: 1 addition & 0 deletions OpenCast/app/command/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class CreatePlayer(Command):
@command
class PlayVideo(Command):
video_id: ModelId
playlist_id: ModelId


@command
Expand Down
4 changes: 4 additions & 0 deletions OpenCast/app/controller/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ def _ok(self, body):
def _no_content(self):
return self._make_response(204, None)

def _forbidden(self, message: str, details: dict = {}):
body = ErrorSchema().load({"message": message, "details": details})
return self._make_response(403, body)

def _not_found(self):
return self._make_response(404, None)

Expand Down
2 changes: 1 addition & 1 deletion OpenCast/app/controller/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def _media_end_reached(self, evt):
if video_id is None:
self._dispatch(Cmd.StopPlayer)
else:
self._dispatch(Cmd.PlayVideo, video_id)
self._dispatch(Cmd.PlayVideo, video_id, player.queue)

def _dispatch(self, cmd_cls, *args, **kwargs):
player_id = IdentityService.id_player()
Expand Down
42 changes: 35 additions & 7 deletions OpenCast/app/controller/player_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def __init__(self, app_facade, infra_facade, data_facade, service_factory):
)
self._data_facade = data_facade
self._player_repo = data_facade.player_repo
self._playlist_repo = data_facade.playlist_repo
self._video_repo = data_facade.video_repo

self._route("GET", "/", self.get)
Expand Down Expand Up @@ -85,6 +86,7 @@ async def get(self, _):
async def stream(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):
sources = self._source_service.unfold(source)
Expand All @@ -99,12 +101,18 @@ async def stream(self, req):

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

video = Video(video_id, source, collection_id=None)
self._start_workflow(StreamVideoWorkflow, video_id, self._data_facade, video)
self._start_workflow(
StreamVideoWorkflow, video_id, self._data_facade, video, playlist_id
)

return self._no_content()

Expand All @@ -130,6 +138,7 @@ async def stream(self, req):
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):
sources = self._source_service.unfold(source)
Expand All @@ -144,13 +153,22 @@ async def queue(self, req):

workflow_id = IdentityService.id_workflow(QueuePlaylistWorkflow, video_id)
self._start_workflow(
QueuePlaylistWorkflow, workflow_id, self._data_facade, videos
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, queue_front=False
QueueVideoWorkflow,
video_id,
self._data_facade,
video,
playlist_id,
queue_front=False,
)

return self._no_content()
Expand All @@ -161,13 +179,20 @@ async def queue(self, req):
description="Play the media selected by ID",
operationId="playMedia",
parameters=[
{
"in": "query",
"name": "playlist_id",
"description": "ID of the playlist",
"type": "string",
"required": True,
},
{
"in": "query",
"name": "id",
"description": "ID of the media",
"type": "string",
"required": True,
}
},
],
responses={
200: {"description": "Successful operation"},
Expand All @@ -176,12 +201,15 @@ async def queue(self, req):
},
)
async def play(self, req):
playlist_id = Id(req.query["playlist_id"])
video_id = Id(req.query["id"])
if not self._video_repo.exists(video_id):
if not self._playlist_repo.exists(playlist_id) or not self._video_repo.exists(
video_id
):
return self._not_found()

handlers, channel = self._make_default_handlers(PlayerEvt.PlayerStarted)
self._observe_dispatch(handlers, Cmd.PlayVideo, video_id)
self._observe_dispatch(handlers, Cmd.PlayVideo, video_id, playlist_id)

return await channel.receive()

Expand Down
14 changes: 14 additions & 0 deletions OpenCast/app/controller/playlist_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from OpenCast.app.command import playlist as Cmd
from OpenCast.app.service.error import OperationError
from OpenCast.domain.constant import HOME_PLAYLIST
from OpenCast.domain.event import playlist as PlaylistEvt
from OpenCast.domain.model import Id
from OpenCast.domain.model.playlist import PlaylistSchema
Expand All @@ -28,6 +29,7 @@ def __init__(self, app_facade, infra_facade, data_facade):
self._route("GET", "/{id:" + self.UUID + "}/videos", handle=self.list_videos)
self._route("PATCH", "/{id:" + self.UUID + "}", handle=self.update)
self._route("DELETE", "/{id:" + self.UUID + "}", handle=self.delete)
self._route("GET", "/events", handle=self.stream_events)

@docs(
tags=["playlist"],
Expand Down Expand Up @@ -235,6 +237,9 @@ async def delete(self, req):
if not self._playlist_repo.exists(id):
return self._not_found()

if id == HOME_PLAYLIST.id:
return self._forbidden(f"{HOME_PLAYLIST.name} playlist can't be deleted")

channel = self._io_factory.make_janus_channel()

def on_success(evt):
Expand All @@ -245,3 +250,12 @@ def on_success(evt):
)

return await channel.receive()

@docs(
tags=["playlist"],
summary="Stream playlist events",
description="Stream playlist events over WebSocket",
operationId="streamPlaylistEvents",
)
async def stream_events(self, request):
return await self._stream_ws_events(request, PlaylistEvt)
2 changes: 1 addition & 1 deletion OpenCast/app/service/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def impl(ctx):
def _play_video(self, cmd):
def impl(player):
video = self._video_repo.get(cmd.video_id)
player.play(video.id)
player.play(video.id, cmd.playlist_id)

self._player.play(video.location, video.streamable())

Expand Down
16 changes: 16 additions & 0 deletions OpenCast/app/service/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import structlog

from OpenCast.app.command import playlist as playlist_cmds
from OpenCast.domain.constant import HOME_PLAYLIST
from OpenCast.domain.event import playlist as PlaylistEvt
from OpenCast.domain.event import video as VideoEvt
from OpenCast.domain.model.playlist import Playlist

Expand All @@ -15,6 +17,7 @@ def __init__(self, app_facade, service_factory, data_facade):
super().__init__(app_facade, logger, playlist_cmds)

self._observe_event(VideoEvt.VideoDeleted)
self._observe_event(PlaylistEvt.PlaylistDeleted)

self._playlist_repo = data_facade.playlist_repo
self._queueing_service = service_factory.make_queueing_service(
Expand Down Expand Up @@ -64,6 +67,19 @@ def impl(ctx):

# Event handler implementation

def _playlist_deleted(self, evt):
def impl(ctx):
home_playlist = self._playlist_repo.get(HOME_PLAYLIST.id)
for video_id in evt.ids:
if video_id in home_playlist.ids:
continue
home_playlist.ids = self._queueing_service.queue(
home_playlist, video_id, front=False
)
ctx.update(home_playlist)

self._start_transaction(self._playlist_repo, evt.id, impl)

def _video_deleted(self, evt):
def impl(ctx):
playlists = self._playlist_repo.list_containing(evt.model_id)
Expand Down
6 changes: 5 additions & 1 deletion OpenCast/app/tool/json_encoder.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
""" Custom JSON encoder definitions """

from enum import Enum
from json import JSONEncoder
from pathlib import PosixPath

from OpenCast.domain.event.event import Event
from OpenCast.domain.model.entity import Entity
Expand All @@ -9,8 +11,10 @@

class EnhancedJSONEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, Id):
if isinstance(obj, Id) or isinstance(obj, PosixPath):
return str(obj)
if isinstance(obj, Enum):
return obj.name
return super().default(obj)


Expand Down
7 changes: 3 additions & 4 deletions OpenCast/app/workflow/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from OpenCast.app.command import player as PlayerCmd
from OpenCast.app.command import playlist as PlaylistCmd
from OpenCast.app.command import video as VideoCmd
from OpenCast.domain.constant import PLAYER_PLAYLIST_NAME
from OpenCast.domain.constant import HOME_PLAYLIST
from OpenCast.domain.event import player as PlayerEvt
from OpenCast.domain.event import video as VideoEvt
from OpenCast.domain.service.identity import IdentityService
Expand Down Expand Up @@ -63,14 +63,13 @@ def __init__(

# States
def on_enter_CREATING_PLAYER(self):
playlist_id = IdentityService.id_playlist()
cmd = make_cmd(PlaylistCmd.CreatePlaylist, playlist_id, PLAYER_PLAYLIST_NAME)
cmd = make_cmd(PlaylistCmd.CreatePlaylist, HOME_PLAYLIST.id, HOME_PLAYLIST.name)
self._cmd_dispatcher.dispatch(cmd)
self._observe_dispatch(
PlayerEvt.PlayerCreated,
PlayerCmd.CreatePlayer,
IdentityService.id_player(),
playlist_id,
HOME_PLAYLIST.id,
)

def on_enter_PURGING_VIDEOS(self, *_):
Expand Down
Loading

0 comments on commit 1ed0713

Please sign in to comment.