From 1e4cf3b221ce27a9737517b3b371ab5c090af022 Mon Sep 17 00:00:00 2001 From: zhaomaoniu <2667292003@qq.com> Date: Sun, 5 May 2024 13:23:42 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20support=20video=20streaming?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_anime_downloader/routes.py | 65 ++++++++++++++----- .../templates/index.html | 10 ++- nonebot_plugin_anime_downloader/test.py | 32 --------- 3 files changed, 57 insertions(+), 50 deletions(-) delete mode 100644 nonebot_plugin_anime_downloader/test.py diff --git a/nonebot_plugin_anime_downloader/routes.py b/nonebot_plugin_anime_downloader/routes.py index d26c606..37451e8 100644 --- a/nonebot_plugin_anime_downloader/routes.py +++ b/nonebot_plugin_anime_downloader/routes.py @@ -2,8 +2,8 @@ from pathlib import Path from nonebot.log import logger from fastapi import FastAPI, Request -from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates +from fastapi.responses import StreamingResponse class VideoManager: @@ -14,30 +14,65 @@ def __init__(self, app: FastAPI, download_path: Path): self.ids: List[int] = [] self.app = app self.download_path = download_path - self.templates = Jinja2Templates( - directory=(Path(__file__).parent / "templates").resolve() - ) - - app.mount( - "/video", - StaticFiles(directory=download_path.resolve()), - name="video", - ) + self.templates = Jinja2Templates(directory=Path(__file__).parent / "templates") def add_route(self, video_path: Path, torrent_id: int) -> None: - if torrent_id not in self.ids: - logger.success(f"Added route for video {video_path.name}.") + if torrent_id in self.ids: + return None self.ids.append(torrent_id) @self.app.get(f"/anime/{torrent_id}") - async def anime_display(request: Request): + async def anime_page(request: Request): return self.templates.TemplateResponse( "index.html", { "request": request, "title": video_path.name, - "video_path": f"/{video_path.parent.name}/{video_path.name}", - "video_type": f"video/{video_path.suffix[1:]}", + "video_url": f"/res/{torrent_id}", + "mime_type": f"video/{video_path.suffix[1:]}", }, ) + + @self.app.get(f"/res/{torrent_id}") + async def anime_res(request: Request): + file_size = video_path.stat().st_size + start, end = 0, file_size - 1 + range_header = request.headers.get("Range") + if range_header: + start, end = range_header.replace("bytes=", "").split("-") + start = int(start) + end = int(end) if end else file_size - 1 + status_code = 206 + else: + status_code = 200 + + def iterfile(): + with open(video_path, mode="rb") as file_like: + file_like.seek(start) + bytes_to_send = end - start + 1 + while bytes_to_send > 0: + chunk_size = min( + bytes_to_send, 1024 * 1024 + ) # 1MB chunks or less + data = file_like.read(chunk_size) + if not data: + break + yield data + bytes_to_send -= len(data) + + headers = { + "Content-Range": f"bytes {start}-{end}/{file_size}", + "Accept-Ranges": "bytes", + "Content-Length": str(end - start + 1), + "Content-Type": f"video/{video_path.suffix[1:]}", + } + + return StreamingResponse( + iterfile(), + status_code=status_code, + headers=headers, + media_type=f"video/{video_path.suffix[1:]}", + ) + + logger.success(f"Added route for video {video_path.name}.") diff --git a/nonebot_plugin_anime_downloader/templates/index.html b/nonebot_plugin_anime_downloader/templates/index.html index 3518fd9..876a66b 100644 --- a/nonebot_plugin_anime_downloader/templates/index.html +++ b/nonebot_plugin_anime_downloader/templates/index.html @@ -2,12 +2,16 @@ {{ title }} + -

{{ title }}

-