430 lines
33 KiB
Markdown
430 lines
33 KiB
Markdown
# photos Specification
|
||
|
||
## Purpose
|
||
|
||
Загрузка, хранение в S3 и просмотр фотографий с генерацией вариантов и извлечением EXIF.
|
||
|
||
## Requirements
|
||
|
||
### Requirement: Загрузка фото
|
||
Пользователь с правом загрузки SHALL иметь возможность загружать медиа (изображения и
|
||
видео) по одному и массово; система SHALL принимать ввод `image/*` и `video/*`.
|
||
|
||
#### Scenario: Успешная загрузка
|
||
- **WHEN** участник с правом загрузки отправляет файл изображения
|
||
- **THEN** система сохраняет оригинал и создаёт запись фото с метаданными
|
||
|
||
#### Scenario: Успешная загрузка видео
|
||
- **WHEN** участник с правом загрузки отправляет видеофайл
|
||
- **THEN** система сохраняет оригинал и создаёт медиа-запись с признаком видео
|
||
|
||
#### Scenario: Загрузка без права
|
||
- **WHEN** наблюдатель пытается загрузить фото
|
||
- **THEN** система отклоняет запрос
|
||
|
||
#### Scenario: Неподдерживаемый файл
|
||
- **WHEN** загружается файл, не являющийся изображением или видео
|
||
- **THEN** система отклоняет загрузку
|
||
|
||
### Requirement: Варианты изображений
|
||
|
||
Система SHALL генерировать четыре варианта: thumb (200×200, квадратный кроп), medium
|
||
(800×800, квадратный кроп), preview (1200×1200, вписанный) и оригинал, в WebP, и извлекать
|
||
EXIF (дата, координаты, размеры).
|
||
|
||
#### Scenario: Генерация вариантов
|
||
|
||
- **WHEN** фото загружено
|
||
- **THEN** система асинхронно генерирует thumb, medium и preview и заполняет EXIF-поля
|
||
|
||
#### Scenario: Форма вариантов сетки
|
||
|
||
- **WHEN** генерируются thumb и medium
|
||
- **THEN** они обрезаны до квадрата, а preview вписан с сохранением пропорций
|
||
|
||
### Requirement: Листинг фото с пагинацией
|
||
Система SHALL возвращать фото альбома с cursor-пагинацией.
|
||
|
||
#### Scenario: Пагинация по курсору
|
||
- **WHEN** клиент запрашивает список с курсором и лимитом
|
||
- **THEN** система возвращает не более лимита фото и курсор следующей страницы
|
||
|
||
### Requirement: Получение фото по требованию
|
||
Клиент SHALL иметь возможность получить актуальные данные фото с подписанным URL,
|
||
обслуживаемым бэкендом, независимо от бэкенда хранения. Подпись SHALL быть
|
||
детерминированной в пределах окна действия, чтобы повторные выдачи одного и того же
|
||
объекта давали идентичный URL.
|
||
|
||
#### Scenario: Выдача свежего URL
|
||
- **WHEN** клиент запрашивает фото по id
|
||
- **THEN** система возвращает подписанный URL бэкенда для просмотра
|
||
|
||
#### Scenario: Повторная выдача совпадает
|
||
- **WHEN** один и тот же объект запрашивается повторно в пределах окна действия подписи
|
||
- **THEN** система возвращает идентичный URL
|
||
|
||
### Requirement: Скачивание одиночного фото
|
||
Любой участник, включая viewer, SHALL иметь возможность скачать одиночное фото.
|
||
|
||
#### Scenario: Скачивание фото
|
||
- **WHEN** участник запрашивает скачивание фото
|
||
- **THEN** система возвращает подписанный URL оригинала, обслуживаемый бэкендом
|
||
|
||
### Requirement: Удаление фото
|
||
Автор SHALL иметь возможность удалить своё фото; владелец и админ SHALL иметь возможность удалить любое фото. Удаление SHALL помещать фото в корзину и SHALL не удалять объекты хранилища.
|
||
|
||
#### Scenario: Удаление своего фото
|
||
- **WHEN** автор удаляет своё фото
|
||
- **THEN** фото помещается в корзину, а объекты хранилища сохраняются
|
||
|
||
#### Scenario: Удаление чужого фото участником
|
||
- **WHEN** участник с ролью member пытается удалить чужое фото
|
||
- **THEN** система отклоняет запрос
|
||
|
||
### Requirement: Обложка по умолчанию
|
||
При загрузке фото система SHALL назначать его обложкой альбома, если обложка ещё не задана,
|
||
то есть обложкой становится первое загруженное фото. Когда фото, выбранное обложкой, покидает
|
||
альбом (помещено в корзину или перенесено в другой альбом), система SHALL снять обложку.
|
||
|
||
#### Scenario: Первое фото
|
||
- **WHEN** фото загружено, и у альбома нет обложки
|
||
- **THEN** система назначает это фото обложкой альбома
|
||
|
||
#### Scenario: Обложка уже задана
|
||
- **WHEN** фото загружено, а обложка уже задана
|
||
- **THEN** система не меняет обложку
|
||
|
||
#### Scenario: Фото-обложка в корзине
|
||
- **WHEN** фото, назначенное обложкой, помещается в корзину
|
||
- **THEN** система снимает обложку, и альбом отображается без обложки
|
||
|
||
#### Scenario: Фото-обложка перенесено
|
||
- **WHEN** фото, назначенное обложкой, переносится в другой альбом
|
||
- **THEN** система снимает обложку исходного альбома
|
||
|
||
### Requirement: Переименование фото
|
||
Автор фото, владелец и админ альбома SHALL иметь возможность изменить имя файла фото;
|
||
остальные SHALL получать отказ, а пустое имя SHALL отклоняться.
|
||
|
||
#### Scenario: Переименование автором
|
||
- **WHEN** автор меняет имя файла своего фото на непустое
|
||
- **THEN** система сохраняет новое имя и транслирует изменение подписчикам альбома
|
||
|
||
#### Scenario: Пустое имя
|
||
- **WHEN** пользователь подтверждает пустое имя файла
|
||
- **THEN** система отклоняет запрос
|
||
|
||
#### Scenario: Нет прав
|
||
- **WHEN** участник без прав переименовывает чужое фото
|
||
- **THEN** система отклоняет запрос
|
||
|
||
### Requirement: Ограничение размера загрузки
|
||
Система SHALL ограничивать размер загружаемых файлов отдельно для изображений и видео и
|
||
SHALL отвечать понятной ошибкой при превышении лимита, а не ошибкой разбора формы.
|
||
|
||
#### Scenario: Превышение лимита
|
||
- **WHEN** загружаемый файл превышает максимальный размер
|
||
- **THEN** система отклоняет загрузку с явной ошибкой о превышении размера
|
||
|
||
#### Scenario: Превышение лимита видео
|
||
- **WHEN** загружаемое видео превышает лимит видео
|
||
- **THEN** система отклоняет загрузку с явной ошибкой о превышении размера
|
||
|
||
#### Scenario: Файл в пределах лимита
|
||
- **WHEN** файл не превышает лимит
|
||
- **THEN** система принимает multipart-загрузку и обрабатывает фото
|
||
|
||
### Requirement: Метаданные фото (EXIF)
|
||
При загрузке фото система SHALL извлекать распознаваемые EXIF-атрибуты и SHALL возвращать их
|
||
вместе с данными фото; при отсутствии метаданных SHALL возвращать пустой набор.
|
||
|
||
#### Scenario: Фото с метаданными
|
||
- **WHEN** загружается фото с EXIF
|
||
- **THEN** данные фото содержат извлечённые атрибуты (камера, экспозиция, ISO, дата съёмки и др.)
|
||
|
||
#### Scenario: Фото без метаданных
|
||
- **WHEN** загружается фото без читаемых EXIF
|
||
- **THEN** данные фото не содержат атрибутов, а загрузка выполняется успешно
|
||
|
||
### Requirement: Перенос и копирование фото между альбомами
|
||
Пользователь с правом удаления в исходном альбоме и правом загрузки в целевом SHALL иметь
|
||
возможность перенести или скопировать фото в другой альбом. Операции SHALL изменять только
|
||
метаданные и SHALL не изменять объекты хранилища.
|
||
|
||
#### Scenario: Перенос
|
||
- **WHEN** пользователь переносит фото в другой альбом
|
||
- **THEN** фото исчезает из исходного альбома и появляется в целевом, объекты хранилища не меняются
|
||
|
||
#### Scenario: Копирование
|
||
- **WHEN** пользователь копирует фото в другой альбом
|
||
- **THEN** в целевом альбоме появляется новая запись, ссылающаяся на те же объекты хранилища
|
||
|
||
### Requirement: Корзина фото
|
||
Удаление фото SHALL помещать его в корзину (мягкое удаление). Восстановление SHALL возвращать
|
||
фото в его альбом. Окончательное удаление SHALL удалять запись, а объекты хранилища SHALL
|
||
удаляться только когда на них не остаётся ссылок ни в одном альбоме. Окончательное удаление
|
||
SHALL быть доступно и для фото без альбома (например, после удаления альбома).
|
||
|
||
#### Scenario: Удаление в корзину
|
||
- **WHEN** пользователь удаляет фото
|
||
- **THEN** фото пропадает из альбома и появляется в корзине
|
||
|
||
#### Scenario: Восстановление
|
||
- **WHEN** пользователь восстанавливает фото из корзины
|
||
- **THEN** фото возвращается в свой альбом
|
||
|
||
#### Scenario: Окончательное удаление без ссылок
|
||
- **WHEN** пользователь удаляет фото из корзины навсегда, и на те же объекты нет ссылок в других альбомах
|
||
- **THEN** запись и объекты хранилища удаляются
|
||
|
||
#### Scenario: Окончательное удаление при наличии ссылок
|
||
- **WHEN** на объект хранилища ссылается копия в другом альбоме
|
||
- **THEN** запись удаляется, а объект хранилища сохраняется
|
||
|
||
#### Scenario: Окончательное удаление фото без альбома
|
||
- **WHEN** пользователь удаляет навсегда фото из корзины, у которого нет альбома
|
||
- **THEN** запись удаляется, а операция не завершается ошибкой отсутствующего альбома
|
||
|
||
### Requirement: Удаление альбома и восстановление фото без альбома
|
||
Удаление альбома SHALL помещать его фото в корзину, не удаляя объекты хранилища. Если при
|
||
восстановлении фото его альбом отсутствует, система SHALL предложить выбрать существующий
|
||
альбом или создать новый.
|
||
|
||
#### Scenario: Удаление альбома
|
||
- **WHEN** владелец удаляет альбом
|
||
- **THEN** его фото оказываются в корзине без привязки к альбому, файлы сохраняются
|
||
|
||
#### Scenario: Восстановление без альбома
|
||
- **WHEN** пользователь восстанавливает фото, у которого нет альбома
|
||
- **THEN** система предлагает выбрать альбом или создать новый и восстанавливает фото в него
|
||
|
||
### Requirement: Метки фото
|
||
Пользователь SHALL присваивать фото свои метки (область — пользователь): создавать новые и
|
||
выбирать из списка с поиском; метки SHALL отдаваться вместе с фото и сниматься.
|
||
|
||
#### Scenario: Создание метки
|
||
- **WHEN** пользователь вводит новую метку и подтверждает
|
||
- **THEN** метка создаётся и привязывается к фото
|
||
|
||
#### Scenario: Выбор из списка с поиском
|
||
- **WHEN** пользователь вводит часть названия метки
|
||
- **THEN** показываются подходящие метки пользователя для выбора
|
||
|
||
#### Scenario: Снятие метки
|
||
- **WHEN** пользователь убирает метку с фото
|
||
- **THEN** метка отвязывается от фото
|
||
|
||
### Requirement: Кэширование объектов
|
||
Доставка объектов SHALL помечать ответы как кэшируемые, чтобы браузер переиспользовал
|
||
уже полученные превью, а повторное открытие альбома не вызывало повторную загрузку
|
||
неизменившихся объектов.
|
||
|
||
#### Scenario: Заголовки кэширования
|
||
- **WHEN** клиент получает объект (оригинал, превью или thumb) по подписанному URL
|
||
- **THEN** ответ содержит заголовок кэширования с ограниченным сроком годности
|
||
|
||
#### Scenario: Повторный показ без перезагрузки
|
||
- **WHEN** сетка превью перерисовывается, а URL объектов не изменились
|
||
- **THEN** браузер отдаёт объекты из кэша без повторного запроса к бэкенду
|
||
|
||
### Requirement: Медиа-запись фото
|
||
Данные фото SHALL включать вид медиа (`image` | `video`), длительность для видео и адрес
|
||
воспроизведения, когда видео-вариант готов; для изображений адрес воспроизведения
|
||
отсутствует.
|
||
|
||
#### Scenario: Запись изображения
|
||
- **WHEN** клиент получает данные изображения
|
||
- **THEN** вид медиа — изображение, длительность и адрес воспроизведения отсутствуют
|
||
|
||
#### Scenario: Запись видео без готового варианта
|
||
- **WHEN** клиент получает данные видео, а вариант ещё не готов
|
||
- **THEN** вид медиа — видео, длительность присутствует, адрес воспроизведения отсутствует
|
||
|
||
#### Scenario: Запись видео с готовым вариантом
|
||
- **WHEN** видео-вариант готов
|
||
- **THEN** данные медиа содержат адрес воспроизведения
|
||
|
||
### Requirement: Очистка корзины
|
||
Пользователь SHALL иметь возможность окончательно удалить все элементы корзины одним действием.
|
||
Система SHALL удалять только те элементы, которые пользователь вправе удалить навсегда, и SHALL
|
||
пропускать остальные. Операция SHALL возвращать число удалённых элементов.
|
||
|
||
#### Scenario: Очистка корзины
|
||
- **WHEN** пользователь очищает корзину
|
||
- **THEN** все элементы, которые он вправе удалить навсегда, удаляются
|
||
|
||
#### Scenario: Чужие элементы
|
||
- **WHEN** в корзине есть фото, которые пользователь не вправе удалить навсегда
|
||
- **THEN** они остаются в корзине, а остальные удаляются
|
||
|
||
#### Scenario: Пустая корзина
|
||
- **WHEN** пользователь очищает пустую корзину
|
||
- **THEN** операция завершается успешно, удалив ноль элементов
|
||
|
||
### Requirement: Контрольная сумма оригинала
|
||
|
||
Данные фото SHALL включать контрольную сумму содержимого оригинала, чтобы клиенты
|
||
могли сопоставлять локальные файлы с серверными фото и избегать повторной загрузки.
|
||
Контрольная сумма SHALL быть детерминированной для одного и того же содержимого.
|
||
|
||
#### Scenario: Выдача контрольной суммы
|
||
|
||
- **WHEN** клиент получает данные фото, у которого зарегистрирован объект-оригинал
|
||
- **THEN** ответ содержит контрольную сумму оригинала
|
||
|
||
#### Scenario: Одинаковое содержимое
|
||
|
||
- **WHEN** два фото ссылаются на объекты с одинаковым содержимым
|
||
- **THEN** их контрольные суммы совпадают
|
||
|
||
#### Scenario: Контрольная сумма доступна в списке
|
||
|
||
- **WHEN** клиент запрашивает список фото альбома
|
||
- **THEN** контрольные суммы входят в данные полученных фото
|
||
|
||
### Requirement: Неразрушающее редактирование фотографии
|
||
Система SHALL поддерживать неразрушающее редактирование фотографий. Параметры правок (свет и цвет) SHALL храниться в метаданных фото, а отредактированная версия SHALL создаваться как отдельные объекты (роли `edit`, `edit_thumb`, `edit_medium`, `edit_preview`) рядом с исходными вариантами. Рендер SHALL выполняться по исходному объекту фото, а не по его уменьшенным вариантам. Исходный объект SHALL оставаться неизменным, а скачивание SHALL отдавать оригинал. Сброс правок SHALL удалять и параметры, и отредактированную версию. Правки SHALL применяться только к изображениям.
|
||
|
||
#### Scenario: Сохранение правок
|
||
- **WHEN** пользователь сохраняет правки фото (свет и/или цвет)
|
||
- **THEN** параметры сохраняются в метаданных, отредактированная версия рендерится в фоне, а после рендера фото получает URL отредактированных вариантов
|
||
|
||
#### Scenario: Фоновая подготовка версий
|
||
- **WHEN** правки фото сохранены
|
||
- **THEN** система готовит варианты сетки и полноразмерную версию в фоне и сообщает о готовности
|
||
|
||
#### Scenario: Рендер по оригиналу
|
||
- **WHEN** готовится отредактированная версия фото
|
||
- **THEN** световые и цветовые правки применяются к пикселям исходного объекта, а не к его уменьшенной копии
|
||
|
||
#### Scenario: Неразрушачность
|
||
- **WHEN** фото отредактировано
|
||
- **THEN** исходный объект и его варианты остаются неизменными
|
||
|
||
#### Scenario: Сброс правок
|
||
- **WHEN** пользователь сбрасывает правки
|
||
- **THEN** параметры и отредактированная версия удаляются, а фото снова показывается в исходном виде
|
||
|
||
#### Scenario: Права на правки
|
||
- **WHEN** правки сохраняет пользователь без прав на это фото
|
||
- **THEN** система отклоняет запрос
|
||
|
||
#### Scenario: Редактирование видео
|
||
- **WHEN** пользователь пытается отредактировать видео
|
||
- **THEN** система отклоняет запрос
|
||
|
||
#### Scenario: Цветовые правки сохраняются и рендерятся
|
||
- **WHEN** пользователь сохраняет цветовые правки фото: баланс белого (температура/оттенок), сочность (vibrance), насыщенность, цветовой микшер по оттенкам, цветокоррекцию (тени/средние тона/света, яркость зон, наложение, баланс) или поканальную коррекцию
|
||
- **THEN** параметры цвета сохраняются в метаданных, отредактированная версия рендерится в фоне, а после рендера фото получает URL отредактированных вариантов
|
||
|
||
#### Scenario: Цветовые правки применяются к пикселям исходного объекта
|
||
- **WHEN** готовится отредактированная версия фото с цветовыми правками
|
||
- **THEN** цветовые правки применяются к пикселям исходного объекта после световых правок, а не к его уменьшенной копии
|
||
|
||
#### Scenario: Пустые цветовые правки не сохраняются
|
||
- **WHEN** в цветовых параметрах заданы только наложение/баланс или оттенок колеса без силы и яркости
|
||
- **THEN** правки считаются пустыми и фото не получает цветовую версию
|
||
|
||
### Requirement: Пипетка баланса белого
|
||
Клиент SHALL позволять выбрать нейтральную точку на фото и SHALL устанавливать температуру и оттенок так, чтобы цвет этой точки стал нейтральным. Выборка SHALL браться из исходного (неотредактированного) превью, а пресет баланса белого SHALL переходить в «Пользовательский».
|
||
|
||
#### Scenario: Выбор нейтральной точки
|
||
- **WHEN** пользователь включает пипетку и кликает по нейтральному участку фото
|
||
- **THEN** температура и оттенок обновляются, и цвет выбранной точки становится нейтральным
|
||
|
||
#### Scenario: Пипетка доступна только в просмотрщике
|
||
- **WHEN** фото не открыто в просмотрщике
|
||
- **THEN** пипетка недоступна
|
||
|
||
### Requirement: Живой предпросмотр цветовых правок
|
||
Клиент SHALL показывать результат цветовых правок в просмотрщике до завершения серверного рендера. После завершения рендера SHALL показываться серверная версия.
|
||
|
||
#### Scenario: Правки видны сразу
|
||
- **WHEN** пользователь двигает цветовой контрол
|
||
- **THEN** просмотрщик обновляет изображение без ожидания серверного рендера
|
||
|
||
#### Scenario: Серверная версия заменяет предпросмотр
|
||
- **WHEN** серверный рендер цветовых правок завершён
|
||
- **THEN** просмотрщик показывает отрендеренную версию
|
||
|
||
### Requirement: Тоновая кривая
|
||
Тоновая кривая SHALL задаваться контрольными точками для каналов RGB, R, G и B. Крайние точки SHALL существовать по умолчанию, SHALL двигаться по обеим осям и SHALL задавать плоские участки вне себя. Кривая SHALL быть плавной и монотонной между точками; первый клик по полю SHALL добавлять точку между крайними. Сброс SHALL возвращать линейную кривую.
|
||
|
||
#### Scenario: Крайние узлы существуют с самого начала
|
||
- **WHEN** пользователь открывает инструмент «Кривая»
|
||
- **THEN** у каждого канала уже есть крайние точки, и их можно перетаскивать
|
||
|
||
#### Scenario: Добавление точки
|
||
- **WHEN** пользователь кликает по свободному полю кривой
|
||
- **THEN** между крайними точками добавляется новая точка, а крайние остаются на месте
|
||
|
||
#### Scenario: Подвижные крайние узлы
|
||
- **WHEN** пользователь двигает крайнюю точку по x или y
|
||
- **THEN** вне крайних точек кривая остаётся плоской, а между точками — плавной
|
||
|
||
#### Scenario: Плавная интерполяция
|
||
- **WHEN** кривая сохранена и отрендерена
|
||
- **THEN** предпросмотр и итоговое фото используют одну и ту же плавную монотонную кривую
|
||
|
||
#### Scenario: Встроенные пресеты
|
||
- **WHEN** пользователь выбирает «Линейный», «Средний контраст» или «Сильный контраст»
|
||
- **THEN** кривые всех четырёх каналов заменяются кривыми пресета (контраст — в RGB, R/G/B — линейные), а подпись показывает выбранный пресет
|
||
|
||
#### Scenario: Пользовательская правка
|
||
- **WHEN** пользователь вручную меняет точки кривой
|
||
- **THEN** подпись показывает «Пользовательский»
|
||
|
||
#### Scenario: Сброс
|
||
- **WHEN** пользователь сбрасывает кривую
|
||
- **THEN** текущий канал возвращается к линейной кривой с крайними точками
|
||
|
||
#### Scenario: Независимые каналы
|
||
- **WHEN** пользователь выбирает канал R, G или B
|
||
- **THEN** он меняет только выбранный канал, остальные каналы остаются прежними
|
||
|
||
### Requirement: Переключение фото в редакторе
|
||
При переключении фото правая панель SHALL сохранять выбранную вкладку. Несохранённые правки SHALL сохраняться тому фото, для которого они сделаны, в том числе если пользователь уже переключился на другое фото. Ответы фоновых запросов SHALL NOT подменять текущее фото или его параметры.
|
||
|
||
#### Scenario: Вкладка не меняется
|
||
- **WHEN** пользователь переключает фото
|
||
- **THEN** выбранная вкладка правой панели остаётся прежней
|
||
|
||
#### Scenario: Правки уходят текущему фото
|
||
- **WHEN** пользователь меняет настройки после переключения
|
||
- **THEN** изменения сохраняются текущему фото, а не предыдущему
|
||
|
||
#### Scenario: Фоновое сохранение не подменяет фото
|
||
- **WHEN** сохранение предыдущего фото завершается после переключения
|
||
- **THEN** панель продолжает показывать текущее фото, а его параметры не меняются
|
||
|
||
### Requirement: Пользовательские пресеты кривой
|
||
Пользователь SHALL сохранять текущие кривые всех каналов (RGB, R, G и B) как именованный пресет, применять, переименовывать и удалять его. Пресет SHALL содержать кривую для каждого из четырёх каналов. Пресеты SHALL храниться в профиле пользователя и SHALL быть приватными. Импорт и экспорт SHALL выполняться JSON-файлом; импорт SHALL добавлять пресеты в профиль.
|
||
|
||
#### Scenario: Сохранение пресета
|
||
- **WHEN** пользователь сохраняет текущие кривые под именем
|
||
- **THEN** пресет появляется в списке и сохраняется в профиле вместе с кривыми RGB, R, G и B
|
||
|
||
#### Scenario: Применение пресета
|
||
- **WHEN** пользователь выбирает пользовательский пресет
|
||
- **THEN** кривые всех четырёх каналов заменяются сохранёнными, а текущий канал не меняется
|
||
|
||
#### Scenario: Переименование и удаление
|
||
- **WHEN** пользователь переименовывает или удаляет пресет
|
||
- **THEN** список пресетов и профиль обновляются
|
||
|
||
#### Scenario: Приватность пресетов
|
||
- **WHEN** другой пользователь открывает свои пресеты
|
||
- **THEN** он не видит пресеты первого пользователя
|
||
|
||
#### Scenario: Экспорт пресетов
|
||
- **WHEN** пользователь экспортирует пресеты
|
||
- **THEN** он получает JSON-файл с пользовательскими пресетами и кривыми всех четырёх каналов
|
||
|
||
#### Scenario: Импорт пресетов
|
||
- **WHEN** пользователь импортирует корректный JSON-файл
|
||
- **THEN** пресеты добавляются или обновляются в его профиле
|
||
|
||
#### Scenario: Некорректный файл или пресет
|
||
- **WHEN** имя или кривая хотя бы одного канала недопустимы
|
||
- **THEN** система отклоняет пресет и не меняет профиль
|