Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/OCP_backends #207

Merged
merged 11 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 3 additions & 3 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ jobs:
sudo apt-get update
sudo apt install python3-dev
python -m pip install build wheel
- name: Install core repo
run: |
pip install .
- name: Install test dependencies
run: |
pip install -r requirements/test.txt
- name: Install core repo
run: |
pip install .
- name: Run unittests
run: |
pytest --cov=ovos_plugin_manager --cov-report xml test/unittests
Expand Down
4 changes: 4 additions & 0 deletions ovos_plugin_manager/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
from ovos_utils.log import LOG
from ovos_bus_client.util import get_mycroft_bus
from ovos_config import Configuration
from ovos_utils.log import log_deprecation

log_deprecation("ovos_plugin_manager.audio has been deprecated on ovos-audio, "
"move to ovos_plugin_manager.media", "0.1.0")


def find_plugins(*args, **kwargs):
Expand Down
54 changes: 38 additions & 16 deletions ovos_plugin_manager/ocp.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
from ovos_plugin_manager.utils import PluginTypes, PluginConfigTypes
from ovos_plugin_manager.utils import PluginTypes
from ovos_plugin_manager.templates.ocp import OCPStreamExtractor
from ovos_plugin_manager.templates.media import AudioPlayerBackend, VideoPlayerBackend, WebPlayerBackend
from ovos_utils.log import LOG
from functools import lru_cache

from ovos_plugin_manager.utils import find_plugins

def find_plugins(*args, **kwargs):
# TODO: Deprecate in 0.1.0
LOG.warning("This reference is deprecated. "
"Import from ovos_plugin_manager.utils directly")
from ovos_plugin_manager.utils import find_plugins
return find_plugins(*args, **kwargs)

def find_ocp_plugins() -> dict:
"""
Find all installed plugins
@return: dict plugin names to entrypoints
"""
return find_plugins(PluginTypes.STREAM_EXTRACTOR)

def load_plugin(*args, **kwargs):
# TODO: Deprecate in 0.1.0
LOG.warning("This reference is deprecated. "
"Import from ovos_plugin_manager.utils directly")
from ovos_plugin_manager.utils import load_plugin
return load_plugin(*args, **kwargs)

def find_ocp_audio_plugins() -> dict:
"""
Find all installed plugins
@return: dict plugin names to entrypoints
"""
return find_plugins(PluginTypes.AUDIO_PLAYER)

def find_ocp_plugins() -> dict:

def find_ocp_video_plugins() -> dict:
"""
Find all installed plugins
@return: dict plugin names to entrypoints
"""
from ovos_plugin_manager.utils import find_plugins
return find_plugins(PluginTypes.STREAM_EXTRACTOR)
return find_plugins(PluginTypes.VIDEO_PLAYER)


def find_ocp_web_plugins() -> dict:
"""
Find all installed plugins
@return: dict plugin names to entrypoints
"""
return find_plugins(PluginTypes.WEB_PLAYER)


class StreamHandler:
Expand Down Expand Up @@ -92,3 +103,14 @@ def extract_stream(self, uri, video=True):

# no extractor available, return raw url
return meta or {"uri": uri}


@lru_cache() # to avoid loading StreamHandler more than once
def load_stream_extractors():
return StreamHandler()


def available_extractors():
xtract = load_stream_extractors()
return ["/", "http:", "https:", "file:"] + \
[f"{sei}//" for sei in xtract.supported_seis]
170 changes: 39 additions & 131 deletions ovos_plugin_manager/templates/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,26 @@
These classes can be used to create an Audioservice plugin extending
OpenVoiceOS's media playback options.
"""
from abc import ABCMeta, abstractmethod
from ovos_bus_client.message import Message

from ovos_plugin_manager.templates.media import AudioPlayerBackend as _AB
from ovos_utils import classproperty
from ovos_utils.fakebus import FakeBus
from ovos_utils.log import log_deprecation
from ovos_utils.ocp import PlaybackType, TrackState
from ovos_utils.process_utils import RuntimeRequirements

log_deprecation("ovos_plugin_manager.templates.audio has been deprecated on ovos-audio, "
"move to ovos_plugin_manager.templates.media", "0.1.0")

class AudioBackend(metaclass=ABCMeta):

class AudioBackend(_AB):
"""Base class for all audio backend implementations.

Arguments:
config (dict): configuration dict for the instance
bus (MessageBusClient): OpenVoiceOS messagebus emitter
"""

def __init__(self, config=None, bus=None):
self._track_start_callback = None
self.supports_mime_hints = False
self.config = config or {}
self.bus = bus or FakeBus()

@classproperty
def runtime_requirements(self):
""" skill developers should override this if they do not require connectivity
Expand Down Expand Up @@ -59,143 +58,52 @@ def runtime_requirements(self):
no_internet_fallback=True,
no_network_fallback=True)

@property
def playback_time(self):
return 0

@abstractmethod
def supported_uris(self):
"""List of supported uri types.

Returns:
list: Supported uri's
"""

@abstractmethod
# methods below deprecated and handled by OCP directly
# playlists are no longer managed plugin side
# this is just a compat layer forwarding commands to OCP
def clear_list(self):
"""Clear playlist."""
msg = Message('ovos.common_play.playlist.clear')
self.bus.emit(msg)

@abstractmethod
def add_list(self, tracks):
"""Add tracks to backend's playlist.

Arguments:
tracks (list): list of tracks.
"""

@abstractmethod
def play(self, repeat=False):
"""Start playback.

Starts playing the first track in the playlist and will contiune
until all tracks have been played.

Arguments:
repeat (bool): Repeat playlist, defaults to False
"""

@abstractmethod
def stop(self):
"""Stop playback.

Stops the current playback.

Returns:
bool: True if playback was stopped, otherwise False
"""

def set_track_start_callback(self, callback_func):
"""Register callback on track start.

This method should be called as each track in a playlist is started.
"""
self._track_start_callback = callback_func

def pause(self):
"""Pause playback.

Stops playback but may be resumed at the exact position the pause
occured.
"""

def resume(self):
"""Resume paused playback.

Resumes playback after being paused.
"""
tracks = tracks or []
if isinstance(tracks, (str, tuple)):
tracks = [tracks]
elif not isinstance(tracks, list):
raise ValueError
tracks = [self._uri2meta(t) for t in tracks]
msg = Message('ovos.common_play.playlist.queue',
{'tracks': tracks})
self.bus.emit(msg)

@staticmethod
def _uri2meta(uri):
if isinstance(uri, list):
uri = uri[0]
try:
from ovos_ocp_files_plugin.plugin import OCPFilesMetadataExtractor
return OCPFilesMetadataExtractor.extract_metadata(uri)
except:
meta = {"uri": uri,
"skill_id": "mycroft.audio_interface",
"playback": PlaybackType.AUDIO, # TODO mime type check
"status": TrackState.QUEUED_AUDIO,
}
return meta

def next(self):
"""Skip to next track in playlist."""
self.bus.emit(Message("ovos.common_play.next"))

def previous(self):
"""Skip to previous track in playlist."""

def lower_volume(self):
"""Lower volume.

This method is used to implement audio ducking. It will be called when
OpenVoiceOS is listening or speaking to make sure the media playing isn't
interfering.
"""

def restore_volume(self):
"""Restore normal volume.

Called when to restore the playback volume to previous level after
OpenVoiceOS has lowered it using lower_volume().
"""

def get_track_length(self):
"""
getting the duration of the audio in milliseconds
NOTE: not yet supported by mycroft-core
"""

def get_track_position(self):
"""
get current position in milliseconds
NOTE: not yet supported by mycroft-core
"""

def set_track_position(self, milliseconds):
"""
go to position in milliseconds
NOTE: not yet supported by mycroft-core
Args:
milliseconds (int): number of milliseconds of final position
"""

def seek_forward(self, seconds=1):
"""Skip X seconds.

Arguments:
seconds (int): number of seconds to seek, if negative rewind
"""

def seek_backward(self, seconds=1):
"""Rewind X seconds.

Arguments:
seconds (int): number of seconds to seek, if negative jump forward.
"""

def track_info(self):
"""Get info about current playing track.

Returns:
dict: Track info containing atleast the keys artist and album.
"""
ret = {}
ret['artist'] = ''
ret['album'] = ''
return ret

def shutdown(self):
"""Perform clean shutdown.

Implements any audio backend specific shutdown procedures.
"""
self.stop()
self.bus.emit(Message("ovos.common_play.previous"))


class RemoteAudioBackend(AudioBackend):
Expand Down
Loading
Loading