"""Сервисы для операций над заданиями: удаление и чистка артефактов.""" import logging from pathlib import Path from django.conf import settings from django.db import transaction from ..models import TimelapseJob logger = logging.getLogger('camlaps') def _resolve_output_video_path(job: TimelapseJob) -> Path | None: """Возвращает абсолютный путь к mp4 из output_rel_path или None, если видео не задано/путь небезопасен.""" rel = (job.output_rel_path or '').strip().lstrip('/') if not rel: return None filename = Path(rel).name export_dir = Path(settings.TIMELAPS_EXPORT_DIR).resolve() candidate = (export_dir / filename).resolve() if candidate != export_dir and export_dir not in candidate.parents: return None return candidate @transaction.atomic def delete_job_and_artifacts(job_id: int) -> bool: """Удаляет задачу и её видеофайл (если существует). Возвращает True, если файл видео был удалён.""" logger.info('jobs:delete:start job_id=%s', job_id) job = TimelapseJob.objects.select_for_update().filter(pk=job_id).first() if not job: logger.info('jobs:delete:done job_not_found=true job_id=%s', job_id) return False video_path = _resolve_output_video_path(job) video_deleted = False if video_path and video_path.exists(): try: video_path.unlink() video_deleted = True except Exception: logger.exception('jobs:delete:video_unlink_error job_id=%s', job_id) job.delete() logger.info('jobs:delete:done job_id=%s video_deleted=%s', job_id, video_deleted) return video_deleted