diff --git a/src/ffmpeg.lisp b/src/ffmpeg.lisp index d47e02f..77c83b9 100644 --- a/src/ffmpeg.lisp +++ b/src/ffmpeg.lisp @@ -1,6 +1,39 @@ (in-package :visp) +(defun validate-output-path (output) + "Validate output path before ffmpeg execution" + (let* ((output-pathname (pathname output)) + (output-dir (uiop:pathname-directory-pathname output-pathname))) + ;; ディレクトリ存在確認 + ;; 相対パスの場合はカレントディレクトリからの解決を行う + (unless (uiop:directory-exists-p output-dir) + (format t "~a Output directory does not exist: ~a~%" + (log-tag "error") (namestring output-dir)) + (uiop:quit 1)) + + ;; 出力ファイル名の基本チェック + (let ((filename (file-namestring output-pathname))) + (when (string= filename "") + (format t "~a Invalid output filename: ~a~%" + (log-tag "error") output) + (uiop:quit 1))))) + +(defun cleanup-partial-output (output) + "Clean up partially created output file if it exists and is empty/corrupted" + (when (probe-file output) + (let ((file-size (with-open-file (stream output :direction :input + :if-does-not-exist nil) + (when stream (file-length stream))))) + ;; 0バイトまたは非常に小さいファイル(ヘッダーのみ等)は削除 + (when (and file-size (< file-size 1024)) + (delete-file output) + (format t "~a Cleaned up incomplete output file: ~a~%" + (log-tag "info") output))))) + (defun run-cmd (cmd output dry-run) + ;; Phase 1: 事前検証 + (validate-output-path output) + (when (probe-file output) (format t "~a Output file '~a' already exists. It will be overwritten.~%" (log-tag "warn") output)) @@ -11,7 +44,13 @@ (format t "~a Command: ~{~a ~}~%" (log-tag "dry-run") cmd)) (progn (format t "~a Running: ~{~a ~}~%" (log-tag "info") cmd) - (uiop:run-program cmd :output t :error-output t)))) + ;; Phase 1: 実行前後でのクリーンアップ対応準備 + (handler-case + (uiop:run-program cmd :output t :error-output t) + (error (e) + ;; 簡易的なエラー処理(Phase 2で本格化) + (cleanup-partial-output output) + (error e)))))) (defun build-gif-cmd (opts output fps) "Construct the ffmpeg command list for GIF mode using input filename and target fps." diff --git a/src/package.lisp b/src/package.lisp index 39364dc..00207a6 100644 --- a/src/package.lisp +++ b/src/package.lisp @@ -63,6 +63,8 @@ :build-cmd :encoder-available-p :ffmpeg-available-p + :validate-output-path + :cleanup-partial-output ;; log.lisp :log-tag diff --git a/t/test-ffmpeg.lisp b/t/test-ffmpeg.lisp index 4450e75..f1eeba4 100644 --- a/t/test-ffmpeg.lisp +++ b/t/test-ffmpeg.lisp @@ -1,7 +1,9 @@ (defpackage :visp.test.ffmpeg (:use :cl :rove) (:import-from :visp - :make-visp-options)) + :make-visp-options + :validate-output-path + :cleanup-partial-output)) (in-package :visp.test.ffmpeg) @@ -100,4 +102,43 @@ (ok (equal (visp:build-cmd opts output) '("ffmpeg" "-y" "-i" "input.mp4" "-vf" "scale=1920:1080,setpts=PTS/0.5,hflip" - "complex-speed.mp4")))))) \ No newline at end of file + "complex-speed.mp4")))))) + +(deftest validate-output-path-tests + (testing "Validates existing directory paths" + ;; カレントディレクトリは存在するはず + (ok (not (signals error (validate-output-path "test-output.mp4")))) + (ok (not (signals error (validate-output-path "./test-output.mp4"))))) + + ;; NOTE: uiop:quitを呼ぶ関数のテストは現在のテストフレームワークでは困難 + ;; 実際のエラーケースのテストは統合テストで実行する + ) + +(deftest cleanup-partial-output-tests + (testing "Cleans up small/empty files" + ;; 小さなテストファイルを作成 + (let ((test-file "test-partial.mp4")) + ;; 空ファイルを作成 + (with-open-file (stream test-file :direction :output + :if-exists :supersede) + (write-string "" stream)) + + ;; クリーンアップが動作することを確認 + (ok (probe-file test-file)) + (cleanup-partial-output test-file) + (ok (not (probe-file test-file))))) + + (testing "Preserves normal-sized files" + ;; 通常サイズのファイルは削除されない + (let ((test-file "test-normal.mp4")) + ;; 十分なサイズのファイルを作成 + (with-open-file (stream test-file :direction :output + :if-exists :supersede) + (write-string (make-string 2048 :initial-element #\a) stream)) + + (ok (probe-file test-file)) + (cleanup-partial-output test-file) + (ok (probe-file test-file)) + + ;; テスト後のクリーンアップ + (delete-file test-file)))) \ No newline at end of file diff --git a/t/test-util.lisp b/t/test-util.lisp index 738f0e3..ec53e3d 100644 --- a/t/test-util.lisp +++ b/t/test-util.lisp @@ -19,7 +19,7 @@ (deftest parse-frame-rate-tests (testing "Parses valid frame rate strings" (ok (= (visp:parse-frame-rate "30") 30.0)) - (ok (= (visp:parse-frame-rate "30000/1001") (/ 30000.0 1001)))) + (ok (= (visp:parse-frame-rate "30000/1001") (/ 30000 1001)))) (testing "Handles invalid input gracefully" (ok (null (visp:parse-frame-rate "not-a-number")))