Skip to content

Commit

Permalink
Merge pull request #7 from Tastyep/feature/replace-omxplayer-with-vlc
Browse files Browse the repository at this point in the history
Feature/replace omxplayer with vlc
  • Loading branch information
Tastyep authored Jun 5, 2020
2 parents d03d508 + 57f7ea6 commit 49baa1e
Show file tree
Hide file tree
Showing 49 changed files with 639 additions and 530 deletions.
8 changes: 6 additions & 2 deletions OpenCast.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ function stop() {
# Todo hardcoded port
lsof -t -i :2020 | xargs kill >/dev/null 2>&1
lsof -t -i :8081 | xargs kill >/dev/null 2>&1
sudo killall omxplayer.bin >/dev/null 2>&1
echo "Done."
}

Expand Down Expand Up @@ -81,7 +80,12 @@ function test() {
if [ -z "$1" ]; then
run_in_env python -m unittest discover -v
else
run_in_env python -m unittest "$TEST_DIR.$1"
local selector="$1"

if [[ "$selector" != "$TEST_DIR"* ]]; then
selector="$TEST_DIR.$selector"
fi
run_in_env python -m unittest "$selector"
fi
}

Expand Down
8 changes: 5 additions & 3 deletions OpenCast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from concurrent.futures import ThreadPoolExecutor

import structlog
from vlc import Instance as VlcInstance

from .app.controller.module import ControllerModule
from .app.facade import AppFacade
Expand Down Expand Up @@ -42,9 +43,10 @@ def main(argv=None):
repo_factory = RepoFactory()
data_facade = DataFacade(repo_factory)

downloader_executor = ThreadPoolExecutor(config["downloader.max_concurrency"])
io_factory = IoFactory(downloader_executor)
media_factory = MediaFactory()
io_factory = IoFactory()
media_factory = MediaFactory(
VlcInstance(), ThreadPoolExecutor(config["downloader.max_concurrency"])
)
infra_facade = InfraFacade(io_factory, media_factory)

ControllerModule(app_facade, infra_facade, data_facade, service_factory)
Expand Down
2 changes: 1 addition & 1 deletion OpenCast/app/command/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class QueueVideo(Command):


@command
class StopVideo(Command):
class StopPlayer(Command):
pass


Expand Down
5 changes: 5 additions & 0 deletions OpenCast/app/command/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ class RetrieveVideo(Command):
output_directory: str


@command
class ParseVideo(Command):
pass


@command
class FetchVideoSubtitle(Command):
language: str
10 changes: 10 additions & 0 deletions OpenCast/app/controller/controller.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import inspect

from OpenCast.domain.service.identity import IdentityService
from OpenCast.util.naming import name_handler_method


class Controller:
Expand All @@ -11,6 +14,13 @@ def _dispatch(self, cmd_cls, component_id, *args, **kwargs):
cmd_id = IdentityService.id_command(cmd_cls, component_id)
self._cmd_dispatcher.dispatch(cmd_cls(cmd_id, component_id, *args, **kwargs))

def _observe(self, module):
classes = inspect.getmembers(module, inspect.isclass)
for _, cls in classes:
if cls.__module__ == module.__name__:
handler_name = name_handler_method(cls)
self._evt_dispatcher.observe(None, {cls: getattr(self, handler_name)})

def _start_workflow(self, workflow_cls, resource_id, *args, **kwargs):
workflow_id = IdentityService.id_workflow(workflow_cls, resource_id)
workflow = workflow_cls(workflow_id, self._app_facade, *args, **kwargs)
Expand Down
2 changes: 2 additions & 0 deletions OpenCast/app/controller/module.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .file import FileController
from .player import PlayerController
from .player_monitor import PlayerMonitController


Expand All @@ -8,3 +9,4 @@ def __init__(self, app_facade, infra_facade, data_facade, service_factory):
self._player_monitor = PlayerMonitController(
app_facade, infra_facade, data_facade, service_factory
)
self._player_controller = PlayerController(app_facade, data_facade)
33 changes: 33 additions & 0 deletions OpenCast/app/controller/player.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import structlog
from OpenCast.app.command import player as Cmd
from OpenCast.domain.service.identity import IdentityService
from OpenCast.infra.event import player as player_events

from .controller import Controller


class PlayerController(Controller):
def __init__(self, app_facade, data_facade):
super(PlayerController, self).__init__(app_facade)

self._logger = structlog.get_logger(__name__)
self._player_repo = data_facade.player_repo
self._video_repo = data_facade.video_repo

self._observe(player_events)

# Infra event handler interface implementation

def _media_end_reached(self, evt):
model = self._player_repo.get_player()
video = model.next_video()
if video is None:
self._dispatch(Cmd.StopPlayer)
else:
self._dispatch(Cmd.PlayVideo, video.id)

def _dispatch(self, cmd_cls, *args, **kwargs):
player_id = IdentityService.id_player()
return super(PlayerController, self)._dispatch(
cmd_cls, player_id, *args, **kwargs
)
18 changes: 10 additions & 8 deletions OpenCast/app/controller/player_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ class PlayerMonitController(Controller):
def __init__(self, app_facade, infra_facade, data_facade, service_factory):
super(PlayerMonitController, self).__init__(app_facade)

media_factory = infra_facade.media_factory
self._source_service = service_factory.make_source_service(
infra_facade.io_factory.make_downloader(app_facade.evt_dispatcher)
media_factory.make_downloader(app_facade.evt_dispatcher),
media_factory.make_video_parser(),
)
self._player_repo = data_facade.player_repo
self._video_repo = data_facade.video_repo
Expand Down Expand Up @@ -80,15 +82,15 @@ def _video(self):
if control == "pause":
self._dispatch(Cmd.ToggleVideoState)
elif control == "stop":
self._dispatch(Cmd.StopVideo)
self._dispatch(Cmd.StopPlayer)
elif control == "right":
self._dispatch(Cmd.SeekVideo, 30)
self._dispatch(Cmd.SeekVideo, Player.SHORT_TIME_STEP)
elif control == "left":
self._dispatch(Cmd.SeekVideo, -30)
self._dispatch(Cmd.SeekVideo, -Player.SHORT_TIME_STEP)
elif control == "longright":
self._dispatch(Cmd.SeekVideo, 300)
self._dispatch(Cmd.SeekVideo, Player.LONG_TIME_STEP)
elif control == "longleft":
self._dispatch(Cmd.SeekVideo, -300)
self._dispatch(Cmd.SeekVideo, -Player.LONG_TIME_STEP)
elif control == "prev":
self._dispatch(Cmd.PrevVideo)
elif control == "next":
Expand All @@ -100,9 +102,9 @@ def _video(self):

def _sound(self):
if request.query["vol"] == "more":
self._dispatcher(Cmd.ChangeVolume, Player.VOLUME_STEP)
self._dispatch(Cmd.ChangeVolume, Player.VOLUME_STEP)
else:
self._dispatcher(Cmd.ChangeVolume, -Player.VOLUME_STEP)
self._dispatch(Cmd.ChangeVolume, -Player.VOLUME_STEP)
return "1"

def _subtitle(self):
Expand Down
2 changes: 1 addition & 1 deletion OpenCast/app/service/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ def __init__(self, app_facade, infra_facade, data_facade, service_factory):
app_facade, data_facade, infra_facade.media_factory
)
self._video_service = VideoService(
app_facade, service_factory, data_facade, infra_facade.io_factory
app_facade, service_factory, data_facade, infra_facade.media_factory
)
89 changes: 33 additions & 56 deletions OpenCast/app/service/player.py
Original file line number Diff line number Diff line change
@@ -1,64 +1,41 @@
from functools import partial

import structlog
from OpenCast.app.command import player as player_cmds
from OpenCast.app.error import CommandFailure
from OpenCast.domain.event import player as player_events
from OpenCast.config import config
from OpenCast.domain.model.player_state import PlayerState
from OpenCast.infra.event import player as infra_events

from .service import Service


class PlayerService(Service):
def __init__(self, app_facade, data_facade, media_factory):
logger = structlog.get_logger(__name__)
super(PlayerService, self).__init__(
app_facade, logger, self, player_cmds, infra_events
)
super(PlayerService, self).__init__(app_facade, logger, self, player_cmds)

self._player_repo = data_facade.player_repo
self._video_repo = data_facade.video_repo
self._player = media_factory.make_player(app_facade.evt_dispatcher)

# Infra event handler interface implementation

def _player_stopped(self, evt):
def stop_player(model):
model.stop()

def play_next(model):
video = model.next_video()
if video is not None:
self._player.play(video, model.volume)
model.play(video)

self._update(evt.id, stop_player)
if evt.id is not None:
return

self._update(evt.id, play_next)
model = self._player_model()
self._player.set_volume(model.volume)

# Command handler interface implementation

def _play_video(self, cmd):
def queue_video(model, video):
model.queue(video, with_priority=True)

video = self._video_repo.get(cmd.video_id)
self._update(cmd.id, queue_video, video)
self._queue_video_impl(cmd.id, video)
self._play_video_impl(cmd.id, video)

def _queue_video(self, cmd):
def queue_video(model):
video = self._video_repo.get(cmd.video_id)
model.queue(video)
video = self._video_repo.get(cmd.video_id)
self._queue_video_impl(cmd.id, video)

self._update(cmd.id, queue_video)
def _stop_player(self, cmd):
def stop_video(model):
model.stop()
self._player.stop()

def _stop_video(self, cmd):
self._player.stop(cmd.id)
# Model updates are done from the infra event handler
self._update(cmd.id, stop_video)

def _toggle_video_state(self, cmd):
def pause(model):
Expand Down Expand Up @@ -101,45 +78,45 @@ def _prev_video(self, cmd):
self._play_video_impl(cmd.id, prev_video)

def _toggle_subtitle(self, cmd):
def impl(model, state):
model.subtitle_state = state
def impl(model):
model.subtitle_state = not model.subtitle_state

model = self._player_model()
state = not model.subtitle_state
self._player.update_subtitle_state(state)
self._update(cmd.id, impl, state)
self._player.toggle_subtitle()
self._update(cmd.id, impl)

def _increase_subtitle_delay(self, cmd):
def impl(model):
model.subtitle_delay = model.subtitle_delay + cmd.amount
self._player.set_subtitle_delay(model.subtitle_delay)

self._player.increase_subtitle_delay()
self._update(cmd.id, impl)

def _decrease_subtitle_delay(self, cmd):
def impl(model):
model.subtitle_delay = model.subtitle_delay - cmd.amount
self._player.set_subtitle_delay(model.subtitle_delay)

self._player.increase_subtitle_delay()
self._update(cmd.id, impl)

# Private

def _play_video_impl(self, cmd_id, video):
def play_video(model, video, *_):
def impl(model):
model.play(video)

self._player.play(video, model.volume)
self._update(cmd_id, impl)
def play_video(model):
model.play(video)

self._player.play(video.id, str(video.path))
player = self._player_model()
if player.subtitle_state is True:
sub_stream = video.stream("subtitle", config["subtitle.language"])
if sub_stream is not None:
self._player.select_subtitle_stream(sub_stream.index)
self._update(cmd_id, play_video)

def _queue_video_impl(self, cmd_id, video):
def queue_video(model):
model.queue(video)

model = self._player_model()
if model.state is not PlayerState.STOPPED:
callback = partial(play_video, model, video)
self._evt_dispatcher.once(player_events.PlayerStopped, callback)
self._player.stop(cmd_id)
else:
play_video(model, video)
self._update(cmd_id, queue_video)

def _player_model(self):
return self._player_repo.get_player()
Expand Down
21 changes: 15 additions & 6 deletions OpenCast/app/service/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@


class VideoService(Service):
def __init__(self, app_facade, service_factory, data_facade, io_factory):
def __init__(self, app_facade, service_factory, data_facade, media_factory):
logger = structlog.get_logger(__name__)
super(VideoService, self).__init__(app_facade, logger, self, video_cmds)
self._video_repo = data_facade.video_repo
self._downloader = io_factory.make_downloader(app_facade.evt_dispatcher)
self._source_service = service_factory.make_source_service(self._downloader)
self._subtitle_service = service_factory.make_subtitle_service(
io_factory.make_ffmpeg_wrapper(), self._downloader
self._downloader = media_factory.make_downloader(app_facade.evt_dispatcher)
self._source_service = service_factory.make_source_service(
self._downloader, media_factory.make_video_parser()
)
self._subtitle_service = service_factory.make_subtitle_service(self._downloader)

# Command handler interface implementation
def _create_video(self, cmd):
Expand Down Expand Up @@ -54,7 +54,7 @@ def impl(ctx, video):
ctx.update(video)

video = self._video_repo.get(cmd.model_id)
if video.is_file():
if video.from_disk():
self._start_transaction(self._video_repo, cmd.id, impl, video)
return

Expand All @@ -75,6 +75,15 @@ def abort_operation(evt):
)
self._downloader.download_video(cmd.id, video.source, str(video.path))

def _parse_video(self, cmd):
def impl(ctx):
video = self._video_repo.get(cmd.model_id)
streams = self._source_service.list_streams(video)
video.streams = streams
ctx.update(video)

self._start_transaction(self._video_repo, cmd.id, impl)

def _fetch_video_subtitle(self, cmd):
def impl(ctx):
video = self._video_repo.get(cmd.model_id)
Expand Down
Loading

0 comments on commit 49baa1e

Please sign in to comment.