Адрес: ул. Б. Очаковская 32 Москва Россия
Наши официальные канал и чат в telegram, группа в ВКонтакте

Простой видеоредактор на Yabasic - от идеи до рабочей программы

Программа демонстрирует работу с FFmpeg, локализацию интерфейса и принципы простого программирования

Библиотеки и примеры программ на языке Yabasic
Аватара пользователя
Anton
Site Admin
Сообщения: 138
Зарегистрирован: Чт фев 08, 2024 7:03 pm
Откуда: Москва

Обсуждение идеи Простой видеоредактор на Yabasic - от идеи до рабочей программы

Сообщение Anton »

Простой видеоредактор на Yabasic - от идеи до рабочей программы
Простейшая программа для нарезки видео с помощью FFmpeg, которая наглядно показывает возможности YaBasic и работает на linux, windows, mac

Цель проекта: Создать полезную утилиту для вырезания фрагментов из видео, используя только 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$ = "_dur.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$ = "_part1_temp" + G_ext$
    p2$ = "_part2_temp" + G_ext$
    list_file$ = "_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 файла
    system(cmd_concat$)

    // 7. ОЧИСТКА: Обнуляем временные файлы 
    clear_file(p1$)
    clear_file(p2$)
    clear_file(list_file$)
    clear_file("_dur.tmp")

end sub
Файл lang.txt (дабы избежать проблем с текстовым форматом привожу транслитом, каждый потом исправит)

Код: Выделить всё

MSG_WELCOME=--- Programma YaCut v1.0 (Yabasic + Cut FFmpeg) ---
MSG_ASK_IN_FILE=Vvedite imya fayla:
MSG_ASK_T1=Vremya nachala voprosov (HH:MM:SS):
MSG_ASK_T2=Vremya kontsa voprosov (HH:MM:SS):
MSG_ERR_NO_FILE=Oshibka: Fayl ne nayden!
MSG_PROCESS=Obrabotka...
MSG_ASK_OUT_FILE=Vvedite imya vyhodnogo fayla:
MSG_FILE_EXISTS=Fayl uzhe sushchestvuet. Perezapisat?
MSG_ASK_FAST=Bystraya obrabotka? [Y/n]:
MSG_DONE=Fayl uspeshno sozdan!


Как использовать программу:
  1. Установите FFmpeg и (для windows добавьте его в PATH или директорию с исполняемым файлом)
  2. Скопируйте код в файл с расширением .yab (например, yacut.yab)
  3. Создайте файл lang.txt с локализацией (пример выше)
  4. Запустите программу одним из способов:

    Код: Выделить всё

    yabasic yacut.yab

    Код: Выделить всё

    yabasic yacut.yab "my_video.mp4"
Пример работы программы:


Автор: Антон Шехетов
Лицензия: MIT (свободное использование и модификация)
Дата публикации: 16 января 2026 г.