カメラからのデータ取り込みコマンドを作ってみた
どうも、キヨタです。
プログラミングちゃんと勉強してこなかったので趣味がてら色々作ってみようと思ってPython始めました。 Pythonは初心者なのでお作法とかあまり分からず、とりあえずググりながら書いてみています。
今年の3月に人生初のMacを購入し、子供が寝静まった深夜にニヤニヤしながらカタカタしているのですが、Win機のころ使用していたカメラ画像の取り込みソフトがMacでサポートされておらず写真整理が滞っていました。 そこで記念すべき第1段はカメラからの動画/静止画取り込みを想定した俺得コマンドを作る事にしました。
仕様
- 取り込み元メディアでは指定ディレクトリ以下のサブディレクトリまで探索し、拡張子でマッチして取り込むファイルを決める
- 取り込み先ではyyyy/yyyy-mm/yyyy-mm-ddのような撮影日の年月日ベースの階層構造でディレクトリを分けて格納する
- オプションで静止画のみ/動画のみの取り込みにも対応できるようにする
- ファイル作成日などのメタデータは保持してコピーしてくる、元ファイルは消さない
- 取り込み先に同一名のファイルがある場合にはサイズ比較で同一性を確かめてログに吐く
試してみたこと
- コマンドライン引数の処理 (sys.argv)
- ファイルパス操作 (pathlib)
- ファイルのメタ情報の確認 (.stat())
- 文字列フォーマットの練習 (.format)
使い方と実行結果
使い方はシンプル。
コピー元とコピー先の親ディレクトリをコマンドライン引数で指定します。
-i オプションをつけると静止画のみ、-mをつけると動画のみをコピーします。
> python camera_import.py Error : Too few arguments. Usage : camera_import.py [-h | -i | -m] source_dir target_dir -h Show usage. -i Copying image files only. -m Copying movie files only. > python camera_import.py /Volumes/Source /Volumes/Destination -------------------------------------------------- camera_import.py Source dir : /Volumes/Source Target dir : /Volumes/Destination File type : ('JPG', 'jpg', 'mp4', 'MP4', 'm2ts', 'M2TS') -------------------------------------------------- Make directory : /Volumes/Destination/2020/2020-05/2020-05-20 DSC01052.JPG [2020/05/20] ...Copied to /Volumes/Destination/2020/2020-05/2020-05-20 DSC01053.JPG [2020/05/20] ...Copied to /Volumes/Destination/2020/2020-05/2020-05-20 DSC01054.JPG [2020/05/20] ...Copied to /Volumes/Destination/2020/2020-05/2020-05-20 DSC01055.JPG [2020/05/20] ...Copied to /Volumes/Destination/2020/2020-05/2020-05-20 DSC01056.JPG [2020/05/20] ...Copied to /Volumes/Destination/2020/2020-05/2020-05-20 DSC01057.JPG [2020/05/20] ...Copied to /Volumes/Destination/2020/2020-05/2020-05-20 DSC01058.JPG [2020/05/20] ...Copied to /Volumes/Destination/2020/2020-05/2020-05-20 DSC01059.JPG [2020/05/20] ...Copied to /Volumes/Destination/2020/2020-05/2020-05-20 Make directory : /Volumes/Destination/2020/2020-05/2020-05-21 DSC01071.JPG [2020/05/21] ...Copied to /Volumes/Destination/2020/2020-05/2020-05-21 DSC01072.JPG [2020/05/21] ...Copied to /Volumes/Destination/2020/2020-05/2020-05-21 ...
ソースコード
#!/usr/bin/env python3 # # camera_import.py # # Author : Yuji.Kiyota@gmail.com # Create : 2020/05/22 # from pathlib import Path import sys import shutil import datetime # Define directory format dir_format = '%Y/%Y-%m/%Y-%m-%d' # Define copying file extentions (case sensitive) image_ext_tuple = ( 'JPG', 'jpg' ) movie_ext_tuple = ( 'mp4', 'MP4', 'm2ts', 'M2TS' ) # Process arguments args = sys.argv usage = ' Usage : ' + args[0].replace( './', '' ) + ' [-h | -i | -m] source_dir target_dir' usage += """ -h Show usage. -i Copying image files only. -m Copying movie files only. """ for arg in args : if arg == args[0] : pass elif arg == '-h' or arg == '--help' : print( '\n' + usage ) elif arg == '-i' : ext_tuple = image_ext_tuple elif arg == '-m' : ext_tuple = movie_ext_tuple elif 'path_obj_src' not in locals() : path_obj_src = Path( arg ) elif 'path_obj_dst' not in locals() : path_obj_dst = Path( arg ) if 'ext_tuple' not in locals() : ext_tuple = image_ext_tuple + movie_ext_tuple # Check input exeptions if 'path_obj_dst' not in locals() : print( '\n Error : Too few arguments.' ) print( usage ) sys.exit(1) # Print header print( '-' * 50 + '\n' ) print( ' ' + str( args[0] ).replace( './', '' ) + '\n' ) print( ' Source dir : \n ' + str( path_obj_src ) ) print( ' Destination root dir : \n ' + str( path_obj_dst ) ) print( ' File type : \n ' + str( ext_tuple ) + '\n' ) print( '-' * 50 + '\n' ) # Get file list src_file_path_list = [] for file_ext in ext_tuple : src_file_path_list += list( path_obj_src.glob( '**/*.' + file_ext ) ) # Copy files to YYYY/MM/DD directory for src_file_path in src_file_path_list : # Get file create time as YYYY/MM/DD format file_name = src_file_path.name create_date = datetime.datetime.fromtimestamp( src_file_path.stat().st_ctime ).strftime( '%Y/%m/%d' ) date_dir = datetime.datetime.fromtimestamp( src_file_path.stat().st_ctime ).strftime( dir_format ) # Make YYYY/MM/DD directory if it does not exist path_obj_dst_date = path_obj_dst / date_dir if not path_obj_dst_date.exists() : path_obj_dst_date.mkdir(parents=True) print( '\n Make directory : {dir}\n'.format( dir = str( path_obj_dst_date ) ) ) # Skip copying if target file is already exist dst_file_path = path_obj_dst_date / file_name if dst_file_path.exists() : # Judge as 'same file' if target file size is same as source if dst_file_path.stat().st_size == src_file_path.stat().st_size : status = '...Skipped : Target file is already exist.' else : status = '...Skipped : Target file is already exist. (different size)' # Copy file with meta-data (copy2) else : shutil.copy2( src_file_path, dst_file_path ) status = '...Copied to ' + str( path_obj_dst_date ) # Print terminal message print( ' {filename} [{ctime}] {stat}'.format( \ filename = file_name, \ ctime = create_date, \ stat = status ) )
まとめと残課題
- とりあえず思った通りの取り込み動作の実装はできたので良かった
- shutil.copy2()がコケると処理が止まってしまうので例外処理とか必要っぽい
- 内部処理を関数化?オブジェクト化?して、直接呼び出しの時のみ引数処理をするような書き方がもっとPythonらしい気がする
- GooglePhotoにUploadするコマンドも作りたい
今後もマイペースに続けていきたいと思います。