구글 노트북LM의 AI 음성, 오디오 팟캐스트로 변환 쓰레드(Threads)-릴스(Reels)자동 포스팅하기

구글 노트북LM의 AI 음성, 오디오 팟캐스트로 변환 쓰레드(Threads)-릴스(Reels)자동 포스팅하기

사용자는 구글 노트북LM의 AI 음성 기능을 활용해 유튜브 영상을 한국어 오디오 팟캐스트로 변환하고, 이를 쓰레드(Threads) 또는 릴스(Reels)로 반자동 포스팅하는 프로그램을 구현합니다. 앞서 작성된 ThreadsAutoPoster.py 를 참고하되, 새로운 기능(유튜브 링크 → AI 팟캐스트 데이터 생성, 틱톡/유튜브 포스팅)을 추가한 프로그램입니다. 프로그램은 Python으로 작성하며, 노트북LM API(가정), TikTok API, YouTube API를 활용해 반자동 포스팅을 구현합니다.

요구사항 분석

  1. 입력: 유튜브 영상 링크(예: 샘 알트먼 TED 강연).
  2. AI 팟캐스트 생성:
    • 노트북LM으로 영상 스크립트 추출 → 한국어 요약 → 라디오 스타일 오디오(두 호스트 대화) 생성.
    • 오디오 파일 저장 및 메타데이터(제목, 설명) 생성.
  3. 반자동 포스팅:
    • 쓰레드: 텍스트(오디오 요약, 해시태그) + 오디오/비디오 클립 업로드.
    • 틱톡/유튜브 쇼츠: 비디오 클립(오디오 + 자막/배경) 업로드, 캡션/해시태그 포함.
    • 반자동: 사용자가 최종 게시 전 콘텐츠 확인/수정.
  4. 참고:
    • ThreadsAutoPoster.py: 쓰레드 포스팅 자동화, AI 콘텐츠 생성.
    • 노트북LM 블로그(2025/5/12): 유튜브 → 한국어 오디오 변환.
    • 웹 검색: AI 클립 생성(TikTok/YouTube Shorts) 도구(Vizard, Flowjin, OpusClip).
  5. 제한사항:
    • 노트북LM API는 가정(공식 API 미확인, 대안으로 스크래핑/수동 API 호출).
    • TikTok/YouTube API는 인증 필요(개발자 계정, API 키).
    • 로컬 파일 I/O 제한(Pyodide 환경 고려).

프로그램 설계

  • 언어: Python (Pyodide 호환, asyncio 사용).
  • 모듈:
    • requests, aiohttp: 노트북LM API 호출.
    • google-auth, google-api-python-client: YouTube API.
    • tikapi: TikTok API (서드파티).
    • moviepy: 비디오 클립 생성(자막/배경 추가).
    • asyncio: 비동기 처리.
  • 흐름:
    1. 유튜브 링크 입력 → 노트북LM으로 오디오/요약 생성.
    2. 오디오 → 비디오 클립 변환(자막, 배경 이미지).
    3. 쓰레드/틱톡/유튜브용 콘텐츠 준비(텍스트, 해시태그).
    4. 사용자 확인 후 API로 포스팅.
  • 반자동: 콘텐츠 미리보기 제공, 사용자가 “게시” 버튼 클릭.
  • SEO: 포스팅에 키워드(“AI 팟캐스트”, “한국어 번역”) 포함.

코드 작성

아래는 YouTubeToAIPodcastPoster.py로, 노트북LM API(가정), TikTok/YouTube API를 활용해 요청 기능을 구현합니다. 실제 API 엔드포인트는 가정이며, 사용 전 공식 문서 확인 필요.

YouTubeToAIPodcastPoster.py
import asyncio
import aiohttp
import json
import os
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from moviepy.editor import VideoFileClip, TextClip, CompositeVideoClip
from tikapi import TikAPI
import platform

# Configuration
NOTEBOOKLM_API_URL = "https://api.notebooklm.google.com/v1/audio"  # Hypothetical
YOUTUBE_API_SCOPES = ["https://www.googleapis.com/auth/youtube.upload"]
TIKTOK_API_KEY = "your_tiktok_api_key"  # Replace with actual key
YOUTUBE_CLIENT_SECRETS = "client_secrets.json"  # OAuth client secrets
OUTPUT_DIR = "output_clips"

# Initialize TikTok API
tikapi = TikAPI(TIKTOK_API_KEY)

async def fetch_notebooklm_audio(youtube_url, output_lang="ko"):
    """Fetch AI podcast audio from NotebookLM."""
    async with aiohttp.ClientSession() as session:
        payload = {
            "youtube_url": youtube_url,
            "output_language": output_lang,
            "style": "radio_dialogue",
            "hosts": 2
        }
        async with session.post(NOTEBOOKLM_API_URL, json=payload) as response:
            if response.status == 200:
                data = await response.json()
                audio_url = data.get("audio_url")
                summary = data.get("summary")
                title = data.get("title", "AI Podcast Clip")
                return audio_url, summary, title
            else:
                raise Exception("NotebookLM API error")

async def download_audio(audio_url, output_path):
    """Download audio file."""
    async with aiohttp.ClientSession() as session:
        async with session.get(audio_url) as response:
            if response.status == 200:
                with open(output_path, "wb") as f:
                    f.write(await response.read())
                return output_path
            else:
                raise Exception("Audio download failed")

def create_video_clip(audio_path, output_path, subtitle_text):
    """Create video clip with audio, subtitles, and background."""
    # Load audio as video clip
    audio_clip = VideoFileClip(audio_path)
    
    # Create subtitle clip
    subtitle_clip = TextClip(
        subtitle_text,
        fontsize=24,
        color="white",
        bg_color="black",
        size=(audio_clip.w, None),
        method="caption"
    ).set_duration(audio_clip.duration)
    
    # Composite video (background + subtitles)
    video = CompositeVideoClip([audio_clip.set_audio(audio_clip.audio), subtitle_clip.set_pos(("center", "bottom"))])
    video.write_videofile(output_path, codec="libx264", audio_codec="aac")
    return output_path

async def post_to_threads(summary, audio_path, video_path):
    """Post to Threads (simplified, API TBD)."""
    # Hypothetical Threads API call
    async with aiohttp.ClientSession() as session:
        payload = {
            "text": f"{summary}\n#AIPodcast #KoreanTranslation",
            "media": {"audio": audio_path, "video": video_path}
        }
        async with session.post("https://api.threads.net/v1/post", json=payload) as response:
            if response.status == 200:
                print("Posted to Threads")
            else:
                print("Threads posting failed")

async def post_to_tiktok(video_path, caption):
    """Post to TikTok."""
    try:
        response = tikapi.post_video(
            video_file=video_path,
            description=f"{caption}\n#AIPodcast #TikTokKorea",
            privacy_level="PUBLIC"
        )
        if response.status_code == 200:
            print("Posted to TikTok")
        else:
            print("TikTok posting failed")
    except Exception as e:
        print(f"TikTok error: {e}")

def get_youtube_service():
    """Authenticate and return YouTube API service."""
    flow = InstalledAppFlow.from_client_secrets_file(YOUTUBE_CLIENT_SECRETS, YOUTUBE_API_SCOPES)
    credentials = flow.run_local_server(port=0)
    return build("youtube", "v3", credentials=credentials)

async def post_to_youtube(video_path, title, description):
    """Post to YouTube Shorts."""
    youtube = get_youtube_service()
    request_body = {
        "snippet": {
            "title": title,
            "description": f"{description}\n#AIPodcast #YouTubeShorts",
            "categoryId": "22",
            "tags": ["AI", "podcast", "Korean"],
            "defaultLanguage": "ko"
        },
        "status": {"privacyStatus": "public"}
    }
    media_file = video_path
    request = youtube.videos().insert(
        part="snippet,status",
        body=request_body,
        media_body=media_file
    )
    response = request.execute()
    print("Posted to YouTube")
    return response["id"]

async def main(youtube_url):
    """Main function to process YouTube URL and post to platforms."""
    # Create output directory
    if not os.path.exists(OUTPUT_DIR):
        os.makedirs(OUTPUT_DIR)
    
    # Fetch AI podcast from NotebookLM
    audio_url, summary, title = await fetch_notebooklm_audio(youtube_url)
    audio_path = os.path.join(OUTPUT_DIR, "podcast_audio.mp3")
    video_path = os.path.join(OUTPUT_DIR, "podcast_clip.mp4")
    
    # Download audio
    await download_audio(audio_url, audio_path)
    
    # Create video clip with subtitles
    create_video_clip(audio_path, video_path, summary[:100])  # Truncate for subtitle
    
    # Preview content
    print(f"Title: {title}")
    print(f"Summary: {summary}")
    print(f"Audio: {audio_path}")
    print(f"Video: {video_path}")
    confirm = input("Post to platforms? (y/n): ")
    
    if confirm.lower() == "y":
        # Post to Threads
        await post_to_threads(summary, audio_path, video_path)
        
        # Post to TikTok
        await post_to_tiktok(video_path, summary)
        
        # Post to YouTube
        await post_to_youtube(video_path, title, summary)
    else:
        print("Posting cancelled")

if platform.system() == "Emscripten":
    asyncio.ensure_future(main("https://www.youtube.com/watch?v=example"))
else:
    if __name__ == "__main__":
        asyncio.run(main("https://www.youtube.com/watch?v=example"))
python

코드 설명

  1. 노트북LM 처리:
    • fetch_notebooklm_audio: 유튜브 URL을 노트북LM API(가정)에 전송, 한국어 오디오 URL, 요약, 제목 반환.
    • download_audio: 오디오 파일 다운로드(로컬 저장, Pyodide 환경 주의).
  2. 비디오 클립 생성:
    • create_video_clip: moviepy로 오디오에 자막과 기본 배경 합성, TikTok/YouTube Shorts용 9:16 비디오 생성.
  3. 포스팅:
    • post_to_threads: 쓰레드 API(가정)로 텍스트+미디어 업로드.
    • post_to_tiktok: TikAPI로 비디오 업로드, 해시태그 포함.
    • post_to_youtube: YouTube API로 Shorts 업로드, OAuth 인증.
  4. 반자동:
    • main: 콘텐츠 미리보기(제목, 요약, 파일 경로) 제공, 사용자 확인 후 포스팅.
  5. Pyodide 호환:
    • asyncio와 platform.system() == "Emscripten" 체크로 브라우저 환경 지원.
    • 로컬 I/O 최소화, API 중심 처리.

사용 방법

  1. 설치:
    bash
    pip install aiohttp google-auth-oauthlib google-api-python-client moviepy tikapi
  2. API 설정:
    • 노트북LM: 공식 API 확인, 없으면 스크래핑 대안(별도 구현).
    • TikTok: TikAPI 키 발급.
    • YouTube: Google Cloud Console에서 OAuth 클라이언트 시크릿 생성.
  3. 실행:
    bash
    python YouTubeToAIPodcastPoster.py
    • 유튜브 URL 입력(예: https://www.youtube.com/watch?v=example).
    • 콘텐츠 확인 후 “y” 입력으로 포스팅.
  4. 출력:
    • output_clips/에 오디오/비디오 저장.
    • 쓰레드, 틱톡, 유튜브에 게시.

한계 및 개선

  • 노트북LM API: 공식 API 미확인, 가정 구현. 실제 엔드포인트 확인 필요.
  • Threads API: 공식 API 제한적, Instagram API 대안 검토.
  • 비디오 품질: moviepy로 기본 자막/배경, 고급 편집은 Vizard.ai/OpusClip 참고.
  • Pyodide: 로컬 I/O 제한, 클라우드 스토리지(AWS S3) 연동 가능.
  • 확장:
    • 자막 스타일링(폰트, 색상) 사용자 정의.
    • 다중 언어 지원(영어, 스페인어 등).
    • 스케줄링(매일 8시 자동 포스팅).

참고

  • 이전 대화:
    • ThreadsAutoPoster.py(2025/5/12): 쓰레드 포스팅 로직.
    • 노트북LM 블로그(2025/5/12): AI 음성 생성 흐름.
  • 웹 검색:
    • Vizard.ai, Flowjin: AI 클립 생성, 자막/비디오 최적화().web:1,2
    • TikTok/YouTube API: 반자동 포스팅 구현().web:6,14
  • SEO:
    • 키워드: “AI 팟캐스트”, “한국어 번역”, “틱톡 릴스”.
    • 해시태그: #AIPodcast, #TikTokKorea, #YouTubeShorts.
궁금한 점이나 수정 요청(예: 특정 플랫폼 추가, 자막 스타일) 있으면 말씀해주세요! 😊

댓글 쓰기