mov2mp4.py (6353B)
1 2 import glob 3 import json 4 import os 5 import subprocess 6 7 8 def get_video_info(file_path): 9 cmd = [ 10 "ffprobe", 11 "-v", 12 "error", 13 "-select_streams", 14 "v:0", 15 "-show_entries", 16 "stream=codec_name,bit_rate", 17 "-show_entries", 18 "format=bit_rate", 19 "-of", 20 "json", 21 file_path, 22 ] 23 try: 24 result = subprocess.run( 25 cmd, capture_output=True, text=True, check=True, encoding="utf-8" 26 ) 27 data = json.loads(result.stdout) 28 29 video_stream = data.get("streams", [{}])[0] 30 codec_name = video_stream.get("codec_name", "unknown") 31 32 bitrate = video_stream.get("bit_rate") 33 if not bitrate or bitrate == "N/A": 34 bitrate = data.get("format", {}).get("bit_rate") 35 36 return codec_name, int(bitrate) if bitrate and bitrate != "N/A" else None 37 except Exception as e: 38 print(f" ⚠️ 動画情報の取得に失敗しました: {e}") 39 return "unknown", None 40 41 42 def convert_mov_to_mp4_h265_nvenc(input_dir, output_dir=None): 43 if not os.path.isdir(input_dir): 44 print(f"エラー: 入力フォルダー '{input_dir}' が見つかりません。") 45 return 46 47 if output_dir is None: 48 output_dir = input_dir 49 50 if not os.path.exists(output_dir): 51 os.makedirs(output_dir) 52 print(f"出力フォルダー '{output_dir}' を作成しました。") 53 54 video_files = [] 55 # 拡張子の大文字小文字を網羅するため、globの検索パターンを修正 56 for ext in ["*.mov", "*.MOV", "*.mp4", "*.MP4", "*.mkv", "*.MKV"]: 57 video_files.extend(glob.glob(os.path.join(input_dir, ext))) 58 59 if not video_files: 60 print(f"'{input_dir}' 内に動画ファイルが見つかりませんでした。") 61 return 62 63 # 重複してヒットする可能性を排除 64 video_files = sorted(list(set(os.path.abspath(f) for f in video_files))) 65 print(f"--- 変換処理を開始します (合計 {len(video_files)} ファイル) ---") 66 67 converted_count = 0 68 BITRATE_THRESHOLD = 1_000_000 69 70 for input_file_path in video_files: 71 file_name_without_ext, ext = os.path.splitext(os.path.basename(input_file_path)) 72 73 is_mp4_input = ext.lower() == ".mp4" 74 if is_mp4_input: 75 output_file_path = os.path.join( 76 output_dir, file_name_without_ext + "-hevc.mp4" 77 ) 78 else: 79 output_file_path = os.path.join(output_dir, file_name_without_ext + ".mp4") 80 81 output_file_path = os.path.abspath(output_file_path) 82 83 print(f"\n🔍 解析中: '{os.path.basename(input_file_path)}' ...") 84 codec, bitrate = get_video_info(input_file_path) 85 86 if codec in ["hevc", "h265"]: 87 print(f" ⏭️ スキップ: 既に HEVC コーデックです。") 88 continue 89 90 if bitrate and bitrate < BITRATE_THRESHOLD: 91 print( 92 f" ⏭️ スキップ: ビットレートが低いため変換のメリットがありません ({bitrate / 1_000_000:.2f} Mbps < {BITRATE_THRESHOLD / 1_000_000} Mbps)" 93 ) 94 continue 95 96 final_output_path = os.path.abspath(os.path.join(output_dir, file_name_without_ext + ".mp4")) 97 98 # Windowsの仕様に合わせ、比較時にすべて小文字化して判定する 99 if os.path.exists(final_output_path) and final_output_path.lower() != input_file_path.lower(): 100 print(f"\n❌ エラー: 出力予定のファイル '{final_output_path}' が既に存在します。") 101 print("既存のファイルを上書きしないよう、このファイルの処理をスキップします。") 102 continue 103 104 ffmpeg_command = [ 105 "ffmpeg", 106 "-i", 107 input_file_path, 108 "-c:v", 109 "hevc_nvenc", 110 "-cq", 111 "30", 112 output_file_path, 113 ] 114 115 print(f"\n✅ 変換中: '{os.path.basename(input_file_path)}' -> '{os.path.basename(output_file_path)}'") 116 117 try: 118 subprocess.run( 119 ffmpeg_command, 120 check=True, 121 stdout=subprocess.PIPE, 122 stderr=subprocess.PIPE, 123 encoding="utf-8", 124 ) 125 print(" ✨ 変換成功。") 126 127 input_size = os.path.getsize(input_file_path) 128 output_size = os.path.getsize(output_file_path) 129 130 THRESHOLD = 0.9 131 ratio = output_size / input_size if input_size > 0 else 1 132 133 if ratio < THRESHOLD: 134 os.remove(input_file_path) 135 136 # すでに小文字のfinal_output_pathが存在し、それが大文字のinput_file_pathであった場合は 137 # os.removeで消えているため、安全にrename可能 138 if is_mp4_input: 139 os.rename(output_file_path, final_output_path) 140 141 print(f" 🗑️ サイズが十分に削減されたため、元ファイルを整理しました。") 142 print(f" ({input_size:,} bytes -> {output_size:,} bytes, 削減率: {1 - ratio:.1%})") 143 converted_count += 1 144 else: 145 os.remove(output_file_path) 146 print(f" ⚠️ サイズ削減が不十分なため、変換後ファイルを破棄し元ファイルを維持します。") 147 print(f" (比率: {ratio:.1%} / 閾値: {THRESHOLD:.1%})") 148 149 except subprocess.CalledProcessError as e: 150 print(f" ❌ エラーが発生しました: '{os.path.basename(input_file_path)}'") 151 print(f" FFmpeg出力:\n{e.stderr}...") 152 except FileNotFoundError: 153 print(" ❌ エラー: FFmpegがインストールされていないか、PATHが通っていません。") 154 return 155 156 print(f"\n--- 処理完了: {converted_count} 個のファイルが正常に変換されました ---") 157 158 159 INPUT_FOLDER = r"E:\Pictures\他人作品\ホロ\temp" 160 OUTPUT_FOLDER = None 161 162 if __name__ == "__main__": 163 convert_mov_to_mp4_h265_nvenc(INPUT_FOLDER, OUTPUT_FOLDER)