Skip to content

Instantly share code, notes, and snippets.

@aipro-jp
Created May 5, 2025 02:26
Show Gist options
  • Save aipro-jp/3b9c00eb6847eb055649c02c34f77743 to your computer and use it in GitHub Desktop.
Save aipro-jp/3b9c00eb6847eb055649c02c34f77743 to your computer and use it in GitHub Desktop.
YouTube動画を検索し要約を表示するアプリ
import os
import re
import sys
# --- 外部関数のインポート ---
# 実際のプロジェクト構成に合わせてパスを調整してください
# 例: sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'libs')))
try:
from youtube_search import search_youtube_videos
from youtube_transcript import get_transcript
from gemini_summarize import summarize_text
except ImportError as e:
print(f"必要なモジュールのインポートに失敗しました: {e}")
print("youtube_search.py, youtube_transcript.py, gemini_summarize.py が同じディレクトリにあるか確認してください。")
sys.exit(1)
# --- 定数 ---
MAX_SEARCH_RESULTS = 10
MAX_SUMMARY_CHARS = 1000
def sanitize_filename(filename):
"""ファイル名として使用できない文字を除去または置換する"""
# OSで禁止されている文字を除去 (WindowsとUnix系を考慮)
sanitized = re.sub(r'[\\/*?:"<>|]', "", filename)
# コントロール文字を除去
sanitized = re.sub(r'[\x00-\x1f\x7f]', '', sanitized)
# 先頭や末尾のスペースやピリオドを除去 (Windows)
sanitized = sanitized.strip(' .')
# ファイル名が空になったり、予約名になった場合のデフォルト名
if not sanitized or sanitized.upper() in ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9']:
sanitized = "untitled_summary"
# 長すぎるファイル名を切り詰める(オプション)
# max_len = 200 # 例
# if len(sanitized) > max_len:
# sanitized = sanitized[:max_len]
return sanitized
def main():
"""メイン処理"""
# 1. 検索キーワードの入力
query = input("検索したいキーワードを入力してください: ")
if not query:
print("キーワードが入力されていません。処理を終了します。")
return
# 2. 動画検索
print(f"\n'{query}' でYouTube動画を検索しています (最大{MAX_SEARCH_RESULTS}件)...")
try:
# search_youtube_videos は [{'id': 'videoId', 'title': 'videoTitle'}, ...] のようなリストを返すと仮定
videos = search_youtube_videos(query, max_results=MAX_SEARCH_RESULTS)
except Exception as e:
print(f"動画検索中にエラーが発生しました: {e}")
return
if not videos:
print("検索結果が見つかりませんでした。")
return
# 3. 検索結果の表示
print("\n--- 検索結果 ---")
for i, video in enumerate(videos):
print(f"{i + 1}. {video.get('title', 'タイトルなし')} (ID: {video.get('id', 'IDなし')})")
print("----------------")
# 4. 要約する動画の選択
selected_video = None
while True:
try:
choice = input(f"\n要約したい動画の番号を入力してください (1-{len(videos)}): ")
choice_index = int(choice) - 1
if 0 <= choice_index < len(videos):
selected_video = videos[choice_index]
break
else:
print(f"無効な番号です。1から{len(videos)}の間で入力してください。")
except ValueError:
print("数値を入力してください。")
except KeyboardInterrupt:
print("\n処理を中断しました。")
return
video_id = selected_video.get('id')
video_title = selected_video.get('title', 'タイトルなし')
print(f"\n動画 '{video_title}' (ID: {video_id}) を選択しました。")
# 5. 文字起こしの取得
print("文字起こしを取得しています...")
try:
transcript = get_transcript(video_id)
if transcript is None or transcript.strip() == "":
print(f"動画 (ID: {video_id}) の文字起こしを取得できませんでした。字幕がないか、取得に失敗した可能性があります。")
return
print("文字起こしを取得しました。")
except Exception as e:
print(f"文字起こし取得中にエラーが発生しました: {e}")
return
# 6. テキストの要約
print(f"文字起こしを要約しています (最大{MAX_SUMMARY_CHARS}文字)...")
try:
summary = summarize_text(transcript, max_chars=MAX_SUMMARY_CHARS)
if not summary:
print("要約の生成に失敗しました。")
return
print("要約が完了しました。")
except Exception as e:
print(f"要約中にエラーが発生しました: {e}")
return
# 7. 結果の表示
print("\n--- 要約結果 ---")
print(summary)
print("--- 要約結果ここまで ---")
# 8. Markdownファイルへの保存
safe_title = sanitize_filename(video_title)
output_filename = f"{safe_title}_summary.md"
# スクリプトのあるディレクトリに保存する場合
script_dir = os.path.dirname(os.path.abspath(__file__))
output_filepath = os.path.join(script_dir, output_filename)
# カレントディレクトリに保存する場合
# output_filepath = os.path.abspath(output_filename)
print(f"\n要約をMarkdownファイルとして保存しています...")
try:
with open(output_filepath, "w", encoding="utf-8") as f:
f.write(f"# {video_title} の要約\n\n")
f.write(f"**動画URL:** https://www.youtube.com/watch?v={video_id}\n\n")
f.write("---\n\n")
f.write(summary)
print(f"ファイルに保存しました: {output_filepath}") # 絶対パスで表示
except IOError as e:
print(f"ファイル保存中にエラーが発生しました: {e}")
except Exception as e:
print(f"予期せぬエラーが発生しました: {e}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment