You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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格式,但还是出现这个错误,求解决。


解决方案

核心问题分析

  1. 重复调用元数据编辑:download_audio函数内已经调用了一次edit_metadata,回到main后又调用了一次,第二次调用时原文件已经被重命名,导致Shazam找不到正确的文件。
  2. 文件未完全写入磁盘:yt-dlp完成转码后,文件可能还在缓冲写入阶段,此时立即读取会导致格式识别失败。
  3. 文件名拼接错误:手动拼接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()

关键修复点

  1. 移除重复的edit_metadata调用:只在main函数中调用一次元数据编辑。
  2. 正确获取转码后的文件名:通过ydl.prepare_filename(info_dict)获取yt-dlp生成的文件名,再替换为mp3后缀,避免手动拼接错误。
  3. 添加磁盘写入等待:用time.sleep(2)确保文件完全写入磁盘后再进行识别。
  4. 文件存在性检查:在编辑元数据前先确认文件存在,避免报错。
  5. 安全文件名处理:清理歌曲标题中的特殊字符,避免重命名失败;同时处理文件名重复的情况。
  6. 初始化音频标签:如果MP3文件没有标签,先调用initTag()初始化,防止eyed3报错。

内容的提问来源于stack exchange,提问作者skagamer543

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 15:53:20