Skip to content

Commit af1c606

Browse files
committed
Initial Load
1 parent fa74ea7 commit af1c606

File tree

2 files changed

+224
-0
lines changed

2 files changed

+224
-0
lines changed

.gitignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.pyc
4+
5+
# Virtual environment
6+
venv/
7+
8+
# Visual Studio Code Settings Files
9+
.vscode/
10+
11+
# Environment files
12+
.env

src/VideoTrim.py

+212
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import tkinter as tk
2+
import ttkbootstrap as ttk
3+
from tkinter import filedialog, messagebox
4+
import subprocess
5+
import os
6+
import cv2
7+
from PIL import Image, ImageTk
8+
9+
10+
class VideoTrimmerApp(ttk.Window):
11+
def __init__(self):
12+
super().__init__(themename='darkly')
13+
self.title("Video Trimmer")
14+
self.geometry("1000x400")
15+
16+
# Variables for slider values
17+
self.start_time = tk.DoubleVar(value=0)
18+
self.end_time = tk.DoubleVar()
19+
20+
# Button to start encoding
21+
self.encode_button = tk.Button(
22+
self, text="Start Encoding", command=self.trim_video
23+
)
24+
self.encode_button.pack(side=tk.RIGHT, anchor='n')
25+
26+
# Button to choose file
27+
self.choose_button = tk.Button(
28+
self, text="Choose File", command=self.choose_file
29+
)
30+
self.choose_button.pack(side=tk.RIGHT, anchor='n')
31+
32+
# Frame for start preview
33+
self.start_frame_group = tk.Frame(self)
34+
self.start_frame_group.pack(side="left", padx=50)
35+
36+
# Buttons to adjust start time
37+
self.start_dec_button = tk.Button(
38+
self.start_frame_group, text="<", command=self.decrease_start_time
39+
)
40+
self.start_dec_button.grid(row=0, column=0)
41+
42+
self.start_time_label = tk.Label(
43+
self.start_frame_group, text="Start: 0:00"
44+
)
45+
self.start_time_label.grid(row=0, column=1)
46+
47+
self.start_slider = ttk.Scale(
48+
self.start_frame_group,
49+
from_=0,
50+
to=0,
51+
length=220,
52+
orient="horizontal",
53+
variable=self.start_time,
54+
command=self.load_previews,
55+
)
56+
self.start_slider.grid(row=1, column=0, columnspan=3, pady=10)
57+
58+
self.start_inc_button = tk.Button(
59+
self.start_frame_group, text=">", command=self.increase_start_time
60+
)
61+
self.start_inc_button.grid(row=0, column=2)
62+
63+
self.preview_label_start = tk.Label(self.start_frame_group)
64+
self.preview_label_start.grid(row=2, column=0, columnspan=3)
65+
66+
# Frame for end preview
67+
self.end_frame_group = tk.Frame(self)
68+
self.end_frame_group.pack(side="right", padx=50)
69+
70+
# Buttons to adjust end time
71+
self.end_dec_button = tk.Button(
72+
self.end_frame_group, text="<", command=self.decrease_end_time
73+
)
74+
self.end_dec_button.grid(row=0, column=0)
75+
76+
self.end_time_label = tk.Label(self.end_frame_group, text="End: 0:00")
77+
self.end_time_label.grid(row=0, column=1)
78+
79+
self.end_slider = ttk.Scale(
80+
self.end_frame_group,
81+
from_=0,
82+
to=0,
83+
length=220,
84+
orient="horizontal",
85+
variable=self.end_time,
86+
command=self.load_previews,
87+
)
88+
self.end_slider.grid(row=1, column=0, columnspan=3, pady=10)
89+
90+
self.end_inc_button = tk.Button(
91+
self.end_frame_group, text=">", command=self.increase_end_time
92+
)
93+
self.end_inc_button.grid(row=0, column=2)
94+
95+
self.preview_label_end = tk.Label(self.end_frame_group)
96+
self.preview_label_end.grid(row=2, column=0, columnspan=3)
97+
98+
# Video capture objects
99+
self.video_capture_start = None
100+
self.video_capture_end = None
101+
self.total_frames = 0
102+
103+
def choose_file(self):
104+
self.file_path = filedialog.askopenfilename(
105+
filetypes=[
106+
('Video files', '*.mp4'),
107+
('Video files', '*.m4v'),
108+
('Video files', '*.ts')]
109+
)
110+
if self.file_path:
111+
self.video_capture_start = cv2.VideoCapture(self.file_path)
112+
self.video_capture_end = cv2.VideoCapture(self.file_path)
113+
self.total_frames = int(
114+
self.video_capture_start.get(cv2.CAP_PROP_FRAME_COUNT)
115+
)
116+
video_length = int(
117+
self.total_frames / self.video_capture_start.get(
118+
cv2.CAP_PROP_FPS
119+
)
120+
)
121+
self.start_time.set(0)
122+
self.end_time.set(video_length)
123+
self.start_slider.config(from_=0, to=video_length)
124+
self.end_slider.config(from_=0, to=video_length)
125+
self.load_previews()
126+
127+
def load_previews(self):
128+
start_time_sec = int(self.start_time.get())
129+
end_time_sec = int(self.end_time.get())
130+
131+
self.video_capture_start.set(
132+
cv2.CAP_PROP_POS_MSEC, start_time_sec * 1000
133+
)
134+
success_start, frame_start = self.video_capture_start.read()
135+
if success_start:
136+
frame_start = Image.fromarray(
137+
cv2.cvtColor(frame_start, cv2.COLOR_BGR2RGB)
138+
)
139+
frame_start = frame_start.resize((300, 169))
140+
frame_start = ImageTk.PhotoImage(frame_start)
141+
self.preview_label_start.configure(image=frame_start)
142+
self.preview_label_start.image = frame_start
143+
self.start_time_label.config(
144+
text=f"Start: {start_time_sec//60}:{start_time_sec%60:02d}"
145+
)
146+
147+
self.video_capture_end.set(cv2.CAP_PROP_POS_MSEC, end_time_sec * 1000)
148+
success_end, frame_end = self.video_capture_end.read()
149+
if success_end:
150+
frame_end = Image.fromarray(
151+
cv2.cvtColor(frame_end, cv2.COLOR_BGR2RGB)
152+
)
153+
frame_end = frame_end.resize((300, 169))
154+
frame_end = ImageTk.PhotoImage(frame_end)
155+
self.preview_label_end.configure(image=frame_end)
156+
self.preview_label_end.image = frame_end
157+
self.end_time_label.config(
158+
text=f"End: {end_time_sec//60}:{end_time_sec%60:02d}"
159+
)
160+
161+
def decrease_start_time(self):
162+
current_start_time = self.start_time.get()
163+
if current_start_time > 0:
164+
self.start_time.set(current_start_time - 1)
165+
self.load_previews()
166+
167+
def increase_start_time(self):
168+
current_start_time = self.start_time.get()
169+
self.start_time.set(current_start_time + 1)
170+
self.load_previews()
171+
172+
def decrease_end_time(self):
173+
current_end_time = self.end_time.get()
174+
if current_end_time > 0:
175+
self.end_time.set(current_end_time - 1)
176+
self.load_previews()
177+
178+
def increase_end_time(self):
179+
current_end_time = self.end_time.get()
180+
self.end_time.set(current_end_time + 1)
181+
self.load_previews()
182+
183+
def trim_video(self):
184+
start_time_sec = int(self.start_time.get())
185+
end_time_sec = int(self.end_time.get())
186+
187+
# Get filename without extension
188+
output_file_name, output_file_extension = os.path.splitext(
189+
os.path.basename(self.file_path)
190+
)
191+
output_path = os.path.splitext(
192+
os.path.dirname(self.file_path)
193+
)[0]
194+
output_file = (
195+
f'{output_path}/{output_file_name}'
196+
f'_trimmed{output_file_extension}'
197+
)
198+
199+
# Execute FFMPEG command for trimming
200+
command = (
201+
f'ffmpeg -i "{self.file_path}" '
202+
f'-ss {start_time_sec} -to {end_time_sec} '
203+
f'-c:v copy -c:a copy "{output_file}" -y'
204+
)
205+
subprocess.call(command, shell=True)
206+
207+
messagebox.showinfo("Success", "Video trimmed successfully!")
208+
209+
210+
if __name__ == "__main__":
211+
app = VideoTrimmerApp()
212+
app.mainloop()

0 commit comments

Comments
 (0)