Commit graph

78 commits

Author SHA1 Message Date
0fa9bb6130 Клип встаёт на курсор, монтаж едет следом, сборка ждёт конца правки
Три жалобы из настоящей работы над фильмом, и все про одно: положить
кусок туда, куда нужно, было нельзя.

Файл из списка падал в конец дорожки, стоило месту под ним оказаться
занятым, — а занято оно почти всегда: клип длиной в целый файл упирается
хвостом в то, что правее, даже когда начало легло в пустоту. Теперь
монтаж раздвигается: MakeRoomAt режет все дорожки в точке вставки и
сдвигает всё, что начинается с неё и правее, на длину нового клипа. Звук
и надписи едут вместе с картинкой, иначе синхрон рассыплется.

Точка вставки при этом округляется к кадровой сетке. Без этого вставка
давала наезд в пять миллисекунд: рез внутри раздвижки округляет время
сам, а клип клался на неокруглённый курсор. Отсюда и был «кроссфейд без
видимого перекрытия».

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

Тяжёлая сборка больше не начинается с первым движением: список кусков
пересобирается сразу, а ffmpeg запускается, когда монтаж постоял тихо
полторы секунды и клип никто не держит мышью. Сам наезд перестал быть
сюрпризом — строка состояния называет дорожку, величину и клипы.

Проверки числами: раздвижка монтажа и прилипание хвостом. С окном:
insertat.sh, dragscroll.sh, genquiet.sh. Все три прогнаны и на прежней
сборке, где падают: клип в конце дорожки вместо курсора, 0.667 с вместо
4.238 с, сборка через 0.20 с вместо 1.62 с.
2026-10-15 23:33:58 +03:00
6c81fd0907 Проверки с окном не трогают чужой редактор и не врут о поломке
genrestart.sh закрывал все окна редактора разом, включая работу
пользователя на рабочем экране. Закрывает теперь только свои — те, что
живут на экране проверки.

Он же падал по всем случаям, потому что съёмки, на которую смотрит его
монтаж, на диске больше нет: ffmpeg открывать нечего, и прерывать нечего.
Теперь сразу называет ненайденные файлы и не притворяется ни пройденной,
ни сломанной.

zoomanchor.py не передавал XAUTHORITY своим командам: экран, поднятый
через xvfb-run, пускает только по своему файлу доступа.
2026-10-15 23:33:46 +03:00
3bfd8bc688 README: проверки на невидимом экране и полная таблица модулей
Четыре проверки, которые гоняют саму программу, собраны в одном месте с
припиской: каждую стоит прогонять и на прежней сборке, иначе она не
проверяет ничего. Названы монтажи, которые им нужны, и почему их нет в
репозитории.

В таблицу модулей дописаны десять, которых там не было: текст и его
замер, разметка для libass, окна ключа, перекодирования, сборки и «о
программе», страница текста со ссылками, запуск ffmpeg с ходом работы и
вшитые значки.
2026-10-01 16:35:59 +03:00
d75596bca3 Доля растёт ровно: строка «progress=continue» больше не обнуляет место
Показатель висел на пяти процентах, потом прыгал к семидесяти девяти,
потом сразу готово. Виноват не ffmpeg: замерено, что он отдаёт ход
безупречно ровно — блок каждые полсекунды, 7%, 14%, 20%, 27% и дальше
без единого скачка.

Блок заканчивается строкой «progress=continue», в которой места нет:
она сообщает только, что работа продолжается, и разбор честно оставляет
переданное число как было. Я передавал туда ноль, поэтому каждый блок
заканчивался обнулением доли. Показатель стоял и дёргался вверх лишь
тогда, когда опрос попадал между строкой со временем и этой.

Теперь отсчёт начинается с уже известного места — как это давно делает
сборка ролика. Проверено глазами на невидимом экране: шесть снимков
подряд через полторы секунды дают 54, 65, 74, 82, 91, 99 процентов,
полоса растёт вместе с числом.

Ловушку закрепляет проверка в tests/lfmcheck: «progress=continue» и
«progress=end» узнаются, но времени не трогают.
2026-10-01 16:24:59 +03:00
e8cb3cc392 Ручку затухания можно взять и во второй раз
Зона захвата совпадала с нарисованным квадратиком — девять точек на
девять. Пока затухание нулевое, ручка стоит в углу клипа и целиться есть
куда; стоит её сдвинуть, она висит посреди клипа сама по себе, и попасть
в неё почти нельзя.

Зона захвата теперь шире нарисованного: по шесть точек в стороны и семь
вниз, внутрь клипа, — рука уводит курсор туда, а не выше клипа. Если на
узком клипе зоны обеих ручек налезли друг на друга, берётся та, к которой
ближе.

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

Проверено протяжкой мыши на невидимом экране (tests/fadegrab.sh): первый
захват за угол даёт затухание 1.387 с, второй — с прицелом на пять точек
ниже и четыре в стороне от середины ручки — доводит до 2.261 с. На сборке
из прошлого коммита второй захват не делает ничего.
2026-10-01 16:13:37 +03:00
44c0a5de24 Показатель работы: число и полоса показывают одно и то же
Полоса ползла по догадке, а подпись показывала замер — и человек видел
полосу, заполненную на девять десятых, рядом с нулём процентов. Что в
этот момент происходит, по такой паре понять нельзя.

Теперь доля одна на обоих. Берётся она у самого ffmpeg: сборка участка с
затуханием идёт минуту и дольше, а «кусок 1 из 1» всё это время не
меняется, поэтому ffmpeg спрашивают ключом -progress, докуда он дошёл.
Пока он не ответил ни разу, полоса трогается с места на пару процентов и
там стоит — пустая полоса и есть правда о только что начатой работе.

Догадка заодно перестала закрывать промежуток до вехи целиком: предел —
девять десятых, остаток виден глазом. Проверка на это добавлена в
tests/progress.

Полоса в окне сборки была системной, и под gtk2 заполнялась ступенями по
восемь кусков на ширину. Теперь там та же своя полоса, что и внизу
главного окна, — заполняется сплошь. Чтобы она читалась из файла формы,
у неё опубликованы Anchors, Align и Visible: читатель формы берёт только
опубликованные свойства, и без этого окно падало при открытии.

Проверено глазами на невидимом экране: «Собираю наложение 1 из 1 — 2%»
при полосе в 2%, через четыре секунды 69% при полосе в 69%.
2026-10-01 15:50:10 +03:00
8d1cb9af5c Сведение слоёв распаковывает исходник видеокартой
Затухание в полторы-две минуты собиралось минутами, и всё это время
предпросмотр ждёт. Виноваты оказались не фильтры: на десяти секундах
2688x1512 цепочка со сведением стоит 13.5 с, а вовсе без фильтров —
12.7 с. Время уходит на разбор HEVC процессором.

Ключ -hwaccel cuda перед входом убирает почти всё: настоящее затухание
на семьдесят секунд собирается 18.3 с вместо 75.9 с, картинка бит в бит
та же (одинаковый md5 потока, 4196 кадров там и там).

Просится он только там, где кодировать будет та же карта — то есть
выбран NVENC. На машине без неё ключ не появляется вовсе. Сверх того у
подготовки вставок есть откат: не пошло — повторяем на процессоре, как
это уже делает склейка с перекодированием.

Сборка ролика пока по-старому: там кусок с наложением идёт в общий
поток, и повторить его после отказа посреди работы нельзя — данные уже
ушли в трубу. Это отдельная задача.
2026-10-01 15:11:43 +03:00
d2a49343c3 Сборка вставки не начинается заново от каждой правки
Затухание в семьдесят секунд не собиралось никогда: сборка сведённого
куска шла полторы минуты, и любая правка в это время сбрасывала её на
ноль. В журнале это видно как «Расстановка изменилась» и тот же самый
запуск ffmpeg по кругу.

Причина — в вопросе, который задавался. «Нужна ли ещё эта работа»
проверялось по списку недостающего, а недостающее у нас — это то, чего
нет на диске. У куска, который делается прямо сейчас, файл уже есть:
ffmpeg создаёт его в первую же секунду. Кусок в список не попадал, ответ
был «не нужна», работа отменялась.

Теперь уплощение отдельно отдаёт список нужного — независимо от того,
лежит файл на диске или ещё пишется. Заодно закрыта та же дыра со
вставками тишины: проверка их вообще не видела, потому что звуковые
дорожки считаются отдельным проходом.

Проверено обеими сторонами (tests/genrestart.sh, на настоящем материале,
затухание 70 с): пять резов в стороне от затухания — один запуск и ни
одной отмены, было шесть запусков и десять отмен. Удалили всё — работа
прекращается за две секунды, как и прежде.
2026-10-01 00:47:28 +03:00
235cd00e9a Предпросмотр показывает кадрирование клипа
Клип со своим кадрированием играл в окне во весь кадр: цепочка фильтров
у mpv одна на весь список кусков, а включить её на одном куске и
выключить на следующем нечем — из фильтров ffmpeg по времени
переключается только наложение.

Обошлось без цепочки вовсе. У вывода mpv своя обрезка, и делает её
видеокарта: курсор зашёл в клип с кадрированием — выставили, вышел —
сняли. Ни перестройки цепочки, ни копирования кадров из памяти
видеокарты в обычную.

Рамку источника считает теперь один код на две стороны: по ней окно
«Кадрирование и поворот» рисует рамку, и её же плеер отдаёт как обрезку.
Проверка в tests/lfmcheck сверяет рамку с фильтром сборки: увеличение
2.77 даёт 970x546 посередине, и это ровно то, что вырезает
crop=2688:1512:2379:1338 после scale=7446:4188.

Проверено глазами на невидимом экране, на настоящем материале с дрона:
картинка в окне совпала с готовым роликом, на границе с обычным клипом
обрезка снимается и возвращается. Сверено со сборкой из прошлого
коммита — там на том же месте кадр без кадрирования.

Не покрыто и записано в BACKLOG: отражение (свойства для него нет),
растяжение и участки с наложением — там кадр уже сведён цепочкой.
2026-10-01 00:04:57 +03:00
2160c33790 BACKLOG: кадрирование в окне и два видео рядом — по замеру
Про кадрирование обычного клипа было написано, что цепочка фильтров для
2688x1512 слишком дорога. Это память о другом замере: на деле 330 к/с
без фильтра против 214 с веткой, включаемой по времени, при нужных 60 —
запас трёхкратный. Заодно записан путь дешевле: mpv умеет обрезать и
увеличивать на выводе, силами видеокарты.

Про два видео рядом заведён свой раздел: граф сборки проверен на
настоящих файлах, и названы три вещи, которых не хватает. Главная —
клипу негде записать, какое место в кадре он занимает: масштаб сейчас
всегда равномерный, и полосу в половину кадра им не выразить.
2026-09-30 22:00:49 +03:00
d0ec869743 Масштаб таймлайна берётся от точки под мышкой
Ctrl с колесом должен оставлять на месте то, на что смотрит мышь, а
оставлял точку левее — ровно на левый край таймлайна. При мыши у левого
края опора и вовсе уходила за пределы окна.

Причина: MousePos у DoMouseWheel по описанию экранный, но под gtk2 в нём
уже клиентские координаты, и ScreenToClient переводил их второй раз.
Берём Mouse.CursorPos — он экранный в любой системе.

Замерено на невидимом экране: до правки мышь в 700, 1000 и 1300 давала
опору в 260, 560 и 860; после — ровно 700, 1000 и 1300. Проверка
оформлена в tests/zoomanchor.py и прогнана на обеих сборках: на прежней
она падает, на новой проходит.
2026-09-30 20:45:32 +03:00
4526f65fcc Записано, чего сборка потоком не даёт
Четыре оговорки с ценой каждой: продолжение стоит места вдвое, отступ
назад идёт до целого куска, MP4 продолжать нельзя вовсе, и потоком идёт
не всё. Все четыре известны заранее, а не вскроются через месяц.
2026-09-30 16:50:18 +03:00
f841ed79ec Честное предупреждение о контейнере и отметка недописанной сборки
У MP4 два неудобства, о которых человек узнавал поздно: прерванную
сборку в нём продолжить нельзя вовсе, а перекодированный стык точного
реза воспроизводится правильно не везде. Теперь об обоих говорится до
начала работы, одним окном с кнопками «Собрать в MKV», «Всё равно mp4»,
«Не собирать».

Признак точного реза заодно исправлен: смотрели на отметку edPrecise,
а резы стали точными всегда — предупреждение молчало. Теперь признак —
клип, начинающийся не с начала файла.

Продолжить прерванную сборку предлагается по отметке рядом с файлом, а
не по его наличию: оборванный MKV открывается и играет, и отличить его
от готового больше нечем. Отметка ставится при неудаче, снимается при
успехе и хранит число записанных кадров — его и показывают в вопросе.

Ещё: на каждом стыке кусков приёмник писал «Packet corrupt», по паре
строк на кусок. Данные целы, но журнал забивался, и настоящую ошибку в
нём было не найти. Лечится ключом -mpegts_flags +initial_discontinuity
у отправителя: сверено по кадрам и по md5, результат тот же.
2026-09-30 16:48:31 +03:00
4522f0000a Продолжение прерванной сборки потоком — с недописанного файла
Потоком кэша кусков нет, и продолжать было не с чего. Но недописанный
файл сам по себе — уже собранная часть фильма: в нём видно, сколько
целых кадров успело записаться. Программа отступает до границы
последнего целого куска, переносит эту часть и досылает остаток.

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

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

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

В nlvetool у render появились --cancel-after=<сек> и сочетание слов:
cache resume проверяет, что старый путь через кэш не сломался.
2026-09-30 16:37:28 +03:00
38f4b295b9 Сборка потоком: куски идут сразу в готовый файл, без кэша
Раньше каждый кусок монтажа вырезался в отдельный файл, и только потом
список этих файлов шёл на склейку. Кэш весил столько же, сколько готовый
фильм — замерено 110 ГБ на часе материала, — а диск писал всё дважды.

Теперь каждый кусок режется своим ffmpeg прямо в MPEG-TS на стандартный
вывод, все потоки подряд переливаются в один принимающий ffmpeg, а тот
пишет готовый файл. Место куску задаёт -output_ts_offset, считаемый в
кадрах от начала. На диск ложится только результат и звуковая дорожка.

Через кэш по-прежнему собирается то, что потоком не выходит: кодек, не
переносимый в MPEG-TS, и пересборка всего ролика под площадку.

Сверено на четырёх монтажах, включая настоящий материал с дрона
(HEVC 2688x1512, 55 Мбит/с, файлы с шестью потоками): картинка совпала
попиксельно, PSNR бесконечен на каждом кадре, длина и размер те же.
Сверка шла и с прошлым выпуском, а не только нового пути со старым.
На отрывке в 17.5 с: 14.2 с и 421 КБ на диске против 17.5 с и 152 МБ.

По дороге найдены две ловушки, обе записаны в README:
-avoid_negative_ts make_zero отменяет -output_ts_offset, а -fflags
+igndts у приёмника сминает кадры на стыках.
2026-09-30 16:12:00 +03:00
aba9bd57e4 Кнопки внизу: подсказка не перехватывает щелчок, щелчки не теряются
Две беды у всех кнопок нижней полосы.

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

Второй щелчок подряд виджетсет отдавал как двойной, и кнопка его не
видела: быстрая очередь щелчков срабатывала через раз. У кнопки убран
csDoubleClicks, а на случай, если двойной всё же дойдёт, он считается
за обычный.

Проверено: tests/lfmcheck, раздел «Кнопки нижней панели», и
tests/buttonclicks.sh мышью на невидимом экране — по всем шести
переключателям очереди из 1-5 щелчков отрабатываются полностью.
2026-09-30 15:44:29 +03:00
569d0382f7 Кадрирование показывает кадр из клипа, а не из середины файла
Окно кадрирования брало кадр на десятой части от начала исходного файла,
не глядя, какой клип правят. Клип обычно вырезан из середины долгой
съёмки, и в окне была совсем другая сцена: прицелиться увеличением было
нечем.

Теперь для клипа берётся его собственное время: место курсора, если он
внутри клипа, иначе середина клипа. Кадрирование файла целиком из списка
работает как раньше.

Проверено на съёмке с дрона: клип из 900-й секунды, окно показало кадр
именно оттуда (совпал с эталонным кадром на 900 с), раньше показывало
кадр на пятой секунде.
2026-09-30 12:05:44 +03:00
0ba3bd40a6 Огрызкам на резах задаётся битрейт проекта
Перекодированный кусочек от точки реза до опорного кадра шёл без ключа
битрейта, и NVENC брал своё умолчание. Замерено в готовом ролике
пользователя: 5.7-6.8 Мбит/с в огрызке против 54-60 в скопированном
материале рядом — мутное пятно на полсекунды.

Раньше это задевало только редкие точные резы, а теперь точны все: в том
ролике 98 кусков, то есть 98 таких пятен.

После правки на том же материале: огрызок 86.8 Мбит/с (потолок в полтора
битрейта проекта, для куска в 0.7 с с опорным кадром это нормально),
соседний скопированный 61.6. Сверка кадров и звука проходит.
2026-09-30 00:55:09 +03:00
6617e29514 Выделение по времени, рез по краям промежутка, курсор идёт за резом
Режимов выделения стало два, переключаются клавишей D и кнопкой внизу,
режим показан её значком.

Рамкой по клипам — как было. По времени — протяжка отмечает промежуток по
всем дорожкам сразу, он остаётся виден пунктиром, и S режет все дорожки по
обоим его краям. Выделенной после этого остаётся ровно середина: следующим
действием её обычно удаляют, а выделение на старых клипах убрало бы не то.

Курсор теперь идёт за резом: после S встаёт на место реза (в режиме по
времени — на начало промежутка), после удаления куска — на место стыка,
после схлопывания дыры — туда же. Раньше в это место приходилось попадать
мышью заново.

Рез кладётся на кадровую сетку: между кадрами времени нет, и рез на доле
кадра оставлял бы в монтаже время, которого не бывает.

Выделение рамкой требует настоящего перекрытия, а не касания краями:
сосед, кончающийся ровно на краю рамки, попадал в выделение из-за разницы
в последнем знаке дробей.

Кнопка журнала и строка состояния сдвинуты вправо: переключатель выделения
встал в один ряд с прилипанием и схлопыванием.

Значки — icons8, набор ios-glyphs, авторство дописано в attribution.txt.
Набора «ios26» на их раздаче нет (404), взят ближайший по виду к нынешним.

Проверки: рез по курсору и по краям промежутка, кадровая сетка, выделение
середины, курсор после удаления и после схлопывания.
2026-09-29 23:58:30 +03:00
37787ce96c Починка проекта: клипы на кадровую сетку, пункт в меню
Меню «Проект» → «Проверить и починить проект...». Показывает найденное и
чинит только по согласию, отменяется через Ctrl+Z.

Ищутся клипы мимо кадровой сетки, клипы на дорожках чужого вида и
повторяющиеся имена дорожек. Первое — наследство: в старых проектах
времена записаны как попало (2.457599 с при 59.94 к/с — это 147.31 кадра),
и пока монтаж просит доли кадра, предпросмотр и сборка округляют их
каждый по-своему. Сдвиг при починке не больше полкадра.

Края отмеченного участка при сборке тоже кладутся на сетку: подрезка
возвращала в крайние куски доли кадра, которые уплощение только что
убрало.

Проверено на копии настоящего проекта: 82 клипа, после починки мимо сетки
ни одного, клипы остались встык, общая длина изменилась на 0.028 с за два
часа; Ctrl+Z возвращает как было. Плюс проверка в lfmcheck, включая клип
короче кадра — он не должен схлопнуться в ничто.
2026-09-16 20:39:33 +03:00
019d016db9 Звук больше не отстаёт от картинки
Рез всегда точный. Раньше начало куска притягивалось к ближайшему
опорному кадру, а звук брался из непритянутой точки: по журналам сборки
средняя притяжка 0.24 с, худшая 0.99 с, и на длинном куске этот сдвиг
держится все его минуты. Плата за точность — доли секунды перекодирования
на рез. Второй вид реза (Shift+S) стал тем же самым и убран.

Разбор заголовка MP4 не учитывал список правок (elst): метки опорных
кадров уезжали на задержку показа, на пробе два кадра. Сверка быстрого
пути с ffprobe теперь совпадает точно, в том числе на записи с B-кадрами.

Куску заказывается точное число кадров, а не только время: при
копировании ffmpeg отмеряет конец по времени декодирования, и у записи с
B-кадрами кусок выходил длиннее.

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

Звука в кусках картинки больше нет: он не используется, а в MPEG-TS ещё и
сбивает выравнивание склейки на 36 мс.

Границы кусков ложатся на кадровую сетку: картинка выходит целым числом
кадров, звук набирается до отсчёта, и доли кадра разводили их в стороны.

Разогрев кодировщика звука компенсируется: сжатый AAC начинается со
служебных отсчётов, MP4 помечает их пропускаемыми, Matroska — нет, и звук
ехал на 21.3 мс позже. Величина меряется пробным сжатием.

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

В журнал сборки добавлена проверка синхронности: сколько кадров вышло и
сколько просил монтаж, с вердиктом и худшей частью.

Замер (tests/syncprobe.sh, исходник со вспышкой и щелчком на каждой
секунде): было до 0.6 с и росло по ходу ролика, стало 0.02-0.04 с без
накопления при точности измерения 0.017 с.
2026-09-16 18:28:29 +03:00
18e55d466e Работа не теряется: автосохранение, видимое сохранение, запасной файл
Сохранение теперь отвечает в строке состояния: «Проект сохранён: имя,
время». Раньше об этом знал только журнал, а он по умолчанию закрыт —
нажал Ctrl+S и не знаешь, дошло ли до файла.

Проект пишется во временный файл рядом и подставляется целиком; прежний
остаётся как .bak. Оборванная запись больше не может оставить огрызок
вместо проекта.

Автосохранение: пока есть несохранённое, раз в полминуты рядом с
проектом пишется копия .autosave. При открытии программа сравнивает её
время с временем проекта и, если копия новее, показывает оба времени и
предлагает её открыть. После сохранения проекта копия убирается.

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

Клавиша \ подтягивает окно к курсору и ставит его посередине: её жмут
после реза или выделения. Переключатель «курсор посередине» остался
кнопкой с целью.
2026-09-14 16:06:40 +03:00
86c28a3308 Выделение областью, рез всех дорожек, курсор не улетает
Рамка выделения: протяжка по пустому месту дорожек обтягивает клипы,
Delete убирает всё, что она задела. Щелчок без движения по-прежнему
ставит курсор.

Рез по курсору (S) режет все дорожки, а не ту, что под указателем мыши:
стоило указателю оказаться над пустой дорожкой, и клавиша молча не
делала ничего. Теперь рез отвечает числом разрезанных клипов.

Курсор после правки монтажа улетал в конец: при перезагрузке списка
кусков mpv сбрасывает отсчёт времени, программа ждала перемотки, не
дожидалась и подтягивала курсор к плееру. На паузе курсор теперь
остаётся там, куда его поставил человек. Это и делало Ctrl+Shift+R
неудобным: после каждого схлопывания приходилось целиться заново.

Схлопывание дыры ищет её с допуском и по всем дорожкам, а не только под
мышью, и называет дорожку в строке состояния.

Ответ на команду держится в строке состояния несколько секунд: раньше
его через долю секунды затирало сообщение о пересборке предпросмотра.

Проверки: рамка, рез всех дорожек, допуск при схлопывании (lfmcheck).
2026-09-14 15:27:43 +03:00
00af5e1eec Отмена генерации не ошибка, длинная тишина набирается повторами
Отмену вставок просили мы сами, когда правка сделала работу ненужной, а
журнал сообщал «Не удалось подготовить вставки: Отменено» — неправда там,
где всё шло по плану. Теперь отмена докладывается как отмена и сразу
планирует пересборку предпросмотра, а не ждёт таймера.

Вчерашнее дробление дыр не дошло до звуковых дорожек: они уплощаются
отдельной функцией. Кусок тишины в три минуты ссылался на минутный файл, и
плеер жаловался «uses 192.5 seconds, but file has only 60». Теперь длинная
тишина набирается повторами, а одинаковые файлы не заказываются дважды.

Проверено живьём на материале 2688x1512: отмена ловится, обе строки в
журнале верные, предупреждений о коротких файлах ноль. Проверка 15 в
flattest следит за дроблением тишины.
2026-09-12 00:51:19 +03:00
8258de7145 Логи сессии и бэклог: прокрутка, копирование клипов, непроверенная отмена 2026-09-11 21:19:43 +03:00
ab719dfd1e Копирование клипов: Ctrl+C и Ctrl+V
Отбивку или текстовый блок проще размножить копией, чем заводить заново
через меню и настраивать оформление с нуля.

Копируются выделенные клипы, вставляются по курсору. Связанные берутся
вместе с парой — у картинки со звуком иначе уехала бы половина, — но
связка у копий своя: копия и подлинник не должны ездить вместе. Взаимное
расположение сохраняется, набор сдвигается к курсору.

Дорожка выбирается сама: сначала та, откуда копировали, если это время на
ней свободно; потом любая подходящая по виду; в крайнем случае заводится
новая. Наложить копию на соседа по дорожке нельзя — это означало бы
переход, которого никто не заказывал.

В модели появился TClip.CopyAllFrom: полная копия, кроме места на
монтаже. Проверка в lfmcheck перебирает перенесённые свойства и отдельно
убеждается, что место и связка не переносятся.
2026-09-11 21:14:52 +03:00
407eb02f2d Прокрутка во время воспроизведения больше не отскакивает
При выключенном «курсоре посередине» окно всё равно подтягивалось к
курсору, стоило тому выйти за край видимой части. Со стороны это выглядело
так: Shift с колесом, первый поворот прокручивает, второй возвращает
обратно. Человек отматывает осознанно, и возвращать его силой нельзя.

Теперь выключенный переключатель означает именно неподвижное окно.
Подтягивание при пуске воспроизведения осталось: там курсор надо привести
в поле зрения.

Проверено на невидимом экране при идущем воспроизведении: оба поворота
колеса двигают линейку (1651 и 1581 изменившихся точек), и к исходному
виду она не возвращается.
2026-09-11 20:44:32 +03:00
939184fce7 Чёрные вставки стоят копейки, текст режет кусок
Дыра в сорок минут требовала двухчасового чёрного файла: лестница готовых
длин доходила до восьми часов, и NVENC кодировал такое минутами. Отсюда
«Делаю чёрную вставку 1 из 3», которое не кончается. Теперь длины две, 15
и 60 секунд, длинная дыра набирается повторами минутного файла, а само
чёрное кодируется мегабитом вместо полусотни.

Промежуток короче половины кадра дырой больше не считается: между
клипами, сдвинутыми встык мышью, оставались микросекунды. Мышь при этом
кладёт клипы на кадровую сетку — между кадрами времени не существует.

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

Ctrl+Shift+R работает без мыши: дыра ищется на активной дорожке, если
указатель не над дорожками, и команда отвечает в строке состояния, а не
молчит.

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

Проверки: flattest 13 и 14, схлопывание без мыши в lfmcheck, сборка с
надписью внутри длинного куска — 1.000000 с пересборки вместо 6.
2026-09-11 19:35:26 +03:00
82929a8d5b В репозитории только исходники
Из истории убраны собранный ролик, скачанные зависимости под Windows,
отладочные символы macOS, пробники замеров, изначальная постановка и
рабочие бумаги — планы с логами сессий. Всё это осталось на диске, но
больше не отслеживается и закрыто .gitignore: они попадали в коммиты
через git add -A по корню.
2026-09-02 02:08:33 +03:00
1ed479739d Значок цели: исходник и запись в генераторе значков 2026-09-02 02:06:05 +03:00
45777bbdbb Ролик больше не хранится на диске дважды
Картинка собиралась в промежуточный файл, а потом сшивалась со звуком в
конечный. На часовом ролике это 91 ГБ в кэше плюс 91 ГБ рядом и лишний
полный проход копирования — ради того, чтобы приписать звуковую дорожку.

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

Куски после удачной сборки удаляются, и в журнале сказано, сколько места
освободилось: на часовой съёмке их набирается больше сотни гигабайт, а
нужны они только чтобы продолжить прерванную сборку. После неудачной
сборки они остаются — с них и продолжают. Настройка keep_parts оставляет
их всегда.
2026-08-31 17:39:40 +03:00
a28955fa2e Свойства клипа, порядок дорожек, оценка времени и продолжение сборки
Копирование оформления: Ctrl+Shift+C берёт с выделенного клипа
кадрирование, поворот, увеличение, смещение, прозрачность, ключ,
затухания и громкость, Ctrl+Shift+V применяет их ко всем выделенным. Вид
клипа и текст не переносятся: свойства копируют между разными клипами.

Порядок дорожек: слева в заголовке две стрелки, у крайних дорожек
соответствующая нарисована тускло и не нажимается. Двойной щелчок по
названию переименовывает дорожку — на монтаже из десятка дорожек «карта»
и «титры» читаются лучше, чем V1 и Т1. То же есть и в меню по правому
щелчку. Ввод имени — своим тёмным окном: системный InputQuery выглядел бы
белым пятном.

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

Прерванная сборка продолжается с готовых кусков. Каждый доведённый до
конца кусок помечается в списке рядом с ними: одного наличия файла мало —
сборку могли прервать посреди записи. Первая строка списка — отпечаток
плана; не совпал, значит монтаж менялся, и всё собирается заново.
Проверено: четыре куска взяты готовыми, два пересобраны, склейка не
потеряла ни одного кадра; при изменённом монтаже готовые куски отвергнуты.
2026-08-31 14:55:19 +03:00
0bbff345ba Оверлей по порядку дорожек, текст в окне, имена дорожек
Миникарта была видна в окне, но не в ролике. Предпросмотр рисовал оверлей
поверх всего, не глядя на порядок дорожек, а сборка живёт по правилу
«верхний клип побеждает целиком»: фотография лежала выше оверлея и в
ролике его закрывала. Теперь окно считает так же — оверлей показывается
только там, где его не закрывает сплошной клип с дорожки выше, а закрытый
посередине разбивается на несколько окон показа.

Текст не появлялся в окне. Разметка лежит в файле с одним именем на весь
проект, а плеер сравнивал имена: имя прежнее — перечитывать нечего.
Правка доходила до окна только вместе с перезагрузкой списка кусков.
Теперь сравнивается сама разметка, и при изменении файл перечитывается.
Отдельно проверено, что цепочка lavfi-complex субтитрам не мешает.

Две дорожки Т2. Имя новой дорожке давалось по счёту дорожек своего вида
плюс единица, и после удаления средней счёт совпадал с занятым именем.
Имя подбирается свободное, а повторы из старых проектов разводятся при
чтении.

Проверки 11 и 12 в nlvetool flattest.
2026-08-31 12:41:26 +03:00
59c778d2f6 Текст вжигается и в фотографию; дорожки и полоса участка
Вжигание не доходило до сгенерированных вставок. Картинки и чёрное
отбирались первой же веткой сборки — до проверки на пересборку — и
копировались целиком вместе с отметкой о разметке. Галка вжигания стояла,
кусок в журнале был помечен «с текстом», а в ролике надписи не было.
Причину показал журнал сборки рядом с файлом: помечено «пересобирается»,
а команда — копирование.

Теперь вставка с разметкой идёт общей веткой пересборки, и время у неё
берётся своё: она начинается с нуля, а не с середины монтажа.

У линейки появился верхний этаж в двенадцать точек — про отметки.
Протяжка по нему отмечает участок, даже если отметок не было; щелчок без
движения их снимает. Уголки по краям тянутся, как и раньше.

Заголовок дорожки: правый щелчок открывает меню — поднять, опустить,
удалить (с клипами — с подтверждением). Заголовок можно и потащить:
порядок дорожек есть порядок перекрытия, и менять его перетаскиванием
привычнее, чем через меню.
2026-08-31 12:01:12 +03:00
07aee86631 Клип не попадает на дорожку чужого вида
В живом проекте фотография оказалась на текстовой дорожке, а надпись — на
дорожке картинки. Уплощение обходит дорожки по видам, и такой клип не
видит ни сборка, ни предпросмотр: на монтаже он нарисован, в готовом
ролике его нет, и ни одной жалобы по дороге. Именно это и было причиной
пропавших фотографии и текста, а не ошибка со стоп-кадром.

Теперь перетаскивание на дорожку чужого вида не проходит, а файл из
списка, брошенный на такую дорожку, ложится на подходящую — с записью в
журнал, куда именно. Новая дорожка, заведённая перетаскиванием вниз,
заводится того вида, какой нужен клипу.

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

Проверка 10 в nlvetool flattest: картинка на текстовой дорожке в
уплощение не попадает, после переноса попадает и остаётся на своём месте.
2026-08-31 11:12:05 +03:00
96672341d2 Уголки участка, курсор посередине и журнал сборки рядом с роликом
Отметки участка стали видимыми уголками на линейке — жёлтые треугольники
по краям, за них можно тянуть. Клавишами это выходило долго: поставил,
посмотрел, промахнулся, ставь заново. Край прилипает к границам клипов,
как и курсор, и края не пропускаются друг за друга.

Клавиша \ и кнопка с целью переключают, держать ли курсор посередине
монтажа при воспроизведении. Раньше монтаж ехал под курсором всегда, а
это мешает, когда разглядываешь конкретное место.

В окне сборки вместо надписи про участок появилась галка «только
отмеченный участок». Надпись легко проглядеть, а вопрос из тех, где
ошибка стоит часа работы: соберёшь кусок вместо ролика или наоборот.
Снятая галка собирает весь монтаж, отметки при этом остаются.

Журнал сборки пишется рядом с готовым файлом и под тем же именем. Общий
журнал программы перезаписывается следующим запуском, а разбираться,
почему в ролике не то, приходится потом. В журнале: время, проект,
участок, кадр и битрейт, что с текстом, и полный список кусков — с
пометками, где слои, где текст и что пересобирается. Строка пишется
сразу, чтобы после падения осталась последняя.
2026-08-31 11:00:30 +03:00
71381bb774 Участок сборки, качество пересборки и пропавшие куски
Отмеченный участок. Клавиши I и O ставят начало и конец по курсору,
полоса под линейкой показывает отмеченное, сборка собирает только его.
Без этого проверить правку на длинном монтаже нельзя: каждая проверка
стоит столько же, сколько идёт весь ролик. Отметки живут в проекте.
Разметка текста при этом берётся с начала участка, иначе надписи легли бы
со сдвигом.

Фотография пропадала из ролика. Кусок с наложением подаёт слои в ffmpeg
как есть, а одиночная картинка даёт ровно один кадр: кусок в три секунды
получался длиной 0.016667 с. Теперь у слоя есть признак «это картинка», и
она повторяется ключом -loop с частотой кадров проекта.

Текст пропадал там же. Вжигание помечает кусок разметкой, но ветка
наложения про неё не знала. Пока оверлей лежит на всём ролике, этой
веткой идут все куски — и текста не было нигде. Теперь разметка вжигается
последним звеном цепочки, поверх сведённой картинки.

Качество пересобранных кусков. Битрейт не задавался вовсе, и кодировщик
брал своё значение по умолчанию: 5.9 Мбит/с там, где исходник 56. Теперь
пересборка целится в наибольший битрейт среди файлов проекта, с потолком
в полтора раза выше и буфером вдвое. Тем же битрейтом делаются вставки из
фотографий.

Проверки: обрезка списка кусков по участку (nlvetool flattest, 9),
сборка фотографии под наложением и вжигание текста поверх наложения
(nlvetool render ... burn).
2026-08-31 10:37:25 +03:00
d9bbffcaac Записан разрыв: предпросмотр не показывает кадрирование обычного клипа 2026-08-31 01:15:42 +03:00
5bd4e822d9 Рез больше не теряет настройки клипа
После разреза хвост оставался без цветового ключа: перенос настроек был
написан перечислением полей, а ключ появился позже и в перечень не попал.
Хвост переставал быть наложением, становился обычным победителем и
закрывал собой всё, что ниже — вместо компаса в углу окно показывало
пурпурную плашку во весь кадр.

Перечисление заменено на CopySettingsFrom: одно место, где сказано, что
переносится при копировании клипа, а что делает клип этим клипом. Иначе
та же ловушка досталась бы следующему свойству.

Проверка в lfmcheck: рез клипа с ключом, прозрачностью, громкостью и
кадрированием — всё остаётся у обеих половин, связки у них разные и
непустые.

Проекты, разрезанные прежней сборкой, правка задним числом не чинит:
у хвоста ключ надо включить заново.
2026-08-31 01:08:02 +03:00
d098f1621e Ключ и прозрачность видно прямо в предпросмотре
Оверлей показывался как есть, со своим фоном: чтобы увидеть, что выйдет
после сборки, приходилось собирать. Теперь выбитый цвет и приглушение
считаются в той же цепочке плеера, на лету.

Строки фильтров стали общими со сборкой (ColorKeyFilter, OpacityFilter
в uTransform): окно и готовый ролик больше не могут разойтись по тому,
как записан цвет или порог.

Переключатель в меню «Вид», состояние переживает перезапуск. Замер на
2688x1512, 600 кадров: основа 2.07 с, с наложением 2.43 с, с ключом и
прозрачностью 2.66 с — вчетверо быстрее реального времени, но на слабой
машине это заметнее, поэтому выключить можно.

В бэклог записано окно настроек: переключателей, которые стоят машинного
времени, набралось много, и живут они кто где — кнопками внизу, в меню,
а выбор кодировщика вообще нигде.
2026-08-31 00:46:58 +03:00
4c422ef57d Курсор прилипает к границам клипов
Курсор воспроизведения по щелчку вставал ровно туда, куда попала мышь.
В стык двух кусков так не попасть: на общем плане одна точка экрана —
это секунды материала, а ставят курсор чаще всего именно на стык.

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

Переключатель свой, со значком клея, рядом с магнитом: клипы липнут при
перетаскивании, курсор — при щелчке, это разные привычки, и одно бывает
нужно без другого. Состояние переживает перезапуск.

Расчёт открыт наружу и проверяется числами: семь проверок в lfmcheck,
от «стык рядом — притянуло» до «выключено — не притягивает».
2026-08-31 00:32:44 +03:00
fb6b60bc3f Свой тип файла для проектов: *.nlve больше не текст
Внутри проект — обычный JSON, и система считала его просто текстом:
открывала редактором и рисовала значок текстового файла.

Теперь у него свой тип application/x-nlve-project, свой значок (лист с
загнутым углом и знаком программы) и открытие двойным щелчком. Тип
объявлен наследником application/json, но не простого текста — иначе
редактор снова оказался бы программой по умолчанию.

Всё, что нужно для встраивания в рабочий стол, лежит в packaging и
ставится скриптом tools/install-desktop.sh в домашний каталог, без прав
администратора. Раньше пункт меню и значки существовали только на одной
машине и нигде не были записаны.
2026-08-31 00:13:50 +03:00
acb921d5f7 Цветовой ключ и живое наложение в предпросмотре
Клипу задаётся цвет, который при сборке становится прозрачным. Окно
ключа показывает кадр: цвет берётся щелчком по нему, выбитое показано
клеткой, допуск и смягчение края — ползунками. Открывается из окна
кадрирования и из меню «Правка», на монтаже клип помечен квадратиком
выбиваемого цвета.

Предпросмотр перестал сводить такие участки в файлы. Плеер играет две
дорожки одна поверх другой: оверлей во весь ролик больше не заставляет
пересобирать весь фильм после каждой правки. Замерено: наложение стоит
0.4 с на 10 с материала 2688x1512, вчетверо быстрее реального времени.

Починено в сборке: уменьшенный клип обкладывался чёрными полями, и они
закрывали собой нижнее видео — оставалась видна только дырка, выбитая
ключом. Теперь верхний слой кладётся по координатам, тем же, по которым
его показывает предпросмотр.

Смещение по горизонтали и вертикали работает и там, где картинка меньше
кадра проекта: раньше уменьшенный клип нельзя было сдвинуть в угол.

Проверки форм теперь создают каждое окно из файла формы: элемент,
забытый в классе, вскрывается на сборке, а не при открытии окна.
2026-08-31 00:10:28 +03:00
1e28dd098b Схлопывание при удалении, разбор внутренних ошибок, дорожки по номерам
Схлопывание при удалении. При включённом автосхлопывании удаление куска
сдвигает всё, что правее, влево до первого касания — дыра не остаётся, и
сборке не приходится заполнять её чернотой.

Сдвиг считается по всем дорожкам сразу, а не по каждой отдельно: иначе
развалилось бы выверенное соответствие картинки и звука. Насколько можно
сдвинуть, решает та дорожка, где запас меньше всего. Дыра не обязана быть
пустой везде: клип, который её перекрывает, никуда не едет, и запас на
его дорожке считается от его конца, поэтому сдвиг сам останавливается там,
где упёрся.

Проверено: клип 0..4 с парным звуком разрезан на 2 с, удалён первый
кусок. Хвост уехал в ноль, следующий клип встал вплотную, дыры нет.

Внутренние ошибки. Окно «Access violation» без единого слова о том, где
именно, не даёт ничего, а при воспроизведении оно повторяется на каждый
кадр и его невозможно закрыть. Теперь ошибка перехватывается: в журнал
пишется разбор стека с именами файлов и номерами строк — сборка идёт с
отладочными сведениями. Окно показывается только на первую ошибку,
дальнейшие лишь считаются.

Звуковые дорожки предпросмотра хранятся номерами, а не указателями.
Указатели были ловушкой: отмена и открытие проекта пересоздают все
дорожки, а список продолжал показывать на освобождённую память —
и обращаются к нему на каждое обновление времени, то есть десятки раз в
секунду.
2026-08-30 21:59:26 +03:00
36ccbd3015 Рез даёт хвосту свою связку: удаление куска больше не уносит весь клип
Del удалял весь исходный клип вместо выделенного куска, и звук вместе с
ним. Причина: при резе хвост наследовал номер связки головы, а удаление
расширяет выделение на всю связку — обе половины оказывались одной
связкой и умирали вместе. Дальнейшие резы наследовали тот же номер, так
что от удаления одного куска исчезали все.

Теперь хвост получает новый номер связки. Один на весь рез: все хвосты,
отрезанные от одной связки, остаются связанными между собой — картинка
со своим звуком, как и было задумано.

Проверено на монтаже: клип 0..4 с парным звуком разрезан на 2 с, удалён
первый кусок. Было — исчезал весь клип и весь его звук. Стало — остались
хвост 2..4 со своим звуком в новой связке и нетронутый соседний клип
4..8.

Проекты, разрезанные прежней сборкой, останутся со сцепленными
половинами: у них один номер связки на всё. Развязать их можно Ctrl+G.
2026-08-30 18:43:21 +03:00
62ba75ff2f Сборка ролика с текстом: вжиганием или отдельной дорожкой
Окно сборки теперь спрашивает не только имя файла: куда собирать, в каком
контейнере и что делать с текстом — это один вопрос, ответ на любую часть
меняет допустимое в остальных.

При MP4 вжигание становится обязательным, и галка запирается. Причина
измерена, а не выдумана: отдельной дорожкой субтитров со всем оформлением
умеет только Matroska, в MP4 их кладут как mov_text, а он не хранит ни
цвета, ни обводки, ни положения. Переключение на MKV возвращает прежний
ответ человека — тот, что он давал до того, как MP4 решил за него.

Вжигание. Куски, которые задевает надпись, помечаются к пересборке, и
каждому подкладывается своя разметка: времена сдвинуты к началу куска и
обрезаны по его длине. Иначе никак — фильтр subtitles рисует по времени
того файла, который ему дали, а кусок начинается со своего нуля. Текст
дорисовывается последним, уже по кадру проекта: раньше он попал бы под
масштабирование и уехал бы вместе с картинкой.

Отдельная дорожка. Разметка вкладывается в готовый файл кодеком ass при
сшивании, ничего не перекодируется.

Предупреждение о пересборке теперь считается после подготовки текста:
раньше оно показывалось до неё и не знало о вжигаемых участках.

Проверено сборкой двух роликов из одного монтажа с надписью на 2..6
секунде. С вжиганием: кадры на 1 и 7 секундах чистые, на 3 и 5 — с
надписью, предупреждение назвало 8 секунд из 15.5. Отдельной дорожкой: в
файле три потока, третий ass с названием «Надписи», картинка чистая.

Своя ошибка, найденная по дороге: установка галки кодом вызывает тот же
обработчик, что и щелчок, и навязанное при MP4 значение записывалось как
осознанный выбор — при возврате на MKV галка оставалась стоять.

Окно хода сборки переименовано в «Ход сборки»: два окна с заголовком
«Сборка ролика» путали и меня, и человека.
2026-08-30 16:34:59 +03:00
d55b624560 Текст на видео: модель, разметка ASS, предпросмотр и редактор
ffmpeg умеет рисовать текст, но не тем фильтром, о котором думают.
drawtext в местной сборке отсутствует: с версии 7.1 ему нужен harfbuzz.
Зато есть libass — и он для задачи лучше. Проверено отрисовкой: цвет,
обводка, тень, шрифт по имени, размер, поля и девять положений. Девять
положений — родное понятие ASS, ровно та сетка, которую просили.

Чего libass не делает — не уменьшает шрифт, чтобы текст влез. Это считаем
мы: сами переносим по словам и сами подбираем размер, а libass получает
готовые переводы строк, чтобы нарисовать ровно то, что мы померили.

Размер ищется делением отрезка пополам. Прикидка «уменьшить во столько же
раз, во сколько не влезли» давала слишком мелко: после переноса строк
становится меньше, и места остаётся вдоволь — на длинной надписи вместо
29 помещался 37. Замер готового блока: ровно 144 точки при разрешённых
144, то есть пятая часть кадра 720p.

Текст живёт на своей дорожке, как звук: правило «верхний клип побеждает»
на него не распространяется, он лежит поверх и ничего не закрывает.

Предпросмотр ничего не стоит. mpv рисует внешний файл ASS поверх нашего
виртуального списка кусков — правка текста видна сразу, без пересборки
участка, в отличие от прозрачности. Подключение сделано в двух местах:
при смене файла и при загрузке нового списка кусков, потому что тогда mpv
забывает все внешние дорожки.

Редактор: текст, шрифт из установленных в системе, размер, три цвета
образцами, толщина обводки и тени, сетка три на три, доля высоты. Рядом
сказано, во что превратится надпись, — считает тот же код, что и сборка
разметки.

Замер ширины подключается снаружи: точный требует холста, а холст —
оконного набора. Служебная программа без экрана обходится оценкой, окно
ставит точный. Из-за этого же из модели убран TColor: она не должна
тянуть за собой оконную библиотеку.

Сборки ролика с текстом пока нет — это следующий этап, он расписан в
IMPLEMENTATION_PLAN.md.
2026-08-30 15:30:02 +03:00
e4a77d1a72 Программа находит свой каталог через символьную ссылку
Ссылка в ~/.local/bin и ярлык в меню запускают программу не по её
настоящему пути. ExtractFilePath(ParamStr(0)) при этом указывал на
каталог ссылки, и рядом с ней оказались бы и журнал, и файл
зависимостей, и поиск ffmpeg «рядом с программой».

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

Заменены все четыре места, где каталог программы вычислялся вручную:
журнал, поиск инструментов, каталог зависимостей и путь заготовки в
проекте.

Проверено: запуск через ~/.local/bin/nlve из домашнего каталога кладёт
журнал в каталог программы, а не рядом со ссылкой.
2026-08-22 13:41:07 +03:00
b792b81a00 Сборка под macOS отдельной целью, ограничения записаны в бэклог
./build.sh macos делает две сборки: nlve_intel для Intel и nlve_arm для
Apple Silicon. Имя цели в проекте одно на все системы, и сборка под
darwin затирает линуксовую — поэтому каждый файл переименовывается сразу,
а сборка под эту систему в конце делается заново.

Обёртку .app, которую делает lazbuild, удаляем: внутри неё ссылка на файл
с именем nlve, которого после переименования уже нет.

BACKLOG.md — то, что известно и сознательно не сделано. Про macOS там
записано главное: собирается, но предпросмотра не будет, пока встраивание
mpv не научится работать через NSView; окно зависимостей советует маку
apt вместо brew; проверить нечем, потому что мака нет. Заодно туда
переехали лицензионный вопрос с libmpv и три мелочи: значок exe, кэш
наложений и четыре секунды на открытие окна кадрирования.
2026-08-22 12:53:05 +03:00
e12d0df974 Напоминание о зависимостях и починка падения под Wine
Падение. Окно зависимостей роняло программу под Wine: «stack overflow»,
шестнадцать мегабайт стека съедались целиком. Причина — качели между
таблицей и своими полосами прокрутки: появление вертикальной полосы
сужает таблицу, от этого перестаёт помещаться содержимое по ширине,
появляется горизонтальная, от неё убавляется высота — и решение о
вертикальной принимается заново. В комментарии стояло обещание, что
второго круга не будет; под Wine круг не сходился никогда.

Теперь глубина пересчёта ограничена тремя кругами. Лишняя полоса
прокрутки не беда, падение — беда.

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

Напоминание. Если не хватает ffmpeg или libmpv, при запуске появляется
окно: чего именно нет, и три кнопки — открыть окно зависимостей,
отложить, больше не напоминать. Последнее запоминается в настройках.
Для этого появился ThemedChoiceDlg: диалог со своими кнопками, готовых
«Да / Нет / Отмена» тут не хватало.

Высота надписи в обоих диалогах считалась по общей ширине текста и не
видела переводов строк — сообщение обрезалось снизу. Теперь каждая
строка считается отдельно.
2026-08-22 12:37:35 +03:00