# -*- coding: utf-8 -*- """同步流式响应垫片(音频分段透传)。 """ class _SyncStreamResponse: """同步流式响应包装(把 aiohttp 的异步流按需拉成同步迭代)。 供流式下载使用:worker 是同步线程,这里经 core.async_bridge 在后台事件循环 逐块取数据,避免引入 requests 依赖(Flask 已删除、requests 已移出依赖)。 """ def __init__(self, url: str, timeout: float): from core import async_bridge self._bridge = async_bridge self._url = url self._timeout = timeout self._agent = None self.status = 0 self.headers: dict[str, str] = {} self._resp = None def __enter__(self): self._agent = self._bridge.run_sync(self._open()) self.status = self._agent[0] self.headers = self._agent[1] return self async def _open(self): from core.http_client import client resp = await client.open('GET', self._url, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 ' '(KHTML, like Gecko) Safari/537.36', 'Referer': 'https://music.163.com/', }, timeout=self._timeout) return resp.status, dict(resp.headers), resp def iter_content(self, chunk_size: int = 256 * 1024): assert self._agent is not None resp = self._agent[2] it = resp.content.iter_chunked(chunk_size) while True: try: chunk = self._bridge.run_sync(it.__anext__()) except StopAsyncIteration: break yield chunk def __exit__(self, *exc): if self._agent is not None and self._agent[2] is not None: try: self._agent[2].close() except Exception: pass return False def _stream_open(url: str, timeout: float = 60) -> _SyncStreamResponse: """打开同步可迭代的流式响应(见 _SyncStreamResponse)。""" return _SyncStreamResponse(url, timeout)