117 lines
3.9 KiB
Python
117 lines
3.9 KiB
Python
|
|
from PySide6.QtCore import QObject, QThread, Signal
|
|||
|
|
from datetime import datetime, timedelta
|
|||
|
|
from pathlib import Path
|
|||
|
|
import shutil
|
|||
|
|
import tempfile
|
|||
|
|
from models.api_model import HelioviewerAPI
|
|||
|
|
from utils.video_creator import VideoCreator
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TimelapseWorker(QThread):
|
|||
|
|
log = Signal(str)
|
|||
|
|
progress = Signal(int, int)
|
|||
|
|
finished = Signal(bool, str)
|
|||
|
|
|
|||
|
|
def __init__(self, source_id, start_date, end_date, output_path, fps=10):
|
|||
|
|
super().__init__()
|
|||
|
|
self.source_id = source_id
|
|||
|
|
self.start_date = start_date
|
|||
|
|
self.end_date = end_date
|
|||
|
|
self.output_path = output_path
|
|||
|
|
self.fps = fps
|
|||
|
|
self.cancelled = False
|
|||
|
|
self.temp_dir = None
|
|||
|
|
|
|||
|
|
def cancel(self):
|
|||
|
|
self.cancelled = True
|
|||
|
|
self.log.emit("⏹️ Отмена процесса...")
|
|||
|
|
|
|||
|
|
def run(self):
|
|||
|
|
try:
|
|||
|
|
# Создаем временную папку
|
|||
|
|
self.temp_dir = Path(tempfile.mkdtemp(prefix="timelapse_"))
|
|||
|
|
self.log.emit(f"📁 Временная папка: {self.temp_dir}")
|
|||
|
|
|
|||
|
|
# Генерируем даты
|
|||
|
|
dates = []
|
|||
|
|
current = self.start_date.replace(hour=12, minute=0, second=0)
|
|||
|
|
while current <= self.end_date:
|
|||
|
|
dates.append(current)
|
|||
|
|
current += timedelta(days=1)
|
|||
|
|
|
|||
|
|
total = len(dates)
|
|||
|
|
self.log.emit(f"📊 Всего файлов: {total}")
|
|||
|
|
|
|||
|
|
downloaded = []
|
|||
|
|
|
|||
|
|
# Скачиваем
|
|||
|
|
for i, date in enumerate(dates):
|
|||
|
|
if self.cancelled:
|
|||
|
|
self.cleanup()
|
|||
|
|
self.finished.emit(False, "Отменено")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
percent = int((i + 1) / total * 100)
|
|||
|
|
self.progress.emit(i + 1, total)
|
|||
|
|
self.log.emit(f"📥 [{i + 1}/{total}] {percent}% - {date.strftime('%Y-%m-%d')}")
|
|||
|
|
|
|||
|
|
filepath = HelioviewerAPI.download_image(self.source_id, date, self.temp_dir)
|
|||
|
|
if filepath:
|
|||
|
|
downloaded.append(filepath)
|
|||
|
|
self.log.emit(f"✅ [{i + 1}/{total}] Успешно")
|
|||
|
|
else:
|
|||
|
|
self.log.emit(f"❌ [{i + 1}/{total}] Ошибка")
|
|||
|
|
|
|||
|
|
if self.cancelled:
|
|||
|
|
self.cleanup()
|
|||
|
|
self.finished.emit(False, "Отменено")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
if not downloaded:
|
|||
|
|
self.cleanup()
|
|||
|
|
self.finished.emit(False, "Нет файлов")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
# Создаем видео
|
|||
|
|
self.log.emit("🎬 Создание видео...")
|
|||
|
|
self.progress.emit(total, total)
|
|||
|
|
|
|||
|
|
video_path = VideoCreator.create_timelapse(downloaded, self.output_path, self.fps)
|
|||
|
|
|
|||
|
|
# Очистка
|
|||
|
|
self.cleanup()
|
|||
|
|
|
|||
|
|
if video_path and video_path.exists():
|
|||
|
|
self.finished.emit(True, str(video_path))
|
|||
|
|
else:
|
|||
|
|
self.finished.emit(False, "Ошибка создания видео")
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
self.cleanup()
|
|||
|
|
self.finished.emit(False, str(e))
|
|||
|
|
|
|||
|
|
def cleanup(self):
|
|||
|
|
if self.temp_dir and self.temp_dir.exists():
|
|||
|
|
shutil.rmtree(self.temp_dir)
|
|||
|
|
self.log.emit("🗑️ Временные файлы удалены")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TimelapseController(QObject):
|
|||
|
|
def __init__(self):
|
|||
|
|
super().__init__()
|
|||
|
|
self.worker = None
|
|||
|
|
self.log = Signal(str)
|
|||
|
|
self.progress = Signal(int, int)
|
|||
|
|
self.finished = Signal(bool, str)
|
|||
|
|
|
|||
|
|
def create(self, source_id, start_date, end_date, output_path, fps=10):
|
|||
|
|
self.worker = TimelapseWorker(source_id, start_date, end_date, output_path, fps)
|
|||
|
|
self.worker.log.connect(self.log)
|
|||
|
|
self.worker.progress.connect(self.progress)
|
|||
|
|
self.worker.finished.connect(self.finished)
|
|||
|
|
self.worker.start()
|
|||
|
|
return self.worker
|
|||
|
|
|
|||
|
|
def cancel(self):
|
|||
|
|
if self.worker:
|
|||
|
|
self.worker.cancel()
|