Простейшая программа для нарезки видео с помощью FFmpeg, которая наглядно показывает возможности YaBasic
Цель проекта: Создать полезную утилиту для вырезания фрагментов из видео, используя только Yabasic и FFmpeg.
Возможности программы:
Файл lang.txt
Как использовать программу:
Автор: Антон Шехетов
Лицензия: MIT (свободное использование и модификация)
Дата публикации: 16 января 2026 г.
Цель проекта: Создать полезную утилиту для вырезания фрагментов из видео, используя только Yabasic и FFmpeg.
Возможности программы:
- Выбор видеофайла через аргументы или ввод
- Указание начала и конца вырезаемого фрагмента
- Два режима работы: быстрое копирование и точное перекодирование
- Локализация интерфейса через внешний файл
- Проверка существования файлов и предупреждения
- K.I.S.S. (Keep It Simple, Stupid) - делаем просто и понятно
- DRY (Don't Repeat Yourself) - избегаем повторения кода
- Modularity - разделение на подпрограммы
Код: Выделить всё
// Списки для хранения ключей и значений перевода из lang.txt
// Инициализируем с размером 1, чтобы потом расширить через REDIM
dim lang_keys$(1)
dim lang_vals$(1)
// Счетчик загруженных фраз локализации
G_lang_count = 0
// Полный путь или имя исходного видеофайла
G_in_file$ = ""
// Имя результирующего файла (куда сохраняем готовую лекцию)
G_out_file$ = ""
// Время начала и конца фрагмента с вопросами (в секундах для FFmpeg)
G_t1_sec = 0
G_t2_sec = 0
// Общая длительность видео в секундах (получаем через ffprobe)
G_total_duration = 0
// Флаг режима обработки: 1 - быстро (копирование), 0 - точно (перекодирование)
// По умолчанию установлен быстрый режим (K.I.S.S.)
G_is_fast = 1
G_ext$ = ".mp4" // Значение по умолчанию для расширений файлов
clear screen
// --- ГЛАВНЫЙ ЦИКЛ ПРОГРАММЫ ---
load_language() // 0. Загружаем словарь
print get_msg$("MSG_WELCOME") // Приветствие
get_input_file() // 1. Получаем файл (аргумент или ввод)
get_video_duration() // 2. Узнаем длину через ffprobe
// Ввод тайм-кодов (используем пользовательские ключи)
print get_msg$("MSG_ASK_T1");
input "" t1_raw$
process_time(t1_raw$, 1)
print get_msg$("MSG_ASK_T2");
input "" t2_raw$
process_time(t2_raw$, 2)
set_mode() // 3. Быстро или Точно
get_output_filename() // 4. Имя результата и проверка
execute_process() // 5. ЗАПУСК FFMPEG (нарезка и склейка)
print get_msg$("MSG_DONE")
input "Press Enter to exit..." a$
end
// Подпрограмма загрузки языка
sub load_language()
local f, line_raw$, pos_eq, count
f = open("lang.txt", "r")
if (f <= 0) then
print "CRITICAL ERROR: 'lang.txt' not found!"
exit 1
endif
// 1. Считаем строки
count = 0
while(not eof(f))
line input #f line_raw$
if (instr(line_raw$, "=") > 0) count = count + 1
wend
close #f
G_lang_count = count
redim lang_keys$(G_lang_count)
redim lang_vals$(G_lang_count)
// 2. Заполняем массивы
f = open("lang.txt", "r")
count = 1
while(not eof(f))
line input #f line_raw$
if (eof(f) and line_raw$ = "") break
pos_eq = instr(line_raw$, "=")
if (pos_eq > 0) then
lang_keys$(count) = left$(line_raw$, pos_eq - 1)
// chomp$ уберет лишние символы переноса для Linux
lang_vals$(count) = chomp$(right$(line_raw$, len(line_raw$) - pos_eq))
count = count + 1
endif
wend
close #f
end sub
// Переводит технические ключи в понятные человеку фразы.
sub get_msg$(key$)
local i
for i = 1 to G_lang_count
if (lang_keys$(i) = key$) return lang_vals$(i)
next i
return "[" + key$ + "]"
end sub
// Подпрограмма получения имени файла
sub get_input_file()
local f_temp, arg$
// Пытаемся получить первый аргумент
arg$ = peek$("argument")
if (arg$ <> "") then
// Если аргумент передан через командную строку
G_in_file$ = arg$
else
// Если аргументов нет — запрашиваем ввод у пользователя
print get_msg$("MSG_ASK_IN_FILE");
input "" G_in_file$
endif
// Проверка существования файла
if (G_in_file$ = "") exit 1
f_temp = open(G_in_file$, "r")
if (f_temp = 0) then
print get_msg$("MSG_ERR_NO_FILE") + ": " + G_in_file$
exit 1
endif
// Находим последнюю точку в имени файла
local i, last_dot
last_dot = 0
for i = 1 to len(G_in_file$)
if (mid$(G_in_file$, i, 1) = ".") last_dot = i
next i
if (last_dot > 0) then
G_ext$ = right$(G_in_file$, len(G_in_file$) - last_dot + 1)
else
G_ext$ = ".mp4" // Если точки нет, считаем что это mp4
endif
close #f_temp
end sub
// Подпрограмма валидации и конвертации
sub process_time(t_str$, mode)
// mode 1 для T1, mode 2 для T2
local hours, minutes, seconds
// Минимальная проверка формата (длина 8: 00:00:00)
if (len(t_str$) <> 8 or mid$(t_str$, 3, 1) <> ":" or mid$(t_str$, 6, 1) <> ":") then
print "Error: Invalid time format! Use HH:MM:SS"
exit 1
endif
hours = val(left$(t_str$, 2))
minutes = val(mid$(t_str$, 4, 2))
seconds = val(right$(t_str$, 2))
if (mode = 1) G_t1_sec = hours * 3600 + minutes * 60 + seconds
if (mode = 2) G_t2_sec = hours * 3600 + minutes * 60 + seconds
end sub
// Получаем длительность через ffprobe
sub get_video_duration()
local cmd$, res$, f_tmp, tmp_file$
// Путь к техническому файлу длительности
tmp_file$ = "tmp/_dur.tmp"
// Создаем папку tmp, если она еще не создана
system("mkdir tmp")
print get_msg$("MSG_PROCESS")
// Формируем команду ffprobe.
// chr$(34) оборачивает путь к видео в кавычки для работы с пробелами.
// Результат перенаправляем в файл через >
cmd$ = "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 " + chr$(34) + G_in_file$ + chr$(34) + " > " + tmp_file$
system(cmd$)
// Открываем созданный файл для чтения результата
f_tmp = open(tmp_file$, "r")
if (f_tmp <= 0) then
print "Error: Could not read duration from " + tmp_file$
exit 1
endif
// Читаем строку с числом секунд
line input #f_tmp res$
close #f_tmp
// Преобразуем строку в число
G_total_duration = val(res$)
// Обнуляем временный файл согласно принципу K.I.S.S.
clear_file(tmp_file$)
// Валидация: если ffprobe не смог прочитать файл, G_total_duration будет 0
if (G_total_duration <= 0) then
print "Error: Invalid video file or duration is zero!"
exit 1
endif
end sub
// Выбор режима
sub set_mode()
local ans$
print get_msg$("MSG_ASK_FAST");
input "" ans$
if (ans$ = "" or lower$(left$(ans$, 1)) = "y") then
G_is_fast = 1
else
G_is_fast = 0
endif
end sub
// Вспомогательная подпрограмма для обнуления файла (вместо удаления)
sub clear_file(f$)
local f_handle
f_handle = open(f$, "w")
if (f_handle > 0) close #f_handle
end sub
// Имя выходного файла
sub get_output_filename()
local f_exist, ans$
print get_msg$("MSG_ASK_OUT_FILE");
input "" G_out_file$
if (G_out_file$ = "") G_out_file$ = "result_" + G_in_file$
// Проверка существования через открытие на чтение
f_exist = open(G_out_file$, "r")
if (f_exist > 0) then
close #f_exist
print get_msg$("MSG_FILE_EXISTS") + " (y/n)? ";
input "" ans$
if (lower$(left$(ans$, 1)) <> "y") exit 0
endif
end sub
// Конвертируем и склеиваем
sub execute_process()
local p1$, p2$, list_file$, codec$, cmd_part1$, cmd_part2$, cmd_concat$, f_list
// 1. Подготовка путей (используем прямой слэш для кроссплатформенности)
p1$ = "tmp/_part1_temp" + G_ext$
p2$ = "tmp/_part2_temp" + G_ext$
list_file$ = "tmp/_list.tmp"
// 2. Выбор кодека на основе режима G_is_fast
if (G_is_fast = 1) then
codec$ = "-c copy"
else
// Режим Slow: точное перекодирование (H.264 + AAC)
codec$ = "-c:v libx264 -crf 20 -c:a aac"
endif
print get_msg$("MSG_PROCESS")
// 3. ШАГ 1: Вырезаем первую часть (от 0 до T1)
// Используем chr$(34) для кавычек вокруг путей
cmd_part1$ = "ffmpeg -y -loglevel error -stats -i " + chr$(34) + G_in_file$ + chr$(34) + " -t " + str$(G_t1_sec) + " " + codec$ + " " + chr$(34) + p1$ + chr$(34)
system(cmd_part1$)
// 4. ШАГ 2: Вырезаем вторую часть (от T2 до конца)
cmd_part2$ = "ffmpeg -y -loglevel error -stats -ss " + str$(G_t2_sec) + " -i " + chr$(34) + G_in_file$ + chr$(34) + " " + codec$ + " " + chr$(34) + p2$ + chr$(34)
system(cmd_part2$)
// 5. ШАГ 3: Создаем файл списка для склейки
f_list = open(list_file$, "w")
if (f_list = 0) then
print "Error: Could not create " + list_file$
exit 1
endif
// Для FFmpeg concat пути внутри файла лучше писать относительно самого файла или полные.
// Так как ffmpeg запускается из корня, пишем полные относительные пути.
print #f_list "file '_part1_temp" + G_ext$ + "'"
print #f_list "file '_part2_temp" + G_ext$ + "'"
close #f_list
// 6. ШАГ 4: Финальная склейка (concat всегда в режиме copy)
// -safe 0 позволяет использовать разные пути, -i должен указывать на файл в tmp
cmd_concat$ = "ffmpeg -y -loglevel error -stats -f concat -safe 0 -i " + list_file$ + " -c copy " + chr$(34) + G_out_file$ + chr$(34)
// Важно: ffmpeg при concat ищет файлы относительно расположения .tmp файла
// Поэтому переходим в папку tmp для этой команды или прописываем пути в файле (мы выбрали второе)
system(cmd_concat$)
// 7. ОЧИСТКА: Обнуляем временные файлы
clear_file(p1$)
clear_file(p2$)
clear_file(list_file$)
clear_file("tmp/_dur.tmp")
end sub
Код: Выделить всё
MSG_WELCOME=--- Программа YaCut v1.0 (Yabasic + Cut FFmpeg) ---
MSG_ASK_IN_FILE=Введите имя файла:
MSG_ASK_T1=Время начала вопросов (ЧЧ:ММ:СС):
MSG_ASK_T2=Время конца вопросов (ЧЧ:ММ:СС):
MSG_ERR_NO_FILE=Ошибка: Файл не найден!
MSG_PROCESS=Обработка...
MSG_ASK_OUT_FILE=Введите имя выходного файла:
MSG_FILE_EXISTS=Файл уже существует. Перезаписать?
MSG_ASK_FAST=Быстрая обработка? [Y/n]:
MSG_DONE=Файл успешно создан!
Как использовать программу:
- Установите FFmpeg и (для windows добавьте его в PATH или директорию с исполняемым файлом)
- Скопируйте код в файл с расширением .yab (например, yacut.yab)
- Создайте файл lang.txt с локализацией (пример выше)
- Запустите программу одним из способов:
Код: Выделить всё
yabasic yacut.yabКод: Выделить всё
yabasic yacut.yab "my_video.mp4"
Автор: Антон Шехетов
Лицензия: MIT (свободное использование и модификация)
Дата публикации: 16 января 2026 г.