AIRider's picture
Update app.py
bb03802 verified
raw
history blame
8.2 kB
import gradio as gr
from gradio_client import Client
import json
import logging
import ast
import openai
import os
import random
import re
logging.basicConfig(filename='youtube_script_extractor.log', level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s')
openai.api_key = os.getenv("OPENAI_API_KEY")
def parse_api_response(response):
try:
if isinstance(response, str):
response = ast.literal_eval(response)
if isinstance(response, list) and len(response) > 0:
response = response[0]
if not isinstance(response, dict):
raise ValueError(f"μ˜ˆμƒμΉ˜ λͺ»ν•œ 응닡 ν˜•μ‹μž…λ‹ˆλ‹€. 받은 데이터 νƒ€μž…: {type(response)}")
return response
except Exception as e:
raise ValueError(f"API 응닡 νŒŒμ‹± μ‹€νŒ¨: {str(e)}")
def split_sentences(text):
sentences = re.split(r"(λ‹ˆλ‹€|μ—μš”|κ΅¬λ‚˜|ν•΄μš”|κ΅°μš”|κ² μ–΄μš”|μ‹œμ˜€|해라|μ˜ˆμš”|μ•„μš”|λ°μš”|λŒ€μš”|μ„Έμš”|μ–΄μš”|κ²Œμš”|κ΅¬μš”|κ³ μš”|λ‚˜μš”|ν•˜μ£ )(?![\w])", text)
combined_sentences = []
current_sentence = ""
for i in range(0, len(sentences), 2):
if i + 1 < len(sentences):
sentence = sentences[i] + sentences[i + 1]
else:
sentence = sentences[i]
if len(current_sentence) + len(sentence) > 100:
combined_sentences.append(current_sentence.strip())
current_sentence = sentence.strip()
else:
current_sentence += sentence
if sentence.endswith(('.', '?', '!')):
combined_sentences.append(current_sentence.strip())
current_sentence = ""
if current_sentence:
combined_sentences.append(current_sentence.strip())
return combined_sentences
def get_youtube_script(url):
logging.info(f"슀크립트 μΆ”μΆœ μ‹œμž‘: URL = {url}")
client = Client("whispersound/YT_Ts_R")
try:
logging.debug("API 호좜 μ‹œμž‘")
result = client.predict(youtube_url=url, api_name="/predict")
logging.debug("API 호좜 μ™„λ£Œ")
parsed_result = parse_api_response(result)
title = parsed_result["data"][0]["title"]
transcription_text = parsed_result["data"][0]["transcriptionAsText"]
sections = parsed_result["data"][0]["sections"]
logging.info("슀크립트 μΆ”μΆœ μ™„λ£Œ")
return title, transcription_text, sections
except Exception as e:
error_msg = f"슀크립트 μΆ”μΆœ 쀑 였λ₯˜ λ°œμƒ: {str(e)}"
logging.exception(error_msg)
return "", "", []
def call_api(prompt, max_tokens, temperature, top_p):
try:
response = openai.ChatCompletion.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=max_tokens,
temperature=temperature,
top_p=top_p
)
return response['choices'][0]['message']['content']
except Exception as e:
logging.exception("LLM API 호좜 쀑 였λ₯˜ λ°œμƒ")
return "μš”μ•½μ„ μƒμ„±ν•˜λŠ” λ™μ•ˆ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. λ‚˜μ€‘μ— λ‹€μ‹œ μ‹œλ„ν•΄ μ£Όμ„Έμš”."
def summarize_section(section_text):
prompt = f"""
λ‹€μŒ 유튜브 λŒ€λ³Έ μ„Ήμ…˜μ˜ 핡심 λ‚΄μš©μ„ κ°„κ²°ν•˜κ²Œ μš”μ•½ν•˜μ„Έμš”:
1. ν•œκΈ€λ‘œ μž‘μ„±ν•˜μ„Έμš”.
2. μ£Όμš” 논점과 μ€‘μš”ν•œ 세뢀사항을 ν¬ν•¨ν•˜μ„Έμš”.
3. μš”μ•½μ€ 2-3λ¬Έμž₯으둜 μ œν•œν•˜μ„Έμš”.
μ„Ήμ…˜ λ‚΄μš©:
{section_text}
"""
return call_api(prompt, max_tokens=150, temperature=0.3, top_p=0.9)
def format_time(seconds):
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
return f"{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d}"
def generate_timeline_summary(sections):
timeline_summary = ""
for i, section in enumerate(sections, 1):
start_time = format_time(section['start_time'])
summary = summarize_section(section['text'])
timeline_summary += f"{start_time} {i}. {summary}\n\n"
return timeline_summary
def summarize_text(text):
prompt = f"""
1. λ‹€μŒ μ£Όμ–΄μ§€λŠ” 유튜브 λŒ€λ³Έμ˜ 핡심 μ£Όμ œμ™€ λͺ¨λ“  μ£Όμš” λ‚΄μš©μ„ μƒμ„Έν•˜κ²Œ μš”μ•½ν•˜λΌ
2. λ°˜λ“œμ‹œ ν•œκΈ€λ‘œ μž‘μ„±ν•˜λΌ
3. μš”μ•½λ¬Έλ§ŒμœΌλ‘œλ„ μ˜μƒμ„ 직접 μ‹œμ²­ν•œ 것과 λ™μΌν•œ μˆ˜μ€€μœΌλ‘œ λ‚΄μš©μ„ 이해할 수 μžˆλ„λ‘ μƒμ„Ένžˆ μž‘μ„±
4. 글을 λ„ˆλ¬΄ μ••μΆ•ν•˜κ±°λ‚˜ ν•¨μΆ•ν•˜μ§€ 말고, μ€‘μš”ν•œ λ‚΄μš©κ³Ό 세뢀사항을 λͺ¨λ‘ 포함
5. λ°˜λ“œμ‹œ λŒ€λ³Έμ˜ 흐름과 논리 ꡬ쑰λ₯Ό μœ μ§€
6. λ°˜λ“œμ‹œ μ‹œκ°„ μˆœμ„œλ‚˜ μ‚¬κ±΄μ˜ μ „κ°œ 과정을 λͺ…ν™•ν•˜κ²Œ 반영
7. λ“±μž₯인물, μž₯μ†Œ, 사건 λ“± μ€‘μš”ν•œ μš”μ†Œλ₯Ό μ •ν™•ν•˜κ²Œ μž‘μ„±
8. λŒ€λ³Έμ—μ„œ μ „λ‹¬ν•˜λŠ” κ°μ •μ΄λ‚˜ λΆ„μœ„κΈ°λ„ 포함
9. λ°˜λ“œμ‹œ 기술적 μš©μ–΄λ‚˜ μ „λ¬Έ μš©μ–΄κ°€ μžˆμ„ 경우, 이λ₯Ό μ •ν™•ν•˜κ²Œ μ‚¬μš©
10. λŒ€λ³Έμ˜ λͺ©μ μ΄λ‚˜ μ˜λ„λ₯Ό νŒŒμ•…ν•˜κ³ , 이λ₯Ό μš”μ•½μ— λ°˜λ“œμ‹œ 반영
11. 전체글을 보고
---
이 ν”„λ‘¬ν”„νŠΈκ°€ 도움이 λ˜μ‹œκΈΈ λ°”λžλ‹ˆλ‹€.
\n\n
{text}"""
try:
return call_api(prompt, max_tokens=10000, temperature=0.3, top_p=0.9)
except Exception as e:
logging.exception("μš”μ•½ 생성 쀑 였λ₯˜ λ°œμƒ")
return "μš”μ•½μ„ μƒμ„±ν•˜λŠ” λ™μ•ˆ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. λ‚˜μ€‘μ— λ‹€μ‹œ μ‹œλ„ν•΄ μ£Όμ„Έμš”."
with gr.Blocks() as demo:
gr.Markdown("## YouTube 슀크립트 μΆ”μΆœ 및 μš”μ•½ 도ꡬ")
youtube_url_input = gr.Textbox(label="YouTube URL μž…λ ₯")
analyze_button = gr.Button("λΆ„μ„ν•˜κΈ°")
script_output = gr.HTML(label="슀크립트")
timeline_output = gr.HTML(label="νƒ€μž„λΌμΈ μš”μ•½")
summary_output = gr.HTML(label="전체 μš”μ•½")
cached_data = gr.State({"url": "", "title": "", "script": "", "sections": []})
def extract_and_cache(url, cache):
if url == cache["url"]:
return cache["title"], cache["script"], cache["sections"], cache
title, script, sections = get_youtube_script(url)
new_cache = {"url": url, "title": title, "script": script, "sections": sections}
return title, script, sections, new_cache
def display_script(title, script):
if not script:
return "<p>슀크립트λ₯Ό μΆ”μΆœν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€. URL을 ν™•μΈν•˜κ³  λ‹€μ‹œ μ‹œλ„ν•΄ μ£Όμ„Έμš”.</p>"
formatted_script = "\n".join(split_sentences(script))
script_html = f"""<h2 style='font-size:24px;'>{title}</h2>
<details>
<summary><h3>원문 슀크립트 (ν΄λ¦­ν•˜μ—¬ 펼치기)</h3></summary>
<div style="white-space: pre-wrap;">{formatted_script}</div>
</details>"""
return script_html
def display_timeline(sections):
if not sections:
return "<p>νƒ€μž„λΌμΈμ„ μƒμ„±ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€. 슀크립트 μΆ”μΆœμ— μ‹€νŒ¨ν–ˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.</p>"
timeline_summary = generate_timeline_summary(sections)
timeline_html = f"""
<h3>νƒ€μž„λΌμΈ μš”μ•½:</h3>
<div style="white-space: pre-wrap; max-height: 400px; overflow-y: auto; border: 1px solid #ccc; padding: 10px;">
{timeline_summary}
</div>
"""
return timeline_html
def generate_summary(script):
if not script:
return "<p>전체 μš”μ•½μ„ μƒμ„±ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€. 슀크립트 μΆ”μΆœμ— μ‹€νŒ¨ν–ˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.</p>"
summary = summarize_text(script)
summary_html = f"""
<h3>전체 μš”μ•½:</h3>
<div style="white-space: pre-wrap; max-height: 400px; overflow-y: auto; border: 1px solid #ccc; padding: 10px;">
{summary}
</div>
"""
return summary_html
def analyze(url, cache):
title, script, sections, new_cache = extract_and_cache(url, cache)
script_html = display_script(title, script)
timeline_html = display_timeline(sections)
summary_html = generate_summary(script)
return script_html, timeline_html, summary_html, new_cache
analyze_button.click(
analyze,
inputs=[youtube_url_input, cached_data],
outputs=[script_output, timeline_output, summary_output, cached_data]
)
demo.launch(share=True)