Извлечение ID3-тегов MP3-URL с частичной загрузкой с использованием Python

Мне нужно извлечь теги ID3 и метаданные удаленных mp3-файлов.

Я написал несколько строк, которые могли бы получить теги ID3 локального файла:

from mutagen.mp3 import MP3
import urllib2

audio = MP3("Whistle.mp3")

songtitle = audio["TIT2"]
artist = audio["TPE1"]

print "Title: " + str(songtitle)
print "Artist: "+str(artist)

Мне нужно добиться этого для URL-ссылок для mp3-файлов. Я пытался получить частичную загрузку файлов, используя urllib2.

import urllib2
from mutagen.mp3 import MP3

req = urllib2.Request('http://www.1songday.com/wp-content/uploads/2013/08/Lorde-Royals.mp3')
req.headers['Range'] = 'bytes=%s-%s' % (0, 100)
response = urllib2.urlopen(req)
headers = response.info()
print headers.type
print headers.maintype

data = response.read()
print len(data)

Как извлечь теги ID3 из MP3-адреса, не загружая файл полностью?

2 ответа

Теги id3 хранятся в метаданных ID3, которые обычно располагаются перед фреймами mp3 (содержащими аудио), но стандарт mp3 позволяет им также "следовать фреймам mp3".

Для загрузки минимального количества байтов вам необходимо:

  1. загрузите первые 10 байтов mp3, извлеките заголовок ID3v2 и вычислите размер заголовка id3v2
  2. получить полную загрузку тегов id3v2 size байты mp3
  3. использовать библиотеку Python для извлечения тегов ID3

Вот скрипт (python 2 или 3), который извлекает обложку альбома с минимальным объемом загружаемого файла:

try:
    import urllib2 as request # python 2
except ImportError:
    from urllib import request # python 3
    from functools import reduce
import sys
from io import BytesIO
from mutagen.mp3 import MP3

url = sys.argv[1]

def get_n_bytes(url, size):
    req = request.Request(url)
    req.headers['Range'] = 'bytes=%s-%s' % (0, size-1)
    response = request.urlopen(req)
    return response.read()

data = get_n_bytes(url, 10)
if data[0:3] != 'ID3':
    raise Exception('ID3 not in front of mp3 file')

size_encoded = bytearray(data[-4:])
size = reduce(lambda a,b: a*128+b, size_encoded, 0)

header = BytesIO()
# mutagen needs one full frame in order to function. Add max frame size
data = get_n_bytes(url, size+2881) 
header.write(data)
header.seek(0)
f = MP3(header)

if f.tags and 'APIC:' in f.tags.keys():
    artwork = f.tags['APIC:'].data
    with open('image.jpg', 'wb') as img:
       img.write(artwork)

Несколько замечаний:

  • он проверяет, что ID3 находится перед файлом и что это ID3v2
  • размер тегов id3 хранится в байтах от 6 до 9, как описано на id3.org
  • к сожалению, мутагену нужна одна полная аудиофрейм в формате mp3 для анализа тегов id3 Поэтому вам также необходимо загрузить один mp3-кадр (длина которого не более 2881 байт согласно этому комментарию)
  • вместо того, чтобы слепо предполагать, что обложка альбома - jpg, вы должны сначала проверить формат изображения, поскольку id3 допускает много разных типов изображений.
  • протестировано около 10 случайных mp3-файлов из интернета, например: python url.py http://www.fuelfriendsblog.com/listenup/01%20America.mp3

В вашем примере теги ID3 не извлекаются, поэтому вы не можете их извлечь.

Я немного поиграл после прочтения спецификации для ID3, и вот хороший способ начать.

#Search for ID3v1 tags
import string
tagIndex = string.find(data,'TAG')
if (tagIndex>0):
  if data[tagIndex+3]=='+': 
    print "Found extended ID3v1 tag!"
    title = data[tagIndex+3:tagIndex+63]
    print title
  else:
    print  "Found ID3v1 tags"
    title = data[tagIndex+3:tagIndex+33]
    print title
    #So on.
else:
  #Look for ID3v2 tags
  if 'TCOM' in data:
    composerIndex = string.find(data,'TCOM')
    #and so on. See wikipedia for a full list of frame specifications
Другие вопросы по тегам