INIT
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox
|
||||
import os
|
||||
from gui.video_item import VideoItem
|
||||
from core.video_processor import VideoProcessor
|
||||
from core.logger import Logger
|
||||
|
||||
|
||||
class MainWindow:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("Video Editor Pro")
|
||||
self.root.geometry("900x700")
|
||||
|
||||
self.video_items = []
|
||||
self.setup_ui()
|
||||
self.logger = Logger()
|
||||
|
||||
def setup_ui(self):
|
||||
# Main frame
|
||||
main_frame = ttk.Frame(self.root, padding="10")
|
||||
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||
|
||||
# Add files section
|
||||
files_frame = ttk.LabelFrame(main_frame, text="Видео файлы", padding="5")
|
||||
files_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||
|
||||
ttk.Button(files_frame, text="Добавить файлы",
|
||||
command=self.add_video_files).pack(side=tk.LEFT, padx=(0, 10))
|
||||
ttk.Button(files_frame, text="Очистить все",
|
||||
command=self.clear_all).pack(side=tk.LEFT)
|
||||
|
||||
# Scrollable frame for video items
|
||||
canvas = tk.Canvas(main_frame)
|
||||
scrollbar = ttk.Scrollbar(main_frame, orient="vertical", command=canvas.yview)
|
||||
self.scrollable_frame = ttk.Frame(canvas)
|
||||
|
||||
self.scrollable_frame.bind(
|
||||
"<Configure>",
|
||||
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
||||
)
|
||||
|
||||
canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
|
||||
canvas.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
canvas.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||
scrollbar.grid(row=1, column=1, sticky=(tk.N, tk.S))
|
||||
|
||||
# Output settings
|
||||
output_frame = ttk.LabelFrame(main_frame, text="Настройки вывода", padding="5")
|
||||
output_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(10, 0))
|
||||
|
||||
ttk.Label(output_frame, text="Формат:").grid(row=0, column=0, padx=(0, 5))
|
||||
self.format_var = tk.StringVar(value="mp4")
|
||||
format_combo = ttk.Combobox(output_frame, textvariable=self.format_var,
|
||||
values=["mp4", "avi", "mov", "mkv"], state="readonly")
|
||||
format_combo.grid(row=0, column=1, padx=(0, 20))
|
||||
|
||||
ttk.Label(output_frame, text="Качество:").grid(row=0, column=2, padx=(0, 5))
|
||||
self.quality_var = tk.StringVar(value="high")
|
||||
quality_combo = ttk.Combobox(output_frame, textvariable=self.quality_var,
|
||||
values=["low", "medium", "high"], state="readonly")
|
||||
quality_combo.grid(row=0, column=3, padx=(0, 20))
|
||||
|
||||
ttk.Button(output_frame, text="Выбрать папку для сохранения",
|
||||
command=self.select_output_dir).grid(row=0, column=4, padx=(0, 10))
|
||||
|
||||
self.output_dir_var = tk.StringVar(value=os.getcwd())
|
||||
ttk.Label(output_frame, textvariable=self.output_dir_var).grid(row=0, column=5)
|
||||
|
||||
# Process button
|
||||
ttk.Button(main_frame, text="Выполнить",
|
||||
command=self.process_videos, style="Accent.TButton").grid(row=3, column=0, pady=20)
|
||||
|
||||
# Progress bar
|
||||
self.progress_var = tk.DoubleVar()
|
||||
self.progress_bar = ttk.Progressbar(main_frame, variable=self.progress_var, maximum=100)
|
||||
self.progress_bar.grid(row=4, column=0, columnspan=2, sticky=(tk.W, tk.E))
|
||||
|
||||
# Status label
|
||||
self.status_var = tk.StringVar(value="Готов к работе")
|
||||
ttk.Label(main_frame, textvariable=self.status_var).grid(row=5, column=0, columnspan=2)
|
||||
|
||||
# Configure grid weights
|
||||
main_frame.columnconfigure(0, weight=1)
|
||||
main_frame.rowconfigure(1, weight=1)
|
||||
|
||||
def add_video_files(self):
|
||||
files = filedialog.askopenfilenames(
|
||||
title="Выберите видео файлы",
|
||||
filetypes=[
|
||||
("Видео файлы", "*.mp4 *.avi *.mov *.mkv *.wmv *.flv *.webm"),
|
||||
("Все файлы", "*.*")
|
||||
]
|
||||
)
|
||||
|
||||
for file_path in files:
|
||||
video_item = VideoItem(self.scrollable_frame, file_path)
|
||||
video_item.pack(fill=tk.X, padx=5, pady=2)
|
||||
self.video_items.append(video_item)
|
||||
|
||||
self.update_status(f"Добавлено файлов: {len(files)}")
|
||||
|
||||
def clear_all(self):
|
||||
for item in self.video_items:
|
||||
item.destroy()
|
||||
self.video_items.clear()
|
||||
self.update_status("Все файлы удалены")
|
||||
|
||||
def select_output_dir(self):
|
||||
directory = filedialog.askdirectory(title="Выберите папку для сохранения")
|
||||
if directory:
|
||||
self.output_dir_var.set(directory)
|
||||
|
||||
def update_status(self, message):
|
||||
self.status_var.set(message)
|
||||
self.root.update()
|
||||
|
||||
def process_videos(self):
|
||||
if not self.video_items:
|
||||
messagebox.showerror("Ошибка", "Добавьте хотя бы один видео файл")
|
||||
return
|
||||
|
||||
# Collect video data
|
||||
video_data = []
|
||||
for item in self.video_items:
|
||||
time_ranges = item.get_time_ranges()
|
||||
if not time_ranges: # Use entire video if no ranges specified
|
||||
time_ranges = [(0, item.get_duration())]
|
||||
video_data.append({
|
||||
'path': item.file_path,
|
||||
'time_ranges': time_ranges
|
||||
})
|
||||
|
||||
output_format = self.format_var.get()
|
||||
output_quality = self.quality_var.get()
|
||||
output_dir = self.output_dir_var.get()
|
||||
|
||||
try:
|
||||
processor = VideoProcessor()
|
||||
|
||||
def progress_callback(progress, message):
|
||||
self.progress_var.set(progress * 100)
|
||||
self.update_status(message)
|
||||
|
||||
output_path = processor.process_videos(
|
||||
video_data=video_data,
|
||||
output_dir=output_dir,
|
||||
output_format=output_format,
|
||||
quality=output_quality,
|
||||
progress_callback=progress_callback
|
||||
)
|
||||
|
||||
# Save log
|
||||
log_path = output_path.replace(f".{output_format}", "_log.txt")
|
||||
self.logger.save_log(log_path)
|
||||
|
||||
self.update_status(f"Готово! Файл сохранен: {output_path}")
|
||||
messagebox.showinfo("Успех", f"Обработка завершена!\nФайл: {output_path}")
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Ошибка обработки: {str(e)}"
|
||||
self.update_status(error_msg)
|
||||
self.logger.log("ERROR", error_msg)
|
||||
messagebox.showerror("Ошибка", error_msg)
|
||||
finally:
|
||||
self.progress_var.set(0)
|
||||
@@ -0,0 +1,108 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
import os
|
||||
from moviepy.editor import VideoFileClip
|
||||
|
||||
|
||||
class VideoItem(ttk.Frame):
|
||||
def __init__(self, parent, file_path):
|
||||
super().__init__(parent)
|
||||
self.file_path = file_path
|
||||
self.time_ranges = []
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
# File info
|
||||
file_name = os.path.basename(self.file_path)
|
||||
ttk.Label(self, text=file_name, font=('Arial', 9, 'bold')).grid(row=0, column=0, sticky=tk.W)
|
||||
|
||||
# Duration info
|
||||
try:
|
||||
with VideoFileClip(self.file_path) as clip:
|
||||
duration = clip.duration
|
||||
duration_str = self.format_time(duration)
|
||||
ttk.Label(self, text=f"Длительность: {duration_str}").grid(row=1, column=0, sticky=tk.W)
|
||||
except:
|
||||
duration_str = "Неизвестно"
|
||||
ttk.Label(self, text="Ошибка чтения файла").grid(row=1, column=0, sticky=tk.W)
|
||||
|
||||
# Time ranges frame
|
||||
ranges_frame = ttk.LabelFrame(self, text="Временные диапазоны")
|
||||
ranges_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
|
||||
|
||||
self.ranges_container = ttk.Frame(ranges_frame)
|
||||
self.ranges_container.pack(fill=tk.X, padx=5, pady=5)
|
||||
|
||||
# Add range button
|
||||
ttk.Button(ranges_frame, text="+ Добавить диапазон",
|
||||
command=self.add_time_range).pack(pady=5)
|
||||
|
||||
# Buttons
|
||||
ttk.Button(self, text="Удалить",
|
||||
command=self.destroy).grid(row=0, column=2, rowspan=2, padx=5)
|
||||
|
||||
self.columnconfigure(0, weight=1)
|
||||
|
||||
def format_time(self, seconds):
|
||||
hours = int(seconds // 3600)
|
||||
minutes = int((seconds % 3600) // 60)
|
||||
seconds = int(seconds % 60)
|
||||
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
||||
|
||||
def add_time_range(self, start_time=0, end_time=0):
|
||||
range_frame = ttk.Frame(self.ranges_container)
|
||||
range_frame.pack(fill=tk.X, pady=2)
|
||||
|
||||
ttk.Label(range_frame, text="От:").pack(side=tk.LEFT)
|
||||
start_var = tk.StringVar(value=self.format_time(start_time))
|
||||
start_entry = ttk.Entry(range_frame, textvariable=start_var, width=8)
|
||||
start_entry.pack(side=tk.LEFT, padx=(0, 10))
|
||||
|
||||
ttk.Label(range_frame, text="До:").pack(side=tk.LEFT)
|
||||
end_var = tk.StringVar(value=self.format_time(end_time))
|
||||
end_entry = ttk.Entry(range_frame, textvariable=end_var, width=8)
|
||||
end_entry.pack(side=tk.LEFT, padx=(0, 10))
|
||||
|
||||
def remove_range():
|
||||
range_frame.destroy()
|
||||
self.time_ranges = [r for r in self.time_ranges if r['frame'] != range_frame]
|
||||
|
||||
ttk.Button(range_frame, text="×", width=3,
|
||||
command=remove_range).pack(side=tk.LEFT)
|
||||
|
||||
self.time_ranges.append({
|
||||
'frame': range_frame,
|
||||
'start_var': start_var,
|
||||
'end_var': end_var
|
||||
})
|
||||
|
||||
def get_time_ranges(self):
|
||||
ranges = []
|
||||
for range_data in self.time_ranges:
|
||||
try:
|
||||
start_time = self.parse_time(range_data['start_var'].get())
|
||||
end_time = self.parse_time(range_data['end_var'].get())
|
||||
|
||||
if start_time < end_time:
|
||||
ranges.append((start_time, end_time))
|
||||
except:
|
||||
continue
|
||||
return ranges
|
||||
|
||||
def parse_time(self, time_str):
|
||||
parts = time_str.split(':')
|
||||
if len(parts) == 3: # HH:MM:SS
|
||||
hours, minutes, seconds = map(int, parts)
|
||||
return hours * 3600 + minutes * 60 + seconds
|
||||
elif len(parts) == 2: # MM:SS
|
||||
minutes, seconds = map(int, parts)
|
||||
return minutes * 60 + seconds
|
||||
else: # SS
|
||||
return int(time_str)
|
||||
|
||||
def get_duration(self):
|
||||
try:
|
||||
with VideoFileClip(self.file_path) as clip:
|
||||
return clip.duration
|
||||
except:
|
||||
return 0
|
||||
Reference in New Issue
Block a user