Shazamio无法识别音频格式问题求助(附Python完整代码)
问题
我写了一个Python脚本,用来从YouTube下载音乐并编辑元数据(添加封面、名称等),完整代码如下:
from blessings import Terminal from shazamio import Shazam, Serialize import asyncio import yt_dlp import warnings import eyed3 import requests import urllib import os #temporary warnings.filterwarnings("ignore") t = Terminal() def download_audio(url): """ Downloads an audio from youtube """ ydl_opts = { 'format': 'bestaudio/beyst', 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }], 'outtmpl': '%(title)s.%(ext)s', } filename = None with yt_dlp.YoutubeDL(ydl_opts) as ydl: info_dict = ydl.extract_info(url, download=True) video_title = info_dict.get('title', None) print(video_title) filename = f"{video_title}.mp3" edit_metadata(filename) print(t.green+"Successfully Downloaded - check the local folder") return filename async def recognize_audio(audio): shazam = Shazam() out = await shazam.recognize(audio) song_info = {} track_info = out["track"] song_info["title"] = track_info["title"] song_info["artist"] = track_info["subtitle"] song_info["img"] = track_info["images"]["coverart"] return song_info def get_image(url): response = urllib.request.urlopen(url) imagedata = response.read() return imagedata def edit_metadata(audio): songinfo = asyncio.run(recognize_audio(audio)) img_data = get_image(songinfo["img"]) audiofile = eyed3.load(audio) audiofile.tag.artist = songinfo["artist"] audiofile.tag.title = songinfo["title"] audiofile.tag.images.set(type_=3, img_data=img_data, mime_type="images/jpeg", description=u"Cover art", img_url= u""+songinfo["img"]) #renaming the file os.rename(audio, f'{songinfo["title"]}.mp3') audiofile.tag.save() def main(): url = input("Paste a link from YouTube to the song you want to download: ") #getting filename of the download f = download_audio(url) print(f) edit_metadata(f) if __name__ == "__main__": main()
单独调用download_audio和edit_metadata时脚本正常工作,但整体运行时会报错:
Traceback (most recent call last): File "/Users/user/Desktop/programming/musicdownloader/main.py", line 83, in <module> main() File "/Users/user/Desktop/programming/musicdownloader/main.py", line 80, in main edit_metadata(f) File "/Users/user/Desktop/programming/musicdownloader/main.py", line 61, in edit_metadata songinfo = asyncio.run(recognize_audio(audio)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 190, in run return runner.run(main) ^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 118, in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "/Users/user/Desktop/programming/musicdownloader/main.py", line 44, in recognize_audio out = await shazam.recognize(audio) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/user/Desktop/programming/musicdownloader/env/lib/python3.11/site-packages/shazamio/api.py", line 578, in recognize signature = await self.core_recognizer.recognize_path(data) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SignatureError: Unrecognized format
脚本会先下载MP4,转成Webm再转为MP3,我确认下载后的文件是MP3格式,但还是出现这个错误,求解决。
解决方案
核心问题分析
- 重复调用元数据编辑:
download_audio函数内已经调用了一次edit_metadata,回到main后又调用了一次,第二次调用时原文件已经被重命名,导致Shazam找不到正确的文件。 - 文件未完全写入磁盘:yt-dlp完成转码后,文件可能还在缓冲写入阶段,此时立即读取会导致格式识别失败。
- 文件名拼接错误:手动拼接
f"{video_title}.mp3"不可靠,yt-dlp的源格式可能是m4a或webm,转码后的文件名不一定和标题直接对应。
修复后的完整代码
from blessings import Terminal from shazamio import Shazam, Serialize import asyncio import yt_dlp import warnings import eyed3 import urllib import os import time # temporary warnings.filterwarnings("ignore") t = Terminal() def download_audio(url): """ Downloads an audio from youtube """ ydl_opts = { 'format': 'bestaudio/best', 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }], 'outtmpl': '%(title)s.%(ext)s', } filename = None with yt_dlp.YoutubeDL(ydl_opts) as ydl: info_dict = ydl.extract_info(url, download=True) # 直接从yt-dlp获取转码后的正确文件名,避免手动拼接错误 filename = ydl.prepare_filename(info_dict).replace('.webm', '.mp3').replace('.m4a', '.mp3') print(f"Downloaded file: {filename}") print(t.green+"Successfully Downloaded - waiting for file to save...") # 短暂等待确保文件完全写入磁盘 time.sleep(2) return filename async def recognize_audio(audio): shazam = Shazam() out = await shazam.recognize(audio) song_info = {} track_info = out["track"] song_info["title"] = track_info["title"] song_info["artist"] = track_info["subtitle"] song_info["img"] = track_info["images"]["coverart"] return song_info def get_image(url): response = urllib.request.urlopen(url) imagedata = response.read() return imagedata def edit_metadata(audio): # 先检查文件是否存在 if not os.path.exists(audio): print(t.red+"Error: Target file not found!") return songinfo = asyncio.run(recognize_audio(audio)) img_data = get_image(songinfo["img"]) audiofile = eyed3.load(audio) # 初始化标签(如果文件无标签的话) if not audiofile.tag: audiofile.initTag() audiofile.tag.artist = songinfo["artist"] audiofile.tag.title = songinfo["title"] audiofile.tag.images.set(type_=3, img_data=img_data, mime_type="image/jpeg", description="Cover art") # 生成安全文件名,避免特殊字符导致的重命名失败 safe_title = "".join([c for c in songinfo["title"] if c.isalnum() or c in (' ', '-', '_')]).rstrip() new_filename = f'{safe_title}.mp3' # 处理文件名重复的情况 counter = 1 while os.path.exists(new_filename): new_filename = f'{safe_title}_{counter}.mp3' counter +=1 os.rename(audio, new_filename) audiofile.tag.save() print(t.green+f"Metadata updated and file renamed to: {new_filename}") def main(): url = input("Paste a link from YouTube to the song you want to download: ") f = download_audio(url) edit_metadata(f) if __name__ == "__main__": main()
关键修复点
- 移除重复的
edit_metadata调用:只在main函数中调用一次元数据编辑。 - 正确获取转码后的文件名:通过
ydl.prepare_filename(info_dict)获取yt-dlp生成的文件名,再替换为mp3后缀,避免手动拼接错误。 - 添加磁盘写入等待:用
time.sleep(2)确保文件完全写入磁盘后再进行识别。 - 文件存在性检查:在编辑元数据前先确认文件存在,避免报错。
- 安全文件名处理:清理歌曲标题中的特殊字符,避免重命名失败;同时处理文件名重复的情况。
- 初始化音频标签:如果MP3文件没有标签,先调用
initTag()初始化,防止eyed3报错。
内容的提问来源于stack exchange,提问作者skagamer543
相关产品推荐
相关产品推荐

