国内のAI狂い
みんな〜!GeminiとPythonで世界をハックしてる〜!?国内のAI狂いだよ!🐍✨
最近さ、Gemini先生に台本を書いてもらうのはいいんだけど、「それをCSVファイルに整形して保存する」のが地味にめんどくさすぎない!?
クリップボードから貼り付けて、名前を付けて保存して、YMM4を開いて…ああもう!私はクリエイティブなことだけしたいの!単純作業は機械にやらせたいの!😭
というわけで、「APIがないなら物理(座標クリック)で操作すればいいじゃない」精神で、YMM4への台本流し込みからYouTube投稿までを自動化するPythonツールを作っちゃったよ!
今回はその全コードと、実際に動いている様子を公開しちゃうね!動けば正義!🚀
💡 実際の動作を見てみて!
百聞は一見に如かず。実際にPythonが私の代わりにマウスを動かしてYMM4を操作している様子がこちら!
※ 動画内のマウスカーソルはプログラムが自動で動かしています。
💡 このツールの仕組み(自動化フロー)
やってることは超シンプル!APIが公開されていないYMM4を操作するために、以下のステップをPythonで自動実行しているよ。
自動化の4ステップ
- クリップボード取得:Geminiが生成した台本テキストをPythonが吸い取る。
- CSV変換&保存:YMM4が読める「utf-8-sig」形式でCSVファイルを自動生成。
- YMM4物理操作:ソフトを起動し、指定した座標をクリックしてCSVを読み込ませる(ここが力技!)。
- YouTube投稿:動画出力後、APIを使ってタイトル入力だけで自動アップロード&サムネ設定完了!
💡 【コピペOK】全ソースコード公開
これが実際に私が使っているコードの全貌だよ!
pyautogui で座標をクリックしたり、google-api でYouTubeに接続したり、自分用ツールとしては最強に便利!
【Python】AIラジオ動画自動化.py 全コードを表示
# import pyperclip
import pandas as pd
import os
import time
import pyautogui
import pygetwindow as gw
import glob
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
# --- 設定:パスと定数 ---
BASE_DIR = r"C:\Users\AIGURUI\Desktop\AIラジオ"
CSV_FILENAME = "AIラジオ台本.csv" # CSV名は共通でOK
CSV_FULL_PATH = os.path.join(BASE_DIR, CSV_FILENAME)
# ★テンプレートファイルのパス設定
YMMP_LONG = r"C:\Users\AIGURUI\Desktop\AIラジオ\AI海外の反応ラジオテンプレ.ymmp"
YMMP_SHORT = r"C:\Users\AIGURUI\Desktop\AIラジオ\ショートAI海外の反応ラジオテンプレ.ymmp"
# ★投稿時のファイル名設定
VIDEO_NAME_LONG = "ロング.mp4"
VIDEO_NAME_SHORT = "ショート.mp4"
# 投稿アセットフォルダ
ASSET_DIR = r"C:\Users\AIGURUI\Desktop\AIラジオ\投稿アセット"
# 座標設定 (ご自身の環境に合わせて微調整してください)
COORD_SCRIPT_OPEN = (1506, 95) # ファイル(開く)
COORD_TIMELINE_ADD = (1698, 972) # タイムラインに追加
# 待機時間
WAIT_LAUNCH = 10 # YMM4起動待ち
WAIT_UI = 1.0 # クリック間の待ち時間
# --- YouTube投稿設定 ---
os.chdir(BASE_DIR) # 作業フォルダを強制移動
CLIENT_SECRET_FILE = "client_secret.json"
SCOPES = ["https://www.googleapis.com/auth/youtube.upload"]
# 動画タグ
VIDEO_TAGS = ["AI速報", "AIニュース", "海外の反応", "AI", "技術", "VoiceVox"]
# 概要欄テンプレート
DESCRIPTION_TEMPLATE = """ブログ
https://orz-0rz.blog/
X
https://x.com/ai_gurui
みんな、おはよー!国内のAI狂いだよ💖
AIとテクノロジーの最新情報、今回もガッツリお届けしちゃうよ!
#AI速報 #AIニュース #海外の反応
音声読み上げソフト
VOICEVOX:ずんだもん
VOICEVOX:四国めたん
VOICEVOX:ナースロボ_タイプT
VOICEVOX:猫使ビィ
VOICEVOX:春日部つむぎ
BGM:MusMus"""
# ------------------------------
def get_authenticated_service():
creds = None
if os.path.exists("token.json"):
creds = Credentials.from_authorized_user_file("token.json", SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRET_FILE, SCOPES)
creds = flow.run_local_server(port=0)
with open("token.json", "w") as token:
token.write(creds.to_json())
return build("youtube", "v3", credentials=creds)
def upload_video_to_youtube(video_path, thumbnail_path, title):
youtube = get_authenticated_service()
# 1. 動画のアップロード
print(f"動画をアップロード中...: {os.path.basename(video_path)}")
body = {
"snippet": {
"title": title,
"description": DESCRIPTION_TEMPLATE,
"tags": VIDEO_TAGS,
"categoryId": "22",
},
"status": {
"privacyStatus": "public", # 公開
"selfDeclaredMadeForKids": False,
},
}
media = MediaFileUpload(video_path, chunksize=-1, resumable=True)
request = youtube.videos().insert(
part=",".join(body.keys()), body=body, media_body=media
)
response = request.execute()
video_id = response["id"]
print(f"動画アップロード完了! ID: {video_id}")
# 2. サムネイルの設定 (ファイルがある場合のみ)
if thumbnail_path:
print(f"サムネイルを設定中...: {os.path.basename(thumbnail_path)}")
try:
youtube.thumbnails().set(
videoId=video_id, media_body=MediaFileUpload(thumbnail_path)
).execute()
print("サムネイル設定完了!")
except Exception as e:
print(
f"サムネイル設定エラー(ショート動画等は反映されない場合があります): {e}"
)
print("-" * 30)
print(f"投稿成功! YouTube Studioで確認してください。\nhttps://youtu.be/{video_id}")
# 3. サムネイル画像の削除
if thumbnail_path and os.path.exists(thumbnail_path):
try:
os.remove(thumbnail_path)
print(
f"ゴミ箱へポイッ!サムネイル画像を削除しました: {os.path.basename(thumbnail_path)}"
)
except Exception as e:
print(f"サムネイルの削除に失敗しました: {e}")
def wait_for_ymm_window(timeout=60):
"""YMM4のウィンドウが表示されるまでループして待機します"""
print("YMM4の起動完了を監視中...")
start_time = time.time()
while time.time() - start_time < timeout:
# タイトルに「ゆっくりMovieMaker」を含むウィンドウを探す
windows = gw.getWindowsWithTitle("ゆっくりMovieMaker")
if windows:
target_window = windows[0]
print(f"✅ YMM4起動確認!: {target_window.title}")
# ウィンドウをアクティブにする(操作を確実にするため)
try:
if not target_window.isActive:
target_window.activate()
except Exception:
pass
# ウィンドウ枠が見えてから中身(ボタン等)が描画されるまでの予備待機
# ※ここが重要!ウィンドウが出た直後はまだボタンが押せないことがあるため
# ウィンドウ枠が見えてから中身(ボタン等)が描画されるまでの予備待機
# ★修正: プロジェクトファイルの読み込み完了までしっかり待つ(15秒)
print(
"プロジェクト読み込み待機中(15秒)... PCの重さによって調整してください"
)
time.sleep(15.0)
return True
time.sleep(1)
print("⚠️ タイムアウト: YMM4のウィンドウが検知できませんでした。")
return False
def main():
# === 0. モード選択 ===
print("=" * 40)
print("【 AIラジオ 動画生成&投稿ツール 】")
print(" L: ロング動画モード (AI海外の反応ラジオテンプレ.ymmp / ロング.mp4)")
print(
" S: ショート動画モード (ショートAI海外の反応ラジオテンプレ.ymmp / ショート.mp4)"
)
print("=" * 40)
while True:
mode = input("モードを選択してください (l/s) > ").lower().strip()
if mode == "l":
target_ymmp = YMMP_LONG
target_video_name = VIDEO_NAME_LONG
print("★ [ロングモード] で開始します。")
break
elif mode == "s":
target_ymmp = YMMP_SHORT
target_video_name = VIDEO_NAME_SHORT
print("★ [ショートモード] で開始します。")
break
# === 1. クリップボードからCSVを作成 ===
text = pyperclip.paste()
if not text:
print("エラー: クリップボードが空です")
return
print("テキストを取得しました。CSVを作成中...")
lines = [line for line in text.splitlines() if line.strip() != ""]
clean_text = "\n".join(lines)
try:
with open(CSV_FULL_PATH, "w", encoding="utf-8-sig") as f:
f.write(clean_text)
print(f"CSV保存完了: {CSV_FULL_PATH}")
except Exception as e:
print(f"保存エラー: {e}")
return
# CSVパスをクリップボードにコピー
pyperclip.copy(CSV_FULL_PATH)
# === 2. YMM4起動 (選択したテンプレート) ===
print(f"YMM4を起動しています...: {os.path.basename(target_ymmp)}")
if os.path.exists(target_ymmp):
os.startfile(target_ymmp)
else:
print(f"エラー: プロジェクトファイルが見つかりません\n{target_ymmp}")
return
# 固定待機(sleep)をやめて、ウィンドウ検知+予備待機に変更
wait_for_ymm_window()
# === 3. マウス操作とファイル読み込み ===
# === 3. マウス操作とファイル読み込み ===
print("自動操作を開始します...")
# (1) 台本編集の「ファイル(開く)」をクリック
# (1) 台本編集の「ファイル(開く)」をクリック
pyautogui.click(COORD_SCRIPT_OPEN)
print("ダイアログ表示待機中(3秒)...")
time.sleep(3.0) # ★修正: ダイアログが確実に開くまで待つ
# (2) パスを貼り付け
# (2) パスを貼り付け
print("ファイルパスを入力中...")
pyautogui.hotkey("ctrl", "v")
time.sleep(0.5)
pyautogui.press("enter")
print("台本の読み込みを待機中(3秒)...")
time.sleep(3.0) # 読み込み待ち
# (3) 「タイムラインに追加」ボタンをクリック
print("タイムラインに追加ボタンをクリック")
pyautogui.click(COORD_TIMELINE_ADD)
# UIの反応待ち
time.sleep(2.0)
# === 4. YouTube投稿フェーズ ===
print("=" * 40)
print("【YMM4自動化完了】")
print(f"★現在は [{ 'ロング' if mode=='l' else 'ショート' }] モードです。")
print("動画の編集と出力を手動で行い、以下のフォルダにファイルを配置してください。")
print(f"対象フォルダ: {ASSET_DIR}")
print(f"・動画ファイル: {target_video_name}")
print("・サムネイル画像: *.png (あれば)")
print("=" * 40)
# ユーザー入力待ち
while True:
user_title = input(
"\n動画タイトルを入力してください(入力すると投稿開始 / qで終了):\n> "
)
if user_title.lower() == "q":
print("終了します。")
return
if not user_title:
continue
# 動画ファイルを探す(指定の名前のみ)
target_video_path = os.path.join(ASSET_DIR, target_video_name)
# サムネイルを探す(ランダムな名前のPNGを1つ取得)
png_files = glob.glob(os.path.join(ASSET_DIR, "*.png"))
target_thumb_path = png_files[0] if png_files else None
# チェック
if not os.path.exists(target_video_path):
print(f"エラー: 動画ファイルが見つかりません。")
print(f"期待しているファイル名: {target_video_name}")
print(f"場所: {ASSET_DIR}")
continue
# サムネがない場合の警告(ロングモードのみ厳しめに警告してもいいが、今回は続行可能にする)
if not target_thumb_path:
print("注意: PNG画像が見つかりません。サムネイルなしで投稿します。")
# タイトル加工
final_title = f"{user_title} -超訳‼えーあい速報"
print(f"投稿を開始します: {final_title}")
print(f"動画: {os.path.basename(target_video_path)}")
if target_thumb_path:
print(f"サムネ: {os.path.basename(target_thumb_path)}")
else:
print("サムネ: なし")
try:
upload_video_to_youtube(target_video_path, target_thumb_path, final_title)
break # 投稿完了したら終了
except Exception as e:
print(f"投稿エラー: {e}")
print("再試行する場合はもう一度タイトルを入力してください。")
if __name__ == "__main__":
pyautogui.FAILSAFE = True
main()
国内のAI狂い
💡 管理人の考察
「面倒くさい」は発明の母!
最初は「APIがないから無理かな…」って思ったけど、座標をクリックするという超アナログな方法で解決できちゃった。
このツールのおかげで、私はGeminiと会話することだけに集中できるようになったよ!
みんなもPythonで、日々の単純作業を「物理」で殴り飛ばしてみてね!それじゃあ、また次回の記事で!Pythonしか勝たん!💖





