|
@@ -1,28 +1,13 @@
|
|
|
-from __future__ import annotations
|
|
|
-
|
|
|
import asyncio
|
|
|
import functools
|
|
|
import hashlib
|
|
|
-import inspect
|
|
|
-import mimetypes
|
|
|
import socket
|
|
|
import sys
|
|
|
import threading
|
|
|
import time
|
|
|
import webbrowser
|
|
|
-from contextlib import nullcontext
|
|
|
from pathlib import Path
|
|
|
-from typing import TYPE_CHECKING, Any, Awaitable, Callable, Generator, Optional, Tuple, Union
|
|
|
-
|
|
|
-from fastapi import Request
|
|
|
-from fastapi.responses import StreamingResponse
|
|
|
-
|
|
|
-from . import background_tasks, core
|
|
|
-
|
|
|
-if TYPE_CHECKING:
|
|
|
- from .client import Client
|
|
|
-
|
|
|
-mimetypes.init()
|
|
|
+from typing import Any, Optional, Tuple, Union
|
|
|
|
|
|
|
|
|
def is_pytest() -> bool:
|
|
@@ -58,26 +43,6 @@ def hash_file_path(path: Path) -> str:
|
|
|
return hashlib.sha256(path.as_posix().encode()).hexdigest()[:32]
|
|
|
|
|
|
|
|
|
-def safe_invoke(func: Union[Callable[..., Any], Awaitable], client: Optional[Client] = None) -> None:
|
|
|
- """Invoke the potentially async function in the client context and catch any exceptions."""
|
|
|
- try:
|
|
|
- if isinstance(func, Awaitable):
|
|
|
- async def func_with_client():
|
|
|
- with client or nullcontext():
|
|
|
- await func
|
|
|
- background_tasks.create(func_with_client())
|
|
|
- else:
|
|
|
- with client or nullcontext():
|
|
|
- result = func(client) if len(inspect.signature(func).parameters) == 1 and client is not None else func()
|
|
|
- if is_coroutine_function(func):
|
|
|
- async def result_with_client():
|
|
|
- with client or nullcontext():
|
|
|
- await result
|
|
|
- background_tasks.create(result_with_client())
|
|
|
- except Exception as e:
|
|
|
- core.app.handle_exception(e)
|
|
|
-
|
|
|
-
|
|
|
def is_port_open(host: str, port: int) -> bool:
|
|
|
"""Check if the port is open by checking if a TCP connection can be established."""
|
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
@@ -120,40 +85,3 @@ def schedule_browser(host: str, port: int) -> Tuple[threading.Thread, threading.
|
|
|
thread = threading.Thread(target=in_thread, args=(host, port), daemon=True)
|
|
|
thread.start()
|
|
|
return thread, cancel
|
|
|
-
|
|
|
-
|
|
|
-def get_streaming_response(file: Path, request: Request) -> StreamingResponse:
|
|
|
- """Get a StreamingResponse for the given file and request."""
|
|
|
- file_size = file.stat().st_size
|
|
|
- start = 0
|
|
|
- end = file_size - 1
|
|
|
- range_header = request.headers.get('Range')
|
|
|
- if range_header:
|
|
|
- byte1, byte2 = range_header.split('=')[1].split('-')
|
|
|
- start = int(byte1)
|
|
|
- if byte2:
|
|
|
- end = int(byte2)
|
|
|
- content_length = end - start + 1
|
|
|
- headers = {
|
|
|
- 'Content-Range': f'bytes {start}-{end}/{file_size}',
|
|
|
- 'Content-Length': str(content_length),
|
|
|
- 'Accept-Ranges': 'bytes',
|
|
|
- }
|
|
|
-
|
|
|
- def content_reader(file: Path, start: int, end: int, chunk_size: int = 8192) -> Generator[bytes, None, None]:
|
|
|
- with open(file, 'rb') as data:
|
|
|
- data.seek(start)
|
|
|
- remaining_bytes = end - start + 1
|
|
|
- while remaining_bytes > 0:
|
|
|
- chunk = data.read(min(chunk_size, remaining_bytes))
|
|
|
- if not chunk:
|
|
|
- break
|
|
|
- yield chunk
|
|
|
- remaining_bytes -= len(chunk)
|
|
|
-
|
|
|
- return StreamingResponse(
|
|
|
- content_reader(file, start, end),
|
|
|
- media_type=mimetypes.guess_type(str(file))[0] or 'application/octet-stream',
|
|
|
- headers=headers,
|
|
|
- status_code=206,
|
|
|
- )
|