forked from platypush/platypush
Merge branch 'master' into snyk-fix-5ef3afe3fbfad34ca892e17f8d68fd7a
This commit is contained in:
commit
53817413a4
713 changed files with 20493 additions and 5366 deletions
|
@ -6,9 +6,18 @@
|
||||||
ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null
|
ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null
|
||||||
|
|
||||||
# Clone the repository
|
# Clone the repository
|
||||||
|
branch=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
if [ -z "${branch}" ]; then
|
||||||
|
echo "No branch checked out"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
git remote add github git@github.com:/blacklight/platypush.git
|
git remote add github git@github.com:/blacklight/platypush.git
|
||||||
git pull --rebase github "$(git branch | head -1 | awk '{print $2}')" || echo "No such branch on Github"
|
|
||||||
|
if (( "$branch" == "master" )); then
|
||||||
|
git pull --rebase github "${branch}" || echo "No such branch on Github"
|
||||||
|
fi
|
||||||
|
|
||||||
# Push the changes to the GitHub mirror
|
# Push the changes to the GitHub mirror
|
||||||
git push --all -v github
|
git push -f --all -v github
|
||||||
git push --tags -v github
|
git push --tags -v github
|
||||||
|
|
1
.ignore
Normal file
1
.ignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
dist/
|
60
CHANGELOG.md
60
CHANGELOG.md
|
@ -1,5 +1,65 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.3.1]
|
||||||
|
|
||||||
|
- [[#344](https://git.platypush.tech/platypush/platypush/issues/344)]: removed
|
||||||
|
`marshmallow_dataclass` dependency. That package isn't included in the
|
||||||
|
package managers of any supported distros and requires to be installed via
|
||||||
|
pip. Making the Platypush' system packages depend on a pip-only package is
|
||||||
|
not a good idea. Plus, the library seems to be still in heavy development and
|
||||||
|
it has already broken compatibility with at least the `system` package.
|
||||||
|
|
||||||
|
## [1.3.0]
|
||||||
|
|
||||||
|
- [[#333](https://git.platypush.tech/platypush/platypush/issues/333)]: new file
|
||||||
|
browser UI/component. It includes custom MIME type support, a file editor
|
||||||
|
with syntax highlight, file download and file upload.
|
||||||
|
|
||||||
|
- [[#341](https://git.platypush.tech/platypush/platypush/issues/341)]:
|
||||||
|
procedures are now native entities that can be managed from the entities panel.
|
||||||
|
A new versatile procedure editor has also been added, with support for nested
|
||||||
|
blocks, conditions, loops, variables, context autocomplete, and more.
|
||||||
|
|
||||||
|
- [`procedure`]: Added the following features to YAML/structured procedures:
|
||||||
|
|
||||||
|
- `set`: to set variables whose scope is limited to the procedure / code
|
||||||
|
block where they are created. `variable.set` is useful to permanently
|
||||||
|
store variables on the db, `variable.mset` is useful to set temporary
|
||||||
|
global variables in memory through Redis, but sometimes you may just want
|
||||||
|
to assign a value to a variable that only needs to live within a procedure,
|
||||||
|
event hook or cron.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- set:
|
||||||
|
foo: bar
|
||||||
|
temperature: ${output.get('temperature')}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `return` can now return values too when invoked within a procedure:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- return: something
|
||||||
|
# Or
|
||||||
|
- return: "Result: ${output.get('response')}"
|
||||||
|
```
|
||||||
|
|
||||||
|
- The default logging format is now much more compact. The full body of events
|
||||||
|
and requests is no longer included by default in `info` mode - instead, a
|
||||||
|
summary with the message type, ID and response time is logged. The full
|
||||||
|
payloads can still be logged by enabling `debug` logs through e.g. `-v`.
|
||||||
|
|
||||||
|
## [1.2.3]
|
||||||
|
|
||||||
|
- [[#422](https://git.platypush.tech/platypush/platypush/issues/422)]: adapted
|
||||||
|
media plugins to support streaming from the yt-dlp process. This allows
|
||||||
|
videos to have merged audio+video even if they had separate tracks upstream.
|
||||||
|
|
||||||
|
- [`media.*`] Many improvements on the media UI.
|
||||||
|
|
||||||
|
- [`zigbee.mqtt`] Removed synchronous logic from `zigbee.mqtt.device_set`. It
|
||||||
|
was prone to timeouts as well as pointless - the updated device state will
|
||||||
|
anyway be received as an event.
|
||||||
|
|
||||||
## [1.2.2]
|
## [1.2.2]
|
||||||
|
|
||||||
- Fixed regression on older version of Python that don't fully support
|
- Fixed regression on older version of Python that don't fully support
|
||||||
|
|
|
@ -1,24 +1,9 @@
|
||||||
services:
|
services:
|
||||||
platypush:
|
platypush:
|
||||||
restart: "always"
|
# Replace the build section with the next line if instead of building the
|
||||||
command:
|
# image from a local checkout you want to pull the latest base
|
||||||
- platypush
|
# (Alpine-based) image from the remote registry
|
||||||
# Comment --start-redis if you want to run an external Redis service
|
# image: "registry.platypush.tech/platypush:latest"
|
||||||
# In such case you'll also have to ensure that the appropriate Redis
|
|
||||||
# variables are set in the .env file, or the Redis configuration is
|
|
||||||
# passed in the config.yaml, or use the --redis-host and --redis-port
|
|
||||||
# command-line options
|
|
||||||
- --start-redis
|
|
||||||
|
|
||||||
# Custom list of host devices that should be accessible to the container -
|
|
||||||
# e.g. an Arduino, an ESP-compatible microcontroller, a joystick etc.
|
|
||||||
# devices:
|
|
||||||
# - /dev/ttyUSB0
|
|
||||||
|
|
||||||
# Uncomment if you need plugins that require access to low-level hardware
|
|
||||||
# (e.g. Bluetooth BLE or GPIO/SPI/I2C) if access to individual devices is
|
|
||||||
# not enough or isn't practical
|
|
||||||
# privileged: true
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
|
@ -31,6 +16,25 @@ services:
|
||||||
# Fedora base image
|
# Fedora base image
|
||||||
# dockerfile: ./platypush/install/docker/fedora.Dockerfile
|
# dockerfile: ./platypush/install/docker/fedora.Dockerfile
|
||||||
|
|
||||||
|
restart: "always"
|
||||||
|
command:
|
||||||
|
- platypush
|
||||||
|
- --redis-host
|
||||||
|
- redis
|
||||||
|
# Or, if you want to run Redis from the same container as Platypush,
|
||||||
|
# replace --redis-host redis with the line below
|
||||||
|
# - --start-redis
|
||||||
|
|
||||||
|
# Custom list of host devices that should be accessible to the container -
|
||||||
|
# e.g. an Arduino, an ESP-compatible microcontroller, a joystick etc.
|
||||||
|
# devices:
|
||||||
|
# - /dev/ttyUSB0
|
||||||
|
|
||||||
|
# Uncomment if you need plugins that require access to low-level hardware
|
||||||
|
# (e.g. Bluetooth BLE or GPIO/SPI/I2C) if access to individual devices is
|
||||||
|
# not enough or isn't practical
|
||||||
|
# privileged: true
|
||||||
|
|
||||||
# Copy .env.example to .env and modify as needed
|
# Copy .env.example to .env and modify as needed
|
||||||
# env_file:
|
# env_file:
|
||||||
# - .env
|
# - .env
|
||||||
|
@ -40,7 +44,13 @@ services:
|
||||||
# expose it
|
# expose it
|
||||||
- "8008:8008"
|
- "8008:8008"
|
||||||
|
|
||||||
volumes:
|
# volumes:
|
||||||
- /path/to/your/config.yaml:/etc/platypush
|
# Replace with a path that contains/will contain your config.yaml file
|
||||||
- /path/to/a/workdir:/var/lib/platypush
|
# - /path/to/your/config:/etc/platypush
|
||||||
|
# Replace with a path that contains/will contain your working directory
|
||||||
|
# - /path/to/a/workdir:/var/lib/platypush
|
||||||
|
# Optionally, use an external volume for the cache
|
||||||
# - /path/to/a/cachedir:/var/cache/platypush
|
# - /path/to/a/cachedir:/var/cache/platypush
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
``media.omxplayer``
|
|
||||||
=====================================
|
|
||||||
|
|
||||||
.. automodule:: platypush.plugins.media.omxplayer
|
|
||||||
:members:
|
|
||||||
|
|
5
docs/source/platypush/plugins/procedures.rst
Normal file
5
docs/source/platypush/plugins/procedures.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``procedures``
|
||||||
|
==============
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.procedures
|
||||||
|
:members:
|
|
@ -77,7 +77,6 @@ Plugins
|
||||||
platypush/plugins/media.kodi.rst
|
platypush/plugins/media.kodi.rst
|
||||||
platypush/plugins/media.mplayer.rst
|
platypush/plugins/media.mplayer.rst
|
||||||
platypush/plugins/media.mpv.rst
|
platypush/plugins/media.mpv.rst
|
||||||
platypush/plugins/media.omxplayer.rst
|
|
||||||
platypush/plugins/media.plex.rst
|
platypush/plugins/media.plex.rst
|
||||||
platypush/plugins/media.subtitles.rst
|
platypush/plugins/media.subtitles.rst
|
||||||
platypush/plugins/media.vlc.rst
|
platypush/plugins/media.vlc.rst
|
||||||
|
@ -100,6 +99,7 @@ Plugins
|
||||||
platypush/plugins/otp.rst
|
platypush/plugins/otp.rst
|
||||||
platypush/plugins/pihole.rst
|
platypush/plugins/pihole.rst
|
||||||
platypush/plugins/ping.rst
|
platypush/plugins/ping.rst
|
||||||
|
platypush/plugins/procedures.rst
|
||||||
platypush/plugins/pushbullet.rst
|
platypush/plugins/pushbullet.rst
|
||||||
platypush/plugins/pwm.pca9685.rst
|
platypush/plugins/pwm.pca9685.rst
|
||||||
platypush/plugins/qrcode.rst
|
platypush/plugins/qrcode.rst
|
||||||
|
|
|
@ -21,7 +21,7 @@ from .utils import run
|
||||||
# see https://git.platypush.tech/platypush/platypush/issues/399
|
# see https://git.platypush.tech/platypush/platypush/issues/399
|
||||||
when = hook
|
when = hook
|
||||||
|
|
||||||
__version__ = '1.2.2'
|
__version__ = '1.3.1'
|
||||||
__author__ = 'Fabio Manganiello <fabio@manganiello.tech>'
|
__author__ = 'Fabio Manganiello <fabio@manganiello.tech>'
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'Application',
|
'Application',
|
||||||
|
|
|
@ -365,7 +365,13 @@ class Application:
|
||||||
elif isinstance(msg, Response):
|
elif isinstance(msg, Response):
|
||||||
msg.log()
|
msg.log()
|
||||||
elif isinstance(msg, Event):
|
elif isinstance(msg, Event):
|
||||||
msg.log()
|
log.info(
|
||||||
|
'Received event: %s.%s[id=%s]',
|
||||||
|
msg.__class__.__module__,
|
||||||
|
msg.__class__.__name__,
|
||||||
|
msg.id,
|
||||||
|
)
|
||||||
|
msg.log(level=logging.DEBUG)
|
||||||
self.event_processor.process_event(msg)
|
self.event_processor.process_event(msg)
|
||||||
|
|
||||||
return _f
|
return _f
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import os
|
import os
|
||||||
|
import pathlib
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from datetime import datetime as dt
|
from datetime import datetime as dt
|
||||||
from typing import Optional, Tuple
|
from typing import IO, Optional, Tuple
|
||||||
|
|
||||||
from tornado.web import stream_request_body
|
from tornado.web import stream_request_body
|
||||||
|
|
||||||
|
@ -17,6 +18,8 @@ class FileRoute(StreamingRoute):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
BUFSIZE = 1024
|
BUFSIZE = 1024
|
||||||
|
_bytes_written = 0
|
||||||
|
_out_f: Optional[IO[bytes]] = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def path(cls) -> str:
|
def path(cls) -> str:
|
||||||
|
@ -39,6 +42,10 @@ class FileRoute(StreamingRoute):
|
||||||
def file_size(self) -> int:
|
def file_size(self) -> int:
|
||||||
return os.path.getsize(self.file_path)
|
return os.path.getsize(self.file_path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _content_length(self) -> int:
|
||||||
|
return int(self.request.headers.get('Content-Length', 0))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def range(self) -> Tuple[Optional[int], Optional[int]]:
|
def range(self) -> Tuple[Optional[int], Optional[int]]:
|
||||||
range_hdr = self.request.headers.get('Range')
|
range_hdr = self.request.headers.get('Range')
|
||||||
|
@ -105,6 +112,77 @@ class FileRoute(StreamingRoute):
|
||||||
|
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
|
def on_finish(self) -> None:
|
||||||
|
if self._out_f:
|
||||||
|
try:
|
||||||
|
if not (self._out_f and self._out_f.closed):
|
||||||
|
self._out_f.close()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.warning('Error while closing the output file: %s', e)
|
||||||
|
|
||||||
|
self._out_f = None
|
||||||
|
|
||||||
|
return super().on_finish()
|
||||||
|
|
||||||
|
def _validate_upload(self, force: bool = False) -> bool:
|
||||||
|
if not self.file_path:
|
||||||
|
self.write_error(400, 'Missing path argument')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._out_f:
|
||||||
|
if not force and os.path.exists(self.file_path):
|
||||||
|
self.write_error(409, f'{self.file_path} already exists')
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._bytes_written = 0
|
||||||
|
dir_path = os.path.dirname(self.file_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
pathlib.Path(dir_path).mkdir(parents=True, exist_ok=True)
|
||||||
|
self._out_f = open( # pylint: disable=consider-using-with
|
||||||
|
self.file_path, 'wb'
|
||||||
|
)
|
||||||
|
except PermissionError:
|
||||||
|
self.write_error(403, 'Permission denied')
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def finish(self, *args, **kwargs): # type: ignore
|
||||||
|
try:
|
||||||
|
return super().finish(*args, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.warning('Error while finishing the request: %s', e)
|
||||||
|
|
||||||
|
def data_received(self, chunk: bytes):
|
||||||
|
# Ignore unless we're in POST/PUT mode
|
||||||
|
if self.request.method not in ('POST', 'PUT'):
|
||||||
|
return
|
||||||
|
|
||||||
|
force = self.request.method == 'PUT'
|
||||||
|
if not self._validate_upload(force=force):
|
||||||
|
self.finish()
|
||||||
|
return
|
||||||
|
|
||||||
|
if not chunk:
|
||||||
|
self.logger.debug('Received EOF from client')
|
||||||
|
self.finish()
|
||||||
|
return
|
||||||
|
|
||||||
|
assert self._out_f
|
||||||
|
self._out_f.write(chunk)
|
||||||
|
self._out_f.flush()
|
||||||
|
self._bytes_written += len(chunk)
|
||||||
|
self.logger.debug(
|
||||||
|
'Written chunk of size %d to %s, progress: %d/%d',
|
||||||
|
len(chunk),
|
||||||
|
self.file_path,
|
||||||
|
self._bytes_written,
|
||||||
|
self._content_length,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.flush()
|
||||||
|
|
||||||
def get(self) -> None:
|
def get(self) -> None:
|
||||||
with self._serve() as f:
|
with self._serve() as f:
|
||||||
if f:
|
if f:
|
||||||
|
@ -119,3 +197,9 @@ class FileRoute(StreamingRoute):
|
||||||
def head(self) -> None:
|
def head(self) -> None:
|
||||||
with self._serve():
|
with self._serve():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def post(self) -> None:
|
||||||
|
self.logger.info('Receiving file POST upload request for %r', self.file_path)
|
||||||
|
|
||||||
|
def put(self) -> None:
|
||||||
|
self.logger.info('Receiving file PUT upload request for %r', self.file_path)
|
||||||
|
|
|
@ -3,7 +3,9 @@ from typing import Optional
|
||||||
from platypush.backend.http.app.utils import logger, send_request
|
from platypush.backend.http.app.utils import logger, send_request
|
||||||
from platypush.backend.http.media.handlers import MediaHandler
|
from platypush.backend.http.media.handlers import MediaHandler
|
||||||
|
|
||||||
from ._registry import load_media_map, save_media_map
|
from ._registry import clear_media_map, load_media_map, save_media_map
|
||||||
|
|
||||||
|
_init = False
|
||||||
|
|
||||||
|
|
||||||
def get_media_url(media_id: str) -> str:
|
def get_media_url(media_id: str) -> str:
|
||||||
|
@ -17,6 +19,12 @@ def register_media(source: str, subtitles: Optional[str] = None) -> MediaHandler
|
||||||
"""
|
"""
|
||||||
Registers a media file and returns its associated media handler.
|
Registers a media file and returns its associated media handler.
|
||||||
"""
|
"""
|
||||||
|
global _init
|
||||||
|
|
||||||
|
if not _init:
|
||||||
|
clear_media_map()
|
||||||
|
_init = True
|
||||||
|
|
||||||
media_id = MediaHandler.get_media_id(source)
|
media_id = MediaHandler.get_media_id(source)
|
||||||
media_url = get_media_url(media_id)
|
media_url = get_media_url(media_id)
|
||||||
media_map = load_media_map()
|
media_map = load_media_map()
|
||||||
|
|
|
@ -25,10 +25,15 @@ def load_media_map() -> MediaMap:
|
||||||
logger().warning('Could not load media map: %s', e)
|
logger().warning('Could not load media map: %s', e)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
return {
|
parsed_map = {}
|
||||||
media_id: MediaHandler.build(**media_info)
|
for media_id, media_info in media_map.items():
|
||||||
for media_id, media_info in media_map.items()
|
try:
|
||||||
}
|
parsed_map[media_id] = MediaHandler.build(**media_info)
|
||||||
|
except Exception as e:
|
||||||
|
logger().debug('Could not load media %s: %s', media_id, e)
|
||||||
|
continue
|
||||||
|
|
||||||
|
return parsed_map
|
||||||
|
|
||||||
|
|
||||||
def save_media_map(new_map: MediaMap):
|
def save_media_map(new_map: MediaMap):
|
||||||
|
@ -38,3 +43,12 @@ def save_media_map(new_map: MediaMap):
|
||||||
with media_map_lock:
|
with media_map_lock:
|
||||||
redis = get_redis()
|
redis = get_redis()
|
||||||
redis.mset({MEDIA_MAP_VAR: json.dumps(new_map, cls=Message.Encoder)})
|
redis.mset({MEDIA_MAP_VAR: json.dumps(new_map, cls=Message.Encoder)})
|
||||||
|
|
||||||
|
|
||||||
|
def clear_media_map():
|
||||||
|
"""
|
||||||
|
Clears the media map from the server.
|
||||||
|
"""
|
||||||
|
with media_map_lock:
|
||||||
|
redis = get_redis()
|
||||||
|
redis.delete(MEDIA_MAP_VAR)
|
||||||
|
|
|
@ -17,7 +17,7 @@ class MediaStreamRoute(StreamingRoute):
|
||||||
Route for media streams.
|
Route for media streams.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SUPPORTED_METHODS = ['GET', 'PUT', 'DELETE']
|
SUPPORTED_METHODS = ['GET', 'HEAD', 'PUT', 'DELETE']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
@ -50,6 +50,23 @@ class MediaStreamRoute(StreamingRoute):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._on_error(e)
|
self._on_error(e)
|
||||||
|
|
||||||
|
def head(self, media_id: Optional[str] = None):
|
||||||
|
"""
|
||||||
|
Streams a media resource by ID.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not media_id:
|
||||||
|
self.finish()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Strip the extension
|
||||||
|
media_id = '.'.join(media_id.split('.')[:-1])
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.stream_media(media_id, head=True)
|
||||||
|
except Exception as e:
|
||||||
|
self._on_error(e)
|
||||||
|
|
||||||
def put(self, *_, **__):
|
def put(self, *_, **__):
|
||||||
"""
|
"""
|
||||||
The `PUT` route is used to prepare a new media resource for streaming.
|
The `PUT` route is used to prepare a new media resource for streaming.
|
||||||
|
@ -93,10 +110,10 @@ class MediaStreamRoute(StreamingRoute):
|
||||||
"""
|
"""
|
||||||
Returns the list of registered media resources.
|
Returns the list of registered media resources.
|
||||||
"""
|
"""
|
||||||
self.add_header('Content-Type', 'application/json')
|
self.set_header('Content-Type', 'application/json')
|
||||||
self.finish(json.dumps([dict(media) for media in load_media_map().values()]))
|
self.finish(json.dumps([dict(media) for media in load_media_map().values()]))
|
||||||
|
|
||||||
def stream_media(self, media_id: str):
|
def stream_media(self, media_id: str, head: bool = False):
|
||||||
"""
|
"""
|
||||||
Route to stream a media file given its ID.
|
Route to stream a media file given its ID.
|
||||||
"""
|
"""
|
||||||
|
@ -107,11 +124,11 @@ class MediaStreamRoute(StreamingRoute):
|
||||||
range_hdr = self.request.headers.get('Range')
|
range_hdr = self.request.headers.get('Range')
|
||||||
content_length = media_hndl.content_length
|
content_length = media_hndl.content_length
|
||||||
|
|
||||||
self.add_header('Accept-Ranges', 'bytes')
|
self.set_header('Accept-Ranges', 'bytes')
|
||||||
self.add_header('Content-Type', media_hndl.mime_type)
|
self.set_header('Content-Type', media_hndl.mime_type)
|
||||||
|
|
||||||
if 'download' in self.request.arguments:
|
if 'download' in self.request.arguments:
|
||||||
self.add_header(
|
self.set_header(
|
||||||
'Content-Disposition',
|
'Content-Disposition',
|
||||||
'attachment'
|
'attachment'
|
||||||
+ ('; filename="{media_hndl.filename}"' if media_hndl.filename else ''),
|
+ ('; filename="{media_hndl.filename}"' if media_hndl.filename else ''),
|
||||||
|
@ -129,7 +146,7 @@ class MediaStreamRoute(StreamingRoute):
|
||||||
content_length = to_bytes - from_bytes
|
content_length = to_bytes - from_bytes
|
||||||
|
|
||||||
self.set_status(206)
|
self.set_status(206)
|
||||||
self.add_header(
|
self.set_header(
|
||||||
'Content-Range',
|
'Content-Range',
|
||||||
f'bytes {from_bytes}-{to_bytes}/{media_hndl.content_length}',
|
f'bytes {from_bytes}-{to_bytes}/{media_hndl.content_length}',
|
||||||
)
|
)
|
||||||
|
@ -137,7 +154,13 @@ class MediaStreamRoute(StreamingRoute):
|
||||||
from_bytes = 0
|
from_bytes = 0
|
||||||
to_bytes = STREAMING_BLOCK_SIZE
|
to_bytes = STREAMING_BLOCK_SIZE
|
||||||
|
|
||||||
self.add_header('Content-Length', str(content_length))
|
self.set_header('Content-Length', str(content_length))
|
||||||
|
|
||||||
|
if head:
|
||||||
|
self.flush()
|
||||||
|
self.finish()
|
||||||
|
return
|
||||||
|
|
||||||
for chunk in media_hndl.get_data(
|
for chunk in media_hndl.get_data(
|
||||||
from_bytes=from_bytes,
|
from_bytes=from_bytes,
|
||||||
to_bytes=to_bytes,
|
to_bytes=to_bytes,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
from typing import Generator, Optional
|
from typing import Generator, Optional
|
||||||
|
|
||||||
from platypush.message import JSONAble
|
from platypush.message import JSONAble
|
||||||
|
@ -57,9 +56,6 @@ class MediaHandler(JSONAble, ABC):
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
errors[hndl_class.__name__] = str(e)
|
errors[hndl_class.__name__] = str(e)
|
||||||
|
|
||||||
if os.path.exists(source):
|
|
||||||
source = f'file://{source}'
|
|
||||||
|
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
f'The source {source} has no handlers associated. Errors: {errors}'
|
f'The source {source} has no handlers associated. Errors: {errors}'
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,6 +15,9 @@ class FileHandler(MediaHandler):
|
||||||
prefix_handlers = ['file://']
|
prefix_handlers = ['file://']
|
||||||
|
|
||||||
def __init__(self, source, *args, **kwargs):
|
def __init__(self, source, *args, **kwargs):
|
||||||
|
if isinstance(source, str) and os.path.exists(source):
|
||||||
|
source = f'file://{source}'
|
||||||
|
|
||||||
super().__init__(source, *args, **kwargs)
|
super().__init__(source, *args, **kwargs)
|
||||||
|
|
||||||
self.path = os.path.abspath(
|
self.path = os.path.abspath(
|
||||||
|
@ -33,7 +36,7 @@ class FileHandler(MediaHandler):
|
||||||
), f'{source} is not a valid media file (detected format: {self.mime_type})'
|
), f'{source} is not a valid media file (detected format: {self.mime_type})'
|
||||||
|
|
||||||
self.extension = mimetypes.guess_extension(self.mime_type)
|
self.extension = mimetypes.guess_extension(self.mime_type)
|
||||||
if self.url and self.extension:
|
if self.url and self.extension and not self.url.endswith(self.extension):
|
||||||
self.url += self.extension
|
self.url += self.extension
|
||||||
self.content_length = os.path.getsize(self.path)
|
self.content_length = os.path.getsize(self.path)
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/fonts/poppins.css"><title>platypush</title><script defer="defer" src="/static/js/chunk-vendors.05911ac4.js"></script><script defer="defer" src="/static/js/app.1f786d8c.js"></script><link href="/static/css/chunk-vendors.d510eff2.css" rel="stylesheet"><link href="/static/css/app.d1412c5b.css" rel="stylesheet"><link rel="icon" type="image/svg+xml" href="/img/icons/favicon.svg"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#ffffff"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="Platypush"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#ffffff"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/fonts/poppins.css"><title>platypush</title><script defer="defer" src="/static/js/chunk-vendors.5645dfdc.js"></script><script defer="defer" src="/static/js/app.d90fb573.js"></script><link href="/static/css/chunk-vendors.d510eff2.css" rel="stylesheet"><link href="/static/css/app.70fb1f4a.css" rel="stylesheet"><link rel="icon" type="image/svg+xml" href="/img/icons/favicon.svg"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#ffffff"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="Platypush"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#ffffff"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but platypush doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/1019.af89a8dd.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/1019.af89a8dd.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/1054.1651fcc4.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/1054.1651fcc4.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/1421.1a42ddca.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/1421.1a42ddca.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/1449.9ddbde9a.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/1449.9ddbde9a.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/2029.66acebb6.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/2029.66acebb6.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/2140.57230853.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/2140.57230853.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/215.91074688.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/215.91074688.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/2694.515bb415.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/2694.515bb415.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/2718.5a080a62.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/2718.5a080a62.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/2764.7b323478.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/2764.7b323478.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/2831.09fe1331.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/2831.09fe1331.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/293.521a4f1c.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/293.521a4f1c.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/3248.a0e1e73b.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/3248.a0e1e73b.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/3426.50cde06e.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/3426.50cde06e.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/38.b93403c3.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/38.b93403c3.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/3865.8b16d712.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/3865.8b16d712.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/4015.b27ff6b3.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/4015.b27ff6b3.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/4106.2a5e087e.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/4106.2a5e087e.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/4339.10e2638e.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/4339.10e2638e.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/4364.460ea7ea.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/4364.460ea7ea.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/4470.aa130b90.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/4470.aa130b90.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/4795.708edd2b.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/4795.708edd2b.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/5053.af8a2a60.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/5053.af8a2a60.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/5086.1debab08.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/5086.1debab08.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/5144.58c91f04.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/5144.58c91f04.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/5167.27f1bcef.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/5167.27f1bcef.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/5285.9219493c.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/5285.9219493c.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/557.f2c6f156.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/557.f2c6f156.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/css/6429.44836618.css
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/css/6429.44836618.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue