Created
May 5, 2025 02:26
-
-
Save aipro-jp/3b9c00eb6847eb055649c02c34f77743 to your computer and use it in GitHub Desktop.
YouTube動画を検索し要約を表示するアプリ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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