Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fed42b1
Add minimal working cli with plugin support
ashwoods Aug 17, 2020
bd8e76d
Add experimental changes to core
ashwoods Aug 27, 2020
2c0326f
Add hacky command mapping and toolbar prompt
Aug 28, 2020
e8030c5
Merge pull request #10 from lima-tango/BLY-905-export-boombox-command…
ashwoods Aug 30, 2020
5396f95
Add fixes to adapt to registering commands
ashwoods Sep 28, 2020
7aebaef
Add Command class
Sep 29, 2020
e429681
Use Command class in test_core
Sep 29, 2020
d476405
Merge pull request #11 from lima-tango/Export-calibration-method-to-b…
ashwoods Oct 5, 2020
a57d6f1
Add options to context and add hookspec
Oct 6, 2020
6f8be6f
Add hooks for adding options and getting pipeline
Oct 7, 2020
96ac897
Update handling of options
Oct 9, 2020
5867bb6
Update available commands in prompt
Oct 12, 2020
52ce1b1
Fix Command registration default
Oct 12, 2020
76cf1ed
Fix BoomBox options default
Oct 12, 2020
b2071a2
Refactor BoomBox init
Oct 12, 2020
e5ab2df
Refacter Boombox constructor with kwargs
Oct 13, 2020
788333e
Add hook for each state
Oct 13, 2020
4cdf69c
Fix on_state_changed and BoomBox init
Oct 14, 2020
ffeb0ca
Merge pull request #15 from lima-tango/plugin-entrypoint-for-defining…
ashwoods Oct 20, 2020
19a0910
Merge pull request #16 from lima-tango/Refactor-boombox-as-a-library-…
ashwoods Oct 20, 2020
43a9476
Merge pull request #17 from lima-tango/Add-mixtape-hooks-for-each-state
ashwoods Oct 20, 2020
ba3c225
Add initial plugin spec
ashwoods Jan 14, 2021
468b101
Add simplug to requirements
ashwoods Jan 14, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions mixtape/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from .players import AsyncPlayer
from .players import Player
from .boombox import BoomBox, hookspec

__all__ = ["AsyncPlayer"]

__all__ = ["Player", "BoomBox", "hookspec"]
114 changes: 0 additions & 114 deletions mixtape/base.py

This file was deleted.

210 changes: 210 additions & 0 deletions mixtape/boombox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import logging

from collections import ChainMap, UserDict
from typing import Any, List, Mapping, Optional, Tuple

import attr
import gi
import simplug

gi.require_version("Gst", "1.0")
from gi.repository import Gst

from .players import Player
from .exceptions import BoomBoxNotConfigured

logger = logging.getLogger(__name__)


hookspec = simplug.Simplug("mixtape")


class Context(UserDict[Any, Any]):
"""
Application state object
"""


class PluginSpec:
"""Mixtape plugin namespace"""

@hookspec.spec
def mixtape_plugin_init(self, ctx: Context) -> None:
"""Called"""

@hookspec.spec
def mixtape_add_pipelines(self, ctx: Context) -> Mapping[str, Any]:
"""
Hook allowing a plugin to return a pipeline
"""

# player init and teardown

@hookspec.spec
def mixtape_player_setup(self, ctx: Context, player: Player) -> None:
"""
Hook called on player setup
"""

@hookspec.spec
def mixtape_player_teardown(self, ctx: Context, player: Player) -> None:
"""
Hook called on player teardown
"""

# pipeline control and event hooks

@hookspec.spec
async def mixtape_on_message(self, ctx: Context, player: Player, message: Gst.Message) -> None:
"""
Generic hook for all bus messages
"""

@hookspec.spec
async def mixtape_before_state_changed(
self, ctx: Context, player: Player, state: Gst.State
) -> None:
"""
Hook called before a `set_state` call.
"""

@hookspec.spec
async def mixtape_on_ready(self, ctx: Context, player: Player) -> None:
"""
Shortcut Hook called on state changed to READY
"""

@hookspec.spec
async def mixtape_on_pause(self, ctx: Context, player: Player) -> None:
"""
Shortcut Hook called on state changed to PAUSED
"""

@hookspec.spec
async def mixtape_on_play(self, ctx: Context, player: Player) -> None:
"""
Shortcut Hook called on state changed to PLAYING
"""

@hookspec.spec
async def mixtape_on_stop(self, ctx: Context, player: Player) -> None:
"""
Hook called on state changed to NULL
"""

# asyncio player events

@hookspec.spec
async def mixtape_on_eos(self, ctx: Context, player: Player) -> None:
"""
Hook called on eos
"""

@hookspec.spec
async def mixtape_on_error(self, ctx: Context, player: Player) -> None:
"""
Hook called on bus message error
"""


@attr.s
class BoomBox:
"""
Facade object that orchestrates plugin callbacks
and exposes plugin events and properties.
"""

player: Player = attr.ib()
context: Context = attr.ib(repr=False)
_hookspec: simplug.Simplug = attr.ib(repr=False)

@property
def _hooks(self) -> simplug.SimplugHooks:
"""Shortcut property for plugin hooks"""
return self._hookspec.hooks

def setup(self) -> None:
"""Wrapper for player setup"""
self.player.setup()
self._hooks.mixtape_player_setup(ctx=self.context, player=self.player)

def teardown(self) -> None:
"""wrapper for player teardown"""
self._hooks.mixtape_player_teardown(ctx=self.context, player=self.player)
self.player.teardown()

async def ready(self) -> Tuple[Gst.StateChangeReturn, Gst.State, Gst.State]:
"""Wrapper for player ready"""
await self._hooks.mixtape_before_state_changed(
ctx=self.context, player=self.player, state=Gst.State.READY
)
ret = await self.player.ready()
await self._hooks.mixtape_on_ready(ctx=self.context, player=self.player)
return ret

async def pause(self) -> Tuple[Gst.StateChangeReturn, Gst.State, Gst.State]:
"""Wrapper for player pause"""
await self._hooks.mixtape_before_state_changed(
ctx=self.context, player=self.player, state=Gst.State.PAUSED
)
ret = await self.player.pause()
await self._hooks.mixtape_on_pause(ctx=self.context, player=self.player)
return ret

async def play(self) -> Tuple[Gst.StateChangeReturn, Gst.State, Gst.State]:
"""Wrapper for player play"""
await self._hooks.mixtape_before_state_changed(
ctx=self.context, player=self.player, state=Gst.State.PLAYING
)
ret = await self.player.play()
await self._hooks.mixtape_on_play(ctx=self.context, player=self.player)
return ret

async def stop(self) -> Tuple[Gst.StateChangeReturn, Gst.State, Gst.State]:
"""Wrapper for player stop"""
await self._hooks.mixtape_before_state_changed(
ctx=self.context, player=self.player, state=Gst.State.NULL
)
ret = await self.player.stop()
await self._hooks.mixtape_on_stop(ctx=self.context, player=self.player)
return ret

@classmethod
async def init(
cls,
player: Optional[Any] = None,
context: Optional[Context] = None,
plugins: Optional[List[Any]] = None,
**settings: Any,
) -> "BoomBox":
"""Boombox async init method"""

# load plugins
if plugins:
for plugin in plugins:
hookspec.register(plugin)
else:
hookspec.load_entrypoints("mixtape")

# init context

if context is None:
context = Context()

# init plugins

hookspec.hooks.mixtape_plugin_init(ctx=Context)

# init player

if not player:
context["pipelines"] = ChainMap(*hookspec.hooks.mixtape_add_pipelines(ctx=context))

try:
description = context["pipelines"][settings["name"]]
except AttributeError:
raise BoomBoxNotConfigured("Pipeline needed explicitly or provided by hook")
else:
player = await Player.from_description(description)

return cls(player, context, hookspec)
6 changes: 4 additions & 2 deletions mixtape/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@


class States(enum.Enum):
"""Python enum representing the Gst.Pipeline states"""

VOID_PENDING = 0
NULL = 1
READY = 2
Expand All @@ -36,10 +38,10 @@ class TearDownEvent(asyncio.Event):
@attr.s(auto_attribs=True, slots=True, frozen=True)
class PlayerEvents:
"""
Async event syncronization flags
Player async event synchronization flags
"""

# simple events
# core events
setup: asyncio.Event = attr.Factory(SetupEvent)
eos: asyncio.Event = attr.Factory(EosEvent)
error: asyncio.Event = attr.Factory(ErrorEvent)
Expand Down
6 changes: 5 additions & 1 deletion mixtape/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class MixTapeError(Exception):
"""Mixtape exception base clase"""
"""Mixtape exception base class"""


class PlayerNotConfigured(MixTapeError):
Expand All @@ -16,3 +16,7 @@ class PlayerSetStateError(MixTapeError):

class PlayerPipelineError(MixTapeError):
"""Error originating from Gst Pipeline"""


class BoomBoxNotConfigured(MixTapeError):
"""Boombox improperly configured"""
Empty file removed mixtape/features/__init__.py
Empty file.
Empty file removed mixtape/features/cmdline.py
Empty file.
Empty file removed mixtape/features/console.py
Empty file.
Empty file removed mixtape/features/dbus.py
Empty file.
Empty file removed mixtape/features/http.py
Empty file.
Loading