カット作業を自動化するプログラム
プログラムの方針
不要な部分を定義→必要な部分だけを抽出→結合
ffmpegのインストールと環境パス設定が必要です。
chatGPT(無料)に「ffmpegのインストールと環境パス設定を教えて」と言えば5分以内で完了します。 https://chatgpt.com/
import subprocess
from moviepy.editor import VideoFileClip
import soundfile as sf
import numpy as np
from matplotlib import pyplot as plt
import os
import shutil
import time
# 動画のパスを挿入(フォルダ名は英語推奨)
inputVideo_path = "元動画のパス"
outputVideo_path = "出力先のファイルパス"
#〇以下の音を無音とする
thres = 0.25
#〇秒以上無音が続けばカット
min_silence_duration = 0.2
# 無音でない〇秒以下の音は全てカット(咳払いなど一瞬の音)
min_keep_duration = 0.2
# カットの前後〇秒を残す
padding_time = 0.05
ここから先のコードを変更する必要はありません。
ffmpegの都合により日本語のフォルダ名はエラーが発生します。
もしデスクトップなどで動画を実行する場合は移動する必要があります。
#元動画と同じフォルダに作業フォルダを作成(後に削除)
out_dir = os.path.join(os.path.dirname(inputVideo_path), "{}".format(int(time.time())))
os.mkdir(out_dir)
# 動画から音声を抽出
video = VideoFileClip(inputVideo_path)
audio_path = out_dir+"temp_audio.wav"
video.audio.write_audiofile(audio_path)
# 音声ファイル読込
data, samplerate = sf.read(audio_path)
#〇秒以上無音の箇所を探す
amp = np.abs(data)
b = amp > thres
#〇秒以上無音の箇所を抽出
silences = []
prev = 0
entered = 0
for i, v in enumerate(b):
if prev == 1 and v[0] == 0: # enter silence
entered = i
if prev == 0 and v[0] == 1: # exit silence
duration = (i - entered) / samplerate
if duration > min_silence_duration:
silences.append({"from": entered, "to": i, "suffix": "cut"})
entered = 0
prev = v[0]
if entered > 0 and entered < len(b):
silences.append({"from": entered, "to": len(b), "suffix": "cut"})
#全体から無音部分とノイズ部分(瞬間的な音)を除いて残す箇所を抽出
keep_blocks = []
for i, block in enumerate(cut_blocks):
if i == 0 and block["from"] > 0:
keep_blocks.append({"from": 0, "to": block["from"], "suffix": "keep"})
if i > 0:
prev = cut_blocks[i - 1]
keep_blocks.append({"from": prev["to"], "to": block["from"], "suffix": "keep"})
if i == len(cut_blocks) - 1 and block["to"] < len(data):
keep_blocks.append({"from": block["to"], "to": len(data), "suffix": "keep"})
#複数のファイルパスを記入したtxtファイルを作成
file_list_path = os.path.join(out_dir, "file_list.txt")
# カットの前後〇秒を残す
with open(file_list_path, 'w') as file_list:
for i, block in enumerate(keep_blocks):
fr = max(block["from"] / samplerate - padding_time, 0)
to = min(block["to"] / samplerate + padding_time, len(data) / samplerate)
duration = to - fr
out_path = os.path.join(out_dir, "{:2d}_{}.mov".format(i, block["suffix"]))
# ffmpegコマンドを実行
# subprocess.run([
# 'ffmpeg', '-ss', str(fr), '-i', inputVideo_path, '-t', str(duration), out_path
# ])
subprocess.run([
'ffmpeg', '-ss', str(fr), '-i', inputVideo_path, '-t', str(duration), out_path
], check=True)
file_list.write("file '{}'\n".format(out_path))
subprocess.run([
'ffmpeg', '-f', 'concat', '-safe', '0', '-i', file_list_path, '-c', 'copy', outputVideo_path
])
# 一時保存フォルダと音声ファイルを削除
shutil.rmtree(out_dir)
os.unlink(audio_path)