AnimeVideoEditot/gui/video_item.py
2026-03-14 19:52:44 +03:00

147 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import ttk
import os
from moviepy import VideoFileClip
from utils.file_utils import get_file_size
from gui.theme import COLORS
class VideoItem(ttk.Frame):
"""Карточка одного видео в списке с диапазонами и стильным оформлением."""
def __init__(self, parent, file_path, on_remove=None):
super().__init__(parent)
self.file_path = file_path
self.time_ranges = []
self._duration = 0
self.on_remove = on_remove
self.setup_ui()
def setup_ui(self):
# Контейнер-карточка с фоном
self.configure(style="Card.TFrame")
card = tk.Frame(self, bg=COLORS["surface"], highlightbackground=COLORS["border"], highlightthickness=1)
card.pack(fill=tk.BOTH, expand=True, padx=0, pady=0)
inner = tk.Frame(card, bg=COLORS["surface"], padx=12, pady=10)
inner.pack(fill=tk.BOTH, expand=True)
# Верхняя строка: имя файла + размер, длительность, кнопка удалить
top = tk.Frame(inner, bg=COLORS["surface"])
top.pack(fill=tk.X, pady=(0, 6))
file_name = os.path.basename(self.file_path)
try:
size_str = get_file_size(self.file_path)
file_info = f"{file_name} · {size_str}"
except Exception:
file_info = file_name
lbl_name = tk.Label(top, text=file_info, font=("Tahoma", 10, "bold"),
fg=COLORS["text"], bg=COLORS["surface"])
lbl_name.pack(side=tk.LEFT)
try:
with VideoFileClip(self.file_path) as clip:
self._duration = clip.duration
duration_str = self.format_time(self._duration)
lbl_dur = tk.Label(top, text=f" · {duration_str}", font=("Tahoma", 9),
fg=COLORS["subtext"], bg=COLORS["surface"])
lbl_dur.pack(side=tk.LEFT)
except Exception:
lbl_err = tk.Label(top, text=" · Ошибка чтения", font=("Tahoma", 9),
fg=COLORS["red"], bg=COLORS["surface"])
lbl_err.pack(side=tk.LEFT)
btn_remove = tk.Button(top, text="", font=("Tahoma", 9), fg=COLORS["text"],
bg=COLORS["overlay"], activebackground=COLORS["red"],
activeforeground=COLORS["text"], relief=tk.FLAT, cursor="hand2",
command=self._on_remove_click)
btn_remove.pack(side=tk.RIGHT)
# Блок диапазонов на удаление (остальное попадёт в итоговое видео)
ranges_lbl = tk.Label(inner, text="Удалить из видео (исключить эти фрагменты)", font=("Tahoma", 9),
fg=COLORS["subtext"], bg=COLORS["surface"])
ranges_lbl.pack(anchor=tk.W, pady=(4, 4))
self.ranges_container = tk.Frame(inner, bg=COLORS["surface"])
self.ranges_container.pack(fill=tk.X, pady=(0, 6))
ttk.Button(inner, text="+ Добавить диапазон на удаление", command=self.add_time_range).pack(anchor=tk.W)
self.columnconfigure(0, weight=1)
def format_time(self, seconds):
h = int(seconds // 3600)
m = int((seconds % 3600) // 60)
s = int(seconds % 60)
return f"{h:02d}:{m:02d}:{s:02d}"
def add_time_range(self, start_time=0, end_time=0):
row = tk.Frame(self.ranges_container, bg=COLORS["surface"])
row.pack(fill=tk.X, pady=2)
tk.Label(row, text="Удалить с", font=("Tahoma", 8), fg=COLORS["subtext"], bg=COLORS["surface"]).pack(side=tk.LEFT, padx=(0, 4))
start_var = tk.StringVar(value=self.format_time(start_time))
ent_start = tk.Entry(row, textvariable=start_var, width=10, font=("Consolas", 9),
bg=COLORS["overlay"], fg=COLORS["text"], insertbackground=COLORS["text"],
relief=tk.FLAT, highlightthickness=1, highlightbackground=COLORS["border"])
ent_start.pack(side=tk.LEFT, padx=(0, 12), ipady=4, ipadx=6)
tk.Label(row, text="по", font=("Tahoma", 8), fg=COLORS["subtext"], bg=COLORS["surface"]).pack(side=tk.LEFT, padx=(0, 4))
end_var = tk.StringVar(value=self.format_time(end_time))
ent_end = tk.Entry(row, textvariable=end_var, width=10, font=("Consolas", 9),
bg=COLORS["overlay"], fg=COLORS["text"], insertbackground=COLORS["text"],
relief=tk.FLAT, highlightthickness=1, highlightbackground=COLORS["border"])
ent_end.pack(side=tk.LEFT, padx=(0, 8), ipady=4, ipadx=6)
def remove_range():
row.destroy()
self.time_ranges[:] = [r for r in self.time_ranges if r["frame"] != row]
btn_del = tk.Button(row, text="×", font=("Tahoma", 9), fg=COLORS["subtext"],
bg=COLORS["surface"], activebackground=COLORS["red"],
activeforeground=COLORS["text"], relief=tk.FLAT, cursor="hand2",
command=remove_range)
btn_del.pack(side=tk.LEFT)
self.time_ranges.append({"frame": row, "start_var": start_var, "end_var": end_var})
def get_time_ranges(self):
out = []
for r in self.time_ranges:
try:
start = self.parse_time(r["start_var"].get())
end = self.parse_time(r["end_var"].get())
if start < end:
out.append((start, end))
except (ValueError, TypeError):
continue
return out
def parse_time(self, time_str):
time_str = (time_str or "").strip()
if not time_str:
return 0
parts = time_str.split(":")
if len(parts) == 3:
return int(parts[0]) * 3600 + int(parts[1]) * 60 + float(parts[2])
if len(parts) == 2:
return int(parts[0]) * 60 + float(parts[1])
return float(parts[0])
def _on_remove_click(self):
if self.on_remove:
self.on_remove(self)
self.destroy()
def get_duration(self):
if self._duration > 0:
return self._duration
try:
with VideoFileClip(self.file_path) as clip:
return clip.duration
except Exception:
return 0