Add tabs for input, audio input
Browse files
app.py
CHANGED
|
@@ -537,7 +537,7 @@ def hash_file(filepath):
|
|
| 537 |
return hashlib.md5(f.read(1024*1024)).hexdigest()
|
| 538 |
|
| 539 |
@spaces.GPU(duration=120)
|
| 540 |
-
def process_pdf(pdf_file, url_input, yt_url_input, yt_cookies_file, translit_lang, translit_format, target_lang, max_text_char, repetition_penalty_val, last_source_hash, last_korean_words, progress=gr.Progress()):
|
| 541 |
global tts, voice_style
|
| 542 |
|
| 543 |
# Clean language choices from "Family - Language" to just "Language"
|
|
@@ -548,14 +548,20 @@ def process_pdf(pdf_file, url_input, yt_url_input, yt_cookies_file, translit_lan
|
|
| 548 |
|
| 549 |
os.makedirs("log", exist_ok=True)
|
| 550 |
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 555 |
return
|
| 556 |
|
| 557 |
if is_youtube:
|
| 558 |
current_source_hash = hashlib.md5(yt_url_input.strip().encode()).hexdigest()
|
|
|
|
|
|
|
| 559 |
elif is_url:
|
| 560 |
current_source_hash = hashlib.md5(url_input.strip().encode()).hexdigest()
|
| 561 |
else:
|
|
@@ -564,16 +570,6 @@ def process_pdf(pdf_file, url_input, yt_url_input, yt_cookies_file, translit_lan
|
|
| 564 |
vocab_list = []
|
| 565 |
extracted_audio_path = None
|
| 566 |
|
| 567 |
-
# if last_source_hash == current_source_hash and last_korean_words:
|
| 568 |
-
# # Just run text-to-text LLM
|
| 569 |
-
# progress(0.2, desc="Translating previously extracted vocabulary...")
|
| 570 |
-
# korean_words = [item.get("korean") for item in last_korean_words if item.get("korean")]
|
| 571 |
-
# for attempt in range(1, 4):
|
| 572 |
-
# vocab_list = translate_vocabulary(korean_words, translit_lang, translit_format, target_lang, repetition_penalty_val)
|
| 573 |
-
# if vocab_list:
|
| 574 |
-
# break
|
| 575 |
-
# else:
|
| 576 |
-
|
| 577 |
try:
|
| 578 |
if is_youtube:
|
| 579 |
progress(0, desc="Downloading YouTube audio (first 5 min)...")
|
|
@@ -592,6 +588,18 @@ def process_pdf(pdf_file, url_input, yt_url_input, yt_cookies_file, translit_lan
|
|
| 592 |
# Log the transcription
|
| 593 |
with open("log/debug_yt_transcription.txt", "w", encoding="utf-8") as f:
|
| 594 |
f.write(f"Title: {yt_title}\n\n{content_text}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 595 |
elif is_url:
|
| 596 |
progress(0, desc="Fetching Website...")
|
| 597 |
content_text, images = extract_website_content(url_input.strip())
|
|
@@ -980,6 +988,16 @@ def get_example_pdf():
|
|
| 980 |
print(f"Failed to download example PDF: {e}")
|
| 981 |
return file_path if os.path.exists(file_path) else None
|
| 982 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 983 |
@spaces.GPU(duration=120)
|
| 984 |
def process_pdf_force(partial_text, pdf_file, url_input, translit_lang, translit_format, target_lang, max_text_char, repetition_penalty_val, last_source_state, last_korean_words_state):
|
| 985 |
"""Force JSON generation using the current partial stream_box text."""
|
|
@@ -1062,6 +1080,7 @@ def process_pdf_force(partial_text, pdf_file, url_input, translit_lang, translit
|
|
| 1062 |
|
| 1063 |
def create_demo():
|
| 1064 |
example_pdf = get_example_pdf()
|
|
|
|
| 1065 |
|
| 1066 |
custom_theme = gr.themes.Soft(
|
| 1067 |
primary_hue="violet",
|
|
@@ -1168,27 +1187,39 @@ def create_demo():
|
|
| 1168 |
|
| 1169 |
with gr.Blocks(title="LocalDuo", theme=custom_theme, css=css) as demo:
|
| 1170 |
gr.Markdown("# π°π·β¨ LocalDuo - Learn Korean from PDFs, Websites & YouTube")
|
| 1171 |
-
gr.Markdown("Enter a website URL π, a
|
|
|
|
|
|
|
| 1172 |
|
| 1173 |
with gr.Row():
|
| 1174 |
with gr.Column(scale=1):
|
| 1175 |
-
|
| 1176 |
-
|
| 1177 |
-
|
| 1178 |
-
|
| 1179 |
-
|
| 1180 |
-
|
| 1181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1182 |
|
| 1183 |
-
|
| 1184 |
-
|
| 1185 |
-
|
| 1186 |
-
|
| 1187 |
-
|
| 1188 |
-
type="filepath")
|
| 1189 |
-
gr.Markdown("*Optional. Helps bypass YouTube bot detection. Install the [cookies.txt](https://addons.mozilla.org/firefox/addon/cookies-txt/) extension, go to youtube.com while logged in, click the extension β 'Current Site' to export.*", elem_classes=["hint-text"])
|
| 1190 |
-
|
| 1191 |
-
pdf_input = gr.File(label="Or Upload Book PDF π", file_types=[".pdf"], value=example_pdf)
|
| 1192 |
|
| 1193 |
gr.Markdown("### βοΈ Customization Settings")
|
| 1194 |
translit_lang = gr.Dropdown(
|
|
@@ -1217,7 +1248,7 @@ def create_demo():
|
|
| 1217 |
with gr.Accordion("π Extracted Source Content", open=True):
|
| 1218 |
extracted_text_box = gr.Textbox(label="Extracted Text", lines=10, max_lines=15, interactive=False)
|
| 1219 |
extracted_images_gallery = gr.Gallery(label="Extracted Images", columns=4, height="auto", object_fit="contain")
|
| 1220 |
-
extracted_audio_player = gr.Audio(label="Extracted Audio (YouTube)", type="filepath", interactive=False)
|
| 1221 |
|
| 1222 |
last_source_state = gr.State(None)
|
| 1223 |
last_korean_words_state = gr.State(None)
|
|
@@ -1229,7 +1260,7 @@ def create_demo():
|
|
| 1229 |
|
| 1230 |
generate_event = submit_btn.click(
|
| 1231 |
fn=process_pdf,
|
| 1232 |
-
inputs=[pdf_input, url_input, yt_url_input, yt_cookies_input, translit_lang, translit_format, target_lang, max_text_char_input, repetition_penalty_input, last_source_state, last_korean_words_state],
|
| 1233 |
outputs=[output_html, last_source_state, last_korean_words_state, stream_box, extracted_text_box, extracted_images_gallery, extracted_audio_player]
|
| 1234 |
)
|
| 1235 |
|
|
|
|
| 537 |
return hashlib.md5(f.read(1024*1024)).hexdigest()
|
| 538 |
|
| 539 |
@spaces.GPU(duration=120)
|
| 540 |
+
def process_pdf(pdf_file, url_input, audio_file_input, yt_url_input, yt_cookies_file, translit_lang, translit_format, target_lang, max_text_char, repetition_penalty_val, last_source_hash, last_korean_words, active_tab, progress=gr.Progress()):
|
| 541 |
global tts, voice_style
|
| 542 |
|
| 543 |
# Clean language choices from "Family - Language" to just "Language"
|
|
|
|
| 548 |
|
| 549 |
os.makedirs("log", exist_ok=True)
|
| 550 |
|
| 551 |
+
# Determine input source based on active tab
|
| 552 |
+
is_url = (active_tab == "Website URL") and bool(url_input and url_input.strip())
|
| 553 |
+
is_youtube = (active_tab == "YouTube Link") and bool(yt_url_input and yt_url_input.strip() and is_youtube_url(yt_url_input.strip()))
|
| 554 |
+
is_audio_upload = (active_tab == "Upload Audio") and (audio_file_input is not None)
|
| 555 |
+
is_pdf = (active_tab == "Upload PDF") and (pdf_file is not None)
|
| 556 |
+
|
| 557 |
+
if not is_url and not is_youtube and not is_audio_upload and not is_pdf:
|
| 558 |
+
yield "<p>Please provide input in the active tab.</p>", None, None, "", "", [], None
|
| 559 |
return
|
| 560 |
|
| 561 |
if is_youtube:
|
| 562 |
current_source_hash = hashlib.md5(yt_url_input.strip().encode()).hexdigest()
|
| 563 |
+
elif is_audio_upload:
|
| 564 |
+
current_source_hash = hash_file(audio_file_input)
|
| 565 |
elif is_url:
|
| 566 |
current_source_hash = hashlib.md5(url_input.strip().encode()).hexdigest()
|
| 567 |
else:
|
|
|
|
| 570 |
vocab_list = []
|
| 571 |
extracted_audio_path = None
|
| 572 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 573 |
try:
|
| 574 |
if is_youtube:
|
| 575 |
progress(0, desc="Downloading YouTube audio (first 5 min)...")
|
|
|
|
| 588 |
# Log the transcription
|
| 589 |
with open("log/debug_yt_transcription.txt", "w", encoding="utf-8") as f:
|
| 590 |
f.write(f"Title: {yt_title}\n\n{content_text}")
|
| 591 |
+
elif is_audio_upload:
|
| 592 |
+
progress(0, desc="Transcribing uploaded audio with Cohere ASR...")
|
| 593 |
+
content_text = transcribe_audio_with_asr(audio_file_input)
|
| 594 |
+
images = []
|
| 595 |
+
extracted_audio_path = audio_file_input
|
| 596 |
+
|
| 597 |
+
if not content_text.strip():
|
| 598 |
+
yield "<p>Could not transcribe any text from the uploaded audio.</p>", current_source_hash, None, "", "", [], extracted_audio_path
|
| 599 |
+
return
|
| 600 |
+
|
| 601 |
+
with open("log/debug_audio_transcription.txt", "w", encoding="utf-8") as f:
|
| 602 |
+
f.write(content_text)
|
| 603 |
elif is_url:
|
| 604 |
progress(0, desc="Fetching Website...")
|
| 605 |
content_text, images = extract_website_content(url_input.strip())
|
|
|
|
| 988 |
print(f"Failed to download example PDF: {e}")
|
| 989 |
return file_path if os.path.exists(file_path) else None
|
| 990 |
|
| 991 |
+
def get_example_audio():
|
| 992 |
+
url = "https://raw.githubusercontent.com/ShayekhBinIslam/file-host/main/new_1min.wav"
|
| 993 |
+
file_path = "new_1min.wav"
|
| 994 |
+
if not os.path.exists(file_path):
|
| 995 |
+
try:
|
| 996 |
+
urllib.request.urlretrieve(url, file_path)
|
| 997 |
+
except Exception as e:
|
| 998 |
+
print(f"Failed to download example audio: {e}")
|
| 999 |
+
return file_path if os.path.exists(file_path) else None
|
| 1000 |
+
|
| 1001 |
@spaces.GPU(duration=120)
|
| 1002 |
def process_pdf_force(partial_text, pdf_file, url_input, translit_lang, translit_format, target_lang, max_text_char, repetition_penalty_val, last_source_state, last_korean_words_state):
|
| 1003 |
"""Force JSON generation using the current partial stream_box text."""
|
|
|
|
| 1080 |
|
| 1081 |
def create_demo():
|
| 1082 |
example_pdf = get_example_pdf()
|
| 1083 |
+
example_audio = get_example_audio()
|
| 1084 |
|
| 1085 |
custom_theme = gr.themes.Soft(
|
| 1086 |
primary_hue="violet",
|
|
|
|
| 1187 |
|
| 1188 |
with gr.Blocks(title="LocalDuo", theme=custom_theme, css=css) as demo:
|
| 1189 |
gr.Markdown("# π°π·β¨ LocalDuo - Learn Korean from PDFs, Websites & YouTube")
|
| 1190 |
+
gr.Markdown("Enter a website URL π, upload a PDF π, upload an audio file π΅, or paste a YouTube link π¬. The app uses a **Vision-Language Model (VLM)** π§ , **ASR** π€, and **TTS** π£οΈ to generate vocabulary flashcards.")
|
| 1191 |
+
|
| 1192 |
+
active_tab = gr.State("Website URL")
|
| 1193 |
|
| 1194 |
with gr.Row():
|
| 1195 |
with gr.Column(scale=1):
|
| 1196 |
+
with gr.Tabs() as input_tabs:
|
| 1197 |
+
with gr.Tab("Website URL", id="tab_url") as tab_url:
|
| 1198 |
+
url_input = gr.Textbox(label="Enter a Website URL π",
|
| 1199 |
+
placeholder=r"e.g. https://www.bbc.com/korean/articles/cn0p7rkvxdgo",
|
| 1200 |
+
value=r"https://www.bbc.com/korean/articles/cn0p7rkvxdgo")
|
| 1201 |
+
|
| 1202 |
+
with gr.Tab("Upload PDF", id="tab_pdf") as tab_pdf:
|
| 1203 |
+
pdf_input = gr.File(label="Upload Book PDF π", file_types=[".pdf"], value=example_pdf)
|
| 1204 |
+
|
| 1205 |
+
with gr.Tab("Upload Audio", id="tab_audio") as tab_audio:
|
| 1206 |
+
audio_file_input = gr.File(label="Upload Audio File π΅", file_types=[".wav", ".mp3", ".m4a", ".ogg", ".flac", ".opus", ".webm"], value=example_audio)
|
| 1207 |
+
gr.Markdown("*Upload a Korean audio file. It will be transcribed using Cohere ASR and vocabulary will be extracted from the transcript.*", elem_classes=["hint-text"])
|
| 1208 |
+
|
| 1209 |
+
with gr.Tab("YouTube Link", id="tab_yt") as tab_yt:
|
| 1210 |
+
yt_url_input = gr.Textbox(label="Enter a YouTube Link π¬",
|
| 1211 |
+
placeholder=r"e.g. https://www.youtube.com/watch?v=...",
|
| 1212 |
+
value="https://www.youtube.com/watch?v=9Nj7l73PBWE",
|
| 1213 |
+
info="Audio from the first 5 minutes will be transcribed using Cohere ASR")
|
| 1214 |
+
yt_cookies_input = gr.File(label="YouTube Cookies (cookies.txt)", file_types=[".txt"], value=None,
|
| 1215 |
+
type="filepath")
|
| 1216 |
+
gr.Markdown("*Optional. Helps bypass YouTube bot detection. Install the [cookies.txt](https://addons.mozilla.org/firefox/addon/cookies-txt/) extension, go to youtube.com while logged in, click the extension β 'Current Site' to export.*", elem_classes=["hint-text"])
|
| 1217 |
|
| 1218 |
+
# Track active tab
|
| 1219 |
+
tab_url.select(fn=lambda: "Website URL", inputs=None, outputs=active_tab)
|
| 1220 |
+
tab_pdf.select(fn=lambda: "Upload PDF", inputs=None, outputs=active_tab)
|
| 1221 |
+
tab_audio.select(fn=lambda: "Upload Audio", inputs=None, outputs=active_tab)
|
| 1222 |
+
tab_yt.select(fn=lambda: "YouTube Link", inputs=None, outputs=active_tab)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1223 |
|
| 1224 |
gr.Markdown("### βοΈ Customization Settings")
|
| 1225 |
translit_lang = gr.Dropdown(
|
|
|
|
| 1248 |
with gr.Accordion("π Extracted Source Content", open=True):
|
| 1249 |
extracted_text_box = gr.Textbox(label="Extracted Text", lines=10, max_lines=15, interactive=False)
|
| 1250 |
extracted_images_gallery = gr.Gallery(label="Extracted Images", columns=4, height="auto", object_fit="contain")
|
| 1251 |
+
extracted_audio_player = gr.Audio(label="Extracted Audio (YouTube / Uploaded)", type="filepath", interactive=False)
|
| 1252 |
|
| 1253 |
last_source_state = gr.State(None)
|
| 1254 |
last_korean_words_state = gr.State(None)
|
|
|
|
| 1260 |
|
| 1261 |
generate_event = submit_btn.click(
|
| 1262 |
fn=process_pdf,
|
| 1263 |
+
inputs=[pdf_input, url_input, audio_file_input, yt_url_input, yt_cookies_input, translit_lang, translit_format, target_lang, max_text_char_input, repetition_penalty_input, last_source_state, last_korean_words_state, active_tab],
|
| 1264 |
outputs=[output_html, last_source_state, last_korean_words_state, stream_box, extracted_text_box, extracted_images_gallery, extracted_audio_player]
|
| 1265 |
)
|
| 1266 |
|