diff --git a/Script/pdf2video b/Script/pdf2video deleted file mode 100755 index e28c9d5..0000000 --- a/Script/pdf2video +++ /dev/null @@ -1,255 +0,0 @@ -#!/bin/bash -# Общественное достояние, 2024, Алексей Безбородов (Alexei Bezborodov) - -# Озвучивание русского текста из файла pdf и сохранение в видео - -version=1.0 - -# Формат: -# "Однобуквенная комманда|Расширенная комманда|Справка|Параметр|Значение по умолчанию|Команда на исполнение" -# Параметр: Пусто - нет параметров, : - есть параметр, :: - параметр не обязателен - -common_params=( - "h|help|Посмотреть помощь.|||ShowHelp; exit;" - "v|version|Посмотреть версию программы.|||echo \$version; exit;" - "V|verbose|Подробный вывод.|||verbose=true" -# "|||||" - ) - -sound_params=( - "i|input|Входной текстовый файл.|:||" - "e|emotion|Эмоциональный настрой говорящего. Может принимать значения 'neutral', 'good', 'evil'. По умолчанию '!DEFAULT!'.|:|'neutral'|" - "s|speaker|Голос говорящего. Может принимать значения 'oksana','jane','omazh','zahar','ermil','silaerkan','erkanyavas','alyss', 'nick'. По умолчанию '!DEFAULT!'.|:|'erkanyavas'|" - "S|speed|Скорость озвучки. По умолчанию '!DEFAULT!'.|:|'1.0'|" - "O|ffmpeg_opt|Дополнительные параметры ffmpeg.|:|''|" - "f|format|Выходной формат. Может быть либо 'mp3', либо 'wav'. По умолчанию '!DEFAULT!'.|:|'mp3'|" - "q|quality|Качество выходного файла. Может быть либо 'hi', либо 'lo'. По умолчанию '!DEFAULT!'.|:|'hi'|" - "l|lang|Язык озвучки. По умолчанию '!DEFAULT!'.|:|'ru_RU'|" -# "|||:||" - ) - -video_params=( - "o|output|Выходной видео файл.|:|''|" - "k|split|Деление страницы пополам. Может быть либо 'yes', либо 'no'. По умолчанию '!DEFAULT!'.|:|'yes'|" - "W|video_width|Размер видео в пикселях по ширине. По умолчанию '!DEFAULT!'.|:|1920|" - "H|video_height|Размер видео в пикселях по высоте. По умолчанию '!DEFAULT!'.|:|1080|" - "p|ffmpeg_pre_options|Опции ffmpeg в самом начале. По умолчанию '!DEFAULT!'.|:|'-loop 1 -r 2'|" - "P|ffmpeg_options|Опции ffmpeg. По умолчанию '!DEFAULT!'.|:|'-c:v libx264 -tune stillimage -preset ultrafast -crf 20 -shortest -pix_fmt yuv420p'|" - "r|page_range|Указывает страницы из выходного файла для обработки. Пример '{1..32}', '{2..10..2}', '\$(seq 5 3 30)'|:|''|" - "m|minimum_text_on_page|Минимальное количество символов на странице при котором происходит разделение страницы на две. По умолчанию '!DEFAULT!'.|:|1000|" -# "|||:||" - ) - -all_params=("${common_params[@]}" "${sound_params[@]}" "${video_params[@]}") - -# Загружаем библиотеку -function GetExec { - local exec_file_name="$1" - - exec="$exec_file_name" - [ ! -f "$exec" ] && exec="./$exec_file_name" - [ ! -f "$exec" ] && exec="~/$exec_file_name" - - echo "$exec" -} - -eval "source $(GetExec "parse_arg_lib")" - -function ShowHelp() { -cat << EOF -Использование: pdf2video -i [-o ] [-hV] -Озвучивание русского текста из файла pdf и сохранение в видео - -Общие параметры -$(ProcessParams common_params Params2Help) - -Параметры звука -$(ProcessParams sound_params Params2Help) - -Параметры видео -$(ProcessParams video_params Params2Help) -EOF -} - -# ------------------------------------------- - -while true -do -cur_arg="$1" -[ "$cur_arg" = '--' ] && { shift; break; } -ProcessParams all_params Params2Case "$cur_arg" "$2" -shift -done - -input_file="$input" -out_file="$output" - -unuse_param="$*" -if [ "${input_file}" = "" ] || [ "${unuse_param}" != "" ]; then - [ "${unuse_param}" != "" ] && echo "Параметры не расшифрованы \"$unuse_param\"" - ShowHelp - exit -fi - -[ "$out_file" = "" ] && { out_file="${input_file}.mp4"; [ $verbose ] && echo "Выходное имя файла \"$out_file\""; } - -#---------------------------------------------------- - -page_count=$(pdfinfo "${input_file}" | awk '/^Pages:/ {print $2}') - -video_file_names_array=() - -function Text2mp3 { - local text_file=$1 - local mp3_file=$2 - verb="" - [ $verbose ] && verb="-V" - - [ $verbose ] && echo "Найден исполняемый файл для преобразования в звук текста $(GetExec txt2mp3)" - eval "$(GetExec "txt2mp3") -i '${text_file}' -o '${mp3_file}' -e '${emotion}' -s '${speaker}' -S '${speed}' -f '${format}' -q '${quality}' -l '${lang}' '${verb}'" -} - -function MakeVideo { - local page_image_file=$1 - local page_mp3_file=$2 - local page_mp4_file=$3 - - local resized_page_image_file="${page_image_file}_resized.png" - - ffmpeg -y -i "${page_image_file}" -vf "scale=${video_width}:${video_height}:force_original_aspect_ratio=decrease,pad=${video_width}:${video_height}:(ow-iw)/2:(oh-ih)/2" "${resized_page_image_file}" - - local time_play=$(mp3info -p "%S\n" "${page_mp3_file}") - local time_opt="-c:a copy" - if [ ${minimum_time_on_page} -ge ${time_play} ]; then - local add_time=5 # $(( 5 - ${time_play} )) - time_opt="-c:a mp3 -af adelay=${add_time}s:all=true" # - [ $verbose ] && echo "time_opt ${time_opt}" - fi - - ffmpeg ${ffmpeg_pre_options} -i "${resized_page_image_file}" -i "${page_mp3_file}" ${ffmpeg_options} ${time_opt} "${page_mp4_file}" - - SAVE_IFS=$IFS - IFS="" - video_file_names_array+=(${page_mp4_file}) - IFS=$SAVE_IFS - - rm "${resized_page_image_file}" -} - -[ $verbose ] && echo "Всего страниц $page_count" - -for ((page=1;page<=${page_count};page++)); do - - if [ $page_range ]; then - skip="true" - for p in $(eval echo "$page_range"); - do - if [ $p = $page ]; then - skip="false" - break - fi - done - - if [ $skip = "true" ]; then - [ $verbose ] && echo "Пропускаем страницу №$page" - continue - fi - fi - - [ $verbose ] && echo "------------------------------------------------" - [ $verbose ] && echo "Обрабатываем страницу №$page" - - page_text_file="${input_file}_${page}.txt" - page_image_file="${input_file}_${page}" - pdftotext -f $page -l $page "${input_file}" "$page_text_file" - pdftoppm -r 300 -f $page -l $page -png -singlefile "${input_file}" "$page_image_file" - - page_image_file="${page_image_file}.png" - - source_text="$(cat "${page_text_file}")" - - if [ "$split" = "yes" ] && [ ${#source_text} -ge $minimum_text_on_page ]; then - - space_char=" " - split_size=$(( ${#source_text} / 2 + 2)) # Половина с небольшим запасом - file_index=0 - for ((i=1;i<=${#source_text};i++)); do - cur_char=${source_text:$i-1:1} - cur_text="${cur_text}${cur_char}" - if [ "$cur_char" = "$space_char" ] && [ ${#cur_text} -ge $split_size ] || [ $i = ${#source_text} ]; then - let file_index+=1 - - echo "$cur_text" > "${page_text_file}_half${file_index}" - - cur_text="" - fi - done - - file_txt_half1="${page_text_file}_half1" - file_txt_half2="${page_text_file}_half2" - - page_mp3_file_half1="${file_txt_half1}.mp3" - page_mp3_file_half2="${file_txt_half2}.mp3" - - Text2mp3 "$file_txt_half1" "$page_mp3_file_half1" - Text2mp3 "$file_txt_half2" "$page_mp3_file_half2" - - width=$(identify -format "%w" "$page_image_file")> /dev/null - height=$(identify -format "%h" "$page_image_file")> /dev/null - - height_half=$(( $height / 2 + $height / 20 )) - - page_image_file_half1="${page_image_file}_half1.png" - page_image_file_half2="${page_image_file}_half2.png" - - # format (widthxheight+left+top / wxh+l+t) - convert "$page_image_file" -crop ${width}x${height_half}+0+0 "$page_image_file_half1" - convert "$page_image_file" -crop ${width}x${height_half}+0+$(( $height - $height_half )) "$page_image_file_half2" - - page_mp4_file_half1="${input_file}_${page}_half1.mp4" - page_mp4_file_half2="${input_file}_${page}_half2.mp4" - - MakeVideo "$page_image_file_half1" "$page_mp3_file_half1" "$page_mp4_file_half1" - - MakeVideo "$page_image_file_half2" "$page_mp3_file_half2" "$page_mp4_file_half2" - - rm "$page_image_file_half1" - rm "$page_image_file_half2" - - rm "$file_txt_half1" - rm "$file_txt_half2" - rm "$page_mp3_file_half1" - rm "$page_mp3_file_half2" - - else - page_mp3_file="${page_text_file}.mp3" - - Text2mp3 "$page_text_file" "$page_mp3_file" - - page_mp4_file="${input_file}_${page}.mp4" - - MakeVideo "$page_image_file" "$page_mp3_file" "$page_mp4_file" - - rm "$page_mp3_file" - - fi - - rm "$page_image_file" - rm "$page_text_file" - -done - -SAVE_IFS=$IFS -IFS="" -[ $verbose ] && echo "Объединяем файлы ${video_file_names_array[*]} в $out_file" -ffmpeg -f concat -safe 0 -i <(for ((i = 0; i < ${#video_file_names_array[@]}; i++)) do echo "file '$PWD/${video_file_names_array[$i]}'"; done) -acodec copy -vcodec copy "$out_file" - -for ((i = 0; i < ${#video_file_names_array[@]}; i++)) do - f="${video_file_names_array[$i]}" - [ $verbose ] && echo "Удаляем файл '$f'" - rm "$f" -done -IFS=$SAVE_IFS - -[ $verbose ] && echo "Конечный файл создан '$out_file'!" - diff --git a/Script/txt2mp3 b/Script/txt2mp3 deleted file mode 100755 index df0a468..0000000 --- a/Script/txt2mp3 +++ /dev/null @@ -1,163 +0,0 @@ -#!/bin/bash -# Общественное достояние, 2024, Алексей Безбородов (Alexei Bezborodov) - -# Озвучивание текста из файла - -version=1.0 - -# Формат: -# "Однобуквенная комманда|Расширенная комманда|Справка|Параметр|Значение по умолчанию|Команда на исполнение" -# Параметр: Пусто - нет параметров, : - есть параметр, :: - параметр не обязателен - -common_params=( - "h|help|Посмотреть помощь.|||ShowHelp; exit;" - "v|version|Посмотреть версию программы.|||echo \$version; exit;" - "V|verbose|Подробный вывод.|||verbose=true" -# "|||||" - ) - -sound_params=( - "i|input|Входной текстовый файл.|:||" - "o|output|Выходной видео файл.|:|''|" - "e|emotion|Эмоциональный настрой говорящего. Может принимать значения 'neutral', 'good', 'evil'. По умолчанию '!DEFAULT!'.|:|'neutral'|" - "s|speaker|Голос говорящего. Может принимать значения 'oksana','jane','omazh','zahar','ermil','silaerkan','erkanyavas','alyss', 'nick'. По умолчанию '!DEFAULT!'.|:|'erkanyavas'|" - "S|speed|Скорость озвучки. По умолчанию '!DEFAULT!'.|:|'1.0'|" - "O|ffmpeg_opt|Дополнительные параметры ffmpeg.|:|''|" - "f|format|Выходной формат. Может быть либо 'mp3', либо 'wav'. По умолчанию '!DEFAULT!'.|:|'mp3'|" - "q|quality|Качество выходного файла. Может быть либо 'hi', либо 'lo'. По умолчанию '!DEFAULT!'.|:|'hi'|" - "l|lang|Язык озвучки. По умолчанию '!DEFAULT!'.|:|'ru_RU'|" -# "|||:||" - ) - -all_params=("${common_params[@]}" "${sound_params[@]}") - -# Загружаем библиотеку -function GetExec { - local exec_file_name="$1" - - exec="$exec_file_name" - [ ! -f "$exec" ] && exec="./$exec_file_name" - [ ! -f "$exec" ] && exec="~/$exec_file_name" - - echo "$exec" -} - -eval "source $(GetExec "parse_arg_lib")" - -function ShowHelp() { -cat << EOF -Использование: pdf2mp3 -i [-o ] [-hV] -Озвучивание текста из файла - -Общие параметры -$(ProcessParams common_params Params2Help) - -Параметры звука -$(ProcessParams sound_params Params2Help) -EOF -} - -# ------------------------------------------- - -while true -do -cur_arg="$1" -[ "$cur_arg" = '--' ] && { shift; break; } -ProcessParams all_params Params2Case "$cur_arg" "$2" -shift -done - -input_file="$input" -out_file="$output" - -unuse_param="$*" -if [ "${input_file}" = "" ] || [ "$unuse_param" != "" ]; then - [ "$unuse_param" != "" ] && echo "Параметры не расшифрованы \"$unuse_param\"" - ShowHelp - exit -fi - -[ "$out_file" = "" ] && { out_file="$input_file.mp3"; [ $verbose ] && echo "Выходное имя файла \"$out_file\""; } - -source_text=$(cat "${input_file}") - -# Удаляем все пробелы в начале и в конце строк и заменяем два и более пробелов на один, удаляем все непечатаемые символы -source_text="$(echo "${source_text//[$'\t\r\n']/' '}" | sed 's/^ *//;s/[ ^]*$//;s/ */ /;s/[^[:blank:][:print:]]//g')" - -#[ $verbose ] && echo "Исходный текст $source_text" >> "out.txt" - -ping -c 3 ya.ru &>/dev/null || { echo "Интернет недоступен."; exit; } - -split_size=1450 -[ $verbose ] && echo "Длина текста ${#source_text}: Разбиваем на части по $split_size" - -txt_array=() - -space_char=" " -for ((i=1;i<=${#source_text};i++)); do - cur_char=${source_text:$i-1:1} - cur_text="${cur_text}${cur_char}" - if [ "$cur_char" = "$space_char" ] && [ ${#cur_text} -ge $split_size ] || [ $i = ${#source_text} ]; then - - # Максимальная длина SEND_IRI - 1590 символов, длина SEND_IRI без текста = 75 символов - # Максимальная длина текста = 1590 - 75 = 1515 символов - text_count=${#cur_text} - [ $text_count -ge 1515 ] && { echo "Превышено максимальное колличество символов - 1515"; exit; } - - SAVE_IFS=$IFS - IFS="" - txt_array+=($cur_text) - IFS=$SAVE_IFS - - cur_text="" - fi -done - -audio_file_names_array=() - -# Если текст пустой, то всё равно создаём выходной файл -[ ${#txt_array[@]} -le 0 ] && { txt_array+="."; } - -SAVE_IFS=$IFS -IFS="" -file_index=0 -for ((i = 0; i < ${#txt_array[@]}; i++)) do - cur_text="${txt_array[$i]}" - - let file_index+=1 - [ $verbose ] && echo "Часть номер $file_index" - [ $verbose ] && echo "------------------------------" - [ $verbose ] && echo $cur_text - [ $verbose ] && echo "------------------------------" - - #[ $verbose ] && echo $cur_text >> "out.txt" - - audio_file_name="${input_file}_${file_index}.mp3" - - [ $verbose ] && echo -en "\nЗагрузка аудио в файл '$audio_file_name'...\n" - - #touch "$audio_file_name" - wget -q -O "$audio_file_name" "http://tts.voicetech.yandex.net/tts?format=mp3&quality=hi&lang=ru_RU&speed=${speed}&speaker=${speaker}&emotion=${emotion}&text=${cur_text}" || { echo "Ошибка при загрузке аудио."; exit; } - - [ $verbose ] && echo "Файл '$audio_file_name' загружен." - - SAVE_IFS=$IFS - IFS="" - audio_file_names_array+=($audio_file_name) - IFS=$SAVE_IFS -done - -[ $verbose ] && echo "Объединяем файлы ${audio_file_names_array[*]} в $out_file" - -ffmpeg -f concat -safe 0 -i <(for ((i = 0; i < ${#audio_file_names_array[@]}; i++)) do echo "file '$PWD/${audio_file_names_array[$i]}'"; done) -acodec copy -vcodec copy ${ffmpeg_opt} "$out_file" - -for ((i = 0; i < ${#audio_file_names_array[@]}; i++)) do - f="${audio_file_names_array[$i]}" - [ $verbose ] && echo "Удаляем файл '$f'" - rm "$f" -done -IFS=$SAVE_IFS - -[ $verbose ] && echo "Конечный файл создан '$out_file'!" - - diff --git a/video_tools/img_and_wav2video b/video_tools/img_and_wav2video new file mode 100755 index 0000000..a1d42c5 --- /dev/null +++ b/video_tools/img_and_wav2video @@ -0,0 +1,129 @@ +#!/bin/bash +# Общественное достояние, 2024, Алексей Безбородов (Alexei Bezborodov) + +# Озвучивание русского текста из файла pdf и сохранение в видео + +version=1.0 + +# Формат: +# "Однобуквенная команда|Расширенная команда|Справка|Параметр|Значение по умолчанию|Команда на исполнение" +# Параметр: Пусто - нет параметров, : - есть параметр, :: - параметр не обязателен + +common_params=( + "h|help|Посмотреть помощь.|||ShowHelp; exit;" + "v|version|Посмотреть версию программы.|||echo \$version; exit;" + "V|verbose|Подробный вывод.|||verbose=true" + ) + +sound_params=( + "a|audio|Входной файл с аудиодорожкой.|:||" + "b|audio_options||:|'-c:a aac -b:a 128k'|" + ) + +video_params=( + "i|image|Входной файл с изображением.|:||" + "o|output|Выходной видео файл.|:|''|" + "W|video_width|Размер видео в пикселях по ширине. По умолчанию '!DEFAULT!'.|:|1920|" + "H|video_height|Размер видео в пикселях по высоте. По умолчанию '!DEFAULT!'.|:|1080|" + "p|ffmpeg_pre_options|Опции ffmpeg в самом начале. По умолчанию '!DEFAULT!'.|:|'-loop 1 -r 1'|" + "P|ffmpeg_options|Опции ffmpeg. По умолчанию '!DEFAULT!'.|:|'-c:v libx264 -tune stillimage -preset ultrafast -crf 20 -shortest -pix_fmt yuv420p'|" + ) + +all_params=("${common_params[@]}" "${sound_params[@]}" "${video_params[@]}") + +# Загружаем библиотеку +function GetExec { + local exec_file_name="$1" + + exec="./$exec_file_name" + [ ! -f "$exec" ] && exec="~/$exec_file_name" + + echo "$exec" +} + +eval "source $(GetExec "parse_arg_lib")" + +function ShowHelp() { +cat << EOF +Использование: pdf2video -i [-o ] [-hV] +Озвучивание русского текста из файла pdf и сохранение в видео + +Общие параметры +$(ProcessParams common_params Params2Help) + +Параметры звука +$(ProcessParams sound_params Params2Help) + +Параметры видео +$(ProcessParams video_params Params2Help) + +Примеры: + +# Расширенный вывод +img_and_wav2video -i image.jpg -a audio.wav -o out.mp4 -V + +EOF +} + +# ------------------------------------------- + +# Главный цикл обработки входных параметров +eval "$main_loop" + +image_file="$image" +audio_file="$audio" +out_file="$output" + +unuse_param="$*" +if [ "${image_file}" = "" ] || [ "${audio_file}" = "" ] || [ "${unuse_param}" != "" ]; then + [ "${unuse_param}" != "" ] && echo "Параметры не расшифрованы \"$unuse_param\"" + ShowHelp + exit +fi + +[ "$out_file" = "" ] && { out_file="${image_file}.mp4"; +[ $verbose ] && echo "Выходное имя файла \"$out_file\""; } + +#---------------------------------------------------- + +function PlayTime { + local audio_file=$1 + + out=$(soxi -D "${audio_file}") + int=$(echo "$out/1" | bc) + echo "$int" +} + +function MakeVideo { + local image_file=$1 + local audio_file=$2 + local mp4_file=$3 + + local resized_image_file=$(mktemp -t "MakeVideo_resized_page_image_XXXXXXXXXXX.png" +) + + local cmd="ffmpeg -y -i \"${image_file}\" -filter_complex \"[0]scale=${video_width}:${video_height}:force_original_aspect_ratio=decrease,pad=${video_width}:${video_height}:(ow-iw)/2:(oh-ih)/2[scale];[scale]split=2[bg][fg];[bg]drawbox=c=white@1:replace=1:t=fill[bg];[bg][fg]overlay=format=auto\" \"${resized_image_file}\"" + [ $verbose ] && echo "cmd $cmd" + eval "$cmd" + [ $verbose ] && echo "ffmpeg $?" + + local play_time=$(PlayTime "${audio_file}") + [ $verbose ] && echo "play_time ${play_time}" + play_time_plus1=$(( $play_time + 1 )) + + water_mark='' + float_image='' +# video_filter="-filter_complex \"${water_mark}\" -map \"1:a\"" + video_filter="-map 0 -map \"1:a\"" + + cmd="ffmpeg -y ${ffmpeg_pre_options} -i \"${resized_image_file}\" ${float_image} -i \"${audio_file}\" ${video_filter} ${ffmpeg_options} ${audio_options} -t \"${play_time}\" \"${mp4_file}\"" + [ $verbose ] && echo "cmd $cmd" + eval "$cmd" + [ $verbose ] && echo "ffmpeg $?" + + rm "${resized_image_file}" +} + +MakeVideo "${image_file}" "${audio_file}" "${out_file}" + + diff --git a/Script/parse_arg_lib b/video_tools/parse_arg_lib similarity index 89% rename from Script/parse_arg_lib rename to video_tools/parse_arg_lib index 7aefba9..05f3c12 100755 --- a/Script/parse_arg_lib +++ b/video_tools/parse_arg_lib @@ -1,10 +1,10 @@ #!/bin/bash -# Общественное достояние, 2024, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2024, Алексей Безбородов (Alexei Bezborodov) # Обработка входных параметров # Формат: -# "Однобуквенная комманда|Расширенная комманда|Справка|Параметр|Значение по умолчанию|Команда на исполнение" +# "Однобуквенная команда|Расширенная команда|Справка|Параметр|Значение по умолчанию|Команда на исполнение" # Параметр: Пусто - нет параметров, : - есть параметр, :: - параметр не обязателен # Пример @@ -106,3 +106,5 @@ options=$(getopt --long "$large_params_list" -o "$small_params_list" -a -- "$@") # are set to the arguments, even if some of them begin with a ‘-’. eval set -- "$options" +main_loop='while true; do cur_arg="$1"; [ "$cur_arg" = "--" ] && { shift; break; }; ProcessParams all_params Params2Case "$cur_arg" "$2"; shift; done' +