瀏覽代碼

provide api to also add single media files

Rodja Trappe 1 年之前
父節點
當前提交
bbc6723813
共有 2 個文件被更改,包括 38 次插入7 次删除
  1. 18 2
      nicegui/app.py
  2. 20 5
      tests/test_serving_files.py

+ 18 - 2
nicegui/app.py

@@ -1,5 +1,6 @@
 from pathlib import Path
-from typing import Awaitable, Callable, Union
+from typing import Awaitable, Callable, Optional, Union
+from uuid import uuid4
 
 from fastapi import FastAPI, Request
 from fastapi.responses import StreamingResponse
@@ -81,7 +82,7 @@ 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:
+    def add_media_files(self, url_path: str, local_directory: Union[str, Path]) -> None:
         """Add media files.
 
         `add_media_files()` allows a local files to be streamed from a specified endpoint, e.g. `'/media'`.
@@ -89,10 +90,25 @@ class App(FastAPI):
         @self.get(f'{url_path}/' + '{filename}')
         async def read_item(request: Request, filename: str) -> StreamingResponse:
             filepath = Path(local_directory) / filename
+            ic(filepath)
             if not filepath.is_file():
                 return {"detail": "Not Found"}, 404
+            ic(filepath)
             return helpers.get_streaming_response(filepath, request)
 
+    def add_media_file(self, local_file: str, url_path: Optional[str] = None) -> None:
+        file = Path(local_file)
+        if not file.is_file():
+            raise ValueError(f'File not found: {local_file}')
+        if url_path is None:
+            url_path = '/_nicegui/auto/media/' + uuid4().hex
+
+        @self.get(url_path)
+        async def read_item(request: Request) -> StreamingResponse:
+            return helpers.get_streaming_response(file, request)
+
+        return url_path
+
     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]

+ 20 - 5
tests/test_serving_files.py

@@ -3,6 +3,7 @@ import os
 from pathlib import Path
 
 import httpx
+import pytest
 
 from nicegui import app, ui
 
@@ -10,8 +11,9 @@ from .screen import PORT, Screen
 from .test_helpers import TEST_DIR
 
 
-def test_media_files_can_be_streamed(screen: Screen):
-    mp4 = Path(TEST_DIR / 'media/test.mp4')
+@pytest.fixture(autouse=True)
+def provide_media_files():
+    mp4 = Path(TEST_DIR / 'media' / 'test.mp4')
     if not mp4.exists():
         mp4.parent.mkdir(exist_ok=True)
         url = 'https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/360/Big_Buck_Bunny_360_10s_1MB.mp4'
@@ -19,13 +21,26 @@ def test_media_files_can_be_streamed(screen: Screen):
             with open(mp4, 'wb') as file:
                 for chunk in response.iter_raw():
                     file.write(chunk)
-    app.add_media_files('/media', mp4.parent)
 
-    screen.open('/')
+
+def assert_video_file_streaming(path: str) -> None:
     with httpx.Client() as http_client:
-        r = http_client.get(f'http://localhost:{PORT}/media/test.mp4', headers={'Range': 'bytes=0-1000'})
+        r = http_client.get(f'http://localhost:{PORT}{path}', headers={'Range': 'bytes=0-1000'})
         assert r.status_code == 206
         assert r.headers['Accept-Ranges'] == 'bytes'
         assert r.headers['Content-Range'].startswith('bytes 0-1000/')
         assert r.headers['Content-Length'] == '1001'
         assert r.headers['Content-Type'] == 'video/mp4'
+
+
+def test_media_files_can_be_streamed(screen: Screen):
+    app.add_media_files('/media', Path(TEST_DIR) / 'media')
+    screen.open('/')
+    assert_video_file_streaming('/media/test.mp4')
+
+
+def test_adding_single_media_file(screen: Screen):
+    url_path = app.add_media_file(Path(TEST_DIR) / 'media' / 'test.mp4')
+
+    screen.open('/')
+    assert_video_file_streaming(url_path)