На монтаже рисовался косой крест через целый клип, строка состояния обещала переход, а наложения не было: ролик собирался верно. Крест уходил сам после Ctrl+Z, после любого действия с отменой и после схлопывания. Это был не наезд, а сбитый порядок клипов в списке дорожки. Список отсортирован по времени начала, и соседи по списку обязаны быть соседями по времени — на этом стоят и отрисовка креста, и сообщение о наезде. Перетаскивание же двигает не одну дорожку: связка ведёт звук за картинкой, схлопывание ведёт соседей, в режиме «все дорожки» — сразу на нескольких. Сортировалась только та дорожка, с которой тащили. Клип, уехавший с конца монтажа в середину, оставался в списке чужой дорожки последним, и её соседями по списку становились клипы, далёкие друг от друга по времени. Отмена и схлопывание лечили ровно потому, что пересортировывают списки. Сборка всё это время была права: она идёт по границам и сортирует дорожку сама. Теперь порядок возвращается всем дорожкам, чьи клипы поехали (SortMovedTracks). Оба места, считающие соседей по списку, заодно пропускают пару, идущую не по времени: страховка от того же симптома в другом месте. Проверка tests/dragorder.sh тащит связку за звук с конца монтажа в середину и читает порядок из сохранённого файла. На прежней сборке дорожка картинки остаётся в порядке 0, 14, 7.991 — проверка падает.
145 lines
6.9 KiB
Bash
Executable file
145 lines
6.9 KiB
Bash
Executable file
#!/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
|