Skip to content

Commit 884cd10

Browse files
committed
fix(components): Record audio files incorrectly saved with .jpg extension
Record.convert_to_file_path() was using download_image_by_url() for audio URLs, causing audio files to be saved with .jpg extension. The same bug existed in convert_to_base64(). Added download_audio_by_url() and save_temp_audio() to io.py for proper audio download handling, and updated Record methods to use them. Also fixed base64 decoded audio to use .audio extension instead of .jpg. Fixes #6853 Fixes #6509
1 parent a78a55b commit 884cd10

2 files changed

Lines changed: 49 additions & 6 deletions

File tree

astrbot/core/message/components.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@
3636

3737
from astrbot.core import astrbot_config, file_token_service, logger
3838
from astrbot.core.utils.astrbot_path import get_astrbot_temp_path
39-
from astrbot.core.utils.io import download_file, download_image_by_url, file_to_base64
39+
from astrbot.core.utils.io import ( # noqa: I001
40+
download_audio_by_url,
41+
download_file,
42+
download_image_by_url,
43+
file_to_base64,
44+
)
4045

4146

4247
class ComponentType(str, Enum):
@@ -157,16 +162,16 @@ async def convert_to_file_path(self) -> str:
157162
if self.file.startswith("file:///"):
158163
return self.file[8:]
159164
if self.file.startswith("http"):
160-
file_path = await download_image_by_url(self.file)
165+
file_path = await download_audio_by_url(self.file)
161166
return os.path.abspath(file_path)
162167
if self.file.startswith("base64://"):
163168
bs64_data = self.file.removeprefix("base64://")
164-
image_bytes = base64.b64decode(bs64_data)
169+
audio_bytes = base64.b64decode(bs64_data)
165170
file_path = os.path.join(
166-
get_astrbot_temp_path(), f"recordseg_{uuid.uuid4()}.jpg"
171+
get_astrbot_temp_path(), f"recordseg_{uuid.uuid4()}.audio"
167172
)
168173
with open(file_path, "wb") as f:
169-
f.write(image_bytes)
174+
f.write(audio_bytes)
170175
return os.path.abspath(file_path)
171176
if os.path.exists(self.file):
172177
return os.path.abspath(self.file)
@@ -185,7 +190,7 @@ async def convert_to_base64(self) -> str:
185190
if self.file.startswith("file:///"):
186191
bs64_data = file_to_base64(self.file[8:])
187192
elif self.file.startswith("http"):
188-
file_path = await download_image_by_url(self.file)
193+
file_path = await download_audio_by_url(self.file)
189194
bs64_data = file_to_base64(file_path)
190195
elif self.file.startswith("base64://"):
191196
bs64_data = self.file

astrbot/core/utils/io.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,44 @@
1919
logger = logging.getLogger("astrbot")
2020

2121

22+
def save_temp_audio(audio_data: bytes) -> str:
23+
"""Save audio data to a temporary file with a proper extension."""
24+
temp_dir = get_astrbot_temp_path()
25+
timestamp = f"{int(time.time())}_{uuid.uuid4().hex[:8]}"
26+
p = os.path.join(temp_dir, f"recordseg_{timestamp}.audio")
27+
with open(p, "wb") as f:
28+
f.write(audio_data)
29+
return p
30+
31+
32+
async def download_audio_by_url(url: str) -> str:
33+
"""Download audio from URL. Returns local file path."""
34+
try:
35+
ssl_context = ssl.create_default_context(cafile=certifi.where())
36+
connector = aiohttp.TCPConnector(ssl=ssl_context)
37+
async with aiohttp.ClientSession(
38+
trust_env=True,
39+
connector=connector,
40+
) as session:
41+
async with session.get(url) as resp:
42+
data = await resp.read()
43+
return save_temp_audio(data)
44+
except (aiohttp.ClientConnectorSSLError, aiohttp.ClientConnectorCertificateError):
45+
logger.warning(
46+
f"SSL certificate verification failed for {url}. "
47+
"Disabling SSL verification (CERT_NONE) as a fallback."
48+
)
49+
ssl_context = ssl.create_default_context()
50+
ssl_context.check_hostname = False
51+
ssl_context.verify_mode = ssl.CERT_NONE
52+
async with aiohttp.ClientSession() as session:
53+
async with session.get(url, ssl=ssl_context) as resp:
54+
data = await resp.read()
55+
return save_temp_audio(data)
56+
except Exception as e:
57+
raise e
58+
59+
2260
def on_error(func, path, exc_info) -> None:
2361
"""A callback of the rmtree function."""
2462
import stat

0 commit comments

Comments
 (0)