lotta changes
Browse filesSigned-off-by: Balazs Horvath <acsipont@gmail.com>
- .zshrc +20 -16
- cringe_move_exaple_images.py +33 -0
- move_example_section +33 -0
- select_and_concat_captions +78 -0
- txt2tags +20 -0
- videocaption +111 -0
- git-wrapper.zsh → zsh/git-wrapper.zsh +0 -0
- zsh/png2mp4.zsh +3 -3
.zshrc
CHANGED
@@ -8,6 +8,19 @@
|
|
8 |
# - conda-env: Adds support for Conda environment management
|
9 |
# 4. Set the custom theme for the shell prompt
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
# The `export QT_QPA_PLATFORM=offscreen` command is used to set the `QT_QPA_PLATFORM`
|
12 |
# environment variable to `offscreen`. This is particularly useful when running Qt
|
13 |
# applications in a headless environment, such as a server or a CI/CD pipeline,
|
@@ -19,11 +32,11 @@ export QT_QPA_PLATFORM=offscreen
|
|
19 |
|
20 |
# Enable the experimental Just-In-Time (JIT) compiler for Python 3.13.
|
21 |
# This can improve performance by compiling Python code to machine code at runtime.
|
22 |
-
# Note: The JIT is only available for x86_64 builds of Python in conda
|
23 |
export PYTHON_JIT=1
|
24 |
|
25 |
# Load the custom git wrapper script
|
26 |
-
source $HOME/toolkit/git-wrapper.zsh
|
27 |
|
28 |
# Set the path to the Oh My Zsh installation directory
|
29 |
export ZSH="$HOME/.oh-my-zsh"
|
@@ -204,19 +217,6 @@ alias rl="source ~/.zshrc"
|
|
204 |
# Alias for quickly editing and reloading the zsh configuration file
|
205 |
alias ezc="nvim ~/.zshrc && source ~/.zshrc"
|
206 |
|
207 |
-
display_git_help() {
|
208 |
-
echo "Git"
|
209 |
-
echo "---"
|
210 |
-
echo "ga: \`git add . && git commit -avs --verbose && git push\`"
|
211 |
-
echo "gc: \`git commit -avs --verbose\`"
|
212 |
-
echo "gcs: \`git clone --recurse-submodules\`"
|
213 |
-
echo "grh: \`git reset --hard\`"
|
214 |
-
echo "wd: \`git diff --word-diff-regex='[^,]+' --patience\`"
|
215 |
-
echo "gs: \`git status\`"
|
216 |
-
echo "gcx: \`git clean -fxd\`"
|
217 |
-
}
|
218 |
-
display_git_help
|
219 |
-
|
220 |
# This function copies the sample prompts file to each dataset directory.
|
221 |
# It iterates through all directories in ~/datasets that start with "by_"
|
222 |
# and copies the kade-sample-prompts.txt file from the toolkit directory
|
@@ -1047,6 +1047,9 @@ conda_prompt_info() {
|
|
1047 |
fi
|
1048 |
}
|
1049 |
|
|
|
|
|
|
|
1050 |
# Function: display_custom_help
|
1051 |
# Description:
|
1052 |
# This function displays custom help information for user-defined functions and aliases.
|
@@ -1060,4 +1063,5 @@ conda_prompt_info() {
|
|
1060 |
#
|
1061 |
# Note:
|
1062 |
# Add or modify entries in this function to keep your personal command reference up-to-date.
|
1063 |
-
display_custom_help
|
|
|
|
8 |
# - conda-env: Adds support for Conda environment management
|
9 |
# 4. Set the custom theme for the shell prompt
|
10 |
|
11 |
+
#display_git_help() {
|
12 |
+
# echo "Git"
|
13 |
+
# echo "---"
|
14 |
+
# echo "ga: \`git add . && git commit -avs --verbose && git push\`"
|
15 |
+
# echo "gc: \`git commit -avs --verbose\`"
|
16 |
+
# echo "gcs: \`git clone --recurse-submodules\`"
|
17 |
+
# echo "grh: \`git reset --hard\`"
|
18 |
+
# echo "wd: \`git diff --word-diff-regex='[^,]+' --patience\`"
|
19 |
+
# echo "gs: \`git status\`"
|
20 |
+
# echo "gcx: \`git clean -fxd\`"
|
21 |
+
#}
|
22 |
+
#display_git_help
|
23 |
+
|
24 |
# The `export QT_QPA_PLATFORM=offscreen` command is used to set the `QT_QPA_PLATFORM`
|
25 |
# environment variable to `offscreen`. This is particularly useful when running Qt
|
26 |
# applications in a headless environment, such as a server or a CI/CD pipeline,
|
|
|
32 |
|
33 |
# Enable the experimental Just-In-Time (JIT) compiler for Python 3.13.
|
34 |
# This can improve performance by compiling Python code to machine code at runtime.
|
35 |
+
# Note: The JIT is only available for x86_64 builds of Python in conda.
|
36 |
export PYTHON_JIT=1
|
37 |
|
38 |
# Load the custom git wrapper script
|
39 |
+
source $HOME/toolkit/zsh/git-wrapper.zsh
|
40 |
|
41 |
# Set the path to the Oh My Zsh installation directory
|
42 |
export ZSH="$HOME/.oh-my-zsh"
|
|
|
217 |
# Alias for quickly editing and reloading the zsh configuration file
|
218 |
alias ezc="nvim ~/.zshrc && source ~/.zshrc"
|
219 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
220 |
# This function copies the sample prompts file to each dataset directory.
|
221 |
# It iterates through all directories in ~/datasets that start with "by_"
|
222 |
# and copies the kade-sample-prompts.txt file from the toolkit directory
|
|
|
1047 |
fi
|
1048 |
}
|
1049 |
|
1050 |
+
# Setup zoxide
|
1051 |
+
eval "$(zoxide init zsh)"
|
1052 |
+
|
1053 |
# Function: display_custom_help
|
1054 |
# Description:
|
1055 |
# This function displays custom help information for user-defined functions and aliases.
|
|
|
1063 |
#
|
1064 |
# Note:
|
1065 |
# Add or modify entries in this function to keep your personal command reference up-to-date.
|
1066 |
+
#display_custom_help
|
1067 |
+
|
cringe_move_exaple_images.py
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import re
|
3 |
+
|
4 |
+
def move_example_images_section(file_path):
|
5 |
+
with open(file_path, 'r') as file:
|
6 |
+
content = file.read()
|
7 |
+
|
8 |
+
example_images_section = re.search(r'## Example Images.*?(?=##|$)', content, re.DOTALL)
|
9 |
+
introduction_index = content.find('## Introduction')
|
10 |
+
|
11 |
+
if example_images_section and introduction_index != -1:
|
12 |
+
example_images_text = example_images_section.group()
|
13 |
+
|
14 |
+
# Remove the "## Example Images" section from its original position
|
15 |
+
content = content.replace(example_images_text, '')
|
16 |
+
|
17 |
+
# Insert the "## Example Images" section above the "## Introduction" section
|
18 |
+
new_content = content[:introduction_index] + example_images_text + '\n' + content[introduction_index:]
|
19 |
+
|
20 |
+
with open(file_path, 'w') as file:
|
21 |
+
file.write(new_content)
|
22 |
+
|
23 |
+
def recurse_directories(target_dir='.'):
|
24 |
+
for root, dirs, files in os.walk(target_dir):
|
25 |
+
for file in files:
|
26 |
+
if file.endswith('.md'):
|
27 |
+
file_path = os.path.join(root, file)
|
28 |
+
move_example_images_section(file_path)
|
29 |
+
|
30 |
+
if __name__ == '__main__':
|
31 |
+
target_directory = '.' # Default to current directory
|
32 |
+
recurse_directories(target_directory)
|
33 |
+
|
move_example_section
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
|
3 |
+
def add_example_images_section(file_path):
|
4 |
+
with open(file_path, 'r') as file:
|
5 |
+
lines = file.readlines()
|
6 |
+
|
7 |
+
introduction_index = None
|
8 |
+
for i, line in enumerate(lines):
|
9 |
+
if line.strip().startswith('## Introduction'):
|
10 |
+
introduction_index = i
|
11 |
+
break
|
12 |
+
|
13 |
+
if introduction_index is not None:
|
14 |
+
example_images_section = '## Example Images\n\n'
|
15 |
+
if example_images_section not in lines:
|
16 |
+
lines.insert(introduction_index, example_images_section)
|
17 |
+
|
18 |
+
with open(file_path, 'w') as file:
|
19 |
+
file.writelines(lines)
|
20 |
+
|
21 |
+
def recurse_directories(target_dir='.'):
|
22 |
+
for root, dirs, files in os.walk(target_dir):
|
23 |
+
for file in files:
|
24 |
+
if file.endswith('.md'):
|
25 |
+
file_path = os.path.join(root, file)
|
26 |
+
add_example_images_section(file_path)
|
27 |
+
|
28 |
+
if __name__ == '__main__':
|
29 |
+
target_directory = input('Enter the target directory (leave blank for current directory): ')
|
30 |
+
if not target_directory:
|
31 |
+
target_directory = '.'
|
32 |
+
recurse_directories(target_directory)
|
33 |
+
|
select_and_concat_captions
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
This script walks through a directory, identifies image files, and checks for the existence of corresponding
|
4 |
+
.caption and .tags files. It then concatenates the contents of .caption and .tags files into the .txt files.
|
5 |
+
|
6 |
+
Usage:
|
7 |
+
- Place the script in the directory containing the image files.
|
8 |
+
- Run the script to concatenate .caption and .tags files into .txt files.
|
9 |
+
- Use the dry_run flag to preview the changes without writing to the .txt files.
|
10 |
+
|
11 |
+
Functions:
|
12 |
+
get_files(path): Walks through the directory and yields image files along with their .caption and .tags files.
|
13 |
+
concat(caption_path, tags_path, txt_path, dry_run=False): Concatenates the contents of .caption and .tags files into the .txt file.
|
14 |
+
"""
|
15 |
+
from pathlib import Path
|
16 |
+
import os
|
17 |
+
import re
|
18 |
+
|
19 |
+
FILE_EXTS = {".png", ".jpg", ".jpeg", ".tiff", ".bmp", ".gif", ".jxl"}
|
20 |
+
|
21 |
+
def get_files(path):
|
22 |
+
path = Path(path)
|
23 |
+
# Walk the directory, looking for image files
|
24 |
+
for root, dirs, files in os.walk(path):
|
25 |
+
root = path / root
|
26 |
+
for file in files:
|
27 |
+
file = root / file
|
28 |
+
if file.suffix not in FILE_EXTS:
|
29 |
+
continue
|
30 |
+
caption = file.with_suffix(".caption")
|
31 |
+
tags = file.with_suffix(".tags")
|
32 |
+
txt = file.with_suffix(".txt")
|
33 |
+
if not caption.exists():
|
34 |
+
print(f"{caption} does not exist")
|
35 |
+
if not tags.exists():
|
36 |
+
print(f"{tags} does not exist")
|
37 |
+
yield file, caption, tags, txt
|
38 |
+
|
39 |
+
def select_best_caption(caption_path):
|
40 |
+
with open(caption_path, "r") as f:
|
41 |
+
content = f.read().strip()
|
42 |
+
|
43 |
+
captions = re.split(r'----------', content)
|
44 |
+
captions = [caption.replace('\n', ' ').strip() for caption in captions if caption.strip()]
|
45 |
+
|
46 |
+
best_caption = ""
|
47 |
+
for caption in captions:
|
48 |
+
if caption and caption[-1] in ".!?":
|
49 |
+
if len(caption) > len(best_caption):
|
50 |
+
best_caption = caption
|
51 |
+
|
52 |
+
return best_caption
|
53 |
+
|
54 |
+
def concat(caption_path, tags_path, txt_path, dry_run=False):
|
55 |
+
best_caption = select_best_caption(caption_path)
|
56 |
+
if not best_caption:
|
57 |
+
print(f"No suitable caption found in {caption_path}")
|
58 |
+
return
|
59 |
+
|
60 |
+
with open(tags_path, "r") as f:
|
61 |
+
tags = f.read().strip(", \n")
|
62 |
+
|
63 |
+
txt = f"{tags}, {best_caption}"
|
64 |
+
|
65 |
+
if dry_run:
|
66 |
+
print(f"{txt_path}:")
|
67 |
+
print(txt)
|
68 |
+
print()
|
69 |
+
else:
|
70 |
+
with open(txt_path, 'w') as f:
|
71 |
+
f.write(txt)
|
72 |
+
print(f"wrote {txt_path}")
|
73 |
+
|
74 |
+
if __name__ == "__main__":
|
75 |
+
dry_run = False
|
76 |
+
for f in get_files("."):
|
77 |
+
concat(*f[1:], dry_run=dry_run)
|
78 |
+
|
txt2tags
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python
|
2 |
+
|
3 |
+
import os
|
4 |
+
from pathlib import Path
|
5 |
+
|
6 |
+
def rename_txt_to_tags(target_dir='.'):
|
7 |
+
for root, dirs, files in os.walk(target_dir):
|
8 |
+
for file in files:
|
9 |
+
if file.endswith('.txt'):
|
10 |
+
txt_file = Path(root) / file
|
11 |
+
tags_file = txt_file.with_suffix('.tags')
|
12 |
+
os.rename(txt_file, tags_file)
|
13 |
+
print(f"Renamed {txt_file} to {tags_file}")
|
14 |
+
|
15 |
+
if __name__ == '__main__':
|
16 |
+
target_directory = input('Enter the target directory (leave blank for current directory): ')
|
17 |
+
if not target_directory:
|
18 |
+
target_directory = '.'
|
19 |
+
rename_txt_to_tags(target_directory)
|
20 |
+
|
videocaption
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python
|
2 |
+
|
3 |
+
import io
|
4 |
+
|
5 |
+
import argparse
|
6 |
+
import numpy as np
|
7 |
+
import torch
|
8 |
+
from decord import cpu, VideoReader, bridge
|
9 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
10 |
+
|
11 |
+
MODEL_PATH = "THUDM/cogvlm2-llama3-caption"
|
12 |
+
|
13 |
+
DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
|
14 |
+
TORCH_TYPE = torch.bfloat16 if torch.cuda.is_available() and torch.cuda.get_device_capability()[
|
15 |
+
0] >= 8 else torch.float16
|
16 |
+
|
17 |
+
parser = argparse.ArgumentParser(description="CogVLM2-Video CLI Demo")
|
18 |
+
parser.add_argument('--quant', type=int, choices=[4, 8], help='Enable 4-bit or 8-bit precision loading', default=0)
|
19 |
+
args = parser.parse_args([])
|
20 |
+
|
21 |
+
|
22 |
+
def load_video(video_data, strategy='chat'):
|
23 |
+
bridge.set_bridge('torch')
|
24 |
+
mp4_stream = video_data
|
25 |
+
num_frames = 24
|
26 |
+
decord_vr = VideoReader(io.BytesIO(mp4_stream), ctx=cpu(0))
|
27 |
+
|
28 |
+
frame_id_list = None
|
29 |
+
total_frames = len(decord_vr)
|
30 |
+
if strategy == 'base':
|
31 |
+
clip_end_sec = 60
|
32 |
+
clip_start_sec = 0
|
33 |
+
start_frame = int(clip_start_sec * decord_vr.get_avg_fps())
|
34 |
+
end_frame = min(total_frames,
|
35 |
+
int(clip_end_sec * decord_vr.get_avg_fps())) if clip_end_sec is not None else total_frames
|
36 |
+
frame_id_list = np.linspace(start_frame, end_frame - 1, num_frames, dtype=int)
|
37 |
+
elif strategy == 'chat':
|
38 |
+
timestamps = decord_vr.get_frame_timestamp(np.arange(total_frames))
|
39 |
+
timestamps = [i[0] for i in timestamps]
|
40 |
+
max_second = round(max(timestamps)) + 1
|
41 |
+
frame_id_list = []
|
42 |
+
for second in range(max_second):
|
43 |
+
closest_num = min(timestamps, key=lambda x: abs(x - second))
|
44 |
+
index = timestamps.index(closest_num)
|
45 |
+
frame_id_list.append(index)
|
46 |
+
if len(frame_id_list) >= num_frames:
|
47 |
+
break
|
48 |
+
|
49 |
+
video_data = decord_vr.get_batch(frame_id_list)
|
50 |
+
video_data = video_data.permute(3, 0, 1, 2)
|
51 |
+
return video_data
|
52 |
+
|
53 |
+
|
54 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
55 |
+
MODEL_PATH,
|
56 |
+
trust_remote_code=True,
|
57 |
+
)
|
58 |
+
|
59 |
+
model = AutoModelForCausalLM.from_pretrained(
|
60 |
+
MODEL_PATH,
|
61 |
+
torch_dtype=TORCH_TYPE,
|
62 |
+
trust_remote_code=True
|
63 |
+
).eval().to(DEVICE)
|
64 |
+
|
65 |
+
|
66 |
+
def predict(prompt, video_data, temperature):
|
67 |
+
strategy = 'chat'
|
68 |
+
|
69 |
+
video = load_video(video_data, strategy=strategy)
|
70 |
+
|
71 |
+
history = []
|
72 |
+
query = prompt
|
73 |
+
inputs = model.build_conversation_input_ids(
|
74 |
+
tokenizer=tokenizer,
|
75 |
+
query=query,
|
76 |
+
images=[video],
|
77 |
+
history=history,
|
78 |
+
template_version=strategy
|
79 |
+
)
|
80 |
+
inputs = {
|
81 |
+
'input_ids': inputs['input_ids'].unsqueeze(0).to('cuda'),
|
82 |
+
'token_type_ids': inputs['token_type_ids'].unsqueeze(0).to('cuda'),
|
83 |
+
'attention_mask': inputs['attention_mask'].unsqueeze(0).to('cuda'),
|
84 |
+
'images': [[inputs['images'][0].to('cuda').to(TORCH_TYPE)]],
|
85 |
+
}
|
86 |
+
gen_kwargs = {
|
87 |
+
"max_new_tokens": 2048,
|
88 |
+
"pad_token_id": 128002,
|
89 |
+
"top_k": 1,
|
90 |
+
"do_sample": False,
|
91 |
+
"top_p": 0.1,
|
92 |
+
"temperature": temperature,
|
93 |
+
}
|
94 |
+
with torch.no_grad():
|
95 |
+
outputs = model.generate(**inputs, **gen_kwargs)
|
96 |
+
outputs = outputs[:, inputs['input_ids'].shape[1]:]
|
97 |
+
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
98 |
+
return response
|
99 |
+
|
100 |
+
|
101 |
+
def test():
|
102 |
+
prompt = "Please describe this video in detail."
|
103 |
+
temperature = 0.1
|
104 |
+
video_data = open('test.mp4', 'rb').read()
|
105 |
+
response = predict(prompt, video_data, temperature)
|
106 |
+
print(response)
|
107 |
+
|
108 |
+
|
109 |
+
if __name__ == '__main__':
|
110 |
+
test()
|
111 |
+
|
git-wrapper.zsh → zsh/git-wrapper.zsh
RENAMED
File without changes
|
zsh/png2mp4.zsh
CHANGED
@@ -48,8 +48,8 @@ png2mp4() {
|
|
48 |
vf_options="select='not(mod(n\,$max_images))',$vf_options"
|
49 |
fi
|
50 |
|
51 |
-
ffmpeg -framerate 60 -pattern_type glob -i "$temp_dir/*_${sample}_*.png" -vf "$vf_options" -crf
|
52 |
-
-c:v libx264 -pix_fmt yuv420p -y "$temp_dir/temp_${sample}.mp4"
|
53 |
|
54 |
if [ $? -ne 0 ]; then
|
55 |
echo "Error: ffmpeg command failed for sample ${sample}."
|
@@ -60,7 +60,7 @@ png2mp4() {
|
|
60 |
echo "Processing final video for sample ${sample}..."
|
61 |
duration=$(ffmpeg -i "$temp_dir/temp_${sample}.mp4" 2>&1 | grep 'Duration' | awk '{print $2}' | tr -d , | awk -F: '{print ($1 * 3600) + ($2 * 60) + $3}')
|
62 |
fade_start=$(echo "$duration + 3" | bc)
|
63 |
-
ffmpeg -i "$temp_dir/temp_${sample}.mp4" -vf "tpad=stop_mode=clone:stop_duration=8,fade=t=out:st=$fade_start:d=5" -c:v libx264 -pix_fmt yuv420p -y "$output_filename"
|
64 |
|
65 |
if [ $? -ne 0 ]; then
|
66 |
echo "Error: Final ffmpeg processing failed for sample ${sample}."
|
|
|
48 |
vf_options="select='not(mod(n\,$max_images))',$vf_options"
|
49 |
fi
|
50 |
|
51 |
+
ffmpeg -framerate 60 -pattern_type glob -i "$temp_dir/*_${sample}_*.png" -vf "$vf_options" -crf 18 \
|
52 |
+
-c:v libx264 -b:v 12M -pix_fmt yuv420p -y "$temp_dir/temp_${sample}.mp4"
|
53 |
|
54 |
if [ $? -ne 0 ]; then
|
55 |
echo "Error: ffmpeg command failed for sample ${sample}."
|
|
|
60 |
echo "Processing final video for sample ${sample}..."
|
61 |
duration=$(ffmpeg -i "$temp_dir/temp_${sample}.mp4" 2>&1 | grep 'Duration' | awk '{print $2}' | tr -d , | awk -F: '{print ($1 * 3600) + ($2 * 60) + $3}')
|
62 |
fade_start=$(echo "$duration + 3" | bc)
|
63 |
+
ffmpeg -i "$temp_dir/temp_${sample}.mp4" -vf "tpad=stop_mode=clone:stop_duration=8,fade=t=out:st=$fade_start:d=5" -c:v libx264 -b:v 12M -crf 18 -pix_fmt yuv420p -y "$output_filename"
|
64 |
|
65 |
if [ $? -ne 0 ]; then
|
66 |
echo "Error: Final ffmpeg processing failed for sample ${sample}."
|