2023-11-04 16:09:35 +01:00
|
|
|
from abc import ABC, abstractmethod
|
|
|
|
import hashlib
|
2019-06-21 02:13:14 +02:00
|
|
|
import logging
|
2023-11-12 03:08:54 +01:00
|
|
|
import os
|
2023-11-05 01:33:36 +01:00
|
|
|
from typing import Generator, Optional
|
2019-06-21 02:13:14 +02:00
|
|
|
|
2023-11-04 16:09:35 +01:00
|
|
|
from platypush.message import JSONAble
|
2021-09-16 17:53:40 +02:00
|
|
|
|
2023-11-04 16:09:35 +01:00
|
|
|
|
|
|
|
class MediaHandler(JSONAble, ABC):
|
2019-02-07 14:26:10 +01:00
|
|
|
"""
|
|
|
|
Abstract class to manage media handlers that can be streamed over the HTTP
|
|
|
|
server through the `/media` endpoint.
|
|
|
|
"""
|
|
|
|
|
|
|
|
prefix_handlers = []
|
|
|
|
|
2023-11-04 16:09:35 +01:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
source: str,
|
|
|
|
*_,
|
|
|
|
filename: Optional[str] = None,
|
|
|
|
mime_type: str = 'application/octet-stream',
|
|
|
|
name: Optional[str] = None,
|
|
|
|
url: Optional[str] = None,
|
|
|
|
subtitles: Optional[str] = None,
|
|
|
|
**__,
|
|
|
|
):
|
|
|
|
matched_handlers = [
|
|
|
|
hndl for hndl in self.prefix_handlers if source.startswith(hndl)
|
|
|
|
]
|
2019-02-07 14:26:10 +01:00
|
|
|
|
|
|
|
if not matched_handlers:
|
2023-11-04 16:09:35 +01:00
|
|
|
raise AttributeError(
|
|
|
|
f'No matched handlers found for source "{source}" '
|
|
|
|
f'through {self.__class__.__name__}. Supported handlers: {self.prefix_handlers}'
|
|
|
|
)
|
2019-02-07 14:26:10 +01:00
|
|
|
|
|
|
|
self.name = name
|
2019-02-11 18:46:25 +01:00
|
|
|
self.path = None
|
2019-06-21 02:13:14 +02:00
|
|
|
self.filename = filename
|
2019-02-07 14:26:10 +01:00
|
|
|
self.source = source
|
|
|
|
self.url = url
|
|
|
|
self.mime_type = mime_type
|
2019-02-11 18:46:25 +01:00
|
|
|
self.subtitles = subtitles
|
2019-02-07 14:26:10 +01:00
|
|
|
self.content_length = 0
|
|
|
|
self._matched_handler = matched_handlers[0]
|
|
|
|
|
|
|
|
@classmethod
|
2023-11-04 16:09:35 +01:00
|
|
|
def build(cls, source: str, *args, **kwargs) -> 'MediaHandler':
|
2019-02-07 14:26:10 +01:00
|
|
|
errors = {}
|
|
|
|
|
|
|
|
for hndl_class in supported_handlers:
|
|
|
|
try:
|
|
|
|
return hndl_class(source, *args, **kwargs)
|
|
|
|
except Exception as e:
|
2019-06-21 02:13:14 +02:00
|
|
|
logging.exception(e)
|
2019-02-07 14:26:10 +01:00
|
|
|
errors[hndl_class.__name__] = str(e)
|
|
|
|
|
2023-11-12 03:08:54 +01:00
|
|
|
if os.path.exists(source):
|
|
|
|
source = f'file://{source}'
|
|
|
|
|
2023-11-04 16:09:35 +01:00
|
|
|
raise AttributeError(
|
|
|
|
f'The source {source} has no handlers associated. Errors: {errors}'
|
|
|
|
)
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get_data(
|
|
|
|
self,
|
|
|
|
from_bytes: Optional[int] = None,
|
|
|
|
to_bytes: Optional[int] = None,
|
|
|
|
chunk_size: Optional[int] = None,
|
2023-11-05 01:33:36 +01:00
|
|
|
) -> Generator[bytes, None, None]:
|
2019-02-07 14:26:10 +01:00
|
|
|
raise NotImplementedError()
|
|
|
|
|
2023-11-04 16:09:35 +01:00
|
|
|
@property
|
|
|
|
def media_id(self) -> str:
|
|
|
|
"""
|
|
|
|
:returns: The unique ID of the media handler.
|
|
|
|
"""
|
|
|
|
return self.get_media_id(self.source)
|
|
|
|
|
|
|
|
def set_subtitles(self, subtitles_file: Optional[str]):
|
2019-02-11 18:46:25 +01:00
|
|
|
self.subtitles = subtitles_file
|
|
|
|
|
|
|
|
def remove_subtitles(self):
|
|
|
|
self.subtitles = None
|
|
|
|
|
2019-02-07 14:26:10 +01:00
|
|
|
def __iter__(self):
|
2023-11-04 16:09:35 +01:00
|
|
|
"""
|
|
|
|
Iterate over the attributes of the media handler.
|
|
|
|
"""
|
|
|
|
for attr in [
|
|
|
|
'name',
|
|
|
|
'source',
|
|
|
|
'mime_type',
|
|
|
|
'url',
|
|
|
|
'subtitles',
|
|
|
|
'prefix_handlers',
|
|
|
|
'media_id',
|
|
|
|
]:
|
2019-02-12 01:30:55 +01:00
|
|
|
if hasattr(self, attr):
|
2021-09-16 17:53:40 +02:00
|
|
|
yield attr, getattr(self, attr)
|
2019-02-07 14:26:10 +01:00
|
|
|
|
2023-11-04 16:09:35 +01:00
|
|
|
@staticmethod
|
|
|
|
def get_media_id(source: str) -> str:
|
|
|
|
"""
|
|
|
|
:returns: The ID of a media file given its source.
|
|
|
|
"""
|
|
|
|
return hashlib.sha1(source.encode()).hexdigest()
|
|
|
|
|
|
|
|
def to_json(self):
|
|
|
|
"""
|
|
|
|
:returns: A dictionary representation of the media handler.
|
|
|
|
"""
|
|
|
|
return dict(self)
|
|
|
|
|
2019-02-07 14:26:10 +01:00
|
|
|
|
2023-11-04 16:09:35 +01:00
|
|
|
from .file import FileHandler # noqa
|
2019-06-22 19:05:17 +02:00
|
|
|
|
2019-02-07 14:26:10 +01:00
|
|
|
__all__ = ['MediaHandler', 'FileHandler']
|
|
|
|
|
|
|
|
|
2023-11-04 16:09:35 +01:00
|
|
|
supported_handlers = [eval(hndl) for hndl in __all__ if hndl != MediaHandler.__name__]
|
2019-02-07 14:26:10 +01:00
|
|
|
|
|
|
|
|
|
|
|
# vim:sw=4:ts=4:et:
|