diff --git a/CHANGELOG.md b/CHANGELOG.md index 42e4c33..86a51cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # 更新日志 +## 1.6.0-alpha + +- REST API 支持获取正在录制的 flv 文件的路径和元数据 + ## 1.5.0 - 支持设置日志文件存放位置 diff --git a/src/blrec/__init__.py b/src/blrec/__init__.py index 783da12..41a7897 100644 --- a/src/blrec/__init__.py +++ b/src/blrec/__init__.py @@ -1,4 +1,4 @@ __prog__ = 'blrec' -__version__ = '1.5.0' +__version__ = '1.6.0-alpha' __github__ = 'https://github.com/acgnhiki/blrec' diff --git a/src/blrec/application.py b/src/blrec/application.py index 09799d0..12d0507 100644 --- a/src/blrec/application.py +++ b/src/blrec/application.py @@ -7,6 +7,7 @@ import attr import psutil from . import __prog__, __version__ +from .flv.data_analyser import MetaData from .disk_space import SpaceMonitor, SpaceReclaimer from .bili.helpers import ensure_room_id from .task import ( @@ -201,6 +202,9 @@ class Application: def get_task_param(self, room_id: int) -> TaskParam: return self._task_manager.get_task_param(room_id) + def get_task_metadata(self, room_id: int) -> Optional[MetaData]: + return self._task_manager.get_task_metadata(room_id) + def get_task_video_file_details( self, room_id: int ) -> Iterator[VideoFileDetail]: diff --git a/src/blrec/core/recorder.py b/src/blrec/core/recorder.py index 2d61990..7f4fc78 100644 --- a/src/blrec/core/recorder.py +++ b/src/blrec/core/recorder.py @@ -14,6 +14,7 @@ from .raw_danmaku_receiver import RawDanmakuReceiver from .raw_danmaku_dumper import RawDanmakuDumper, RawDanmakuDumperEventListener from .stream_recorder import StreamRecorder, StreamRecorderEventListener from ..event.event_emitter import EventListener, EventEmitter +from ..flv.data_analyser import MetaData from ..bili.live import Live from ..bili.models import RoomInfo from ..bili.danmaku_client import DanmakuClient @@ -274,6 +275,14 @@ class Recorder( def duration_limit(self, value: int) -> None: self._stream_recorder.duration_limit = value + @property + def recording_path(self) -> Optional[str]: + return self._stream_recorder.recording_path + + @property + def metadata(self) -> Optional[MetaData]: + return self._stream_recorder.metadata + async def _do_start(self) -> None: self._live_monitor.add_listener(self) self._danmaku_dumper.add_listener(self) diff --git a/src/blrec/core/stream_recorder.py b/src/blrec/core/stream_recorder.py index 0f5001d..247bc83 100644 --- a/src/blrec/core/stream_recorder.py +++ b/src/blrec/core/stream_recorder.py @@ -38,6 +38,7 @@ from .statistics import StatisticsCalculator from ..event.event_emitter import EventListener, EventEmitter from ..bili.live import Live from ..bili.typing import QualityNumber +from ..flv.data_analyser import MetaData from ..flv.stream_processor import StreamProcessor, BaseOutputFileManager from ..utils.mixins import AsyncCooperationMix, AsyncStoppableMixin from ..path import escape_path @@ -186,6 +187,13 @@ class StreamRecorder( def recording_path(self) -> Optional[str]: return self._file_manager.curr_path + @property + def metadata(self) -> Optional[MetaData]: + if self._stream_processor is not None: + return self._stream_processor.metadata + else: + return None + def has_file(self) -> bool: return self._file_manager.has_file() diff --git a/src/blrec/flv/data_analyser.py b/src/blrec/flv/data_analyser.py index 1136106..de8322f 100644 --- a/src/blrec/flv/data_analyser.py +++ b/src/blrec/flv/data_analyser.py @@ -14,7 +14,7 @@ from .models import ( from .common import Resolution, is_audio_tag, is_script_tag, is_video_tag -__all__ = 'DataAnalyser', +__all__ = 'DataAnalyser', 'MetaData', 'KeyFrames' @attr.s(auto_attribs=True, slots=True, frozen=True, kw_only=True) diff --git a/src/blrec/flv/stream_processor.py b/src/blrec/flv/stream_processor.py index cb82182..f865b2c 100644 --- a/src/blrec/flv/stream_processor.py +++ b/src/blrec/flv/stream_processor.py @@ -14,7 +14,7 @@ from rx.subject import Subject from rx.core import Observable from .models import FlvHeader, FlvTag, ScriptTag, VideoTag, AudioTag -from .data_analyser import DataAnalyser +from .data_analyser import DataAnalyser, MetaData from .stream_cutter import StreamCutter from .limit_checker import LimitChecker from .parameters_checker import ParametersChecker @@ -117,6 +117,16 @@ class StreamProcessor: for point in self._join_points: yield point + @property + def metadata(self) -> Optional[MetaData]: + if not self._analyse_data: + return None + try: + return self._data_analyser.make_metadata() + except Exception as e: + logger.debug(f'Failed to make metadata data, due to: {repr(e)}') + return None + @property def size_updates(self) -> Observable: return self._size_updates diff --git a/src/blrec/setting/setting_manager.py b/src/blrec/setting/setting_manager.py index 6de0632..fa279f8 100644 --- a/src/blrec/setting/setting_manager.py +++ b/src/blrec/setting/setting_manager.py @@ -1,5 +1,4 @@ from __future__ import annotations -import os import asyncio from typing import Optional, TYPE_CHECKING, cast diff --git a/src/blrec/task/models.py b/src/blrec/task/models.py index 7c8a2ba..206508b 100644 --- a/src/blrec/task/models.py +++ b/src/blrec/task/models.py @@ -29,6 +29,7 @@ class TaskStatus: danmu_count: int # Number of Danmu in total danmu_rate: float # Number of Danmu per minutes real_quality_number: QualityNumber + recording_path: Optional[str] = None postprocessor_status: PostprocessorStatus = PostprocessorStatus.WAITING postprocessing_path: Optional[str] = None postprocessing_progress: Optional[Progress] = None diff --git a/src/blrec/task/task.py b/src/blrec/task/task.py index 6e6e5b2..8f78335 100644 --- a/src/blrec/task/task.py +++ b/src/blrec/task/task.py @@ -21,6 +21,7 @@ from ..core import Recorder from ..postprocess import Postprocessor, PostprocessorStatus, DeleteStrategy from ..postprocess.remuxer import RemuxProgress from ..flv.metadata_injector import InjectProgress +from ..flv.data_analyser import MetaData from ..event.event_submitters import ( LiveEventSubmitter, RecorderEventSubmitter, PostprocessorEventSubmitter ) @@ -132,6 +133,7 @@ class RecordTask: danmu_count=self._recorder.danmu_count, danmu_rate=self._recorder.danmu_rate, real_quality_number=self._recorder.real_quality_number, + recording_path=self.recording_path, postprocessor_status=self._postprocessor.status, postprocessing_path=self._postprocessor.postprocessing_path, postprocessing_progress=( @@ -349,6 +351,14 @@ class RecordTask: def duration_limit(self, value: int) -> None: self._recorder.duration_limit = value + @property + def recording_path(self) -> Optional[str]: + return self._recorder.recording_path + + @property + def metadata(self) -> Optional[MetaData]: + return self._recorder.metadata + @property def remux_to_mp4(self) -> bool: return self._postprocessor.remux_to_mp4 diff --git a/src/blrec/task/task_manager.py b/src/blrec/task/task_manager.py index 0f25f84..bac53a7 100644 --- a/src/blrec/task/task_manager.py +++ b/src/blrec/task/task_manager.py @@ -1,10 +1,11 @@ from __future__ import annotations import asyncio -from typing import Dict, Iterator, TYPE_CHECKING +from typing import Dict, Iterator, Optional, TYPE_CHECKING from .task import RecordTask from .models import TaskData, TaskParam, VideoFileDetail, DanmakuFileDetail +from ..flv.data_analyser import MetaData from ..exception import NotFoundError if TYPE_CHECKING: from ..setting import SettingsManager @@ -154,6 +155,10 @@ class RecordTaskManager: task = self._get_task(room_id) return self._make_task_param(task) + def get_task_metadata(self, room_id: int) -> Optional[MetaData]: + task = self._get_task(room_id) + return task.metadata + def get_task_video_file_details( self, room_id: int ) -> Iterator[VideoFileDetail]: diff --git a/src/blrec/web/routers/tasks.py b/src/blrec/web/routers/tasks.py index 57c1874..50b63ef 100644 --- a/src/blrec/web/routers/tasks.py +++ b/src/blrec/web/routers/tasks.py @@ -53,6 +53,17 @@ async def get_task_param(room_id: int) -> Dict[str, Any]: return attr.asdict(app.get_task_param(room_id)) +@router.get( + '/{room_id}/metadata', + responses={**not_found_responses}, +) +async def get_task_metadata(room_id: int) -> Dict[str, Any]: + metadata = app.get_task_metadata(room_id) + if not metadata: + return {} + return attr.asdict(metadata) + + @router.get( '/{room_id}/videos', responses={**not_found_responses},