Compare commits

...

47 commits

Author SHA1 Message Date
06781cd72c
[#341] Backend implementation of the new procedure entities architecture. 2024-08-25 16:06:56 +02:00
24f7d4a789
Merge branch 'master' into 341/procedure-entities 2024-08-25 14:29:43 +02:00
c788f2d858
[Automatic] Updated components cache
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-25 01:25:37 +00:00
f6b1f92a88
🐛 file.bookmarks must be optional.
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-25 03:24:02 +02:00
9f8fe60cdf
An empty commit to re-trigger the CI/CD pipelines
Some checks failed
continuous-integration/drone/push Build is failing
2024-08-25 03:20:38 +02:00
83d21d3f04
[media] Media played from live streams should be at least 5MB before playback starts.
Some checks failed
continuous-integration/drone/push Build is failing
2024-08-25 03:16:59 +02:00
54a6b34a64
[file] Added support for UI bookmarks on the file plugin. 2024-08-25 03:16:59 +02:00
377b2c2425
[Automatic] Updated UI files
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-25 01:15:10 +00:00
a152b0d734
[#333] Added file browser UI panel.
All checks were successful
continuous-integration/drone/push Build is passing
Closes: #333
2024-08-25 03:13:05 +02:00
496dfdb50b
[Media UI] Adapted media browser to the new file browser plugin. 2024-08-25 03:13:04 +02:00
9493445af6
[Media UI] Renamed play-cache event to play-with-opts.
As we're likely to add more play options in the future, this approach is
much more scalable.
2024-08-25 03:13:04 +02:00
0657c80a5c
[#333] Enhanced file browser component.
- Added support for file/directory add/copy/move/rename/remove
  operations.

- Added automatic detection of MIME types.

- Added support for file view/download.

- Added file uploader component.

- Added custom sorting and other visualization options.

- Added custom `Home` component to show configurable bookmarks above the
  filesystem root level.

- Added file editor with automatic syntax highlight.
2024-08-25 03:13:04 +02:00
e672a7fb5c
[Media UI] Always normalize the duration field to float. 2024-08-25 03:13:04 +02:00
e8acf8615f
[UI] Added disabled property to FloatingButton. 2024-08-25 03:13:04 +02:00
336cb18cb3
[UI] New features for the Modal element.
- Added `uppercase` property (default: true) for the modal title. This
  makes it possible to override the default case of the modal title.

- Added support for custom buttons in the modal titlebar.
2024-08-25 03:13:03 +02:00
342df0eeec
[UI] Added common disabled style to buttons. 2024-08-25 03:13:03 +02:00
e6a358fe27
[UI] Added quick String.hashCode function.
This is needed in several places in the code where we need to compare if
two strings differ, but either the strings are too long (e.g. content of
large files) or we don't want to pass the original values (e.g.
credentials, session tokens etc.).
2024-08-25 03:13:03 +02:00
818f60a468
[UI] Better parsing of the parameter types in getUrlArgs and setUrlArgs. 2024-08-25 03:13:03 +02:00
db34a607e4
[UI] Improvements to the Dropdown element.
- Added `style` property to pass static style rules to the dropdown
  body.

- Better positioning of the dropdown when the resulting body is too long
  and may overflow the top of the screen - in that case, the dropdown
  position needs to be maximized at zero.
2024-08-25 03:13:03 +02:00
8f2e68f0db
[UI] Added visible property to ConfirmDialog element. 2024-08-25 03:13:02 +02:00
8b3c2a8ee1
[UI] Updated highlight.js dependency 2024-08-25 03:13:02 +02:00
92bff4decb
[Automatic] Updated components cache
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-24 22:13:00 +00:00
0bb264792e
[file] Added file.copy and file.move actions.
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-25 00:11:38 +02:00
a5426ede58
[file] Added recursive option to file.rmdir. 2024-08-25 00:11:38 +02:00
2c481c54af
[file] Added POST/PUT /file endpoints. 2024-08-25 00:11:38 +02:00
0010342fb7
Get the original MIME type for symlinks.
If the target resource is a symbolic link, then `get_mime_type` should
retrieve the MIME type of the linked resource.
2024-08-25 00:11:38 +02:00
1e9418b072
[file] file.list, file.is_binary and file.get_user_home actions.
- Added `sort` and `reverse` arguments to `file.list`.

- Added `file.is_binary` and `file.get_user_home` actions.
2024-08-25 00:11:37 +02:00
213498318f
[media] Support extended format/metadata for media dirs. 2024-08-25 00:11:37 +02:00
077e12e9a8
[media] Allow media_dirs to be either a list or a dict.
This allows the user to have some user-friendly names for their
collections on the UI, such as `Movies` instead of
`/mnt/hd/media/movies`.
2024-08-25 00:11:37 +02:00
897e8a9ff7
[Automatic] Updated components cache
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-19 01:08:49 +00:00
b439b8b0f4
[file] Better implementation of file.get_mime_types.
All checks were successful
continuous-integration/drone/push Build is passing
- The MIME magic functions apparently aren't thread safe, and they may
  crash the interpreter if called concurrently. Lock calls to
  `file.get_mime_types`.

- Added an LRU cache for the MIME type results.
2024-08-19 03:07:34 +02:00
f1c640fabb
[Automatic] Updated components cache
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-19 00:14:32 +00:00
666bbe5372
Removed media.omxplayer reference.
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-19 02:11:40 +02:00
9dfb22c23a
[file] Added file.info and file.get_mime_types actions. 2024-08-19 02:10:51 +02:00
2b48edfabc
[media.vlc] Prevent deadlock on media.vlc.quit.
`_on_stop_event` may be set by the callback, but then cleared again when
`_reset_state` is called.

This can result in the `_on_stop_event.wait` call in `quit` to time out.

Instead, `_on_stop_event` should be cleared only when the player goes
into `playing` or `paused` mode. It's only then that we know for sure
that the state isn't `stopped`, and only in that case it makes sense to
wait for a stop.
2024-08-19 02:02:21 +02:00
6e27c9b8e4
Bump version: 1.2.2 → 1.2.3
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-18 15:22:02 +02:00
74a2958ff4
Updated CHANGELOG 2024-08-18 15:22:02 +02:00
8333cc09ee
[Automatic] Updated UI files
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-18 11:05:26 +00:00
01571e2e65
[UI] Many improvements for the media UI.
All checks were successful
continuous-integration/drone/push Build is passing
- Support for _Play_ / _Play (With Cache)_ options for YouTube videos.

- Added `media.chromecast` and `media.gstreamer` UI panels.

- Removed `media.omxplayer` - the plugin has been removed.

- Enriched and improved the media info component.

- Propagate the media loading state to all children components.

- Persist query/search state on the URL.

Closes: #422
2024-08-18 13:03:04 +02:00
a21aaee888
[Automatic] Updated components cache
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-18 10:58:02 +00:00
5080caa38e [#422] Enabled support for yt-dlp mux+transcoding in media plugins
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #423
2024-08-18 12:56:47 +02:00
ca5853cbab
[Automatic] Updated UI files
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-15 17:58:53 +00:00
1f120b167b
Merge pull request #444 from blacklight/dependabot/npm_and_yarn/platypush/backend/http/webapp/axios-1.7.4
All checks were successful
continuous-integration/drone/push Build is passing
Bump axios from 1.7.1 to 1.7.4 in /platypush/backend/http/webapp
2024-08-15 19:54:58 +02:00
dependabot[bot]
09412acba7
Bump axios from 1.7.1 to 1.7.4 in /platypush/backend/http/webapp
Bumps [axios](https://github.com/axios/axios) from 1.7.1 to 1.7.4.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.7.1...v1.7.4)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-15 17:54:22 +00:00
e0ff180fb0
[Automatic] Updated components cache
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-13 11:41:39 +00:00
1189e71539 Added merge_output_format option to media plugins
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-13 13:40:15 +02:00
50beb1460b
[Automatic] Updated components cache
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-12 07:45:30 +00:00
462 changed files with 6454 additions and 2239 deletions

View file

@ -1,5 +1,17 @@
# Changelog # Changelog
## [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

View file

@ -1,6 +0,0 @@
``media.omxplayer``
=====================================
.. automodule:: platypush.plugins.media.omxplayer
:members:

View file

@ -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

View file

@ -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.2.3'
__author__ = 'Fabio Manganiello <fabio@manganiello.tech>' __author__ = 'Fabio Manganiello <fabio@manganiello.tech>'
__all__ = [ __all__ = [
'Application', 'Application',

View file

@ -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)

View file

@ -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):

View file

@ -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}'
) )

View file

@ -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(

View file

@ -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.d9b38fb8.js"></script><script defer="defer" src="/static/js/app.81716459.js"></script><link href="/static/css/chunk-vendors.d510eff2.css" rel="stylesheet"><link href="/static/css/app.92a2d867.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

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

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

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

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

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

Some files were not shown because too many files have changed in this diff Show more