Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import os
- import shutil
- import tkinter as tk
- from pathlib import Path
- from PIL import Image, ImageTk, ImageSequence
- import requests
- from bs4 import BeautifulSoup
- from datetime import datetime
- from fake_useragent import UserAgent
- import threading
- import queue
- import time
- import json
- from tkinter import simpledialog, messagebox
- class CombinedPhotoApp:
- def __init__(self):
- # Инициализируем настройки ПЕРВЫМИ
- self.settings = {
- 'interesting_folder': "interesting_media",
- 'not_interesting_folder': "not_interesting_media",
- 'interesting_button_text': "Интересно",
- 'not_interesting_button_text': "Не интересно",
- 'skip_button_text': "Пропустить"
- }
- self.load_settings()
- # Теперь инициализируем остальные переменные
- self.setup_gui()
- self.download_queue = queue.Queue()
- self.current_media_path = None
- self.download_thread = None
- self.stop_download = False
- self.is_playing_gif = False
- self.gif_frames = []
- self.current_gif_frame = 0
- # Видео переменные
- self.video_capture = None
- self.is_playing_video = False
- self.current_video_path = None
- self.video_duration = 0
- self.video_position = 0
- self.is_seeking = False
- # Создаем папки для сортировки
- Path(self.settings['interesting_folder']).mkdir(exist_ok=True)
- Path(self.settings['not_interesting_folder']).mkdir(exist_ok=True)
- Path("temp_downloads").mkdir(exist_ok=True)
- def load_settings(self):
- """Загружаем настройки из файла"""
- try:
- if os.path.exists("settings.json"):
- with open("settings.json", 'r', encoding='utf-8') as f:
- saved_settings = json.load(f)
- self.settings.update(saved_settings)
- print("Настройки загружены из settings.json")
- except Exception as e:
- print(f"Ошибка загрузки настроек: {e}")
- def save_settings(self):
- """Сохраняем настройки в файл"""
- try:
- with open("settings.json", 'w', encoding='utf-8') as f:
- json.dump(self.settings, f, ensure_ascii=False, indent=2)
- print("Настройки сохранены в settings.json")
- except Exception as e:
- print(f"Ошибка сохранения настроек: {e}")
- def setup_gui(self):
- """Создаем графический интерфейс"""
- self.root = tk.Tk()
- self.root.title("Парсинг и Сортировка Медиа")
- self.root.geometry("1000x900")
- self.root.configure(bg='#f0f0f0')
- # Верхняя панель - настройки парсинга
- settings_frame = tk.LabelFrame(self.root, text="Настройки парсинга", font=('Arial', 12, 'bold'), bg='#f0f0f0')
- settings_frame.pack(pady=10, padx=20, fill='x')
- # Поле для имени
- tk.Label(settings_frame, text="Name:", bg='#f0f0f0').grid(row=0, column=0, padx=5, pady=5, sticky='w')
- self.name_entry = tk.Entry(settings_frame, width=20)
- self.name_entry.grid(row=0, column=1, padx=5, pady=5)
- # Поле для offset
- tk.Label(settings_frame, text="Offset:", bg='#f0f0f0').grid(row=0, column=2, padx=5, pady=5, sticky='w')
- self.offset_entry = tk.Entry(settings_frame, width=10)
- self.offset_entry.insert(0, "3")
- self.offset_entry.grid(row=0, column=3, padx=5, pady=5)
- # Кнопки управления парсингом
- self.start_btn = tk.Button(settings_frame, text="Начать парсинг", command=self.start_parsing,
- bg='#4CAF50', fg='white', font=('Arial', 10, 'bold'))
- self.start_btn.grid(row=0, column=4, padx=10, pady=5)
- self.stop_btn = tk.Button(settings_frame, text="Остановить", command=self.stop_parsing,
- bg='#f44336', fg='white', font=('Arial', 10), state='disabled')
- self.stop_btn.grid(row=0, column=5, padx=5, pady=5)
- # Кнопка для досортировки
- self.resume_btn = tk.Button(settings_frame, text="Досортировать из temp", command=self.resume_sorting,
- bg='#FF9800', fg='white', font=('Arial', 10))
- self.resume_btn.grid(row=0, column=6, padx=5, pady=5)
- # Кнопка настроек
- self.settings_btn = tk.Button(settings_frame, text="⚙ Настройки", command=self.open_settings,
- bg='#2196F3', fg='white', font=('Arial', 10))
- self.settings_btn.grid(row=0, column=7, padx=5, pady=5)
- # Прогресс парсинга
- self.parse_progress = tk.Label(settings_frame, text="Готов к работе", bg='#f0f0f0')
- self.parse_progress.grid(row=1, column=0, columnspan=8, pady=5)
- # Область для медиа
- media_frame = tk.LabelFrame(self.root, text="Просмотр медиа", font=('Arial', 12, 'bold'), bg='#f0f0f0')
- media_frame.pack(pady=10, padx=20, fill='both', expand=True)
- self.media_label = tk.Label(media_frame, text="Медиа будет показано здесь после начала загрузки",
- bg='black', fg='white', relief='sunken', bd=2)
- self.media_label.pack(pady=20, padx=20, fill='both', expand=True)
- # Ползунок для видео
- self.seek_frame = tk.Frame(media_frame, bg='#f0f0f0')
- self.time_label = tk.Label(self.seek_frame, text="00:00 / 00:00", bg='#f0f0f0', font=('Arial', 9))
- self.time_label.pack(side='left', padx=5)
- self.seek_slider = tk.Scale(self.seek_frame, from_=0, to=100, orient='horizontal',
- showvalue=False, length=400, sliderlength=15,
- command=self.on_seek)
- self.seek_slider.pack(side='left', fill='x', expand=True, padx=5)
- # Элементы управления видео
- self.video_controls = tk.Frame(media_frame, bg='#333')
- self.play_btn = tk.Button(self.video_controls, text="▶", command=self.play_video,
- bg='#4CAF50', fg='white', font=('Arial', 10))
- self.pause_btn = tk.Button(self.video_controls, text="⏸", command=self.pause_video,
- bg='#FF9800', fg='white', font=('Arial', 10))
- self.stop_btn_video = tk.Button(self.video_controls, text="⏹", command=self.stop_video,
- bg='#f44336', fg='white', font=('Arial', 10))
- # Информация о файле
- self.info_label = tk.Label(self.root, text="", font=('Arial', 10), bg='#f0f0f0')
- self.info_label.pack(pady=5)
- # Кнопки сортировки
- self.sort_frame = tk.Frame(self.root, bg='#f0f0f0')
- self.sort_frame.pack(pady=10)
- self.update_sort_buttons()
- # Статистика
- stats_frame = tk.LabelFrame(self.root, text="Статистика", font=('Arial', 12, 'bold'), bg='#f0f0f0')
- stats_frame.pack(pady=10, padx=20, fill='x')
- self.stats_label = tk.Label(stats_frame, text="Ожидание начала...", font=('Arial', 10), bg='#f0f0f0')
- self.stats_label.pack(pady=5)
- # Горячие клавиши
- hotkey_label = tk.Label(self.root,
- text="Горячие клавиши: 1 - Интересно, 2 - Не интересно, 3 - Пропустить, Space - Пауза/Плей, Esc - Выход",
- font=('Arial', 9), bg='#f0f0f0', fg='#666')
- hotkey_label.pack(pady=5)
- # Привязываем горячие клавиши
- self.root.bind('1', lambda e: self.mark_interesting() if hasattr(self, 'interesting_btn') and self.interesting_btn['state'] == 'normal' else None)
- self.root.bind('2', lambda e: self.mark_not_interesting() if hasattr(self, 'not_interesting_btn') and self.not_interesting_btn['state'] == 'normal' else None)
- self.root.bind('3', lambda e: self.skip_media() if hasattr(self, 'skip_btn') and self.skip_btn['state'] == 'normal' else None)
- self.root.bind('<space>', lambda e: self.toggle_play_pause())
- self.root.bind('<Escape>', lambda e: self.root.quit())
- self.root.focus_set()
- def update_sort_buttons(self):
- """Обновляем кнопки сортировки согласно настройкам"""
- # Очищаем frame
- for widget in self.sort_frame.winfo_children():
- widget.destroy()
- # Создаем кнопки с текущими настройками
- self.interesting_btn = tk.Button(
- self.sort_frame,
- text=self.settings['interesting_button_text'],
- command=self.mark_interesting,
- bg='#51cf66',
- fg='white',
- font=('Arial', 12, 'bold'),
- width=15,
- height=2,
- state='disabled'
- )
- self.interesting_btn.grid(row=0, column=0, padx=10)
- self.not_interesting_btn = tk.Button(
- self.sort_frame,
- text=self.settings['not_interesting_button_text'],
- command=self.mark_not_interesting,
- bg='#ff6b6b',
- fg='white',
- font=('Arial', 12, 'bold'),
- width=15,
- height=2,
- state='disabled'
- )
- self.not_interesting_btn.grid(row=0, column=1, padx=10)
- self.skip_btn = tk.Button(
- self.sort_frame,
- text=self.settings['skip_button_text'],
- command=self.skip_media,
- bg='#ffd43b',
- font=('Arial', 12),
- width=15,
- height=2,
- state='disabled'
- )
- self.skip_btn.grid(row=0, column=2, padx=10)
- def open_settings(self):
- """Открываем окно настроек"""
- settings_window = tk.Toplevel(self.root)
- settings_window.title("Настройки")
- settings_window.geometry("400x350")
- settings_window.configure(bg='#f0f0f0')
- settings_window.transient(self.root)
- settings_window.grab_set()
- # Создаем фрейм для полей ввода
- input_frame = tk.Frame(settings_window, bg='#f0f0f0')
- input_frame.pack(pady=20, padx=20, fill='both', expand=True)
- # Папка для интересного контента
- tk.Label(input_frame, text="Папка для интересного:", bg='#f0f0f0', anchor='w').grid(row=0, column=0, sticky='w', pady=5)
- interesting_folder_entry = tk.Entry(input_frame, width=25)
- interesting_folder_entry.insert(0, self.settings['interesting_folder'])
- interesting_folder_entry.grid(row=0, column=1, padx=10, pady=5, sticky='ew')
- # Папка для не интересного контента
- tk.Label(input_frame, text="Папка для не интересного:", bg='#f0f0f0', anchor='w').grid(row=1, column=0, sticky='w', pady=5)
- not_interesting_folder_entry = tk.Entry(input_frame, width=25)
- not_interesting_folder_entry.insert(0, self.settings['not_interesting_folder'])
- not_interesting_folder_entry.grid(row=1, column=1, padx=10, pady=5, sticky='ew')
- # Текст кнопки для интересного
- tk.Label(input_frame, text="Кнопка интересно:", bg='#f0f0f0', anchor='w').grid(row=2, column=0, sticky='w', pady=5)
- interesting_button_entry = tk.Entry(input_frame, width=25)
- interesting_button_entry.insert(0, self.settings['interesting_button_text'])
- interesting_button_entry.grid(row=2, column=1, padx=10, pady=5, sticky='ew')
- # Текст кнопки для не интересного
- tk.Label(input_frame, text="Кнопка не интересно:", bg='#f0f0f0', anchor='w').grid(row=3, column=0, sticky='w', pady=5)
- not_interesting_button_entry = tk.Entry(input_frame, width=25)
- not_interesting_button_entry.insert(0, self.settings['not_interesting_button_text'])
- not_interesting_button_entry.grid(row=3, column=1, padx=10, pady=5, sticky='ew')
- # Текст кнопки пропуска
- tk.Label(input_frame, text="Кнопка пропуска:", bg='#f0f0f0', anchor='w').grid(row=4, column=0, sticky='w', pady=5)
- skip_button_entry = tk.Entry(input_frame, width=25)
- skip_button_entry.insert(0, self.settings['skip_button_text'])
- skip_button_entry.grid(row=4, column=1, padx=10, pady=5, sticky='ew')
- # Настраиваем вес колонок для растягивания
- input_frame.columnconfigure(1, weight=1)
- def save_settings():
- new_settings = {
- 'interesting_folder': interesting_folder_entry.get().strip(),
- 'not_interesting_folder': not_interesting_folder_entry.get().strip(),
- 'interesting_button_text': interesting_button_entry.get().strip(),
- 'not_interesting_button_text': not_interesting_button_entry.get().strip(),
- 'skip_button_text': skip_button_entry.get().strip()
- }
- # Проверяем, что названия папок не пустые
- if not new_settings['interesting_folder'] or not new_settings['not_interesting_folder']:
- messagebox.showerror("Ошибка", "Названия папок не могут быть пустыми!")
- return
- # Проверяем, что названия кнопок не пустые
- if not new_settings['interesting_button_text'] or not new_settings['not_interesting_button_text'] or not new_settings['skip_button_text']:
- messagebox.showerror("Ошибка", "Текст кнопок не может быть пустым!")
- return
- # Обновляем настройки
- self.settings.update(new_settings)
- # Создаем новые папки
- Path(self.settings['interesting_folder']).mkdir(exist_ok=True)
- Path(self.settings['not_interesting_folder']).mkdir(exist_ok=True)
- # Сохраняем настройки
- self.save_settings()
- # Обновляем интерфейс
- self.update_sort_buttons()
- self.update_stats()
- settings_window.destroy()
- messagebox.showinfo("Успех", "Настройки сохранены!")
- # Фрейм для кнопок
- button_frame = tk.Frame(settings_window, bg='#f0f0f0')
- button_frame.pack(pady=10)
- tk.Button(button_frame, text="Сохранить", command=save_settings,
- bg='#4CAF50', fg='white', font=('Arial', 10, 'bold')).pack(side='left', padx=10)
- tk.Button(button_frame, text="Отмена", command=settings_window.destroy,
- bg='#f44336', fg='white', font=('Arial', 10)).pack(side='left', padx=10)
- def start_parsing(self):
- """Запускаем парсинг в отдельном потоке"""
- name = self.name_entry.get().strip()
- offset = self.offset_entry.get().strip()
- if not name:
- self.parse_progress.config(text="Ошибка: введите Name!")
- return
- if not offset.isdigit():
- self.parse_progress.config(text="Ошибка: Offset должен быть числом!")
- return
- # Блокируем кнопки настройки
- self.name_entry.config(state='disabled')
- self.offset_entry.config(state='disabled')
- self.start_btn.config(state='disabled')
- self.stop_btn.config(state='normal')
- self.settings_btn.config(state='disabled')
- self.resume_btn.config(state='disabled')
- # Активируем кнопки сортировки
- self.interesting_btn.config(state='normal')
- self.not_interesting_btn.config(state='normal')
- self.skip_btn.config(state='normal')
- self.stop_download = False
- self.download_thread = threading.Thread(target=self.parse_media, args=(name, int(offset)))
- self.download_thread.daemon = True
- self.download_thread.start()
- # Запускаем обработку очереди
- self.process_download_queue()
- def stop_parsing(self):
- """Останавливаем парсинг"""
- self.stop_download = True
- self.stop_media_playback()
- self.parse_progress.config(text="Парсинг остановлен. Можно досортировать файлы из temp_downloads")
- self.stop_btn.config(state='disabled')
- self.name_entry.config(state='normal')
- self.offset_entry.config(state='normal')
- self.start_btn.config(state='normal')
- self.settings_btn.config(state='normal')
- self.resume_btn.config(state='normal')
- def resume_sorting(self):
- """Продолжаем сортировку из папки temp_downloads"""
- temp_files = self.get_temp_files()
- if not temp_files:
- messagebox.showinfo("Информация", "В папке temp_downloads нет файлов для сортировки")
- return
- # Добавляем файлы в очередь
- for file_path in temp_files:
- self.download_queue.put(file_path)
- # Активируем кнопки сортировки
- self.interesting_btn.config(state='normal')
- self.not_interesting_btn.config(state='normal')
- self.skip_btn.config(state='normal')
- self.parse_progress.config(text=f"Досортировка: {len(temp_files)} файлов в очереди")
- # Запускаем обработку очереди
- self.process_download_queue()
- def get_temp_files(self):
- """Получаем список файлов из папки temp_downloads"""
- temp_files = []
- temp_dir = "temp_downloads"
- if os.path.exists(temp_dir):
- for file in os.listdir(temp_dir):
- file_path = os.path.join(temp_dir, file)
- if os.path.isfile(file_path):
- temp_files.append(file_path)
- return temp_files
- def parse_media(self, name, offset):
- """Парсим медиа в отдельном потоке"""
- User_Agent = f'{UserAgent().random}'
- try:
- for month in range(1, 13):
- if self.stop_download:
- break
- for day in range(31, 0, -1):
- if self.stop_download:
- break
- for offset_num in range(1, offset + 1):
- if self.stop_download:
- break
- offset_str = "" if offset_num == 1 else f"-{offset_num}"
- # Обновляем прогресс в GUI потоке
- progress_text = f"Парсинг: {day:02}.{month:02}{offset_str}"
- self.root.after(0, lambda: self.parse_progress.config(text=progress_text))
- # Парсим URL
- HEADERS = {'User-Agent': User_Agent}
- url = f"https://telegra.ph/{name}-{month:02}-{day:02}{offset_str}"
- try:
- response = requests.get(url, headers=HEADERS, timeout=10)
- soup = BeautifulSoup(response.content, 'html.parser')
- # Ищем все медиа-элементы (используем современный find_all вместо устаревшего findAll)
- img_items = soup.find_all('img')
- video_items = soup.find_all('video')
- source_items = soup.find_all('source')
- media_urls = []
- # Обрабатываем изображения и гифки
- for item in img_items:
- src = item.get('src')
- if src:
- if not src.startswith('http'):
- media_urls.append(f"https://telegra.ph{src}")
- else:
- media_urls.append(src)
- # Обрабатываем видео
- for item in video_items:
- src = item.get('src')
- if src:
- if not src.startswith('http'):
- media_urls.append(f"https://telegra.ph{src}")
- else:
- media_urls.append(src)
- # Обрабатываем source теги (для видео)
- for item in source_items:
- src = item.get('src')
- if src:
- if not src.startswith('http'):
- media_urls.append(f"https://telegra.ph{src}")
- else:
- media_urls.append(src)
- # Скачиваем медиа
- for i, media_url in enumerate(media_urls):
- if self.stop_download:
- break
- try:
- response = requests.get(media_url, headers=HEADERS, timeout=15)
- if response.status_code == 200:
- # Определяем расширение файла
- content_type = response.headers.get('content-type', '')
- if 'video' in content_type:
- extension = '.mp4'
- elif 'gif' in content_type:
- extension = '.gif'
- else:
- extension = '.jpg'
- # Сохраняем во временную папку
- temp_filename = f"temp_{month:02}_{day:02}_{offset_num}_{i}{extension}"
- temp_path = os.path.join("temp_downloads", temp_filename)
- with open(temp_path, "wb") as file:
- file.write(response.content)
- # Добавляем в очередь для отображения
- self.download_queue.put(temp_path)
- except Exception as e:
- print(f"Ошибка загрузки: {e}")
- continue
- except Exception as e:
- print(f"Ошибка парсинга: {e}")
- continue
- # Завершение парсинга
- self.root.after(0, self.parsing_completed)
- except Exception as e:
- self.root.after(0, lambda: self.parse_progress.config(text=f"Ошибка: {e}"))
- def process_download_queue(self):
- """Обрабатываем очередь загруженных медиа"""
- try:
- if not self.current_media_path and not self.download_queue.empty():
- next_media_path = self.download_queue.get_nowait()
- self.show_media(next_media_path)
- if not self.stop_download or not self.download_queue.empty():
- self.root.after(100, self.process_download_queue)
- except queue.Empty:
- if not self.stop_download:
- self.root.after(100, self.process_download_queue)
- else:
- # Если очередь пуста и парсинг остановлен, показываем сообщение
- if not self.current_media_path:
- self.parse_progress.config(text="Все файлы обработаны!")
- def show_media(self, media_path):
- """Показываем медиа в интерфейсе"""
- self.stop_media_playback()
- self.current_media_path = media_path
- extension = Path(media_path).suffix.lower()
- try:
- if extension in ['.gif']:
- self.show_gif(media_path)
- elif extension in ['.mp4', '.avi', '.mov', '.webm']:
- self.show_video(media_path)
- else:
- self.show_image(media_path)
- # Обновляем информацию о файле
- filename = os.path.basename(media_path)
- file_size = os.path.getsize(media_path) / 1024
- file_info = f"Файл: {filename} | Размер: {file_size:.1f} KB | Тип: {extension}"
- self.info_label.configure(text=file_info)
- self.update_stats()
- except Exception as e:
- print(f"Ошибка загрузки медиа: {e}")
- self.current_media_path = None
- # Продолжаем обработку очереди
- self.root.after(100, self.process_download_queue)
- def show_image(self, image_path):
- """Показываем изображение"""
- try:
- image = Image.open(image_path)
- self.display_pil_image(image)
- self.hide_video_controls()
- except Exception as e:
- print(f"Ошибка загрузки изображения: {e}")
- def show_gif(self, gif_path):
- """Показываем GIF с анимацией"""
- try:
- self.gif_frames = []
- gif = Image.open(gif_path)
- # Извлекаем все кадры GIF
- for frame in ImageSequence.Iterator(gif):
- photo = self.resize_frame(frame)
- self.gif_frames.append(photo)
- if len(self.gif_frames) > 1:
- # Анимированный GIF
- self.is_playing_gif = True
- self.current_gif_frame = 0
- self.animate_gif()
- self.hide_video_controls()
- else:
- # Статический GIF
- self.display_pil_image(gif)
- self.hide_video_controls()
- except Exception as e:
- print(f"Ошибка загрузки GIF: {e}")
- def show_video(self, video_path):
- """Показываем видео"""
- try:
- self.current_video_path = video_path
- self.video_capture = cv2.VideoCapture(video_path)
- # Получаем информацию о видео
- fps = self.video_capture.get(cv2.CAP_PROP_FPS)
- frame_count = self.video_capture.get(cv2.CAP_PROP_FRAME_COUNT)
- self.video_duration = frame_count / fps if fps > 0 else 0
- # Настраиваем ползунок
- self.seek_slider.config(from_=0, to=100)
- self.seek_slider.set(0)
- self.update_time_label()
- self.is_playing_video = True
- self.show_video_controls()
- self.play_video()
- except Exception as e:
- print(f"Ошибка загрузки видео: {e}")
- def play_video(self):
- """Воспроизводим видео"""
- if self.video_capture and self.is_playing_video:
- ret, frame = self.video_capture.read()
- if ret:
- # Обновляем позицию
- current_frame = self.video_capture.get(cv2.CAP_PROP_POS_FRAMES)
- total_frames = self.video_capture.get(cv2.CAP_PROP_FRAME_COUNT)
- if total_frames > 0:
- self.video_position = current_frame / total_frames
- # Обновляем ползунок
- if not self.is_seeking:
- position_percent = self.video_position * 100
- self.seek_slider.set(position_percent)
- self.update_time_label()
- # Конвертируем BGR в RGB
- frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
- pil_image = Image.fromarray(frame_rgb)
- self.display_pil_image(pil_image)
- # Обновляем состояние кнопок
- self.play_btn.config(state='disabled')
- self.pause_btn.config(state='normal')
- self.root.after(30, self.play_video) # ~33 FPS
- else:
- # Конец видео
- self.stop_video()
- def pause_video(self):
- """Пауза видео"""
- self.is_playing_video = False
- self.play_btn.config(state='normal')
- self.pause_btn.config(state='disabled')
- def stop_video(self):
- """Останавливаем видео"""
- self.is_playing_video = False
- if self.video_capture:
- self.video_capture.set(cv2.CAP_PROP_POS_FRAMES, 0)
- self.play_btn.config(state='normal')
- self.pause_btn.config(state='disabled')
- self.seek_slider.set(0)
- self.update_time_label()
- def toggle_play_pause(self):
- """Переключаем воспроизведение/паузу"""
- if self.current_media_path and Path(self.current_media_path).suffix.lower() in ['.mp4', '.avi', '.mov', '.webm']:
- if self.is_playing_video:
- self.pause_video()
- else:
- self.is_playing_video = True
- self.play_video()
- def on_seek(self, value):
- """Обработка перемотки видео"""
- if self.video_capture:
- self.is_seeking = True
- position = float(value) / 100.0
- total_frames = self.video_capture.get(cv2.CAP_PROP_FRAME_COUNT)
- target_frame = position * total_frames
- self.video_capture.set(cv2.CAP_PROP_POS_FRAMES, target_frame)
- self.video_position = position
- self.update_time_label()
- self.is_seeking = False
- def update_time_label(self):
- """Обновляем метку времени"""
- if self.video_duration > 0:
- current_time = self.video_position * self.video_duration
- total_time = self.video_duration
- current_str = self.format_time(current_time)
- total_str = self.format_time(total_time)
- self.time_label.config(text=f"{current_str} / {total_str}")
- def format_time(self, seconds):
- """Форматируем время в мм:сс"""
- minutes = int(seconds // 60)
- seconds = int(seconds % 60)
- return f"{minutes:02d}:{seconds:02d}"
- def show_video_controls(self):
- """Показываем элементы управления видео"""
- # Показываем ползунок
- self.seek_frame.pack(fill='x', padx=10, pady=5)
- # Показываем кнопки управления
- self.play_btn.pack(side='left', padx=5)
- self.pause_btn.pack(side='left', padx=5)
- self.stop_btn_video.pack(side='left', padx=5)
- self.video_controls.pack(pady=5)
- # Устанавливаем начальное состояние кнопок
- self.play_btn.config(state='disabled')
- self.pause_btn.config(state='normal')
- def hide_video_controls(self):
- """Скрываем элементы управления видео"""
- self.seek_frame.pack_forget()
- self.video_controls.pack_forget()
- def stop_media_playback(self):
- """Останавливаем все воспроизведение медиа"""
- self.is_playing_gif = False
- self.is_playing_video = False
- self.gif_frames = []
- self.is_seeking = False
- if self.video_capture:
- self.video_capture.release()
- self.video_capture = None
- self.hide_video_controls()
- def resize_frame(self, image):
- """Масштабируем кадр под размер окна"""
- window_width = 600
- window_height = 400
- image_ratio = image.width / image.height
- window_ratio = window_width / window_height
- if image_ratio > window_ratio:
- new_width = window_width
- new_height = int(window_width / image_ratio)
- else:
- new_height = window_height
- new_width = int(window_height * image_ratio)
- image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
- return ImageTk.PhotoImage(image)
- def display_pil_image(self, image):
- """Отображаем PIL изображение"""
- photo = self.resize_frame(image)
- self.media_label.configure(image=photo)
- self.media_label.image = photo
- def update_stats(self):
- """Обновляем статистику"""
- try:
- interesting_count = len(os.listdir(self.settings['interesting_folder']))
- not_interesting_count = len(os.listdir(self.settings['not_interesting_folder']))
- stats_text = f"Интересно: {interesting_count} | Не интересно: {not_interesting_count}"
- self.stats_label.configure(text=stats_text)
- except:
- self.stats_label.configure(text="Ошибка загрузки статистики")
- def mark_interesting(self):
- """Помечаем как интересное медиа"""
- if self.current_media_path:
- self.move_media(self.settings['interesting_folder'])
- self.process_download_queue()
- def mark_not_interesting(self):
- """Помечаем как не интересное медиа"""
- if self.current_media_path:
- self.move_media(self.settings['not_interesting_folder'])
- self.process_download_queue()
- def skip_media(self):
- """Пропускаем текущее медиа"""
- if self.current_media_path:
- self.stop_media_playback()
- try:
- os.remove(self.current_media_path)
- except:
- pass
- self.current_media_path = None
- self.process_download_queue()
- def move_media(self, destination_folder):
- """Перемещаем медиа в указанную папку"""
- if self.current_media_path:
- self.stop_media_playback()
- filename = os.path.basename(self.current_media_path)
- # Создаем уникальное имя
- counter = 1
- new_filename = filename
- base_name = Path(filename).stem
- extension = Path(filename).suffix
- while os.path.exists(os.path.join(destination_folder, new_filename)):
- new_filename = f"{base_name}_{counter}{extension}"
- counter += 1
- # Перемещаем файл
- try:
- shutil.move(self.current_media_path, os.path.join(destination_folder, new_filename))
- print(f"Файл перемещен в {destination_folder}")
- except Exception as e:
- print(f"Ошибка перемещения: {e}")
- self.current_media_path = None
- def parsing_completed(self):
- """Завершение парсинга"""
- self.parse_progress.config(text="Парсинг завершен! Можно досортировать файлы из temp_downloads")
- self.stop_btn.config(state='disabled')
- self.name_entry.config(state='normal')
- self.offset_entry.config(state='normal')
- self.start_btn.config(state='normal')
- self.settings_btn.config(state='normal')
- self.resume_btn.config(state='normal')
- if not self.current_media_path and self.download_queue.empty():
- self.media_label.configure(text="Все медиа обработаны!", image='')
- def run(self):
- """Запускаем приложение"""
- self.root.mainloop()
- if __name__ == "__main__":
- # Проверяем наличие OpenCV для поддержки видео
- try:
- import cv2
- print("OpenCV найден, видео поддерживается")
- except ImportError:
- print("Предупреждение: OpenCV не установлен. Видео не будет поддерживаться.")
- print("Установите: pip install opencv-python")
- app = CombinedPhotoApp()
- app.run()
Advertisement
Add Comment
Please, Sign In to add comment