nlve/tests/dragorder.sh
4aiman 95d92a7582 Крест перехода там, где клипы не накладываются
На монтаже рисовался косой крест через целый клип, строка состояния
обещала переход, а наложения не было: ролик собирался верно. Крест уходил
сам после Ctrl+Z, после любого действия с отменой и после схлопывания.

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

Отмена и схлопывание лечили ровно потому, что пересортировывают списки.
Сборка всё это время была права: она идёт по границам и сортирует
дорожку сама.

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

Проверка tests/dragorder.sh тащит связку за звук с конца монтажа в
середину и читает порядок из сохранённого файла. На прежней сборке
дорожка картинки остаётся в порядке 0, 14, 7.991 — проверка падает.
2026-10-16 14:01:51 +03:00

145 lines
6.9 KiB
Bash
Executable file
Raw 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/dragorder.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; }
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; }
# Три связки: две встык в начале и одна отдельно в конце. Дыра между
# второй и третьей — чтобы было куда тащить, не наезжая ни на кого.
python3 - "$SRC" "$PROJ" <<'PY'
import json, sys, copy
d = json.load(open(sys.argv[1]))
V = next(t for t in d['tracks'] if t['name'] == 'V1')
A = next(t for t in d['tracks'] if t['name'] == 'A1')
ов, оа = copy.deepcopy(V['clips'][0]), copy.deepcopy(A['clips'][0])
def клип(обр, ид, группа, старт, длина, вход):
c = copy.deepcopy(обр)
c.update(id=ид, group=группа, tl_start=старт, duration=длина,
src_in=вход, fade_in=0.0, fade_out=0.0)
return c
V['clips'] = [клип(ов, 'a_v', 'ga', 0.0, 4.0, 100.0),
клип(ов, 'b_v', 'gb', 14.0, 6.0, 200.0),
клип(ов, 'd_v', 'gd', 30.0, 3.5, 300.0)]
A['clips'] = [клип(оа, 'a_a', 'ga', 0.0, 4.0, 100.0),
клип(оа, 'b_a', 'gb', 14.0, 6.0, 200.0),
клип(оа, 'd_a', 'gd', 30.0, 3.5, 300.0)]
json.dump(d, open(sys.argv[2], 'w'), ensure_ascii=False)
print(' связки: 0..4, 14..20 и 30..33.5 — последнюю тащим в промежуток')
PY
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"
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
AROW=${NLVE_AROW:-705} # высота звуковой дорожки A1 в окне
# Два щелчка колесом от себя: в окно должен попасть весь монтаж.
$X mousemove 700 "$AROW"
$X keydown ctrl click 5 keyup ctrl; sleep 0.4
$X keydown ctrl click 5 keyup ctrl; sleep 1
# Берём последнюю связку за звук и тащим в промежуток между второй и
# третьей.
$X mousemove 1190 "$AROW" mousedown 1; sleep 0.4
$X mousemove 1000 "$AROW"; sleep 0.3
$X mousemove 850 "$AROW"; sleep 0.3
$X mousemove 761 "$AROW"; sleep 0.5
$X mouseup 1; sleep 1.5
$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 - "$PROJ" <<'PY'
import json, sys
d = json.load(open(sys.argv[1]))
плохо = 0
двинулось = False
for t in d['tracks']:
кл = t['clips']
if len(кл) < 3:
continue
порядок = [c['tl_start'] for c in кл]
print(' %s: %s' % (t['name'], ' '.join('%.3f' % s for s in порядок)))
# Клип, уехавший с конца в середину, обязан занять своё место и в
# списке, а не остаться в нём последним.
if порядок != sorted(порядок):
print(' ПЛОХО: на %s клипы в списке не по времени' % t['name'])
плохо = 1
# Клип уехал в пустой промежуток, значит наезда быть не должно:
# иначе проверка мерила бы заодно и настоящий переход.
по_времени = sorted(кл, key=lambda c: c['tl_start'])
for a, b in zip(по_времени, по_времени[1:]):
наезд = (a['tl_start'] + a['duration']) - b['tl_start']
if наезд > 0.5 * 1001 / 60000:
print(' ПЛОХО: на %s наезд %.6f с — целились в пустое место'
% (t['name'], наезд))
плохо = 1
for c in кл:
if c['id'].startswith('d_') and c['tl_start'] < 13:
двинулось = True
if not двинулось:
print(' ПЛОХО: клип никуда не уехал — проверка ничего не значит')
плохо = 1
if плохо:
raise SystemExit(1)
print(' ok: связка переехала, и обе дорожки остались в порядке')
PY