Мониторинг радиоэфира для вашего музыкального каталога

Источник: AudD•

Мониторинг радиоэфира для вашего музыкального каталога

Отслеживайте 24/7, какие песни из вашего каталога звучат на множестве радиостанций, используя аудиопотоки AudD, сохраняя каждое распознавание и агрегируя его в чарт радиоэфира.

Если вы являетесь лейблом, дистрибьютором или артистом, вам наверняка хочется знать, какие радиостанции крутят ваши треки и как часто. Данный рецепт описывает создание мониторинга эфира: AudD круглосуточно распознает песни из множества радиопотоков, ваш бэкенд записывает каждое воспроизведение, вы фильтруете нужные вам треки и агрегируете строки в чарт.

Что вы создадите

  • Множество потоков, зарегистрированных в AudD (по одному на каждую радиостанцию) — прямые ссылки HLS или Icecast, добавляемые через streams.add, каждая со своим уникальным radio_id.
  • Один обработчик обратного вызова (callback), который принимает каждую распознанную песню с любой станции (в аккаунте используется единственный URL обратного вызова) и записывает по одной строке на каждое воспроизведение.
  • Фильтр, оставляющий только те треки, которые входят в ваш каталог.
  • Запрос для чарта, агрегирующий сохраненные воспроизведения в количество прокруток на трек, на станцию, за день.

API потоков располагается по адресу https://api.audd.io/. По умолчанию AudD отправляет POST-запрос обратного вызова после окончания воспроизведения каждой песни, и этот callback включает общую продолжительность проигрывания — ровно то, что требуется для отчетов по ротации, поэтому, в отличие от живого оверлея, вы оставите настройки по умолчанию (без callbacks=before).

Для тестирования не требуется никакого кода. CLI-утилита AudD подписывается на станцию и сохраняет её воспроизведения локально: audd streams add <stream-url> --id 1, а затем audd streams report --by artist --since 7d. Создайте сервис, описанный ниже, когда вам понадобятся собственные хранилище и отчеты.

Для тестирования не требуется никакого кода. CLI-утилита AudD подписывается на станцию и сохраняет её воспроизведения локально: audd streams add <stream-url> --id 1, а затем audd streams report --by artist --since 7d. Создайте сервис, описанный ниже, когда вам понадобятся собственные хранилище и отчеты.

Предварительные требования

  • API-токен с сайта dashboard.audd.io. Аудиопотоки — это платное дополнение, тарифицируемое за каждый поток в месяц; тестовый токен не работает с потоками, поэтому используйте свой реальный токен.
  • Python 3.10+ с официальным SDK: pip install audd
  • База данных. В примерах для простоты переноса используется SQLite; для продакшена замените ее на Postgres.
  • Список URL-адресов потоков радиостанций. Многие станции публикуют прямые URL-адреса HLS/Icecast; например, https://npr-ice.streamguys1.com/live.mp3.

Пошаговое руководство

Шаг 1: Регистрация URL обратного вызова аккаунта (один раз)

URL обратного вызова настраивается для всего аккаунта — каждый добавленный вами поток отправляет отчеты на этот же URL. Установите его один раз перед добавлением любых станций.

Шаг 2: Добавление потока для каждой станции

Присвойте каждой станции целочисленный radio_id, который будет стабильным в вашей системе — сопоставьте его с вашим реестром станций, чтобы radio_id в callback-запросе позволял понять, какая именно станция проиграла песню.

streams.add принимает те же сокращения, что и любой поток — twitch:<channel>, youtube:<video_id>, youtube-ch:<channel_id>, — но для радио вы обычно будете передавать прямые URL-адреса HLS/Icecast. Чтобы перенаправить станцию, чей URL изменился, используйте audd.streams.set_url(radio_id=radio_id, url=new_url); audd.streams.delete(radio_id=...) удаляет один поток, а audd.streams.list() перечисляет все потоки в аккаунте.

Каждый поток тарифицируется ежемесячно. Добавляйте только те станции, за которыми вы активно следите, и удаляйте те, от которых отказываетесь. audd.streams.list() является источником достоверных данных о том, за что именно вы платите.

Каждый поток тарифицируется ежемесячно. Добавляйте только те станции, за которыми вы активно следите, и удаляйте те, от которых отказываетесь. audd.streams.list() является источником достоверных данных о том, за что именно вы платите.

Шаг 3: Создание базы данных

Две таблицы: сырой лог воспроизведений (по одной строке на каждую распознанную песню с каждой станции) и ваш каталог интересующих вас треков. Сохранение сырого лога означает, что вы сможете перезапустить фильтр каталога или запрос чарта позже без необходимости повторного мониторинга.

Ограничение UNIQUE имеет значение: если ваш обработчик возвращает код, отличный от 200, AudD ставит callback в очередь и отправляет его повторно позже. Операция upsert (вставка с обновлением) для этого ограничения превращает повторно доставленный callback в холостую операцию (no-op) вместо двойного учета.

Шаг 4: Обработчик обратного вызова

AudD отправляет POST-запрос с каждой распознанной песней на ваш единственный URL обратного вызова. Обработчик разбирает тело запроса, записывает строку воспроизведения и — отдельно — отмечает, является ли этот трек частью вашего каталога.

handle_callback считывает тело из запроса и возвращает пару (match, notification) — установлено ровно одно из значений. Объект StreamCallbackMatch содержит radio_id, timestamp, play_length и song (основное совпадение, содержащее блоки artist, title, album, score, song_link и provider, если вы установили return_metadata).

Шаг 5: Фильтрация по вашему каталогу

У вас есть два принципиально разных способа решить, «является ли это моим треком», и этот выбор определяет всю архитектуру пайплайна:

Сравнение с публичной базой данных AudD (любая песня) с последующей фильтрацией. Потоки распознаются по базе данных AudD, насчитывающей 160 миллионов треков, поэтому callback-запросы содержат каждую определяемую песню на станции — как вашу, так и чужую. Вы отфильтровываете свой каталог постфактум, по ISRC или по артисту/названию:

Это правильный подход, когда вам также нужен контектекст — что еще играет станция, как ваши треки располагаются в ротации, доля конкурентов.

Сравнение с вашим собственным пользовательским каталогом (только ваши треки). В качестве альтернативы загрузите свой каталог в функцию пользовательского каталога AudD, и потоки будут сопоставлять аудио только с вашими отпечатками. Тогда каждый callback по определению является одним из ваших треков — постфильтрация не требуется. Обратная сторона: сопоставления с пользовательским каталогом возвращают уникальный ID трека, который вы загрузили (в поле audio_id), а не полные публичные метаданные, и вы теряете контекст окружающей ротации.

Что использовать, зависит от того, нужен ли вам контекст станции (публичная БД, фильтрация после) или только ваши собственные прокрутки (пользовательский каталог, без фильтра). Полное сравнение и информацию о том, как запросить доступ к пользовательскому каталогу, смотрите в разделе Публичная база данных против вашего пользовательского каталога.

Шаг 6: Агрегация в чарт

По мере накопления воспроизведений чарт ротации представляет собой запрос с GROUP BY. Количество прокруток на трек за последние 7 дней:

spins — это главный показатель, stations показывает охват (сколько станций проиграло трек), а total_seconds — общая продолжительность звучания в эфире. Предварительно отфильтруйте таблицу воспроизведений по вашему каталогу (с помощью предложения WHERE isrc IN (SELECT isrc FROM catalog)) для получения чарта только по вашему каталогу.

Масштабирование на множество станций

Один аккаунт может обслуживать большой парк станций через один URL обратного вызова. Несколько моментов, о которых стоит подумать по мере роста числа станций:

  • Ограничение частоты callback-запросов (rate limit). При наличии менее чем 500 потоков AudD отправляет не более 3 callback-запросов в секунду (с пиковым ограничением token-bucket до 15). Ваш обработчик должен быстро отвечать 200 OK — выполняйте запись в базу данных асинхронно или помещайте тело в очередь и отправляйте подтверждение (ack) немедленно, вместо того чтобы блокировать ответ медленной вставкой.
  • Накопление очереди при простоях. Если ваш эндпоинт возвращает код, отличный от 200, или недоступен, AudD ставит callback-запросы в очередь и воспроизводит их заново, когда работоспособность восстанавливается. Ограничение UNIQUE из Шага 3 обеспечивает идемпотентность при повторной отправке.
  • Уведомления — ваш индикатор исправности. Коды 650 (не удается подключиться) и 651 (только белый шум) сообщают о том, что URL-адрес станции устарел. Отслеживайте, какие radio_id работают нестабильно, и обновляйте их с помощью set_url.
  • Один воркер, множество станций. Поскольку все станции используют общий URL обратного вызова, вы масштабируете приемник, а не поллер для каждой отдельной станции. Поместите обработчик за балансировщик нагрузки и очередь; сами станции представляют собой просто строки в streams.add.

Что вы получаете в ответ

Callback-запрос «песня закончилась» (по умолчанию, без callbacks=before) содержит продолжительность воспроизведения:

ISRC и UPC возвращаются в результатах, когда ваша учетная запись имеет тарифный план Startup или выше; читайте их как типизированные свойства s.isrc / s.upc. Любое другое поле, возвращаемое AudD, которое SDK не выставляет в виде типизированного свойства, доступно в карте model_extra модели. Для совпадений с пользовательским каталогом в результате содержится загруженный вами audio_id, а поля artist и title могут быть null.

Обработка ошибок

  • Ошибки аутентификации (AudDAuthenticationError) — неверный/отсутствующий токен или надстройка потоков не включена. Приводят к сбою при запуске.
  • Ошибки неверного запроса (AudDInvalidRequestError) — например, streams.add с некорректным URL или отсутствующим URL обратного вызова. Проверяйте URL перед массовым добавлением станций.
  • Уведомления о потоках (650 / 651) — доставляются в виде обратных вызовов уведомлений, а не исключений. Используйте их для обнаружения неработающих URL станций и перенаправления с помощью set_url.
  • Ошибки подключения (AudDConnectionError) — временные при вызовах управления (add, set_url, delete); повторяйте попытки с экспоненциальной задержкой. SDK уже повторяет идеодовые операции чтения.

Дальнейшие шаги

  • Ежедневные чарты и чарты по станциям. Добавьте GROUP BY radio_id или GROUP BY date(played_at), чтобы разделить проигрывания по станциям или дням.
  • Оповещения о первом воспроизведении. Когда вставка record_play для трека из каталога на станции, которая раньше его не проигрывала, проходит успешно, отправляйте уведомление — это момент, когда новая станция подхватывает ваш релиз.
  • Сверка вашего парка потоков. Запускайте audd.streams.list() по расписанию и сравнивайте его с вашим реестром станций, чтобы никогда не платить за поток, мониторинг которого вы прекратили.
  • Для сопоставления только с вашими собственными треками см. Публичная база данных против вашего пользовательского каталога.
  • Создание виджета «сейчас играет» для прямой трансляции
  • Публичная база данных против вашего пользовательского каталога
  • Документация по Python SDK
  • Справочник по Streams API

О чём эта статья