import gradio as gr import os from transformers import pipeline from pypinyin import pinyin, Style from janome.tokenizer import Tokenizer import re from pytube import Search import requests from chinese_english_lookup import Dictionary as CEDict from jamdict import Jamdict import jieba import json # Hugging Face pipelines translation_pipeline = pipeline("translation", model="Helsinki-NLP/opus-mt-mul-en") lang_detection_pipeline = pipeline("text-classification", model="papluca/xlm-roberta-base-language-detection") # Initialize the dictionary libraries cedict = CEDict() jam = Jamdict() # Pinyin Romanization for Chinese def get_pinyin_from_chinese(lyrics): pinyin_result = pinyin(lyrics, style=Style.TONE) return " ".join(["".join(word) for word in pinyin_result]) # Romanization for Japanese (Romaji) def get_romaji_from_japanese(lyrics): t = Tokenizer() romaji_lyrics = [] for token in t.tokenize(lyrics): romaji_lyrics.append(token.reading.lower().replace("*", "")) return " ".join(romaji_lyrics) # Function to search YouTube and generate embed code def search_and_embed_video(song_name, artist_name): query = f"{song_name} {artist_name} official lyrics" try: s = Search(query) video_id = s.results[0].video_id youtube_url = f"https://www.youtube.com/embed/{video_id}" return f'' except Exception as e: return "

Could not find a YouTube video for this song.

" # Lookup functions for each language def lookup_english_word(word): api_url = f"https://api.dictionaryapi.dev/api/v2/entries/en/{word}" try: response = requests.get(api_url) response.raise_for_status() data = response.json() html_output = f"

{data[0]['word']}

" for meaning in data[0]['meanings']: part_of_speech = meaning['partOfSpeech'] html_output += f"

({part_of_speech})

" for definition in meaning['definitions']: def_text = definition['definition'] html_output += f"

• {def_text}

" if 'example' in definition: example_text = definition['example'] html_output += f"

Example: {example_text}

" return html_output except requests.exceptions.HTTPError as e: return f"

Could not find a definition for '{word}'.

" except Exception as e: return f"

An error occurred: {e}

" def lookup_chinese_word(word): try: result = cedict.lookup(word) if not result: return f"

Could not find a definition for '{word}'.

" html_output = "" for entry in result: html_output += f"

{entry.simp} ({entry.trad})

" for def_entry in entry.definition_entries: pinyin_text = def_entry.pinyin definitions = " / ".join(def_entry.definitions) html_output += f"

{pinyin_text}

" html_output += f"

• {definitions}

" return html_output except Exception as e: return f"

An error occurred during Chinese lookup: {e}

" def lookup_japanese_word(word): try: result = jam.lookup(word) if not result.entries and not result.kanji: return f"

Could not find a definition for '{word}'.

" html_output = "" for entry in result.entries: html_output += f"

{entry.text}

" if entry.kana_forms: html_output += f"

({entry.kana_forms[0].text})

" for gloss in entry.senses: html_output += f"

• {', '.join(gloss.glosses)}

" for k in result.kanji: html_output += f"

Kanji: {k.text}

" if k.meanings: html_output += f"

• Meaning: {', '.join(k.meanings)}

" if k.readings: html_output += f"

• Readings: {', '.join(k.readings)}

" return html_output except Exception as e: return f"

An error occurred during Japanese lookup: {e}

" # The main dictionary lookup function, which acts as a dispatcher def get_dictionary_output(json_data: str): if not json_data: return "Click on a word to get its definition." try: data = json.loads(json_data) word = data.get('word', '') language = data.get('lang', '') if language == 'en': return lookup_english_word(word) elif language == 'zh': return lookup_chinese_word(word) elif language == 'ja': return lookup_japanese_word(word) else: return f"

Dictionary lookup for language '{language}' is not yet supported.

" except json.JSONDecodeError: return "Error: Invalid data received for dictionary lookup." # Helper function to generate the HTML for display def create_html_display(original_lyrics, translated_lyrics, romanized_lyrics=None, language="en"): # This is the most important part of the fix. We are embedding the JS `onclick` # handler directly and ensuring it's a simple, reliable call. original_lines = original_lyrics.strip().split('\n') translated_lines = translated_lyrics.strip().split('\n') num_lines = max(len(original_lines), len(translated_lines)) original_lines.extend([""] * (num_lines - len(original_lines))) translated_lines.extend([""] * (num_lines - len(translated_lines))) romanized_lines = [] if romanized_lyrics: romanized_lines = romanized_lyrics.strip().split('\n') romanized_lines.extend([""] * (num_lines - len(romanized_lines))) html_content = "" for i in range(num_lines): if language == 'zh': words_in_line = jieba.cut(original_lines[i]) elif language == 'ja': words_in_line = [char for char in original_lines[i] if char] else: words_in_line = original_lines[i].split() # # The onclick handler now directly sets the hidden textbox's value. # original_line_html = " ".join([ # f"{word}" # for word in words_in_line # ]) # We no longer need the onclick handler here. original_line_html = " ".join([ f"{word}" for word in words_in_line ]) # original_line_html = " ".join([ # f"{word}" # for word in words_in_line # ]) orig_line = f"

{original_line_html}

" if romanized_lines: rom_line = f"

{romanized_lines[i]}

" trans_line = f"

{translated_lines[i]}

" html_content += f"""
{orig_line}{rom_line}
{trans_line}
""" else: trans_line = f"

{translated_lines[i]}

" html_content += f"""
{orig_line}
{trans_line}
""" css = """ """ # We return the HTML with the language as a data attribute, as before. return f"{css}
{html_content}
" # The main function to handle all the logic def process_lyrics(lyrics: str, song_name: str = "", artist_name: str = ""): if not lyrics: return "", "", "Please paste some lyrics." original_lines = [line.strip() for line in lyrics.strip().split('\n') if line.strip()] language = "unknown" if original_lines: try: prediction = lang_detection_pipeline(original_lines[0])[0] language = prediction['label'] except Exception: language = "unknown" is_chinese = language.startswith("zh") if language else False translated_lines = [] for line in original_lines: try: if not line: translated_lines.append("") continue if language != "en": src_lang_tag = "" if is_chinese: src_lang_tag = ">>zh<<" elif language == "ja": src_lang_tag = ">>ja<<" translation_result = translation_pipeline(line, src_lang=language, tgt_lang="en")[0]['translation_text'] translated_lines.append(translation_result) else: translated_lines.append(line) except Exception as e: translated_lines.append(f"Translation Error: {e}") romanized_lines = None if is_chinese: romanized_lines = [get_pinyin_from_chinese(line) for line in original_lines] elif language == "ja": romanized_lines = [get_romaji_from_japanese(line) for line in original_lines] original_text = "\n".join(original_lines) translated_text = "\n".join(translated_lines) romanized_text = "\n".join(romanized_lines) if romanized_lines else None display_html = create_html_display(original_text, translated_text, romanized_text, language) youtube_embed = search_and_embed_video(song_name, artist_name) return display_html, youtube_embed, "Click on a word to get its definition." def welcome(name): return f"Welcome to Gradio, {name}!" js = """ function createGradioAnimation() { var container = document.createElement('div'); container.id = 'gradio-animation'; container.style.fontSize = '2em'; container.style.fontWeight = 'bold'; container.style.textAlign = 'center'; container.style.marginBottom = '20px'; var text = 'Welcome to Gradio!'; for (var i = 0; i < text.length; i++) { (function(i){ setTimeout(function(){ var letter = document.createElement('span'); letter.style.opacity = '0'; letter.style.transition = 'opacity 0.5s'; letter.innerText = text[i]; container.appendChild(letter); setTimeout(function() { letter.style.opacity = '1'; }, 50); }, i * 250); })(i); } var gradioContainer = document.querySelector('.gradio-container'); gradioContainer.insertBefore(container, gradioContainer.firstChild); return 'Animation created'; } """ # Define the Gradio interface with gr.Blocks(js=js) as demo: inp = gr.Textbox(placeholder="What is your name?") out = gr.Textbox() inp.change(welcome, inp, out) # This is the hidden component that our JavaScript will update. # The `elem_id` is crucial for the JS to find it. hidden_json_input = gr.Textbox(visible=False, elem_id="hidden_json_input") with gr.Row(): with gr.Column(scale=1): lyrics_input = gr.Textbox(label="Paste Lyrics Here", lines=10, placeholder="Paste your lyrics here...") song_input = gr.Textbox(label="Enter Song Name (for Youtube)", placeholder="e.g., 月亮代表我的心") artist_input = gr.Textbox(label="Enter Artist Name (Optional)", placeholder="e.g., Teresa Teng") process_button = gr.Button("Process") with gr.Column(scale=2): lyrics_html_output = gr.HTML(label="Lyrics and Translation") video_embed_output = gr.HTML(label="YouTube Video") with gr.Column(scale=1): dictionary_output = gr.HTML(label="Word Definition") process_button.click( fn=process_lyrics, inputs=[lyrics_input, song_input, artist_input], outputs=[lyrics_html_output, video_embed_output, dictionary_output] ).success( fn=None, inputs=None, outputs=None, js="attach_click_listeners()" ) # We listen for a change to the hidden textbox to trigger our lookup hidden_json_input.change( fn=get_dictionary_output, inputs=[hidden_json_input], outputs=[dictionary_output] ) # hidden_json_input.input( # fn=get_dictionary_output, # inputs=[hidden_json_input], # outputs=[dictionary_output] # ) # Launch the Gradio app if __name__ == "__main__": demo.launch()