diff --git a/CHANGELOG.md b/CHANGELOG.md index d857ba5..7c11798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # 更新日志 +## 1.2.3 + +- 修复直播间标题有特殊字符导致文件创建失败 +- 修复弹幕有特殊字符导致弹幕录制出错 + ## 1.2.2 - 避免 url 失效出现一直超时不能录制 diff --git a/setup.cfg b/setup.cfg index 99d790a..6df25f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,41 +36,41 @@ include_package_data = True python_requires = >= 3.8 install_requires = typing-extensions >= 3.10.0.0 - fastapi >= 0.65.2, < 0.66.0 - email_validator >=1.1.2, < 2.0.0 - typer >= 0.3.2, < 0.4.0 - aiohttp >= 3.6.2, < 3.7.0 - requests >= 2.24.0, < 2.25.0 - aiofiles >= 0.5.0, < 0.6.0 - tenacity >= 6.2.0, < 6.3.0 - colorama >= 0.4.3, < 0.5.0 - humanize >= 3.2.0, < 3.3.0 - tqdm >= 4.56.2, < 4.57.0 - attrs >= 21.2.0, < 21.3.0 - lxml >= 4.6.2, < 4.7.0 + fastapi >= 0.70.0, < 0.71.0 + email_validator >= 1.1.3, < 2.0.0 + typer >= 0.4.0, < 0.5.0 + aiohttp >= 3.8.1, < 4.0.0 + requests >= 2.24.0, < 3.0.0 + aiofiles >= 0.8.0, < 0.9.0 + tenacity >= 8.0.1, < 9.0.0 + colorama >= 0.4.4, < 0.5.0 + humanize >= 3.13.1, < 4.0.0 + tqdm >= 4.62.3, < 5.0.0 + attrs >= 21.2.0, < 22.0.0 + lxml >= 4.6.4, < 5.0.0 toml >= 0.10.2, < 0.11.0 - psutil >= 5.8.0, < 5.9.0 - rx >= 3.1.1, < 3.2.0 - bitarray >= 2.2.5, < 2.3.0 - brotli >= 1.0.9, < 1.1.0 - uvicorn[standard] >=0.12.0, < 0.15.0 + psutil >= 5.8.0, < 6.0.0 + rx >= 3.2.0, < 4.0.0 + bitarray >= 2.2.5, < 3.0.0 + brotli >= 1.0.9, < 2.0.0 + uvicorn[standard] >= 0.15.0, < 0.16.0 [options.extras_require] dev = - flake8 >= 3.9.2, < 4.0.0 + flake8 >= 4.0.1, < 5.0.0 mypy >= 0.910, < 1.000 - setuptools >= 57.0.0, < 58.0.0 + setuptools >= 59.4.0, < 60.0.0 wheel >= 0.37, < 0.38.0 - build >= 0.5.1, < 0.6.0 + build >= 0.7.0, < 0.8.0 # missing stub packages - types-requests >= 2.25.1, < 2.26.0 + types-requests >= 2.26.1, < 3.0.0 types-aiofiles >= 0.1.7, < 0.2.0 - types-toml >= 0.1.3, < 0.2.0 - types-setuptools >= 57.0.0, < 58.0.0 + types-toml >= 0.10.1, < 0.11.0 + types-setuptools >= 57.4.4, < 58.0.0 - speedups = aiohttp[speedups] >= 3.6.2, < 3.7.0 + speedups = aiohttp[speedups] >= 3.8.1, < 4.0.0 [options.packages.find] where = src diff --git a/src/blrec/__init__.py b/src/blrec/__init__.py index ef17d33..acab0d5 100644 --- a/src/blrec/__init__.py +++ b/src/blrec/__init__.py @@ -1,4 +1,4 @@ __prog__ = 'blrec' -__version__ = '1.2.2' +__version__ = '1.2.3' __github__ = 'https://github.com/acgnhiki/blrec' diff --git a/src/blrec/core/retry.py b/src/blrec/core/retry.py index da2c893..2d1619b 100644 --- a/src/blrec/core/retry.py +++ b/src/blrec/core/retry.py @@ -3,7 +3,6 @@ from typing import Any, Callable, Optional, Type, cast from tenacity import wait_exponential, RetryCallState from tenacity import _utils -from tenacity import compat as _compat class wait_exponential_for_same_exceptions(wait_exponential): @@ -25,7 +24,6 @@ class wait_exponential_for_same_exceptions(wait_exponential): self._prev_exc_ts: Optional[float] = None self._last_wait_time: float = 0 - @_compat.wait_dunder_call_accept_old_params def __call__(self, retry_state: RetryCallState) -> float: if ( retry_state.outcome is not None and diff --git a/src/blrec/core/stream_recorder.py b/src/blrec/core/stream_recorder.py index d3be8f4..d1e9944 100644 --- a/src/blrec/core/stream_recorder.py +++ b/src/blrec/core/stream_recorder.py @@ -39,6 +39,7 @@ from ..bili.live import Live from ..bili.typing import QualityNumber from ..flv.stream_processor import StreamProcessor, BaseOutputFileManager from ..utils.mixins import AsyncCooperationMix, AsyncStoppableMixin +from ..path import escape_path from ..flv.exceptions import FlvStreamCorruptedError from ..bili.exceptions import ( LiveRoomHidden, LiveRoomLocked, LiveRoomEncrypted, NoStreamUrlAvailable @@ -501,10 +502,10 @@ class OutputFileManager(BaseOutputFileManager, AsyncCooperationMix): date_time = datetime.fromtimestamp(self._start_time) relpath = self.path_template.format( roomid=self._live.room_id, - uname=self._live.user_info.name, - title=self._live.room_info.title, - area=self._live.room_info.area_name, - parent_area=self._live.room_info.parent_area_name, + uname=escape_path(self._live.user_info.name), + title=escape_path(self._live.room_info.title), + area=escape_path(self._live.room_info.area_name), + parent_area=escape_path(self._live.room_info.parent_area_name), year=date_time.year, month=str(date_time.month).rjust(2, '0'), day=str(date_time.day).rjust(2, '0'), diff --git a/src/blrec/danmaku/io.py b/src/blrec/danmaku/io.py index 99b7e02..4aed8af 100644 --- a/src/blrec/danmaku/io.py +++ b/src/blrec/danmaku/io.py @@ -1,6 +1,8 @@ from __future__ import annotations import html import asyncio +import logging +import unicodedata from typing import AsyncIterator, Final, List, Any from lxml import etree @@ -16,6 +18,9 @@ from .models import ( __all__ = 'DanmakuReader', 'DanmakuWriter' +logger = logging.getLogger(__name__) + + class DanmakuReader: # TODO rewrite def __init__(self, path: str) -> None: self._path = path @@ -146,7 +151,12 @@ class DanmakuWriter: 'user': dm.uname, } elem = etree.Element('d', attrib=attrib) - elem.text = dm.text + try: + elem.text = dm.text + except ValueError: + # ValueError: All strings must be XML compatible: Unicode or ASCII, + # no NULL bytes or control characters + elem.text = remove_control_characters(dm.text) return ' ' + etree.tostring(elem, encoding='utf8').decode() + '\n' def _serialize_gift_send_record(self, record: GiftSendRecord) -> str: @@ -166,7 +176,10 @@ class DanmakuWriter: value_serializer=record_value_serializer, ) elem = etree.Element('sc', attrib=attrib) - elem.text = record.message + try: + elem.text = record.message + except ValueError: + elem.text = remove_control_characters(record.message) return ' ' + etree.tostring(elem, encoding='utf8').decode() + '\n' @@ -180,3 +193,7 @@ def record_value_serializer( if not isinstance(value, str): return str(value) return value + + +def remove_control_characters(s: str) -> str: + return ''.join(c for c in s if unicodedata.category(c) != 'Cc') diff --git a/src/blrec/path/__init__.py b/src/blrec/path/__init__.py index 9c39ff5..456416b 100644 --- a/src/blrec/path/__init__.py +++ b/src/blrec/path/__init__.py @@ -4,6 +4,7 @@ from .helpers import ( danmaku_path, raw_danmaku_path, extra_metadata_path, + escape_path, ) @@ -13,4 +14,5 @@ __all__ = ( 'danmaku_path', 'raw_danmaku_path', 'extra_metadata_path', + 'escape_path', ) diff --git a/src/blrec/path/helpers.py b/src/blrec/path/helpers.py index 2e9d774..1c5fff6 100644 --- a/src/blrec/path/helpers.py +++ b/src/blrec/path/helpers.py @@ -1,3 +1,4 @@ +import re import os from pathlib import PurePath @@ -8,6 +9,7 @@ __all__ = ( 'danmaku_path', 'raw_danmaku_path', 'extra_metadata_path', + 'escape_path', ) @@ -31,3 +33,7 @@ def raw_danmaku_path(video_path: str) -> str: def extra_metadata_path(video_path: str) -> str: return video_path + '.meta.json' + + +def escape_path(path: str) -> str: + return re.sub(r'[\\/:*?"<>|]', '', path)