Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions src/const.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,11 @@
("yuv444p10le" . "ProRes 4444")
("yuva420p" . "HAP Alpha")
("yuv420p" . "H.264 Default")
;; 必要に応じて拡張
;; Extend as needed
))

(defparameter +allowed-input-extensions+
'(".mp4" ".mov" ".flv" ".avi" ".webm"))

(defparameter +gif-scale+ "640:-1")

(defparameter +flip-filters+
'(("hflip" . "hflip")
("vflip" . "vflip")))
36 changes: 17 additions & 19 deletions src/ffmpeg.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
"Validate output path before ffmpeg execution"
(let* ((output-pathname (pathname output))
(output-dir (uiop:pathname-directory-pathname output-pathname)))
;; ディレクトリ存在確認
;; 相対パスの場合はカレントディレクトリからの解決を行う
;; Check directory existence (resolve relative paths from current directory)
(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))

;; 出力ファイル名の基本チェック
;; Basic output filename validation
(let ((filename (file-namestring output-pathname)))
(when (string= filename "")
(format t "~a Invalid output filename: ~a~%"
Expand All @@ -24,14 +23,14 @@
(let ((file-size (with-open-file (stream output :direction :input
:if-does-not-exist nil)
(when stream (file-length stream)))))
;; 0バイトまたは非常に小さいファイル(ヘッダーのみ等)は削除
;; Delete empty or very small files (header-only, etc.)
(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: 事前検証
;; Pre-execution validation
(validate-output-path output)

(when (probe-file output)
Expand All @@ -44,11 +43,11 @@
(format t "~a Command: ~{~a ~}~%" (log-tag "dry-run") cmd))
(progn
(format t "~a Running: ~{~a ~}~%" (log-tag "info") cmd)
;; Phase 1: 実行前後でのクリーンアップ対応準備
;; Error handling with cleanup
(handler-case
(uiop:run-program cmd :output t :error-output t)
(error (e)
;; 簡易的なエラー処理(Phase 2で本格化)
;; Basic error handling (to be enhanced later)
(cleanup-partial-output output)
(error e))))))

Expand Down Expand Up @@ -116,7 +115,7 @@
(width (getf first-video-info :width))
(height (getf first-video-info :height)))

;; defensive check
;; Ensure video metadata is valid
(unless (and fps width height)
(format t "~a Failed to retrieve fps/width/height from ~a~%" (log-tag "error") (car files))
(format t "~a Check if the file is a valid video with proper metadata.~%" (log-tag "error"))
Expand Down Expand Up @@ -148,27 +147,27 @@
(filters '())
(cmd (list "ffmpeg" "-y")))

;; ループ再生(inputより先に無いといけないらしい)
;; Loop playback (must come before input argument)
(when repeat
(setf cmd (append cmd
(list "-stream_loop" (format nil "~a" (1- repeat))))))

(setf cmd (append cmd (list "-i" input)))

;; 解像度(縦:横)
;; Resolution scaling
(when scale
(destructuring-bind (w . h) scale
(push (format nil "scale=~A:~A" w h) filters)))

;; 逆再生
;; Reverse playback
(when rev
(push "reverse" filters))

;; 速度変更
;; Speed adjustment
(when speed
(push (format nil "setpts=PTS/~a" speed) filters))

;; フリップ操作
;; Video flipping
(when hflip
(push "hflip" filters))

Expand All @@ -179,29 +178,28 @@
(push "format=gray" filters))

(when filters
;; OK: -vf "scale=1920:1080,reverse"
;; NG: -vf "reverse,scale=1920:1080"
;; Filter order matters: scale before effects like reverse
(let* ((filters-str (format nil "~{~a~^,~}" (reverse filters)))
(vf-arg (list "-vf" filters-str)))
(setf cmd (append cmd vf-arg))))

;; コーデック
;; Video codec settings
(when codec-info
(let ((encoder (getf codec-info :encoder))
(pix-fmt (getf codec-info :pix_fmt)))
(setf cmd (append cmd (list "-c:v" encoder)))
(when pix-fmt
(setf cmd (append cmd (list "-pix_fmt" pix-fmt))))))

;; ミュート
;; Audio removal
(when mute
(setf cmd (append cmd (list "-an"))))

;; フレームレート
;; Frame rate
(when fps
(setf cmd (append cmd (list "-r" (format nil "~a" fps)))))

;; 出力ファイル名
;; Add output filename
(setf cmd (append cmd (list output))) cmd))

(defun encoder-available-p (name)
Expand Down
8 changes: 4 additions & 4 deletions src/help.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
(format t "~%visp - minimal ffmpeg wrapper written in Common Lisp~%~%")
(format t "=================================================~%")

;; Usageセクション
;; Usage section
(format t "Usage:~%")
(format t " visp --input <file> [options] ;; Normal mode~%")
(format t " visp --merge <file1> <file2> ... ;; Merge multiple mp4 files into one~%")
(format t " visp --gif <file> ;; Convert a single video to GIF~%~%")

;; Normal mode options
;; Options for normal mode
(format t "Normal mode options:~%")
(format t " --input <file> Input video file or directory (required)~%")
(format t " - If a directory is specified, all video files in it will be processed in batch.~%")
Expand All @@ -32,7 +32,7 @@
(format t " --dry-run Show ffmpeg command without executing~%")
(format t " --help Show this help message~%~%")

;; Merge mode explanation
;; Merge mode details
(format t "Merge mode (exclusive):~%")
(format t " --merge <file1> <file2> ...~%")
(format t " Merge multiple .mp4 videos into one output file.~%")
Expand All @@ -44,7 +44,7 @@
(format t " - Other options cannot be combined with --merge (except --output and --dry-run)~%")
(format t " - However, --dry-run can be combined to preview the ffmpeg command~%~%")

;; GIF mode explanation
;; GIF mode details
(format t "GIF mode (exclusive):~%")
(format t " --gif <file>~%")
(format t " Convert video to GIF animation.~%")
Expand Down
10 changes: 5 additions & 5 deletions src/log.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

(defun log-tag (type)
(cond
((string= type "info") (colorize "[INFO]" "34;1")) ; 青
((string= type "dry-run") (colorize "[DRY-RUN]" "33;1")) ; 黄
((string= type "warn") (colorize "[WARN]" "35;1")) ; 紫
((string= type "error") (colorize "[ERROR]" "31;1")) ; 赤
(t type))) ; fallback
((string= type "info") (colorize "[INFO]" "34;1"))
((string= type "dry-run") (colorize "[DRY-RUN]" "33;1"))
((string= type "warn") (colorize "[WARN]" "35;1"))
((string= type "error") (colorize "[ERROR]" "31;1"))
(t type)))
18 changes: 9 additions & 9 deletions src/main.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
(format t "~a ffmpeg not found in your system. Please install ffmpeg first.~%" (log-tag "error"))
(uiop:quit 1))

;; 引数がなければコマンドラインから取得
;; Get command line arguments if not provided
(unless args (setf args (uiop:command-line-arguments)))
(setf args (clean-args args))

Expand All @@ -17,33 +17,33 @@
(dispatch-validation opts)

(cond
;; 1. GIF モード
;; GIF mode
((visp-options-gif opts)
(let* ((input (visp-options-input opts))
(fps (get-video-fps input))
(output (generate-gif-output-filename opts))
(cmd (build-gif-cmd opts output fps)))
(run-cmd cmd output (visp-options-dry-run opts))))

;; 2. 結合モード
;; Merge mode
((visp-options-merge-files opts)
(let* ((output (generate-merge-output-filename opts))
(cmd (build-merge-cmd opts output)))
(run-cmd cmd output (visp-options-dry-run opts))))

;; 3. バッチモード
;; Batch mode
((visp-options-batch-files opts)
(dolist (file (visp-options-batch-files opts))
;; 出力ファイル名やコマンド生成を通常モードと共通化するため、input を一時的に設定
;; Temporarily set input for each file to reuse normal mode logic
(setf (visp-options-input opts) file)
;; 出力ファイル名生成
;; Generate output filename in same directory as input
(let* ((ext (getf (visp-options-codec-info opts) :ext))
(filename (generate-output-filename opts ext)) ;; ファイル名だけ
(output (output-path-in-same-directory file filename)) ;; ディレクトリと結合してフルパス化
(filename (generate-output-filename opts ext))
(output (output-path-in-same-directory file filename))
(cmd (build-cmd opts output)))
(run-cmd cmd output (visp-options-dry-run opts)))))

;; 4. 通常処理
;; Normal mode
(t
(let* ((ext (getf (visp-options-codec-info opts) :ext))
(output (generate-output-filename opts ext))
Expand Down
20 changes: 10 additions & 10 deletions src/options.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
scale
fps
repeat
half ;boolean
rev ;boolean
mute ;boolean
mono ;boolean
hflip ;boolean
vflip ;boolean
speed ;float
dry-run ;boolean
half
rev
mute
mono
hflip
vflip
speed
dry-run
merge-files
batch-files
gif ;boolean
gif
)

(defun parse-args-to-options (args)
Expand Down Expand Up @@ -79,7 +79,7 @@
(incf i))
(setf (visp-options-merge-files opts) (nreverse files))))
((string= key "--gif")
;; --inputが既に設定されている場合はエラー
;; Error if --input already set
(when (visp-options-input opts)
(format t "~a Do not use --input with --gif. Use: visp --gif <video-file>~%"
(log-tag "error"))
Expand Down
6 changes: 3 additions & 3 deletions src/util.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,16 @@ Accepts integers including -1. Returns NIL on malformed input."

(defun generate-output-filename (opts &optional ext)
"Generate output filename based on visp-options and optional ext override."
;; --outputが指定されていれば、それを返す
;; Return custom output filename if specified
(let ((output (visp-options-output opts)))
(if output
output
;; 従来通りの自動生成
;; Auto-generate filename based on options
(let* ((input (visp-options-input opts))
(base (file-namestring input))
(dot-pos (position #\. base :from-end t))
(name (subseq base 0 dot-pos))
;; 拡張子は codec-info の ext 優先、なければ元ファイルの拡張子
;; Use codec extension if available, else original extension
(ext (or ext (subseq base dot-pos)))
(res (visp-options-res opts))
(res-suffix (if res (format nil "_~a" res) ""))
Expand Down
Loading