Parcourir la source

introduce app.add_media_files

Rodja Trappe il y a 2 ans
Parent
commit
17118573ce
3 fichiers modifiés avec 59 ajouts et 102 suppressions
  1. 44 1
      nicegui/app.py
  2. 14 101
      poetry.lock
  3. 1 0
      pyproject.toml

+ 44 - 1
nicegui/app.py

@@ -1,6 +1,10 @@
+import magic
+import os
+from pathlib import Path
 from typing import Awaitable, Callable, Union
 
-from fastapi import FastAPI
+from fastapi import FastAPI, Request
+from fastapi.responses import StreamingResponse
 from fastapi.staticfiles import StaticFiles
 
 from . import globals
@@ -77,6 +81,45 @@ class App(FastAPI):
             raise ValueError('''Path cannot be "/", because it would hide NiceGUI's internal "/_nicegui" route.''')
         globals.app.mount(url_path, StaticFiles(directory=local_directory))
 
+    def add_media_files(self, url_path: str, local_directory: str) -> None:
+        """Add media files.
+
+        `add_media_files()` allows a local files to be streamed from a specified endpoint, e.g. `'/media'`.
+        """
+        @self.get(f'/{url_path}/' + '{filename}')
+        async def read_item(request: Request, filename: str):
+            video_path = Path(local_directory) / filename
+            if not video_path.is_file():
+                return {"detail": "Not Found"}, 404
+            file_size = video_path.stat().st_size
+            start, end = 0, file_size - 1
+            range_header = request.headers.get('Range', None)
+            if range_header:
+                byte1, byte2 = range_header.split('=')[1].split('-')
+                start = int(byte1)
+                if byte2:
+                    end = int(byte2)
+            content_length = (end - start) + 1
+
+            def content_reader(file, chunk_size=8192):
+                with open(file, 'rb') as data:
+                    data.seek(start)
+                    while True:
+                        bytes = data.read(chunk_size)
+                        if not bytes:
+                            break
+                        yield bytes
+            headers = {
+                'Content-Range': f'bytes {start}-{end}/{file_size}',
+                'Content-Length': str(content_length),
+                'Accept-Ranges': 'bytes',
+            }
+
+            return StreamingResponse(content_reader(video_path),
+                                     media_type=magic.from_file(str(video_path), mime=True),
+                                     headers=headers,
+                                     status_code=206)
+
     def remove_route(self, path: str) -> None:
         """Remove routes with the given path."""
         self.routes[:] = [r for r in self.routes if getattr(r, 'path', None) != path]

+ 14 - 101
poetry.lock

@@ -1,10 +1,9 @@
-# This file is automatically @generated by Poetry and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
 
 [[package]]
 name = "aiofiles"
 version = "23.1.0"
 description = "File support for asyncio."
-category = "main"
 optional = false
 python-versions = ">=3.7,<4.0"
 files = [
@@ -16,7 +15,6 @@ files = [
 name = "anyio"
 version = "3.7.0"
 description = "High level compatibility layer for multiple asynchronous event loop implementations"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -39,7 +37,6 @@ trio = ["trio (<0.22)"]
 name = "asttokens"
 version = "2.2.1"
 description = "Annotate AST trees with source code positions"
-category = "dev"
 optional = false
 python-versions = "*"
 files = [
@@ -57,7 +54,6 @@ test = ["astroid", "pytest"]
 name = "async-generator"
 version = "1.10"
 description = "Async generators and context managers for Python 3.5+"
-category = "dev"
 optional = false
 python-versions = ">=3.5"
 files = [
@@ -69,7 +65,6 @@ files = [
 name = "atomicwrites"
 version = "1.4.1"
 description = "Atomic file writes."
-category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 files = [
@@ -80,7 +75,6 @@ files = [
 name = "attrs"
 version = "23.1.0"
 description = "Classes Without Boilerplate"
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -102,7 +96,6 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte
 name = "autopep8"
 version = "1.7.0"
 description = "A tool that automatically formats Python code to conform to the PEP 8 style guide"
-category = "dev"
 optional = false
 python-versions = "*"
 files = [
@@ -118,7 +111,6 @@ toml = "*"
 name = "bidict"
 version = "0.22.1"
 description = "The bidirectional mapping library for Python."
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -135,7 +127,6 @@ test = ["hypothesis", "pytest", "pytest-benchmark[histogram]", "pytest-cov", "py
 name = "bottle"
 version = "0.12.25"
 description = "Fast and simple WSGI-framework for small web-applications."
-category = "main"
 optional = false
 python-versions = "*"
 files = [
@@ -147,7 +138,6 @@ files = [
 name = "certifi"
 version = "2023.5.7"
 description = "Python package for providing Mozilla's CA Bundle."
-category = "dev"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -159,7 +149,6 @@ files = [
 name = "cffi"
 version = "1.15.1"
 description = "Foreign Function Interface for Python calling C code."
-category = "dev"
 optional = false
 python-versions = "*"
 files = [
@@ -236,7 +225,6 @@ pycparser = "*"
 name = "charset-normalizer"
 version = "3.1.0"
 description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-category = "dev"
 optional = false
 python-versions = ">=3.7.0"
 files = [
@@ -321,7 +309,6 @@ files = [
 name = "click"
 version = "8.1.3"
 description = "Composable command line interface toolkit"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -337,7 +324,6 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
 name = "colorama"
 version = "0.4.6"
 description = "Cross-platform colored terminal text."
-category = "main"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
 files = [
@@ -349,7 +335,6 @@ files = [
 name = "contourpy"
 version = "1.0.7"
 description = "Python library for calculating contours of 2D quadrilateral grids"
-category = "main"
 optional = false
 python-versions = ">=3.8"
 files = [
@@ -424,7 +409,6 @@ test-no-images = ["pytest"]
 name = "cycler"
 version = "0.11.0"
 description = "Composable style cycles"
-category = "main"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -436,7 +420,6 @@ files = [
 name = "debugpy"
 version = "1.6.7"
 description = "An implementation of the Debug Adapter Protocol for Python"
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -464,7 +447,6 @@ files = [
 name = "docutils"
 version = "0.19"
 description = "Docutils -- Python Documentation Utilities"
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -476,7 +458,6 @@ files = [
 name = "exceptiongroup"
 version = "1.1.1"
 description = "Backport of PEP 654 (exception groups)"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -491,7 +472,6 @@ test = ["pytest (>=6)"]
 name = "executing"
 version = "1.2.0"
 description = "Get the currently executing AST node of a frame, and other information"
-category = "dev"
 optional = false
 python-versions = "*"
 files = [
@@ -506,7 +486,6 @@ tests = ["asttokens", "littleutils", "pytest", "rich"]
 name = "fastapi"
 version = "0.95.2"
 description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -528,7 +507,6 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6
 name = "fastapi-socketio"
 version = "0.0.10"
 description = "Easily integrate socket.io with your FastAPI app."
-category = "main"
 optional = false
 python-versions = "*"
 files = [
@@ -547,7 +525,6 @@ test = ["pytest"]
 name = "fonttools"
 version = "4.38.0"
 description = "Tools to manipulate font files"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -573,7 +550,6 @@ woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
 name = "fonttools"
 version = "4.39.4"
 description = "Tools to manipulate font files"
-category = "main"
 optional = false
 python-versions = ">=3.8"
 files = [
@@ -599,7 +575,6 @@ woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
 name = "h11"
 version = "0.14.0"
 description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -614,7 +589,6 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
 name = "httptools"
 version = "0.5.0"
 description = "A collection of framework independent HTTP protocol utils."
-category = "main"
 optional = false
 python-versions = ">=3.5.0"
 files = [
@@ -668,7 +642,6 @@ test = ["Cython (>=0.29.24,<0.30.0)"]
 name = "icecream"
 version = "2.1.3"
 description = "Never use print() to debug again; inspect variables, expressions, and program execution with a single, simple function call."
-category = "dev"
 optional = false
 python-versions = "*"
 files = [
@@ -686,7 +659,6 @@ pygments = ">=2.2.0"
 name = "idna"
 version = "3.4"
 description = "Internationalized Domain Names in Applications (IDNA)"
-category = "main"
 optional = false
 python-versions = ">=3.5"
 files = [
@@ -698,7 +670,6 @@ files = [
 name = "importlib-metadata"
 version = "6.6.0"
 description = "Read metadata from Python packages"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -719,7 +690,6 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag
 name = "iniconfig"
 version = "2.0.0"
 description = "brain-dead simple config-ini parsing"
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -731,7 +701,6 @@ files = [
 name = "isort"
 version = "5.11.5"
 description = "A Python utility / library to sort Python imports."
-category = "dev"
 optional = false
 python-versions = ">=3.7.0"
 files = [
@@ -749,7 +718,6 @@ requirements-deprecated-finder = ["pip-api", "pipreqs"]
 name = "itsdangerous"
 version = "2.1.2"
 description = "Safely pass data to untrusted environments and back."
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -761,7 +729,6 @@ files = [
 name = "jinja2"
 version = "3.1.2"
 description = "A very fast and expressive template engine."
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -779,7 +746,6 @@ i18n = ["Babel (>=2.7)"]
 name = "kiwisolver"
 version = "1.4.4"
 description = "A fast implementation of the Cassowary constraint solver"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -860,7 +826,6 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
 name = "markdown2"
 version = "2.4.8"
 description = "A fast and complete Python implementation of Markdown"
-category = "main"
 optional = false
 python-versions = ">=3.5, <4"
 files = [
@@ -877,7 +842,6 @@ wavedrom = ["wavedrom"]
 name = "markupsafe"
 version = "2.1.2"
 description = "Safely add untrusted strings to HTML/XML markup."
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -937,7 +901,6 @@ files = [
 name = "matplotlib"
 version = "3.5.3"
 description = "Python plotting package"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -992,7 +955,6 @@ python-dateutil = ">=2.7"
 name = "matplotlib"
 version = "3.7.1"
 description = "Python plotting package"
-category = "main"
 optional = false
 python-versions = ">=3.8"
 files = [
@@ -1054,7 +1016,6 @@ python-dateutil = ">=2.7"
 name = "numpy"
 version = "1.21.1"
 description = "NumPy is the fundamental package for array computing with Python."
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1092,7 +1053,6 @@ files = [
 name = "numpy"
 version = "1.24.3"
 description = "Fundamental package for array computing in Python"
-category = "main"
 optional = false
 python-versions = ">=3.8"
 files = [
@@ -1130,7 +1090,6 @@ files = [
 name = "orjson"
 version = "3.9.0"
 description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1186,7 +1145,6 @@ files = [
 name = "outcome"
 version = "1.2.0"
 description = "Capture the outcome of Python function calls."
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1201,7 +1159,6 @@ attrs = ">=19.2.0"
 name = "packaging"
 version = "23.1"
 description = "Core utilities for Python packages"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1213,7 +1170,6 @@ files = [
 name = "pandas"
 version = "1.1.5"
 description = "Powerful data structures for data analysis, time series, and statistics"
-category = "dev"
 optional = false
 python-versions = ">=3.6.1"
 files = [
@@ -1255,7 +1211,6 @@ test = ["hypothesis (>=3.58)", "pytest (>=4.0.2)", "pytest-xdist"]
 name = "pandas"
 version = "2.0.2"
 description = "Powerful data structures for data analysis, time series, and statistics"
-category = "dev"
 optional = false
 python-versions = ">=3.8"
 files = [
@@ -1323,7 +1278,6 @@ xml = ["lxml (>=4.6.3)"]
 name = "pillow"
 version = "9.5.0"
 description = "Python Imaging Library (Fork)"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1403,7 +1357,6 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
 name = "plotly"
 version = "5.14.1"
 description = "An open-source, interactive data visualization library for Python"
-category = "main"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -1419,7 +1372,6 @@ tenacity = ">=6.2.0"
 name = "pluggy"
 version = "1.0.0"
 description = "plugin and hook calling mechanisms for python"
-category = "dev"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -1438,7 +1390,6 @@ testing = ["pytest", "pytest-benchmark"]
 name = "proxy-tools"
 version = "0.1.0"
 description = "Proxy Implementation"
-category = "main"
 optional = false
 python-versions = "*"
 files = [
@@ -1449,7 +1400,6 @@ files = [
 name = "pscript"
 version = "0.7.7"
 description = "Python to JavaScript compiler."
-category = "main"
 optional = false
 python-versions = "*"
 files = [
@@ -1461,7 +1411,6 @@ files = [
 name = "py"
 version = "1.11.0"
 description = "library with cross-python path, ini-parsing, io, code, log facilities"
-category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 files = [
@@ -1473,7 +1422,6 @@ files = [
 name = "pycodestyle"
 version = "2.10.0"
 description = "Python style guide checker"
-category = "dev"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -1485,7 +1433,6 @@ files = [
 name = "pycparser"
 version = "2.21"
 description = "C parser in Python"
-category = "main"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 files = [
@@ -1497,7 +1444,6 @@ files = [
 name = "pydantic"
 version = "1.10.8"
 description = "Data validation and settings management using python type hints"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1550,7 +1496,6 @@ email = ["email-validator (>=1.0.3)"]
 name = "pygments"
 version = "2.15.1"
 description = "Pygments is a syntax highlighting package written in Python."
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1565,7 +1510,6 @@ plugins = ["importlib-metadata"]
 name = "pyobjc-core"
 version = "9.1.1"
 description = "Python<->ObjC Interoperability Module"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1582,7 +1526,6 @@ files = [
 name = "pyobjc-framework-cocoa"
 version = "9.1.1"
 description = "Wrappers for the Cocoa frameworks on macOS"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1602,7 +1545,6 @@ pyobjc-core = ">=9.1.1"
 name = "pyobjc-framework-webkit"
 version = "9.1.1"
 description = "Wrappers for the framework WebKit on macOS"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1620,7 +1562,6 @@ pyobjc-framework-Cocoa = ">=9.1.1"
 name = "pyparsing"
 version = "3.0.9"
 description = "pyparsing module - Classes and methods to define and execute parsing grammars"
-category = "main"
 optional = false
 python-versions = ">=3.6.8"
 files = [
@@ -1635,7 +1576,6 @@ diagrams = ["jinja2", "railroad-diagrams"]
 name = "pysocks"
 version = "1.7.1"
 description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
-category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 files = [
@@ -1648,7 +1588,6 @@ files = [
 name = "pytest"
 version = "6.2.5"
 description = "pytest: simple powerful testing with Python"
-category = "dev"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -1674,7 +1613,6 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm
 name = "pytest-asyncio"
 version = "0.19.0"
 description = "Pytest support for asyncio"
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1693,7 +1631,6 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy
 name = "pytest-base-url"
 version = "2.0.0"
 description = "pytest plugin for URL based testing"
-category = "dev"
 optional = false
 python-versions = ">=3.7,<4.0"
 files = [
@@ -1709,7 +1646,6 @@ requests = ">=2.9"
 name = "pytest-html"
 version = "3.2.0"
 description = "pytest plugin for generating HTML reports"
-category = "dev"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -1726,7 +1662,6 @@ pytest-metadata = "*"
 name = "pytest-metadata"
 version = "2.0.4"
 description = "pytest plugin for test session metadata"
-category = "dev"
 optional = false
 python-versions = ">=3.7,<4.0"
 files = [
@@ -1741,7 +1676,6 @@ pytest = ">=3.0.0,<8.0.0"
 name = "pytest-selenium"
 version = "4.0.1"
 description = "pytest plugin for Selenium"
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1766,7 +1700,6 @@ test = ["black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "pytest
 name = "pytest-variables"
 version = "2.0.0"
 description = "pytest plugin for providing variables to tests/fixtures"
-category = "dev"
 optional = false
 python-versions = ">=3.7,<4.0"
 files = [
@@ -1786,7 +1719,6 @@ yaml = ["PyYAML"]
 name = "python-dateutil"
 version = "2.8.2"
 description = "Extensions to the standard Python datetime module"
-category = "main"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
 files = [
@@ -1801,7 +1733,6 @@ six = ">=1.5"
 name = "python-dotenv"
 version = "0.21.1"
 description = "Read key-value pairs from a .env file and set them as environment variables"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1816,7 +1747,6 @@ cli = ["click (>=5.0)"]
 name = "python-engineio"
 version = "4.4.1"
 description = "Engine.IO server and client for Python"
-category = "main"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -1828,11 +1758,21 @@ files = [
 asyncio-client = ["aiohttp (>=3.4)"]
 client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
 
+[[package]]
+name = "python-magic"
+version = "0.4.27"
+description = "File type identification using libmagic"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+files = [
+    {file = "python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b"},
+    {file = "python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3"},
+]
+
 [[package]]
 name = "python-multipart"
 version = "0.0.6"
 description = "A streaming multipart parser for Python"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1847,7 +1787,6 @@ dev = ["atomicwrites (==1.2.1)", "attrs (==19.2.0)", "coverage (==6.5.0)", "hatc
 name = "python-socketio"
 version = "5.8.0"
 description = "Socket.IO server and client for Python"
-category = "main"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -1867,7 +1806,6 @@ client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
 name = "pythonnet"
 version = "2.5.2"
 description = ".Net and Mono integration for Python"
-category = "main"
 optional = false
 python-versions = "*"
 files = [
@@ -1891,7 +1829,6 @@ pycparser = "*"
 name = "pytz"
 version = "2023.3"
 description = "World timezone definitions, modern and historical"
-category = "dev"
 optional = false
 python-versions = "*"
 files = [
@@ -1903,7 +1840,6 @@ files = [
 name = "pywebview"
 version = "4.1"
 description = "Build GUI for your Python program with JavaScript, HTML, and CSS."
-category = "main"
 optional = false
 python-versions = "*"
 files = [
@@ -1931,7 +1867,6 @@ qt = ["PyQt5", "QtPy", "pyqtwebengine"]
 name = "pyyaml"
 version = "6.0"
 description = "YAML parser and emitter for Python"
-category = "main"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -1981,7 +1916,6 @@ files = [
 name = "qtpy"
 version = "2.3.1"
 description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)."
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -1999,7 +1933,6 @@ test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"]
 name = "requests"
 version = "2.31.0"
 description = "Python HTTP for Humans."
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2021,7 +1954,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
 name = "secure"
 version = "0.3.0"
 description = "A lightweight package that adds security headers for Python web frameworks."
-category = "dev"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -2033,7 +1965,6 @@ files = [
 name = "selenium"
 version = "4.9.1"
 description = ""
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2051,7 +1982,6 @@ urllib3 = {version = ">=1.26,<3", extras = ["socks"]}
 name = "six"
 version = "1.16.0"
 description = "Python 2 and 3 compatibility utilities"
-category = "main"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
 files = [
@@ -2063,7 +1993,6 @@ files = [
 name = "sniffio"
 version = "1.3.0"
 description = "Sniff out which async library your code is running under"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2075,7 +2004,6 @@ files = [
 name = "sortedcontainers"
 version = "2.4.0"
 description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
-category = "dev"
 optional = false
 python-versions = "*"
 files = [
@@ -2087,7 +2015,6 @@ files = [
 name = "starlette"
 version = "0.27.0"
 description = "The little ASGI library that shines."
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2106,7 +2033,6 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam
 name = "tenacity"
 version = "8.2.2"
 description = "Retry code until it succeeds"
-category = "main"
 optional = false
 python-versions = ">=3.6"
 files = [
@@ -2121,7 +2047,6 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"]
 name = "toml"
 version = "0.10.2"
 description = "Python Library for Tom's Obvious, Minimal Language"
-category = "dev"
 optional = false
 python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
 files = [
@@ -2133,7 +2058,6 @@ files = [
 name = "trio"
 version = "0.22.0"
 description = "A friendly Python library for async concurrency and I/O"
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2155,7 +2079,6 @@ sortedcontainers = "*"
 name = "trio-websocket"
 version = "0.10.2"
 description = "WebSocket library for Trio"
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2172,7 +2095,6 @@ wsproto = ">=0.14"
 name = "typing-extensions"
 version = "4.6.3"
 description = "Backported and Experimental Type Hints for Python 3.7+"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2184,7 +2106,6 @@ files = [
 name = "tzdata"
 version = "2023.3"
 description = "Provider of IANA time zone data"
-category = "dev"
 optional = false
 python-versions = ">=2"
 files = [
@@ -2196,7 +2117,6 @@ files = [
 name = "urllib3"
 version = "2.0.2"
 description = "HTTP library with thread-safe connection pooling, file post, and more."
-category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2217,7 +2137,6 @@ zstd = ["zstandard (>=0.18.0)"]
 name = "uvicorn"
 version = "0.20.0"
 description = "The lightning-fast ASGI server."
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2233,7 +2152,7 @@ httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standar
 python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
 pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""}
 typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
-uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
+uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
 watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
 websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""}
 
@@ -2244,7 +2163,6 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
 name = "uvloop"
 version = "0.17.0"
 description = "Fast implementation of asyncio event loop on top of libuv"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2289,7 +2207,6 @@ test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "my
 name = "vbuild"
 version = "0.8.1"
 description = "A simple module to extract html/script/style from a vuejs '.vue' file (can minimize/es2015 compliant js) ... just py2 or py3, NO nodejs !"
-category = "main"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
 files = [
@@ -2304,7 +2221,6 @@ pscript = ">=0.7.0,<0.8.0"
 name = "watchfiles"
 version = "0.18.1"
 description = "Simple, modern and high performance file watching and code reload in python."
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2335,7 +2251,6 @@ anyio = ">=3.0.0"
 name = "websockets"
 version = "11.0.3"
 description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2415,7 +2330,6 @@ files = [
 name = "wsproto"
 version = "1.2.0"
 description = "WebSockets state-machine based protocol implementation"
-category = "dev"
 optional = false
 python-versions = ">=3.7.0"
 files = [
@@ -2430,7 +2344,6 @@ h11 = ">=0.9.0,<1"
 name = "zipp"
 version = "3.15.0"
 description = "Backport of pathlib-compatible object wrapper for zip files"
-category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
@@ -2445,4 +2358,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.7"
-content-hash = "01dd4e6d62f913d2f5206dcd946dc9804767c4b57b115ef69eb56a73213ae5e4"
+content-hash = "1a742c40e30d5ab7652be705fef2a96dc06a8a8f23e8ff4cc6b7190212a5a3fe"

+ 1 - 0
pyproject.toml

@@ -30,6 +30,7 @@ pywebview = "^4.0.2"
 importlib_metadata = { version = "^6.0.0", markers = "python_version ~= '3.7'" } # Python 3.7 has no importlib.metadata
 itsdangerous = "^2.1.2"
 aiofiles = "^23.1.0"
+python-magic = "^0.4.27"
 
 [tool.poetry.group.dev.dependencies]
 icecream = "^2.1.0"