write a frontend in a single index.html for this python server import os
import dotenv
import aiohttp
import asyncio
from googleapiclient.discovery import build
from groq import Groq
from quart import Quart, render_template, send_from_directory, request, jsonify
import google.generativeai as genai

app = Quart(__name__)


@app.route("/")
async def index():
    return await render_template("index.html")


@app.route("/search", methods=["POST"])
async def search():
    form = await request.form
    query = form.get("query")
    crawler = Lyrics_Crawler()
    response = crawler.search(query)
    links = extract_hyperlinks(response)
    usable_links = return_usable_links(links)
    link_index = 0
    scrape = await scrape_link(usable_links[link_index])
    lyrics = crawler.extract_lyrics(scrape)
    while True:
        if "Error: 371" in lyrics:
            link_index += 1
            scrape = await scrape_link(usable_links[link_index])
            lyrics = crawler.extract_lyrics(scrape)
        else:
            break
    summary = summarize_lyrics(lyrics)
    return jsonify({"lyrics": lyrics, "summary": summary})


model_map = {
    "Distil-Whisper English": "distil-whisper-large-v3-en",
    "Gemma 2 9B": "gemma-2-9b-it",
    "Gemma 7B": "gemma-7b-it",
    "Llama 3 70B Tool Use (Preview)": "llama3-groq-70b-8192-tool-use-preview",
    "Llama 3 70B Versatile": "llama-3.1-70b-versatile",
    "Mixtral 8x7B": "mixtral-8x7b-32769",
}


def summarize_lyrics(lyrics):
    dotenv.load_dotenv()
    GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
    genai.configure(api_key=GEMINI_API_KEY)
    with open("sysprompt_summary.txt", "r") as f:
        sysprompt = f.read()
    model = genai.GenerativeModel(
        model_name="gemini-1.5-flash", system_instruction=sysprompt
    )
    summary = model.generate_content(lyrics)
    return summary.text


class Lyrics_Crawler:
    def __init__(self):
        with open("sysprompt_extract.txt", "r") as f:
            self.system_prompt = f.read()
        dotenv.load_dotenv()
        self.google_api_key = os.getenv("GOOGLE_CSE_API_KEY")
        self.google_cse_id = os.getenv("GOOGLE_CSE_ID")
        self.groq_api_key = os.getenv("GROQ_API_KEY")
        self.client = Groq(api_key=self.groq_api_key)
        self.model = model_map["Llama 3 70B Versatile"]

    def search(self, search_term):
        search_term = search_term + " lyrics"
        service = build("customsearch", "v1", developerKey=self.google_api_key)
        res = service.cse().list(q=search_term, cx=self.google_cse_id).execute()
        return res

    def extract_lyrics(self, markdown):
        message = [
            {"role": "system", "content": self.system_prompt},
            {"role": "user", "content": markdown},
        ]
        response = self.client.chat.completions.create(
            model=self.model,
            messages=message,
            max_tokens=7000,
            stop=None,
            temperature=0.5,
            top_p=0.1,
        )
        return response.choices[0].message.content


async def scrape_link(url):
    try:
        jina_url = "https://r.jina.ai/" + url
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
        }
        async with aiohttp.ClientSession() as session:
            async with session.get(jina_url, headers=headers) as response:
                response.raise_for_status()
                print(f"Successfully fetched {jina_url}")
                return await response.text()
    except aiohttp.ClientError as e:
        print(f"Client error occurred while fetching {jina_url}: {e}")
        return None
    except asyncio.TimeoutError:
        print(f"Request timed out for {jina_url}")
        return None
    except Exception as e:
        print(f"An unexpected error occurred while fetching {jina_url}: {e}")
        return None


def extract_hyperlinks(cse_response):
    items = cse_response.get("items", [])
    links = [item["link"] for item in items if "link" in item]
    return links


def return_usable_links(links):
    usable_links = []
    for link in links:
        if "youtube" in link.lower():
            continue
        else:
            usable_links.append(link)
    return usable_links


async def main():
    crawler = Lyrics_Crawler()
    response = crawler.search("terminal kolja")
    links = extract_hyperlinks(response)
    usable_links = return_usable_links(links)
    link_index = 0
    scrape = await scrape_link(usable_links[link_index])
    lyrics = crawler.extract_lyrics(scrape)
    while True:
        if "Error: 371" in lyrics:
            link_index += 1
            scrape = await scrape_link(usable_links[link_index])
            lyrics = crawler.extract_lyrics(scrape)
        else:
            break
    print(lyrics)


if __name__ == "__main__":
    app.run(debug=True)