Riff

Альбомы и Singles: реализованная FSM

Riff 1.0.0 включает взвешенную сборку альбомов, персональные и общие Singles, доказательства из имён файлов, владение FILF и удержание недоступных записей. Подписанный FINAL-релиз приложения публикуется вместе с соответствующими исходниками. Singles остаётся классификацией каталога; синтетический жанр не создаётся.

Player показывает персональные корзины как Artist ◇ Singles, а общую — как ◇ Singles. Это текстовые подписи в существующих строках. Идентичности, состав, порядок и входные имена A-Z сохранены; это изменение отображения не требует сброса базы медиатеки.

Область модели

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

Модель состоит из двух расширенных конечных автоматов: классификатора C и автомата публикации P. Данные каталога хранятся в регистрах; поэтому это EFSM, а не автомат с отдельным состоянием на каждый возможный альбом. Автомат доступности ортогонален классификации.

Cuetree определяет состав, Indexer публикует согласованный каталог, Player его читает. Модель запускается заново при изменении согласованных фактов, в том числе после IA. Существующие правила идентичности, конкурирующих изданий, копий и порядка позиций продолжают действовать.

Действующее объединение по весам сохраняется целиком. Состояние G вызывает существующую глобальную группировку RIFF-ALBUM-V2: структурные и артистные правила, ограничения изданий, взвешенный остаточный score AG-9 и проверку всех пар при объединении. Порог 65 и веса AG-9 не заменяются предикатами Singles. Сначала эта группировка собирает полный допустимый состав, затем C решает, показывать готовую группу отдельным альбомом или внутри корзины. Короткие подгруппы нельзя отправлять в Singles до завершения глобального объединения.

Для библиотеки iTunes сохраняются все прежние доказательства из тегов, каталогов и нумерации. Распознавание имени 0 01 … — дополнительное наблюдение для G, когда номер в тегах отсутствует или невалиден; оно не становится обязательным условием группировки. Неполная последовательность имён не отключает существующий score. Если новый предикат Singles не доказан, собранный прежним алгоритмом альбом остаётся альбомом. Изменение касается только классификации и представления результата.

AG-9: действующие веса и ограничения.

Нумерация имён проверяется до группировки

Полный успешный листинг должен содержать хотя бы два непосредственных аудиофайла с уникальной последовательностью 1..N: например, 01 … / 02 … или iTunes 0 01 … / 0 02 …. Неподдерживаемые WMA и аудиокандидаты с ошибкой учитываются как физические соседи, но это не делает их воспроизводимыми. Обложки, текст и плейлисты нейтральны. Вложенные папки, CUE/TOC, неизвестные непрозрачные файлы, ошибки наблюдения и неполный листинг не доказывают полноту.

Удерживаемые члены тоже должны образовать последовательность; все пригодные Track должны совпадать с именами, объявленные итоги не должны конфликтовать, содержательные Album не должны различаться. Структура глав запрещает такой вывод. Удерживаемые отсутствующие члены продолжают учитываться; новые файлы, ещё ожидающие метаданных, не создают преждевременного доказательства полноты. Производный номер заменяет лишь отсутствующее или невалидное доказательство порядка: он не записывается в Track или Total Tracks. Папка не придумывает Album или Artist. Если проверка не прошла, прежняя группировка использует остальные доказательства.

Входы и предикаты

Для поколения k сначала применяются обычные правила удержания и TTL. Вход R(k) содержит все успешно полученные и ещё удерживаемые факты, включая недоступные. Число N(g) — количество логических позиций группы после разрешения эквивалентных копий. Это не количество доступных файлов.

Обозначение Точное значение в реализации
explicit(g) Признанная валидная многотрековая CUE/TOC либо самостоятельно озаглавленная встроенная таблица; также удерживаемое явное владение до обычного истечения.
chapters(g) У одного из базовых MEDIA есть признанная таблица глав без самостоятельного названия альбома. Конкурирующие таблицы проходят существующий explicit-путь.
grouped(g) Обычная группировка независимо допустила группу по действующим правилам. Отсутствие Album не исправляется именем папки.
protected(g) Распознанная группа Book/речи, M4B либо потомок стандартного корня Audiobooks; защищённые обычные группы обходят новые предикаты Singles. Явные структуры и FILF идут по более ранним ветвям.
short(g) 1 <= N(g) <= 3, все длительности известны и 0 < duration < 600 s. Неполная нумерация и больший заявленный Total Tracks не мешают.
versions(g) 4 <= N(g) <= 16; один непротиворечивый диск; уникальные позиции 1..N; каждый присутствующий Total Tracks равен N; общий содержательный кредит исполнителя; не более трёх осмысленных точных основ и хотя бы один маркер версии. Защищённые номера частей должны совпадать.
djset(g) Общий содержательный кредит исполнителя и маркер DJ set, DJ mix, continuous mix или mixset в Album; либо у каждого члена есть одновременно жанровые компоненты Electronic/electronica и Live/live recording.
candidate(g) short(g) OR versions(g) OR djset(g), только после защитных ветвей.
owner(g) Однозначный содержательный кредит исполнителя, общий для всех позиций: сначала Album Artist, затем Track Artist. Неизвестные/служебные значения не создают владельца.

Все отсутствующие положительные доказательства дают false соответствующего предиката. Неизвестная длительность запрещает именно short, а не независимо доказанный versions. Противоречие не исправляется большинством голосов. Числа в названиях сохраняют смысл.

Длительность поступает из уже выполняемой ограниченной проверки источника Media3. Неизвестная длительность не запрещает поддерживаемое аудио и блокирует только short. djset — реализованная экспериментальная эвристика метаданных, а не утверждение об официальном типе релиза. Одного Live, большой длительности или имени артиста недостаточно. Списка разрешённых исполнителей и запросов в Интернет при работе нет.

Классификатор C

C = (Q, Σ, D, δ, E, O), где Q = {E, G, H, C, R, X, A, P, S, Z}, Σ = {evaluate, reset}, D — факты и предикаты выше. E — начальное состояние. O = {X, A, P, S, Z} — результаты: явный альбом, обычный альбом, персональные Singles, общие Singles и отсутствие членов. Буква P внутри C обозначает персональную корзину, а не автомат публикации.

FSM: явное владение, обычная группировка, главы, проверка Singles и выбор корзины

Открыть диаграмму отдельно. Диаграмма обзорная; таблица ниже задаёт все переходы. В каждом состоянии проверяется первая подходящая строка. Обозначение «иначе» — дополнение всех предыдущих условий этого состояния.

Из Условие события evaluate В Действие
E Нет удерживаемых позиций Z Не создавать непустую запись; старые пустые проекции живут по собственному TTL.
E explicit(g) X Сохранить объявленный порядок FILF; исключить соответствующие базовые MEDIA и их копии.
E Иначе G Выполнить существующую глобальную группировку базовых MEDIA, включая AG-9 и его веса.
G Есть главы H Разрешить принадлежность базового MEDIA, затем расширение глав.
G grouped(g), без глав C Передать разрешённые логические позиции.
G Иначе R Передать каждую невостребованную позицию отдельно, не изобретая релиз из общих Singles.
H grouped(g) A Заменить контейнеры упорядоченными FILF; остальные позиции сохранить.
H Иначе X Создать явный fallback-альбом таблицы; не отправлять контейнер или главы в Singles.
C protected(g) A Сохранить обычный альбом.
C candidate(g) R Перенести группу целиком; сохранить исходные теги и порядок внутри группы.
C Иначе A Сохранить обычный альбом.
R destination(g) определён однозначно P Добавить в персональную корзину, показанную как <Artist> ◇ Singles.
R Иначе S Добавить в общую корзину, показанную как ◇ Singles.
X/A/P/S/Z Любое evaluate без новых входов То же Результат вычисления неизменен.
Любое reset с новым согласованным входом E Пересчитать принадлежность по новому снимку.

Последняя строка имеет приоритет для события reset. Остальные неоговорённые события дают переход в то же состояние без изменения регистров. Сборщик сначала устанавливает явное владение во всём снимке; один и тот же логический контейнер нельзя пропустить через разные ветви благодаря разным физическим копиям. Это автомат решения о членстве, а не идентификатор каталожной записи.

Выделение основ и персональные корзины

Парсер основ снимает только распознанные завершающие обозначения версии: например, Song (Radio Edit), Song (Artist Remix), Song - Instrumental. Результат сравнивается точно после уже существующего канонического сравнения. Новая нормализация и Левенштейн не добавляются. Короткая основа вроде Go допустима. Произвольные скобки, номера частей, томов и одинаковый концертный суффикс не доказывают общую композицию. Извлечение основы не меняет фактический Title и не объединяет разные альбомы. Список распознаваемых целых слов задаётся при сборке; английские примеры — mix, remix, edit и instrumental. Шаблонные Track/Untitled/Audio не доказывают общую композицию. Суффикс после тире берётся от последнего разделителя: у Dream - Radio Day - Club Mix остаётся основа Dream - Radio Day.

Языковые признаки выбираются при сборке через genreLocales и catalogVocabulary в config/project.json. В этой версии выбраны английский, испанский, немецкий, русский и португальский. Cuetree получает эти значения от приложения; словарь библиотеки по умолчанию не добавляет языковых доказательств. Роли spoken, live и electronic определяются настроенными идентификаторами GNRE, без отдельного hardcode переводов жанров. Неоднозначные псевдонимы остаются нейтральными. Один словарь используется при сканировании и пересчёте TTL. Его изменение инвалидирует производные метаданные в обычном цикле индексирования.

Перед маршрутизацией рассчитывается фиксированная таблица eligible(artist) для всего снимка:

Условия альтернативны: студийный или полный альбом не обязателен. Учитываются удерживаемые недоступные группы; корзина сама себя не квалифицирует. Маршрутизация не меняет таблицу eligible в процессе обхода. Полные компоненты кредита сравниваются по действующим правилам; запятая или амперсанд не являются разрешением разрезать совместный кредит. Неоднозначный владелец ведёт в S. Персональная корзина и общая корзина не содержат копии одних и тех же назначенных позиций.

Детерминированный выбор корзины и стабильные ID

anchors — владельцы, прошедшие порог независимого полного альбома; eligible дополнительно включает владельцев трёх групп-кандидатов. Общие подтверждения — полные наборы компонентов Album Artist либо Track Artist, присутствующие у каждого члена группы. Основной владелец выбирается по общему Album Artist, затем по общему Track Artist. Условия проверяются по порядку:

  1. Использовать основного владельца, если он входит в anchors.
  2. Иначе использовать единственный общий anchor. Несколько общих anchor ведут в общие Singles.
  3. Иначе использовать основного владельца, прошедшего eligible.
  4. Иначе использовать единственный общий подходящий кредит; без однозначного выбора — общие Singles.

Так вычисляется destination(g) до переноса позиций. Уже невостребованные позиции маршрутизируются по одной и не создают фиктивных релизов; распознанные отдельные книжные источники не создают персональную музыкальную корзину. Выбранная группа-кандидат переносится целиком.

Персональный тип ARTIST_SINGLES получает имя <Artist>: singles и синтетические компоненты Album Artist. Общий тип SINGLES сохраняет имя [[SINGLES]]. У каждой корзины свой стабильный случайный ID; теги MDIA остаются фактическими. Опустевшая персональная корзина становится недоступной осиротевшей записью и истекает по обычному TTL. Только пустая общая корзина остаётся живой. Выделившийся из Singles обычный альбом получает идентичность альбома, а не ID корзины.

Указанные выше имена хранятся в ALBM. Player выбирает подпись по AlbumKind, объединяя полный кредит исполнителей через точку с запятой и пробел перед ромбом. Та же подпись используется в метаданных трека и очереди с контекстом альбома. Имена обычных и явных альбомов, даже похожие на технические, не меняются. Прежние имена по-прежнему задают порядок при равенстве ключей и поиск A-Z.

Публикация и время жизни

Автомат публикации: Qp = {IDLE, BUILD, VALIDATE, COMMIT}, начальное состояние IDLE. Регистр accepted указывает на последний принятый согласованный каталог; pending изолирован. События, не допустимые в текущем состоянии, не меняют accepted.

Из Событие / условие В Действие
IDLE Изменились согласованные входы BUILD Применить TTL; вычислить C для всех групп, затем корзины и связанные каталоги.
IDLE Входы неизменны IDLE Нет публикации и расхода TTL.
BUILD Расчёт завершён VALIDATE Проверить членство, ссылки, порядок, ID и согласованность полного набора.
VALIDATE Проверки пройдены COMMIT Подготовить активацию нового набора.
COMMIT Атомарная активация успешна IDLE Заменить accepted, зафиксировать новое поколение.
BUILD/VALIDATE/COMMIT Ошибка или отмена до активации IDLE Отбросить pending, сохранить accepted.

TTL измеряется поколениями успешной публикации, не минутами и не количеством запусков сканирования. Изменённые промежуточные публикации также расходуют TTL. Счётчик поколения и удаление фактов фиксируются вместе со снимком; проваленная попытка не старит принятый каталог. Если истечение в поколении публикации удалило исходные факты, состав оставшихся групп классифицируется заново — в том числе при публикации после анализа без нового сканирования. Истечение посторонней записи сохраняет актуальное расширение глав.

Автомат доступности V = {AVAILABLE, OFFLINE} на каждом обновлении переходит в AVAILABLE, если доступна хотя бы одна удерживаемая позиция, иначе в OFFLINE. Он не изменяет выход C. Доступность альбома — отдельный признак: есть хотя бы одна доступная позиция либо нет ни одной. Полностью недоступная удерживаемая запись не видна в доступном каталоге Player, но её состав продолжает участвовать в классификации и квалификации исполнителя до истечения удержания.

Проверяемые сценарии и инварианты

Событие Ожидаемый результат
Впервые найдены две короткие позиции из заявленных двенадцати Singles, даже при номерах 4/12 и 9/12.
К этим двум добавились остальные десять разных композиций Вся группа становится отдельным альбомом, если другие признаки Singles отсутствуют.
Из известных двенадцати временно доступны только две Альбом остаётся альбомом до TTL отсутствующих позиций.
Десять позиций действительно истекли по TTL Остаток пересчитывается и может перейти в Singles.
До TTL недоступны все двенадцать Состав удерживается; доступной карточки нет.
Один контейнер содержит десять валидных глав В ALBM представлены FILF; это не однотрековый Singles по числу файлов.
Явная таблица содержит два фрагмента Сохраняется явный альбом, правило короткой группы не применяется.
У исполнителя только три подтверждённых сета Возможна персональная корзина без студийного альбома.
Построение нового каталога отменено Предыдущий согласованный каталог остаётся принятым.

Каждая обычная назначенная позиция получает один выход A/P/S; явные таблицы сохраняют объявленные повторы и независимые варианты. Глобальная дедупликация по Media.ID запрещена. Контейнер никогда не публикуется вместе со своими FILF в альбомах или Singles. Изменение доступности само по себе не сокращает удерживаемый состав. ID корзины не превращается в ID выделенного из неё альбома.

Владельцы реализованных правил: классификация Singles, нумерация имён, Cuetree catalogs, группировка и жизненный цикл. Статья объясняет реализованную механику; нормативными остаются соответствующие контракты.

При исходной проверке Singles прошли 147 core- и шесть CLI-тестов, девять изолированных Android-тестов допуска источника, Android/JVM-проверки сборки и независимая сборка из исходников. Проверены состав, основы версий, порядок из имён, выделение альбома, TTL, FILF и границы ошибок/отмены. Эти результаты не означают завершение анализа всех медиатек или проверку живого автомобильного хоста.

Аудит 2026-10-18 добавил регрессии языковых признаков, имён файлов и границ отказа. До подготовки релиза прошли 661 JVM-тест, 15 Python-тестов и 27 изолированных тестов хранения/восстановления на HU. Это результаты конкретных прогонов, а не утверждение о проверке всех медиатек и устройств.