Всё началось с того, что знакомый музыкант спросил: «Где взять UPC-код релиза? Дистрибьютор потерял, а мне нужно для перевода каталога». Я полез искать — и обнаружил, что простого способа узнать UPC-код нет. Spotify его хранит, но не показывает в интерфейсе. Яндекс Музыка — аналогично. Существующие веб-сервисы поддерживают одну-две платформы и работают через раз.

Я решил написать Telegram-бота, который решает задачу универсально: кидаешь ссылку с любой музыкальной платформы — получаешь UPC-код за секунды. Казалось бы, задача на вечер. В реальности — три недели и парсеры для десяти платформ с совершенно разными API.

Что такое UPC-код и зачем он нужен

Для тех, кто не в музыкальной индустрии: UPC (Universal Product Code) — это уникальный штрих-код, который присваивается каждому музыкальному релизу. Не треку, а именно релизу: альбому, синглу, EP. Это как ISBN для книг, только для музыки.

UPC-код нужен музыкантам в нескольких ситуациях: при переходе между дистрибьюторами (чтобы не потерять историю стримов), при регистрации в организациях по управлению правами, для настройки пресейвов и для корректного учёта продаж. Проблема в том, что дистрибьюторы не всегда сообщают этот код артисту, а найти UPC-код самостоятельно — целый квест.

Помимо UPC, существует ISRC (International Standard Recording Code) — уникальный код для каждого отдельного трека. Если UPC — это «паспорт альбома», то ISRC — «паспорт песни». Оба кода нужны для корректной работы с правами и роялти. Бот умеет находить и то, и другое.

Какие платформы поддерживает бот

Главная техническая задача — универсальный парсер ссылок. Пользователь кидает ссылку, бот должен понять, с какой платформы она пришла, извлечь идентификатор и найти UPC-код релиза. Поддерживаемые платформы:

  • Spotify

  • Apple Music

  • Яндекс Музыка

  • YouTube Music / YouTube

  • Deezer

  • Tidal

  • SoundCloud

  • VK Музыка

  • Звук (Сбер Звук)

  • Amazon Music

У каждой платформы — свой формат ссылок, свои API (или их отсутствие), свои подводные камни.

Архитектура

Telegram (aiogram 3.x)
    ↓
URL Parser → определяет платформу
    ↓
Platform Resolver → извлекает метаданные
    ↓
UPC/ISRC Matcher → сопоставляет данные из нескольких источников
    ↓
Redis (кеш) + PostgreSQL (аналитика)

Ключевое архитектурное решение — разделить парсинг ссылки и поиск метаданных. URL Parser только определяет платформу и извлекает ID. Platform Resolver идёт за данными. Matcher сопоставляет и проверяет.

Парсер ссылок: 10 платформ — 10 форматов

Это первое, что пришлось решить. Каждая платформа имеет свой формат URL, причём у большинства — несколько вариантов:

import re
from dataclasses import dataclass
from enum import Enum

class Platform(Enum):
    SPOTIFY = "spotify"
    APPLE_MUSIC = "apple_music"
    YANDEX_MUSIC = "yandex_music"
    YOUTUBE_MUSIC = "youtube_music"
    DEEZER = "deezer"
    TIDAL = "tidal"
    SOUNDCLOUD = "soundcloud"
    VK_MUSIC = "vk_music"
    ZVUK = "zvuk"
    AMAZON_MUSIC = "amazon_music"

@dataclass
class ParsedLink:
    platform: Platform
    content_type: str  # "album", "track", "artist"
    content_id: str

PATTERNS = [
    # Spotify
    (r"open\.spotify\.com/(album|track)/([a-zA-Z0-9]+)",
     Platform.SPOTIFY),
    # Apple Music
    (r"music\.apple\.com/.+/(album|song)/.+?/(\d+)",
     Platform.APPLE_MUSIC),
    # Яндекс Музыка
    (r"music\.yandex\.\w+/album/(\d+)(?:/track/(\d+))?",
     Platform.YANDEX_MUSIC),
    # YouTube Music
    (r"music\.youtube\.com/watch\?v=([a-zA-Z0-9_-]+)",
     Platform.YOUTUBE_MUSIC),
    # Deezer
    (r"deezer\.com/.+/(album|track)/(\d+)",
     Platform.DEEZER),
    # Tidal
    (r"tidal\.com/.+/(album|track)/(\d+)",
     Platform.TIDAL),
    # VK Музыка
    (r"vk\.com/music/album/(-?\d+_\d+)",
     Platform.VK_MUSIC),
    # Звук
    (r"zvuk\.com/(release|track)/(\d+)",
     Platform.ZVUK),
    # Amazon Music
    (r"music\.amazon\.\w+/.+/(albums|tracks)/([A-Z0-9]+)",
     Platform.AMAZON_MUSIC),
]

Отдельная боль — шорт-ссылки. Spotify использует spotify.link/..., YouTube — youtu.be/..., Яндекс Музыка — укороченные ссылки через music.yandex.ru/.... Перед парсингом нужно развернуть шорт-ссылку:

async def resolve_short_url(url: str) -> str:
    async with aiohttp.ClientSession() as session:
        async with session.head(
            url, allow_redirects=True, timeout=5
        ) as resp:
            return str(resp.url)

Spotify API: UPC есть, но достать непросто

Spotify Web API — главный источник. UPC хранится в объекте альбома в поле external_ids.upc, ISRC — в объекте трека в external_ids.isrc.

{
  "external_ids": {
    "upc": "886445413816"
  },
  "name": "Random Access Memories",
  "label": "Columbia",
  "release_date": "2013-05-17",
  "tracks": {
    "items": [
      {
        "name": "Give Life Back to Music",
        "external_ids": {"isrc": "USCO11300095"}
      }
    ]
  }
}

Проблема 1: UPC иногда не возвращается. Периодически Spotify API отдаёт объект альбома без поля upc в external_ids. Баг известен сообществу, Spotify его чинили, но он возвращается. Решение — retry с параметром market (перебираю US, GB, DE, JP), потому что для разных рынков API может вернуть разные данные.

Проблема 2: поиск по UPC нестабилен. API поддерживает фильтр upc: в поисковом эндпоинте, но на практике работает через раз. Для некоторых UPC возвращает пустой результат, хотя альбом существует. Особенно плохо с региональными релизами.

Проблема 3: rate limits. 180 запросов в минуту на приложение. Один пользовательский запрос может превратиться в 3–5 API-вызовов (поиск + получение альбома + retry по рынкам + ISRC для каждого трека). Redis-кеш с TTL 24 часа спасает:

async def get_album_data(album_id: str) -> dict | None:
    cache_key = f"album:{album_id}"
    cached = await redis.get(cache_key)
    if cached:
        return json.loads(cached)
    
    for market in ["US", "GB", "DE", "JP"]:
        album = await spotify.get_album(album_id, market=market)
        upc = album.get("external_ids", {}).get("upc")
        if upc:
            result = {
                "upc": upc,
                "name": album["name"],
                "artist": album["artists"][0]["name"],
                "label": album.get("label"),
                "release_date": album.get("release_date"),
                "tracks": extract_isrc_list(album),
            }
            await redis.set(cache_key, json.dumps(result), ex=86400)
            return result
    return None

Яндекс Музыка и VK: без официального API

Если Spotify и Apple Music хотя бы имеют документированные API, то Яндекс Музыка и VK Музыка — это совершенно другая история. Официального публичного API нет. Приходится работать с недокументированными эндпоинтами.

Яндекс Музыка имеет внутренний API, который используется мобильным приложением. Формат запросов обратно-инженерен сообществом. UPC-код хранится в метаданных альбома, но отдаётся не всегда. Для получения ISRC нужно запрашивать каждый трек отдельно.

Главная проблема: эти API могут измениться в любой момент без предупреждения. За три месяца работы бота Яндекс Музыка дважды меняла формат ответов — приходилось экстренно обновлять парсеры.

Мульти-версии: один трек — несколько UPC

Неочевидная фича, которая оказалась самой полезной: обнаружение всех версий релиза. Один и тот же трек может быть выпущен как сингл, как часть альбома и как часть сборника — и у каждого варианта будет свой UPC-код.

Это критически важно при переходе между дистрибьюторами: музыканту нужно знать все UPC-коды всех версий, иначе часть стримов «потеряется».

Бот находит все версии через поиск по ISRC трека: если ISRC один и тот же, а UPC-коды разные — значит, это перезалив или другая версия релиза. Пользователю отображаются все найденные варианты.

Полный режим: UPC + ISRC + BPM

Команда /full отдаёт максимум информации о релизе:

  • UPC-код релиза (и всех его версий)

  • ISRC-коды всех треков

  • Лейбл и дата релиза

  • Жанр

  • BPM каждого трека (через Spotify Audio Features API)

BPM — бонусная фича, но музыканты и диджеи её оценили. Spotify Audio Features API отдаёт tempo для каждого трека, точность обычно достаточная.

Обработка пользовательского ввода

Пользователи вводят запросы в непредсказуемых форматах. Коллекция за первый месяц:

"дайте код моего последнего релиза"
"upc Моргенштерн"
"https://open.spotify.com/album/4LH4d3cOWNNsVw41Gqt2kv"
"886445413816"
"как найти upc код релиза"
"https://music.yandex.ru/album/12345"

Бот обрабатывает все варианты: ссылки парсит через URL Parser, чистые числа из 12–13 цифр воспринимает как UPC-код и ищет по нему, текстовые запросы ищет как название артиста/альбома и предлагает варианты inline-кнопками.

Что я бы сделал иначе

MusicBrainz с первого дня. Открытая база данных с UPC-кодами (там они называются «barcode»). Покрытие инди-релизов хуже, чем у Spotify, но как третий источник для перекрёстной проверки — стоило добавить сразу.

Message queue вместо синхронной обработки. При пиковых нагрузках (когда кто-то делает тред в Twitter со ссылкой на бота) все запросы идут одновременно, и бот начинает упираться в rate limits всех платформ сразу. Очередь с приоритетами решила бы проблему элегантнее.

Мониторинг API-изменений. Недокументированные API Яндекс Музыки и VK ломаются без предупреждения. Нужен health-check, который раз в час проверяет, что все платформы отвечают корректно, и алертит в Telegram, если что-то сломалось.

Результаты

Бот работает три месяца. Цифры:

  • ~500 уникальных пользователей в месяц

  • Среднее время ответа — 1.5 секунды (с кешем — 0.3с)

  • Процент успешных поисков — 89%

  • Самый частый сценарий — поиск UPC-кода последнего релиза по ссылке из Spotify

  • Самая популярная платформа среди пользователей — Яндекс Музыка (43%), затем Spotify (31%)

Бот доступен в Telegram: @upc_music_bot

Технические выводы

Работа с музыкальными API — это постоянная борьба с нестабильностью и фрагментацией. У каждой платформы свой формат данных, свои ограничения, свои баги. Единого стандарта нет, и UPC-код, который должен быть универсальным идентификатором, на практике спрятан за десятью разными API.

Главный архитектурный урок: для любого агрегатора, зависящего от внешних API, нужно минимум два источника данных на каждый тип информации и агрессивный кеш. Один API обязательно сломается — вопрос когда, а не если.

Второй урок: недокументированные API — это техдолг с первого дня. Они работают, пока работают. Закладывайте время на экстренные фиксы в ваш roadmap.

Если делаете что-то похожее — начинайте с Spotify API (лучшая документация) и iTunes Search API (бесплатный, без авторизации), а остальные платформы добавляйте по мере необходимости.


Если вы музыкант и вам нужно быстро найти UPC-код вашего релиза — попробуйте бота: t.me/upc_music_bot. Работает с Яндекс Музыкой, Spotify, Apple Music и ещё семью платформами.

Если вы разработчик и работали с музыкальными API — расскажите про свои грабли в комментариях.