Модуль I · IT Skills (Cloud Services & Documents) · тема 5, урок 2 з 2 · 120 хвилин
У тебе вже все є. Шість панелей коміксу лежать у ~/local/comic/images/,
музична підкладка й ефекти — у ~/local/av/. Сьогодні з цього
народжується трейлер на 20–40 секунд.
Але головне не ролик. Ролик змонтувати нескладно. Важче інше.
Через тиждень ти заміниш одну панель — і доведеться згадувати, «а що
я тоді натискав». Тому сьогодні монтаж — це файл. Він зветься
build.sh і збирає ролик сам, від початку до кінця, однією
командою.
Друга частина уроку — субтитри. Це окремий текстовий файл із репліками й часом їх показу. Завдяки йому ролик зрозуміє й той, хто його не почує.
Слова WebVTT, ffprobe і LUFS поки нічого не означають.
Розберемо їх по черзі, кожне після конкретної дії.
~/local/av/. Сервер нічого не рендерить і не перекодовує —
він відхиляє .mp4 автоматично, і залити ролик по FTP не вийде.
На сервер їдуть чотири текстові файли: build.sh,
trailer.vtt, probe.txt, av.meta.json.
Сам ролик учитель дивиться очно, за твоїм екраном, з увімкненими субтитрами.ffmpeg (порядок ключів, -i,
-c:v, -y), склейку через concat
і титр через drawtext ти вже розбирав. Сьогодні ці команди —
будівельний матеріал, а урок про те, як їх скласти в скрипт,
як зробити ролик доступним і як про нього звітувати.| Етап | Хв | Що робимо |
|---|---|---|
| Розминка | 10 | Слухаємо дві підкладки з уроку 9 |
| Чому скрипт | 15 | Відтворюваність, перезбірка, історія змін |
Анатомія build.sh | 20 | Змінні, перевірки, збірка, звіт |
| Практика 1 | 25 | Пишеш свій build.sh і збираєш трейлер |
| Перерва | 10 | — |
| Субтитри | 20 | WebVTT, доступність, редактор на цій сторінці |
| Практика 2 | 15 | probe.txt, av.meta.json, вивантаження по FTP |
| Очна здача | 5 | Дивимося ролик і запускаємо build.sh при вчителі |
| Разом | 120 |
h264, звук aac 192 кбіт/с.
Числа у прикладах probe.txt, av.meta.json
і на схемах узгоджені між собою — можеш перевіряти.~/www/av/
і в локальну ~/local/av/. Теки lesson-10 немає
й не буде — не шукай.build.sh, а не команди в зошитіТри причини, і жодна з них не про «так красивіше»
Уяви двох монтажерів. Перший набирає команди ffmpeg у терміналі,
дивиться результат, щось міняє, набирає знову. Другий пише ті самі команди
у файл build.sh і запускає його. Поки все йде добре, різниці немає.
Різниця з'являється в момент першої правки.
Запусти скрипт сьогодні, через тиждень і на іншому комп'ютері — ролик вийде той самий. Це і зветься відтворюваність. Скрипт відтворюваний, бо в ньому записано рівно ті команди, у рівно тому порядку.
Команди «з пам'яті» — ні. Учора ти поставив -crf 20,
сьогодні -crf 23. Ролик уже інший, а чому — ніхто
не пригадає.
Ти замінив третю панель. Що далі?
| Крок | Команди в зошиті | ./build.sh |
|---|---|---|
| Знайти, що робив | гортаєш історію термінала й зошит | відкриваєш файл |
| Повторити | 7–9 команд руками, кожна — шанс помилитися | одна команда |
| Скільки часу | десять хвилин і більше | стільки, скільки триває збірка |
| Ризик | ролик вийшов «схожий, але інший» | ролик той самий, крім заміненої панелі |
Скрипт — це текст. Текст можна порівняти з учорашнім, показати вчителю, надіслати однокласнику. «Я підняв бітрейт музики зі 128 до 192» — це один змінений рядок, який видно. Те саме, зроблене мишкою у відеоредакторі, не видно ніде: ні тобі, ні перевірці.
А ще текст можна покласти у сховище, яке саме пам'ятає всі попередні версії файлу. Таке сховище в нас є — воно зветься Gitea, і ти сядеш за нього у 8-й темі. Сьогодні воно не потрібне: досить того, що твій монтаж узагалі став текстом.
build.sh пам'ятатиме, і в ньому
будуть твої власні коментарі — рядки, що починаються з #.Ти вже зібрав трейлер, а потім помітив, що третя панель — не та, і замінив
файл p3.png на новий. Що робиш?
build.shП'ять блоків у сталому порядку. Порядок не декоративний
Перший рядок — «шебанг»: він каже системі, чим виконувати файл.
Форма #!/usr/bin/env bash надійніша за #!/bin/bash,
бо шукає bash у PATH, а не за жорстко прописаною адресою.
Другий важливий рядок — set -euo pipefail. Це три
запобіжники в одному:
| Ключ | Що робить | Що ловить у монтажі |
|---|---|---|
| -e | зупиняє скрипт на першій команді, що завершилась помилкою | ffmpeg не зміг прочитати панель — далі йти немає сенсу |
| -u | помилка при звертанні до незаданої змінної | одруківка $OTU замість $OUT: інакше файл поїде «в нікуди» |
| -o pipefail | конвеєр вважається невдалим, якщо впала будь-яка ланка, а не лише остання | ffprobe … | tail: tail відпрацює «успішно» навіть на порожньому вході |
Учень запустив свій скрипт. Панелі p3.png в теці не було.
Але скрипт доробив до кінця і видав trailer.mp4 з п'ятьма
панелями замість шести. У терміналі — жодного червоного рядка.
Що з цим скриптом не так?
Правило просте: якщо рядок повторюється двічі — це змінна. Шлях до теки панелей зустрічається у скрипті сім разів. Перейменуєш теку — правитимеш один рядок, а не сім, і не забудеш жодного.
Зверни увагу на $HOME замість /home/ivan. Скрипт
із $HOME запуститься і в однокласника, і в шкільному кабінеті,
і в тебе вдома. Скрипт із твоїм логіном — тільки в тебе.
І на лапки: "$SRC/$f", а не $SRC/$f. Уяви теку
з назвою «Мої панелі» — вона цілком реальна. У шляху до неї є пробіл.
Без лапок bash розріже такий шлях на два аргументи. І ffmpeg
поскаржиться на файл, якого немає.
У скрипті сім разів написано ~/local/comic/images. Учень
перейменував теку на panels, і скрипт перестав працювати.
Найнадійніше рішення?
Це найкоротший і найкорисніший блок скрипта. Він відповідає на питання «чи є все, що мені потрібно» до того, як почалася збірка.
Конструкція A || { B; exit 1; } читається як «зроби A;
якщо не вдалося — скажи B і вийди».
[ -f "шлях" ] перевіряє два факти одразу. Файл існує.
І це саме файл, а не тека.
command -v ffmpeg шукає програму в PATH
і друкує знайдений шлях. Сам шлях нам не потрібен — потрібне тільки
«знайшов чи ні». Тому вивід відправляють у /dev/null.
exit 1 — і чорний екран без пояснень.
Або немає панелі: /home/ivan/local/comic/images/p3.png — і ти
за секунду бачиш, що переплутав p3.png і p3.PNG
(у Linux це два різні файли). Скрипт пишеться для людини, яка втомилася
і поспішає, — тобто для тебе за десять хвилин до дзвінка.build.sh — це bash-скрипт. У PowerShell він не запуститься:
PowerShell 5.1 не розуміє ні set -euo pipefail, ні
&&. Відкривай Git Bash (він уже стоїть у класі),
переходь у теку і запускай bash build.sh. Автоперевірка
на сервері теж проганяє скрипт через bash -n.Команди ті самі, що у 8 класі. Нове — те, що вони живуть у файлі
Збірка йде у три ходи. Спочатку кожна панель стає окремим кліпом
однакового формату. Це потрібно через concat: він склеює лише
однакові кліпи. Різна роздільність, різна частота кадрів чи різне
співвідношення пікселя — і склейки не буде. Потім кліпи склеюються
за списком. І вже до склейки додаються титр і музика.
| Хід | Що робить | Ключове місце |
|---|---|---|
| 4.1 | панель → кліп на DUR секунд |
-loop 1 -t "$DUR" перетворює нерухому картинку на відео |
| 4.2 | складає list.txt |
рядки виду file '/повний/шлях/clip1.mp4' |
| 4.3 | склейка + титр + музика → trailer.mp4 |
-f concat -safe 0, -shortest, -map |
Фільтр scale=…:force_original_aspect_ratio=decrease разом
із pad вписує будь-яку панель у кадр 16:9. Панель при цьому
не спотворюється: вертикальна картинка отримає поля по боках, а не
розтягнеться.
setsar=1 вирівнює співвідношення пікселя. Без нього
concat лається навіть на кліпи однакового розміру.
У третьому рядку таблиці лишилися ще два ключі. Обидва потрібні саме тому, що ми склеюємо файли за списком.
-safe 0 дозволяє читати з list.txt повні
шляхи виду /home/…/clip1.mp4. Без цього ключа
ffmpeg із міркувань безпеки бере тільки короткі імена
з поточної теки — і зупиняється з помилкою.
-map прямо каже, що з якого файлу брати. У нас два
входи: склеєне відео й музика. -map 0:v означає «картинку
бери з першого», -map 1:a — «звук бери з другого».
Без цих ключів ffmpeg вибирає сам і часто бере не те.
drawtext
У фільтрі drawtext двокрапка — службовий символ, і
C:/Windows/Fonts/arialbd.ttf ламає розбір параметрів. У Git Bash
пишуть fontfile=C\:/Windows/Fonts/arialbd.ttf — з екранованою
двокрапкою. У Linux шлях звичайний:
/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf.Задай параметри свого трейлера — і сторінка складе повний скрипт із перевірками, коментарями і блоком звіту.
Далі його треба прочитати рядок за рядком і підправити під себе. Чужий скрипт, якого ти не розумієш, на очній здачі не захистиш.
Нижні три перемикачі додають у скрипт по кілька рядків кожен. Ось що вони роблять.
Згасання музики в кінці. Останні дві секунди підкладка плавно стихає, а не обривається на півноті.
Виміряти гучність (LUFS). Скрипт додасть команду, яка порахує, наскільки твій ролик гучний загалом. Міряють це не в тих поділках, що на регуляторі гучності, а в LUFS. Це число враховує, як гучність чує вухо за весь ролик, а не в одну мить. Правило просте: чим ближче до −14 LUFS, тим спокійніше твій трейлер стоятиме поруч із чужими відео й не оглушить глядача.
Кадр-постер poster.jpg. Скрипт вирізає з ролика
один кадр і зберігає його окремою картинкою. Це та обкладинка, яку
плеєр показує, поки відео ще не запустили. Без неї на місці ролика
стоїть чорний прямокутник.
ffprobe-ом і сам підставляє їх
у av.meta.json. Через це розбіжність між звітом і роликом стає
неможливою: не тому, що ти уважний, а тому, що числа туди вписує не людина.Панелі дають 30 секунд відео, а музична підкладка триває 1 хвилину
20 секунд. Учень прибрав зі скрипта ключ -shortest.
Яким вийде ролик?
Хто насправді читає субтитри і чому їх не можна «домалювати потім»
Найпоширеніша помилка в думці про субтитри звучить так: «у мене ролик українською, всі почують, субтитри не потрібні». Порахуймо, кому вони потрібні насправді.
| Хто | Ситуація | Без субтитрів |
|---|---|---|
| Глухі та слабочуючі | дивляться будь-де | ролик для них не існує — це не «гірше», це нуль |
| Будь-хто в шумі | метро, коридор школи, вулиця | чути музику, не чути слів |
| Будь-хто без звуку | стрічка соцмережі, урок, ніч удома | дивиться перші 3 секунди й гортає далі |
| Хто вчить мову | іноземець, молодший школяр | на слух втрачає половину слів |
| Пошук і робот | індексація, автопереклад | текст ролика невидимий: у відео нема чого шукати |
Є міжнародний набір правил доступності — WCAG 2.1. У ньому субтитри до записаного відео стоять у критерії 1.2.2 Captions (Prerecorded). Це рівень A — найнижчий поріг. Сторінку без нього взагалі не вважають доступною. Отже, субтитри — не «бажано», а «мінімум».
І практичний бік: субтитри неможливо чесно «домалювати потім», якщо репліки не сплановані. Тому їх пишуть на тому самому таймлайні, на якому розкладені панелі.
drawtext впечений
у пікселі: щоб його виправити, ролик треба перезбирати. Субтитри лежать
поруч у текстовому файлі: помилку в них виправляють за десять секунд,
а глядач може їх вимкнути.Однокласник каже: «Субтитри не потрібні — у нас у класі всі чують, а на екрані вони тільки заважають». Що відповісти по суті?
.vttЗаголовок, порожній рядок, репліки. Усе — звичайний текст у UTF-8
SRT старший і поширеніший у відеоплеєрах на комп'ютері. Для вебу
придатний лише WebVTT: HTML-елемент <track> за специфікацією
читає тільки його.
| Ознака | WebVTT (.vtt) | SubRip (.srt) |
|---|---|---|
| Перший рядок | WEBVTT обов'язковий | немає заголовка |
| Дробова частина секунд | крапка: 00:00:03.500 | кома: 00:00:03,500 |
| Кодування | лише UTF-8, за стандартом | яке завгодно — звідси «кракозябри» |
| Номер репліки | не обов'язковий | обов'язковий |
У браузері через <track> | працює | не працює |
| Позиція, вирівнювання, стилі | є (line, align, ::cue) | немає |
| MIME-тип | text/vtt | не стандартизований |
Атрибут default вмикає доріжку одразу — саме так учитель
дивитиметься твій ролик на очній здачі. Ця сторінка живе локально,
поруч із trailer.mp4: на сервер відео не поїде, а от
trailer.vtt поїде.
file://
Якщо відкрити preview.html подвійним кліком, деякі браузери
не покажуть субтитри: доріжку <track> вони вважають
«стороннім ресурсом». Лікується локальним сервером в одну команду —
python -m http.server 8000 у тій самій теці, далі
http://localhost:8000/preview.html.Учень зробив субтитри у програмі, яка зберігає .srt,
і підключив їх: <track src="trailer.srt" srclang="uk" default>.
У браузері субтитрів немає, помилок теж. Що відбувається?
Це не смак, а виміряні речі. Ось галузеві орієнтири (Netflix, BBC — посилання в «Джерелах»):
| Правило | Значення | Чому саме так |
|---|---|---|
| Рядків у репліці | не більше 2 | третій рядок закриває кадр і відриває око від картинки |
| Символів у рядку | ~40 (Netflix 42, BBC 37) | довший рядок око «сканує» замість читати |
| Швидкість читання | ≤ 17 симв./с (дорослі), 13 (діти) | верхня межа, за якою глядач не встигає |
| Мінімальний показ | 5/6 с ≈ 0,83 с | коротший блимає й дратує навіть на одному слові |
| Максимальний показ | 7 с | довше глядач починає перечитувати той самий рядок |
Із швидкості читання виводиться проста формула мінімальної тривалості репліки: час ≥ кількість символів ÷ 17, але не менше 0,83 с. Репліка на 51 символ вимагає щонайменше 3 секунди на екрані.
У репліці три рядки по 55 символів, показ — 1,2 секунди. Скільки правил порушено?
Репліка 2 закінчується о 00:00:09.000, а репліка 3
починається о 00:00:08.000. Що побачить глядач?
Учитель каже: «Субтитри з'являються приблизно на секунду раніше за репліку». Де саме в файлі шукати помилку?
Розстав репліки на таймлайні, а сторінка перевірить їх за правилами
й видасть готовий trailer.vtt
Правила з попереднього кроку легко переказати і легко порушити. Тут вони перетворені на перевірки.
Щойно репліка стає нечитабельною, її блок на доріжці червоніє. А внизу з'являється пояснення, що саме не так.
Готовий файл — скопіюй і збережи
як trailer.vtt у кодуванні UTF-8:
символи ÷ швидкість
(але не менш ніж 0,83 с). Фраза встигає блимнути, а прочитати
її не встигає ніхто.Репліка «Ліфт зупинився між поверхами.» — це 29 символів. Ти поставив їй показ на рівно 1 секунду, і редактор підсвітив рядок червоним. Скільки часу їй потрібно щонайменше?
probe.txt і av.meta.jsonПаспорт ролика, який неможливо підробити, не переробивши ролик
Учитель не побачить твій trailer.mp4 на сервері — його там
не буде. Замість відео на сервер їде його паспорт: сирий вивід
ffprobe і той самий набір чисел у машиночитному вигляді.
probe.txt — і пастка з порожнім файломffprobe друкує опис доріжок не в стандартний вивід, а в
потік помилок (stderr) — так історично склалося в усьому наборі
ffmpeg. Тому звичайне > probe.txt дає порожній файл.
Потрібне перенаправлення обох потоків.
>
Windows PowerShell 5.1 у перенаправленні > пише файл
у UTF-16. Такий probe.txt відкривається в редакторі
нормально, але чекер на сервері читає його як UTF-8 і бачить сміття —
і ти отримуєш незрозуміле «файл не містить h264», хоча він його містить.
Тому в PowerShell завжди | Out-File -Encoding utf8.-Encoding utf8 додає BOM
Той самий ключ, який рятує probe.txt, ламає
list.txt для ffmpeg -f concat: три службові байти
на початку приклеюються до слова file, і ffmpeg не розуміє
перший рядок. Для list.txt у PowerShell потрібне
-Encoding ascii. Правило: дивись, хто читатиме файл —
чекер на сервері чи інструмент на твоїй машині.Це приклад виводу, а не твої числа: у тебе будуть свої. Але сходитися вони мають так само — 846 + 192 ≈ 1043 кбіт/с разом зі службовими даними контейнера.
Учень виконав ffprobe -hide_banner trailer.mp4 > probe.txt
у Git Bash. Ролик відкривається і грає, а probe.txt —
нульового розміру. Що сталося?
Учениця працює у PowerShell. probe.txt у «Блокноті»
виглядає ідеально, а чекер каже «файл не містить h264».
Найімовірніша причина?
av.meta.json — що з чого зробленоprobe.txt читає людина, av.meta.json — програма.
Це підсумковий звіт про роботу: скільки триває, з чого зібрано, чим зроблено.
| Поле | Звідки береться | Що доводить |
|---|---|---|
duration | ffprobe … format=duration |
ролик справді на 20–40 с, а не «приблизно» |
resolution, fps | змінні W, H, FPS зі скрипта |
параметри збірки, а не побажання |
video_codec, audio_codec | ffprobe … stream=codec_name |
усередині контейнера справді H.264 і AAC |
sources | змінна PANELS |
ролик зібрано з твоїх панелей теми 4, а не з чужих |
music_source, code_file | вписуєш сам | музика — твоя, написана кодом на уроці 9 |
Ключова ідея: усе, що можна виміряти, вимірює скрипт. Руками ти
вписуєш лише те, чого ffprobe знати не може, — звідки взялася
музика і скільки в ролику ефектів.
У av.meta.json написано "duration": 35,
а в probe.txt — Duration: 00:00:30.00.
Учень каже: «ну майже». Що буде на перевірці і як цього уникнути назавжди?
Кроки 1—11 — у твоєму терміналі й редакторі, крок 12 — по FTP, крок 13 — для рівня «+». Галочки зберігаються, сторінку можна закрити.
~/local/av/ — trailer.mp4,
проміжні clip1…clip6.mp4, list.txt — лишається
в тебе. На сервер піднімаються рівно чотири текстові файли:
build.sh, trailer.vtt, probe.txt,
av.meta.json. Спроба залити .mp4 нічим не
закінчиться: FTP-приймальня його просто не прийме.Чекер не дивиться відео. Він звіряє твої чотири текстові файли один з одним — і вигадані числа не сходяться. Спроби не обмежені.
Демонстраційний режим: результат згенеровано для показу. На сервері
ця кнопка викликає POST /api/check, який запускає
check 10 від імені учня.
~/local/av/ — там лежить
trailer.mp4, а поряд build.sh;preview.html або плеєром, який підхоплює .vtt;trailer.mp4 при вчителі й запускаєш
bash build.sh — ролик має зібратися наново сам;setsar=1 і що робить -shortest;trailer.vtt
і показати, що вона поїхала в кадрі.build.sh дописано
«щоб було». Це видно за півхвилини: скрипт або не запускається, або збирає
щось інше, ніж те, що на екрані.Це те, що бачить учитель, коли виставляє оцінку.
| Складник | Вага | Результат |
|---|
ffprobe, а не пам'ять:
тоді звіт не розходиться з роликом навіть теоретично.build.sh написано «з пам'яті» — і він не запускається;.vtt виходять за тривалість ролика — репліки
не з'являються взагалі;.vtt;probe.txt порожній, бо забули 2>&1;probe.txt у UTF-16 після PowerShell — чекер бачить сміття;av.meta.json вписані руками й не збігаються з
probe.txt;Додай у build.sh згасання звуку в кінці
(afade=t=out) і перезбери ролик. Онови після перезбірки
probe.txt і av.meta.json — обидва мають оновитися
самі, бо їх пише скрипт.
Рівень «+»: знайди в ролику місце, де тиша довша за 8 секунд. Додай туди четверту репліку субтитрів. І поясни одним реченням, чому глядачеві там потрібен текст.
Усі правила на цій сторінці — зі стандартів, документації і галузевих настанов. Перевіряти дозволено й корисно.
WEBVTT, формат часу, порожній рядок як роздільник,
UTF-8 як єдине кодування.
w3.org/TR/webvtt1<track>, MDN — атрибути
kind, srclang, label, default
і те, що підтримується лише WebVTT.
developer.mozilla.org/HTML/Element/trackffmpeg — -i, -map,
-shortest, -filter_complex, -loop.
ffmpeg.org/ffmpeg.htmlffprobe — -show_entries,
-of csv=p=0, -select_streams.
ffmpeg.org/ffprobe.htmlscale,
force_original_aspect_ratio, pad,
setsar, drawtext, afade,
ebur128.
ffmpeg.org/ffmpeg-filters.htmlconcat — формат
list.txt і ключ -safe 0.
trac.ffmpeg.org/wiki/Concatenate-e, -u і pipefail.
gnu.org/software/bash/manualOut-File і кодування у PowerShell — чому
> у Windows PowerShell 5.1 дає UTF-16 і як зробити UTF-8.
learn.microsoft.com/PowerShell/Out-Fileabout_Redirection — перенаправлення потоків
у PowerShell, зокрема 2>&1.
learn.microsoft.com/about_Redirectionebur128.
tech.ebu.ch/publications/r128text/vtt у реєстрі IANA — чим сервер має
віддавати файл субтитрів.
iana.org/assignments/media-types/text/vttПовний список із поясненнями, що звідки взято, —
у файлі urok-10-джерела.md поруч із цією сторінкою.