【自作Pythonコード解説 YMM4編①】APIがないなら物理で殴れ!YMM4完全自動化&YouTube投稿ツール自作してみた

本ページはプロモーションが含まれています
国内のAI狂い

みんな〜!GeminiとPythonで世界をハックしてる〜!?国内のAI狂いだよ!🐍✨

最近さ、Gemini先生に台本を書いてもらうのはいいんだけど、「それをCSVファイルに整形して保存する」のが地味にめんどくさすぎない!?

クリップボードから貼り付けて、名前を付けて保存して、YMM4を開いて…ああもう!私はクリエイティブなことだけしたいの!単純作業は機械にやらせたいの!😭

というわけで、「APIがないなら物理(座標クリック)で操作すればいいじゃない」精神で、YMM4への台本流し込みからYouTube投稿までを自動化するPythonツールを作っちゃったよ!

今回はその全コードと、実際に動いている様子を公開しちゃうね!動けば正義!🚀

💡 実際の動作を見てみて!

百聞は一見に如かず。実際にPythonが私の代わりにマウスを動かしてYMM4を操作している様子がこちら!

※ 動画内のマウスカーソルはプログラムが自動で動かしています。

💡 このツールの仕組み(自動化フロー)

やってることは超シンプル!APIが公開されていないYMM4を操作するために、以下のステップをPythonで自動実行しているよ。

自動化の4ステップ
  1. クリップボード取得:Geminiが生成した台本テキストをPythonが吸い取る。
  2. CSV変換&保存:YMM4が読める「utf-8-sig」形式でCSVファイルを自動生成。
  3. YMM4物理操作:ソフトを起動し、指定した座標をクリックしてCSVを読み込ませる(ここが力技!)。
  4. 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しか勝たん!💖

よかったらシェアしてね!
  • URLをコピーしました!
目次