nlve/tests/insertat.sh
4aiman 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

158 lines
7.4 KiB
Bash
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
# Файл из списка встаёт на курсор, а не в конец дорожки.
#
# Раньше клип падал в самый конец, стоило месту под ним оказаться
# занятым, — а оно занято почти всегда: клип длиной в целый файл упирается
# хвостом в то, что правее, даже когда начало легло в свободное место.
# Потом человек тащил его на нужное время и получал наезд, то есть заказ
# на сборку перехода.
#
# Проверяется так: курсор ставится посреди первого клипа, файл из списка
# добавляется на ту же дорожку. Клип обязан встать на курсор, монтаж —
# раздвинуться ровно на его длину, наездов и дыр появиться не должно.
# Всё читается из сохранённого проекта, а не с картинки.
#
# Запуск (экран :99 поднят):
# tests/insertat.sh [<путь к nlve>]
set -e
cd "$(dirname "$0")/.."
BIN=${1:-$PWD/nlve}
DISP=${NLVE_DISPLAY:-:99}
SRC=$PWD/test/поток/пара.nlve
PROJ=$PWD/test/поток/вставка.nlve
[ -f "$SRC" ] || { echo "нет монтажа $SRC"; exit 2; }
# Невидимый экран поднят через xvfb-run, у него свой файл доступа, а в
# окружении обычно лежит файл рабочего экрана. С чужим файлом xdotool не
# видит ни одного окна, и проверка «проходит» вслепую, ничего не проверив.
export DISPLAY=$DISP
reachable() { timeout 5 xdotool getdisplaygeometry >/dev/null 2>&1; }
if ! reachable; then
for p in $(pgrep -x Xvfb); do
A=$(tr '\0' '\n' < /proc/$p/cmdline | grep -A1 -x -- -auth | tail -1)
[ -f "$A" ] || continue
XAUTHORITY=$A; export XAUTHORITY
reachable && break
done
fi
reachable ||
{ echo "нет доступа к экрану $DISP: подними его (xvfb-run) и повтори"; exit 2; }
cp "$SRC" "$PROJ"
for p in $(pgrep -x nlve); do
tr '\0' '\n' < /proc/$p/environ 2>/dev/null | grep -qx "DISPLAY=$DISP" && kill $p
done
sleep 1
SP=$PWD/test/поток/профиль-вставка
rm -rf "$SP"; mkdir -p "$SP"
rm -f "$PROJ.autosave"
XDG_CONFIG_HOME=$SP XDG_CACHE_HOME=$SP XDG_DATA_HOME=$SP \
setsid "$BIN" "$PROJ" >/dev/null 2>&1 < /dev/null &
sleep 14
X="timeout 8 xdotool"
# Окно ищем по имени монтажа и проверяем, что оно на экране: по одному
# «nlve» находятся и обломки прошлых запусков, нажатия в них уходят в
# никуда, и проверка молча показывает «ничего не произошло».
WID=
for w in $($X search --name "вставка.nlve" 2>/dev/null); do
xwininfo -id "$w" 2>/dev/null | grep -q IsViewable && { WID=$w; break; }
done
[ -n "$WID" ] || { echo "живое окно редактора не найдено"; exit 2; }
$X windowmove "$WID" 0 0 windowsize "$WID" 1270 990
sleep 2
$X mousemove 700 466 click 1 # курсор воспроизведения — посреди первого клипа
sleep 1
$X mousemove 200 512 click 1 # выбрать файл в списке проекта
sleep 1
# Меню списка: «Добавить на дорожку» — первый пункт, в подменю первой
# стоит V1, она же и нужна.
$X click 3
sleep 1.5
$X key Down; sleep 0.5
$X key Right; sleep 0.5
$X key Return; sleep 2
$X key ctrl+s
sleep 2
for p in $(pgrep -x nlve); do
tr '\0' '\n' < /proc/$p/environ 2>/dev/null | grep -qx "DISPLAY=$DISP" && kill $p
done
rm -f "$PROJ.autosave"
python3 - "$SRC" "$PROJ" <<'PY'
import json, sys
def clips(path, track):
d = json.load(open(path))
for t in d['tracks']:
if t['name'] == track:
return sorted(t['clips'], key=lambda c: c['tl_start'])
return []
было = clips(sys.argv[1], 'V1')
стало = clips(sys.argv[2], 'V1')
звук = clips(sys.argv[2], 'A1')
плохо = 0
медиа = {c['media'] for c in было}
новые = [c for c in стало if c['media'] not in медиа]
if len(новые) != 1:
print(' ПЛОХО: вставленный клип не найден (новых клипов %d)' % len(новые))
raise SystemExit(1)
новый = новые[0]
конец_было = max(c['tl_start'] + c['duration'] for c in было)
print(' вставленный клип: старт %.3f с, длина %.3f с' %
(новый['tl_start'], новый['duration']))
if новый['tl_start'] > конец_было - 0.001:
print(' ПЛОХО: клип упал в конец дорожки (%.3f с), а не на курсор'
% новый['tl_start'])
плохо = 1
elif новый['tl_start'] < 0.5:
print(' ПЛОХО: клип встал в начало, курсор был посреди первого клипа')
плохо = 1
else:
print(' ok: клип встал на курсор, внутри материала')
# Соседи отъехали ровно на длину вставки: монтаж раздвинулся, а не
# затёрся и не разъехался.
сдвиг = новый['duration']
последний_было = было[-1]
последний_стало = стало[-1]
ожидалось = последний_было['tl_start'] + сдвиг
if abs(последний_стало['tl_start'] - ожидалось) > 0.001:
print(' ПЛОХО: последний клип на %.3f с, ожидалось %.3f с'
% (последний_стало['tl_start'], ожидалось))
плохо = 1
else:
print(' ok: монтаж раздвинут ровно на длину вставки (%.3f с)' % сдвиг)
# Наезд на одной дорожке — это заказ на переход. Его тут быть не должно.
for имя, список in (('V1', стало), ('A1', звук)):
for a, b in zip(список, список[1:]):
зазор = b['tl_start'] - (a['tl_start'] + a['duration'])
if зазор < -0.001:
print(' ПЛОХО: наезд на %s в %.3f с (%.3f с)'
% (имя, b['tl_start'], -зазор))
плохо = 1
elif зазор > 0.001:
print(' ПЛОХО: дыра на %s в %.3f с (%.3f с)'
% (имя, a['tl_start'] + a['duration'], зазор))
плохо = 1
if not плохо:
print(' ok: ни наездов, ни дыр')
# Звук обязан уехать вместе с картинкой, иначе синхрон рассыплется.
if [round(c['tl_start'], 3) for c in звук] != [round(c['tl_start'], 3) for c in стало]:
print(' ПЛОХО: звук разошёлся с картинкой')
print(' картинка: %s' % ['%.3f' % c['tl_start'] for c in стало])
print(' звук: %s' % ['%.3f' % c['tl_start'] for c in звук])
плохо = 1
else:
print(' ok: звук уехал вместе с картинкой')
raise SystemExit(плохо)
PY