ГлавнаяБлогСемантическое разрешение метаданных в музыке: алгоритм на Python
Python

Семантическое разрешение метаданных в музыке: алгоритм на Python

Узнайте, как с помощью Python и алгоритмов сопоставлять ISRC и ISWC, чтобы находить невостребованные роялти. Реализуйте пайплайн разрешения метаданных прямо сейчас.

Al
Редакция Algolitalgolit.ru
10 мин чтения27 июня 2026 г.

Проблема разрыва метаданных в музыкальной индустрии

В глобальной экономике стриминга Spotify, Apple Music и другие платформы ежедневно обрабатывают миллиарды воспроизведений. За этим слоем транзакций скрывается фрагментированная двойная структура авторских прав:

  • Запись (Master Right): идентифицирует аудиофайл через код ISRC.
  • Композиция (Publishing Right): идентифицирует мелодию, текст и аранжировку через код ISWC.

Реестры ISRC и ISWC управляются разными глобальными организациями (IFPI и CISAC), и единого централизованного сопоставления между ними нет. Из-за этого миллионы долларов механических роялти остаются невостребованными в «чёрных ящиках» организаций коллективного управления (CMO). В этой статье мы спроектируем и реализуем высокопроизводительный протокол семантического разрешения сущностей (SERP) для программного устранения этого разрыва.

Пайплайн разрешения SERP

Сопоставление записей требует многоуровневого классификационного пайплайна. Поскольку ручное сопоставление логистически невозможно, мы реализуем трёхуровневый алгоритмический подход:

┌────────────────────────┐
│ Загрузка записей и     │
│ композиций             │
└───────────┬────────────┘
            │
            ▼
┌────────────────────────┐
│ 1. Нормализованный     │ ──[Сходство < 0.85]──> [Очередь несовпадений]
│    фильтр расстояния   │
└───────────┬────────────┘
            │ [Сходство >= 0.85]
            ▼
┌────────────────────────┐
│ 2. Пересечение         │ ──[Нет пересечения]──> [Очередь несовпадений]
│    создателей          │
└───────────┬────────────┘
            │ [Пересечение >= 1]
            ▼
┌────────────────────────┐
│ 3. Проверка допуска    │ ──[Дельта > 4с]──> [Ручная верификация]
│    длительности        │
└───────────┬────────────┘
            │ [Дельта <= 4с]
            ▼
┌────────────────────────┐
│   Верифицированная     │
│   связь для CMO        │
└────────────────────────┘

Шаг 1: Нормализация и фильтр строкового сходства

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

Sim(s1, s2) = 1 − [ Levenshtein(s1, s2) / max(|s1|, |s2|) ]

Если показатель сходства превышает 0.85, запись переходит к следующему этапу.

Шаг 2: Матрица пересечения соавторов

Чтобы избежать сопоставления кавер-версий или семплов с похожими названиями, мы строим матрицу пересечения исполнителей записи и зарегистрированных авторов. Если пересечение создателей больше или равно 1, запись проходит дальше.

Шаг 3: Проверка допуска длительности

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

|D_ISRC − D_ISWC| ≤ 4 секунды

Реализация на Python с Polars

Ниже приведена эталонная реализация с использованием высокопроизводительной библиотеки Polars для выполнения пайплайна над большими наборами данных:

import polars as pl

def resolve_metadata_gap(recordings_df: pl.DataFrame, compositions_df: pl.DataFrame) -> pl.DataFrame:
    # 1. Очистка и нормализация идентификаторов (удаление дефисов, точек, пробелов)
    cleaned_rec = recordings_df.with_columns([
        pl.col("isrc").str.replace_all(r"[\s-]", "").str.to_uppercase(),
        pl.col("track_title").str.to_lowercase().str.replace_all(r"[^\w\s]", "").str.strip_chars()
    ])
    cleaned_comp = compositions_df.with_columns([
        pl.col("iswc").str.replace_all(r"[\s.-]", "").str.to_uppercase(),
        pl.col("work_title").str.to_lowercase().str.replace_all(r"[^\w\s]", "").str.strip_chars()
    ])

    # 2. Соединение наборов данных по нормализованным названиям для сокращения пространства поиска
    joined = cleaned_rec.join(cleaned_comp, left_on="track_title", right_on="work_title", how="inner")

    # 3. Применение проверки допуска длительности (<= 4 секунды)
    matched_candidates = joined.filter(
        (pl.col("duration_sec_rec") - pl.col("duration_sec_comp")).abs() <= 4
    )

    # 4. Проверка пересечения создателей: хотя бы один автор присутствует в исполнителе
    matched_candidates = matched_candidates.filter(
        pl.struct(["artist_name", "songwriters"]).map_batches(
            lambda s: pl.Series([
                any(writer.lower() in row["artist_name"].lower() for writer in row["songwriters"])
                for row in s.to_list()
            ])
        )
    )

    return matched_candidates.select(["isrc", "iswc", "track_title", "artist_name", "duration_sec_rec", "duration_sec_comp"])

# Пример тестовых данных
recordings = pl.DataFrame({
    "isrc": ["US-RC1-23-00001", "GB-AHT-24-99882"],
    "track_title": ["Starlight (Midnight Edit)", "Hold Me Back"],
    "artist_name": ["Jane Doe & The Band", "John Smith"],
    "duration_sec_rec": [242, 180]
})

compositions = pl.DataFrame({
    "iswc": ["T-123.456.789-1", "T-987.654.321-0"],
    "work_title": ["Starlight", "Hold Me Back (Live Version)"],
    "songwriters": [["Jane Doe", "Alex Wright"], ["John Smith"]],
    "duration_sec_comp": [240, 310]  # Второй трек не проходит проверку длительности
})

resolution = resolve_metadata_gap(recordings, compositions)
print("Успешно сопоставленные метаданные:")
print(resolution)

Масштабирование пайплайна

При работе с каталогами, содержащими миллионы строк данных стриминга и отчётов о выплатах, встроенная строковая обработка медленна. В производственных системах загружают кэши Arrow и используют оркестраторы с состоянием (например, LangGraph и векторные базы данных) для обработки семантических краевых случаев.

Практический вывод: прямо сейчас возьмите приведённый код и адаптируйте его под свои данные: замените тестовые DataFrame на реальные таблицы ISRC и ISWC. Начните с одного альбома, затем масштабируйте.

#семантическое разрешение#метаданные музыки#ISRC#ISWC#Polars
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →