Альбомы и 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
обозначает персональную корзину, а не автомат публикации.
Открыть диаграмму отдельно. Диаграмма обзорная; таблица ниже задаёт все переходы. В каждом состоянии проверяется первая подходящая строка. Обозначение «иначе» — дополнение всех предыдущих условий этого состояния.
| Из | Условие события 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) для всего снимка:
- Есть хотя бы один независимо собранный музыкальный альбом вне Singles с не менее чем семью логическими позициями, либо с не менее чем четырьмя позициями и известной суммарной длительностью не менее 30 минут. Нужны полная согласованная нумерация, непротиворечивое покрытие дисков и совпадающие присутствующие итоги. Шаблонный Album или противоречивая структура не дают такого основания.
- Или есть как минимум три независимо выделенные группы
candidateэтого исполнителя. Три трека одного релиза — одна группа, не три. Невостребованные одиночные позиции не создают фиктивные релизы для достижения порога.
Условия альтернативны: студийный или полный альбом не обязателен. Учитываются удерживаемые
недоступные группы; корзина сама себя не квалифицирует. Маршрутизация не меняет таблицу eligible в
процессе обхода. Полные компоненты кредита сравниваются по действующим правилам; запятая или
амперсанд не являются разрешением разрезать совместный кредит. Неоднозначный владелец ведёт в S.
Персональная корзина и общая корзина не содержат копии одних и тех же назначенных позиций.
Детерминированный выбор корзины и стабильные ID
anchors — владельцы, прошедшие порог независимого полного альбома; eligible дополнительно
включает владельцев трёх групп-кандидатов. Общие подтверждения — полные наборы компонентов Album
Artist либо Track Artist, присутствующие у каждого члена группы. Основной владелец выбирается по
общему Album Artist, затем по общему Track Artist. Условия проверяются по порядку:
- Использовать основного владельца, если он входит в
anchors. - Иначе использовать единственный общий anchor. Несколько общих anchor ведут в общие Singles.
- Иначе использовать основного владельца, прошедшего
eligible. - Иначе использовать единственный общий подходящий кредит; без однозначного выбора — общие 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. Это результаты конкретных прогонов, а не утверждение о проверке всех медиатек и устройств.