# Project export: LectureGen

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: Turn any paper into a digestible YouTube lecture with slides!
- Devpost: https://devpost.com/software/lecturegen
- GitHub: https://github.com/degtrdg/paper-to-lecture
- Team: 3 GitHub contributor(s) — Daniel George (27 commits), Joe Cowles (25 commits), Nicolas Huang (4 commits)

## Devpost submission (written by the team)

### Overview

The inspiration for our project, LectureGen, comes from our experience trying to learn about a new domain through papers. These papers, unintentionally or not, often create a boundary for non-experts to understand what’s going on. But whenever our professors create a lecture on the paper for a journal club, they are actually quite straightforward and even inspire us to learn more. With LectureGen, people can digest any paper they want to read in an easy-to-understand YouTube lecture. That lecture can help others who are grappling with the same paper since it’s on YouTube for others to see! Our aim is to lower the barrier for others to dive into new fields and learn what they want to learn with how they learn best. In LectureGen, we take your choice of a paper and we take care of the rest to get your desired lecture. If you choose to watch the YouTube video on our application, you have the option to interrupt the lecture to get clarification on whatever you need! Whether you prefer to take your videos at your own pace or want a little extra help while you’re watching, our application LectureGen has got you covered. We initially process the PDFs with Gemini’s great vision capabilities so that we don’t have unnecessary parts of the paper in content while making the lecture. Our lectures are synthesized with o1-preview to create the most high-quality lecture, and then we have a model create the concise slides along with the in-depth speaker notes that are spoken aloud with Cartesia’s text to speech. Having all three modes of absorbing the information really helps keep you focused and understand the material better just like your professor does! If you watch the videos on our platform, you can interrupt the lecture to talk to our empathetic voice agent powered by Hume whose context is seeded by our RAG pipeline over the paper using Chroma. YouTube doesn’t allow you to upload a video programmatically without doing OAuth by the YouTube account. We found a reasonable tradeoff with our admin panel that approves the videos that a user has generated before uploading them to YouTube. o1-preview doesn’t support structured outputs natively and requires different prompting from other LLMs, so we had to work around that with smaller models to do the parts that it natively couldn’t. Integrating everything! We used many different technologies and had to connect them all together which took longer than expected. We want to have auto-replies on the YouTube channel for each video based on what’s going on in our platform. We think this could create a whole new type of interaction on YouTube where people can learn whatever they want while also getting immediate feedback, all in public. Lots of these LLM tools silo the knowledge that people get which is worse for everyone, and we’d like to help change that. Dynamically change the lectures as we get more information about the user and what peaks their interest in the lecture. This is how I’ve seen good professors do lectures and is something I’d want to see.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 135 recognized source files, 1956 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- Google Gemini (technology) — detected in the code
- Hugging Face (technology) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- PyTorch (technology) — detected in the code
- React (technology) — detected in the code
- Streamlit (technology) — detected in the code
- Supabase (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (120 of 163)

```
.gitignore
.vscode/launch.json
.vscode/settings.json
api/index.py
experiments/.env.template
experiments/1_get_all_text.ipynb
experiments/2_org_prompt.ipynb
experiments/3_get_slides.ipynb
experiments/4_extract_slides.ipynb
experiments/5_slides.ipynb
experiments/admin_panel.py
experiments/example_slides.json
experiments/example_slides2.json
experiments/google.md
experiments/test_create_video.py
experiments/youtube_upload.py
extracted_text.txt
frontend/.env.example
frontend/.gitignore
frontend/app/(auth-pages)/forgot-password/page.tsx
frontend/app/(auth-pages)/layout.tsx
frontend/app/(auth-pages)/sign-in/page.tsx
frontend/app/(auth-pages)/sign-up/page.tsx
frontend/app/(auth-pages)/smtp-message.tsx
frontend/app/actions.ts
frontend/app/api/dispatcher/create-job/jobProcessor.ts
frontend/app/api/dispatcher/create-job/route.ts
frontend/app/api/paper/truncate-pdf/route.ts
frontend/app/api/paper/truncate-text/route.ts
frontend/app/api/slides/extract/route.ts
frontend/app/api/slides/get-slides/route.ts
frontend/app/api/tts/route.d.ts
frontend/app/api/tts/route.ts
frontend/app/auth/callback/route.ts
frontend/app/globals.css
frontend/app/home/page.tsx
frontend/app/layout.tsx
frontend/app/page.tsx
frontend/app/protected/page.tsx
frontend/app/protected/reset-password/page.tsx
frontend/app/settings/page.tsx
frontend/app/videos/page.tsx
frontend/app/videos/viewer/page.tsx
frontend/components.json
frontend/components/deploy-button.tsx
frontend/components/env-var-warning.tsx
frontend/components/form-message.tsx
frontend/components/header-auth.tsx
frontend/components/hero.tsx
frontend/components/mic-button.tsx
frontend/components/next-logo.tsx
frontend/components/sidebar.tsx
frontend/components/submit-button.tsx
frontend/components/supabase-logo.tsx
frontend/components/tutorial/code-block.tsx
frontend/components/tutorial/connect-supabase-steps.tsx
frontend/components/tutorial/fetch-data-steps.tsx
frontend/components/tutorial/sign-up-user-steps.tsx
frontend/components/tutorial/tutorial-step.tsx
frontend/components/typography/inline-code.tsx
frontend/components/ui/badge.tsx
frontend/components/ui/button.tsx
frontend/components/ui/card.tsx
frontend/components/ui/checkbox.tsx
frontend/components/ui/dropdown-menu.tsx
frontend/components/ui/input.tsx
frontend/components/ui/label.tsx
frontend/components/ui/textarea.tsx
frontend/lib/utils.ts
frontend/middleware.ts
frontend/next.config.js
frontend/package.json
frontend/postcss.config.js
frontend/README.md
frontend/tailwind.config.ts
frontend/tsconfig.json
frontend/utils/cn.ts
frontend/utils/supabase/check-env-vars.ts
frontend/utils/supabase/client.ts
frontend/utils/supabase/middleware.ts
frontend/utils/supabase/server.ts
frontend/utils/utils.ts
gemini/main.py
hume/evi_chat.py
marker/.github/workflows/cla.yml
marker/.github/workflows/publish.yml
marker/.github/workflows/tests.yml
marker/.gitignore
marker/benchmarks/overall.py
marker/chunk_convert.py
marker/chunk_convert.sh
marker/CLA.md
marker/convert_single.py
marker/convert.py
marker/data/.gitignore
marker/data/examples/marker/multicolcnn.md
marker/data/examples/marker/switch_transformers.md
marker/data/examples/marker/thinkos.md
marker/data/examples/marker/thinkpython.md
marker/data/examples/nougat/multicolcnn.md
marker/data/examples/nougat/switch_transformers.md
marker/data/examples/nougat/thinkos.md
marker/data/examples/nougat/thinkpython.md
marker/data/latex_to_md.sh
marker/docs/install_ocrmypdf.md
marker/LICENSE
marker/marker_app.py
marker/marker/benchmark/scoring.py
marker/marker/benchmark/table.py
marker/marker/cleaners/bullets.py
marker/marker/cleaners/code.py
marker/marker/cleaners/fontstyle.py
marker/marker/cleaners/headers.py
marker/marker/cleaners/headings.py
marker/marker/cleaners/text.py
marker/marker/cleaners/toc.py
marker/marker/convert.py
marker/marker/debug/data.py
marker/marker/debug/render.py
marker/marker/equations/equations.py
[43 more files omitted for size]
```

### Dependencies

- frontend/package.json: @cartesia/cartesia-js@^1.1.0, @google/generative-ai@^0.21.0, @humeai/voice@^0.1.10, @humeai/voice-react@^0.1.16, @instructor-ai/instructor@^1.5.0, @radix-ui/react-checkbox@^1.1.1, @radix-ui/react-dropdown-menu@^2.1.1, @radix-ui/react-label@^2.1.0, @radix-ui/react-slot@^1.1.0, @supabase/auth-helpers-nextjs@^0.10.0, @supabase/ssr@latest, @supabase/supabase-js@latest, @types/node@20.10.6, @types/react@^18.3.11, @types/react-dom@18.2.18, @types/uuid@^10.0.0, autoprefixer@10.4.17, axios@^1.7.7, class-variance-authority@^0.7.0, clsx@^2.1.1, framer-motion@^11.11.9, geist@^1.2.1, hume@^0.9.1, lucide-react@^0.436.0, next@latest, next-themes@^0.3.0, pdf-lib@^1.17.1, pdf2pic@^3.1.3, pdfjs-dist@^4.7.76, postcss@8.4.33, prettier@^3.3.3, react@18.2.0, react-dom@18.2.0, react-icons@^5.3.0, sharp@^0.33.5, tailwind-merge@^2.5.2, tailwindcss@3.4.1, tailwindcss-animate@^1.0.7, tiktoken@^1.0.17, typescript@5.3.3, uuid@^10.0.0
- marker/pyproject.toml: datasets@^2.21.0, filetype@^1.2.0, ftfy@^6.1.1, jupyter@^1.0.0, modal@^0.64.199, pdftext@^0.3.17, Pillow@^10.1.0, pydantic@^2.4.2, pydantic-settings@^2.0.3, python-dotenv@^1.0.0, rapidfuzz@^3.8.1, regex@^2024.4.28, streamlit@^1.37.1, surya-ocr@^0.6.6, tabled-pdf@^0.1.1, tabulate@^0.9.0, texify@^0.2.0, torch@^2.4.1, tqdm@^4.66.1, transformers@^4.45.2
- requirements.txt: beautifulsoup4, fastapi, google-api-python-client@==1.7.2, google-auth@==1.8.0, google-auth-httplib2@==0.0.3, google-auth-oauthlib@==0.4.1, instructor, markdown, moviepy, Pillow, pydantic, PyPDF2, python-dotenv, python-multipart, requests, supabase, uvicorn

### Recent commits (newest first)

- FInal version
- trying to fix video
- Merge branch 'main' of https://github.com/degtrdg/paper-to-lecture into main
- Working on video gen
- ex
- slides
- Merge branch 'main' of https://github.com/degtrdg/paper-to-lecture into main
- Prod build
- d
- Build fixed
- Working video generation.
- Merge branch 'main' of https://github.com/degtrdg/paper-to-lecture into main
- Working videos (kinda)
- UI changes
- draft admin panel
- nit: type
- remove indent
- Merge branch 'main' of https://github.com/degtrdg/paper-to-lecture into main
- Working slides creation workflow
- Merge remote-tracking branch 'refs/remotes/origin/main'

## Key source files (fetched from GitHub, selected and truncated for size)

### notes.md

```markdown
npx create-next-app -e with-supabase

```

### marker/CLA.md

```markdown
Marker Contributor Agreement

This Marker Contributor Agreement ("MCA") applies to any contribution that you make to any product or project managed by us (the "project"), and sets out the intellectual property rights you grant to us in the contributed materials. The term "us" shall mean Endless Labs, Inc. The term "you" shall mean the person or entity identified below. 

If you agree to be bound by these terms, sign by writing "I have read the CLA document and I hereby sign the CLA" in response to the CLA bot Github comment. Read this agreement carefully before signing. These terms and conditions constitute a binding legal agreement.

1. The term 'contribution' or 'contributed materials' means any source code, object code, patch, tool, sample, graphic, specification, manual, documentation, or any other material posted or submitted by you to the project. 
2. With respect to any worldwide copyrights, or copyright applications and registrations, in your contribution: 
   - you hereby assign to us joint ownership, and to the extent that such assignment is or becomes invalid, ineffective or unenforceable, you hereby grant to us a perpetual, irrevocable, non-exclusive, worldwide, no-charge, royalty free, unrestricted license to exercise all rights under those copyrights. This includes, at our option, the right to sublicense these same rights to third parties through multiple levels of sublicensees or other licensing arrangements, including dual-license structures for commercial customers; 
   - you agree that each of us can do all things in relation to your contribution as if each of us were the sole owners, and if one of us makes a derivative work of your contribution, the one who makes the derivative work (or has it made will be the sole owner of that derivative work; 
   - you agree that you will not assert any moral rights in your contribution against us, our licensees or transferees; 
   - you agree that we may register a copyright in your contribution and exercise all ownership rights associated with it; and 
   - you agree that neither of us has any duty to consult with, obtain the consent of, pay or render an accounting to the other for any use or distribution of vour contribution. 
3. With respect to any patents you own, or that you can license without payment to any third party, you hereby grant to us a perpetual, irrevocable, non-exclusive, worldwide, no-charge, royalty-free license to:
   - make, have made, use, sell, offer to sell, import, and otherwise transfer your contribution in whole or in part, alone or in combination with or included in any product, work or materials arising out of the project to which your contribution was submitted, and
   - at our option, to sublicense these same rights to third parties through multiple levels of sublicensees or other licensing arrangements. 
If you or your affiliates institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the contributi
[truncated — 1585 more characters]
```

### requirements.txt

```
Pillow
markdown
requests
beautifulsoup4
google-api-python-client==1.7.2
PyPDF2
instructor
fastapi
pydantic
python-dotenv
uvicorn
google-auth==1.8.0
google-auth-httplib2==0.0.3
google-auth-oauthlib==0.4.1
moviepy
python-multipart
supabase

```

### marker/pyproject.toml

```
[tool.poetry]
name = "marker-pdf"
version = "0.3.1"
description = "Convert PDF to markdown with high speed and accuracy."
authors = ["Vik Paruchuri <github@vikas.sh>"]
readme = "README.md"
license = "GPL-3.0-or-later"
repository = "https://github.com/VikParuchuri/marker"
keywords = ["pdf", "markdown", "ocr", "nlp"]
packages = [
    {include = "marker"}
]
include = [
    "convert.py",
    "convert_single.py",
    "chunk_convert.sh",
    "chunk_convert.py",
    "marker_app.py",
    "run_marker_app.py"
]

[tool.poetry.dependencies]
python = "^3.10"
Pillow = "^10.1.0"
pydantic = "^2.4.2"
pydantic-settings = "^2.0.3"
transformers = "^4.45.2"
python-dotenv = "^1.0.0"
torch = "^2.4.1"
tqdm = "^4.66.1"
tabulate = "^0.9.0"
ftfy = "^6.1.1"
texify = "^0.2.0"
rapidfuzz = "^3.8.1"
surya-ocr = "^0.6.6"
filetype = "^1.2.0"
regex = "^2024.4.28"
pdftext = "^0.3.17"
tabled-pdf = "^0.1.1"
modal = "^0.64.199"

[tool.poetry.group.dev.dependencies]
jupyter = "^1.0.0"
datasets = "^2.21.0"
streamlit = "^1.37.1"

[tool.poetry.scripts]
marker = "convert:main"
marker_single = "convert_single:main"
marker_chunk_convert = "chunk_convert:main"
marker_gui = "run_marker_app:run_app"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
```

### frontend/package.json

```
{
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "@cartesia/cartesia-js": "^1.1.0",
    "@google/generative-ai": "^0.21.0",
    "@humeai/voice": "^0.1.10",
    "@humeai/voice-react": "^0.1.16",
    "@instructor-ai/instructor": "^1.5.0",
    "@radix-ui/react-checkbox": "^1.1.1",
    "@radix-ui/react-dropdown-menu": "^2.1.1",
    "@radix-ui/react-label": "^2.1.0",
    "@radix-ui/react-slot": "^1.1.0",
    "@supabase/auth-helpers-nextjs": "^0.10.0",
    "@supabase/ssr": "latest",
    "@supabase/supabase-js": "latest",
    "autoprefixer": "10.4.17",
    "axios": "^1.7.7",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.1",
    "framer-motion": "^11.11.9",
    "geist": "^1.2.1",
    "hume": "^0.9.1",
    "lucide-react": "^0.436.0",
    "next": "latest",
    "next-themes": "^0.3.0",
    "pdf-lib": "^1.17.1",
    "pdf2pic": "^3.1.3",
    "pdfjs-dist": "^4.7.76",
    "prettier": "^3.3.3",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-icons": "^5.3.0",
    "sharp": "^0.33.5",
    "tiktoken": "^1.0.17",
    "uuid": "^10.0.0"
  },
  "devDependencies": {
    "@types/node": "20.10.6",
    "@types/react": "^18.3.11",
    "@types/react-dom": "18.2.18",
    "@types/uuid": "^10.0.0",
    "postcss": "8.4.33",
    "tailwind-merge": "^2.5.2",
    "tailwindcss": "3.4.1",
    "tailwindcss-animate": "^1.0.7",
    "typescript": "5.3.3"
  }
}

```

### vapi/main.py

```python
from vapi_python import Vapi

def initialize_vapi():
    vapi_client = Vapi(api_key='d12dbf20-f0fa-487c-b49f-bcd37363bc67')
    return vapi_client

def provide_context(vapi_client, context):
    vapi_client.add_message(role="system", content=context)

def chat_with_vapi(vapi_client, user_message):
    vapi_client.add_message(role="user", content=user_message)
    response = vapi_client.chat()
    return response

def main():
    vapi_client = initialize_vapi()
    
    context = "You are a helpful AI assistant with knowledge about various topics."
    provide_context(vapi_client, context)
    
    while True:
        user_input = input("You: ")
        if user_input.lower() == 'exit':
            break
        
        response = chat_with_vapi(vapi_client, user_input)
        print("AI:", response)

if __name__ == "__main__":
    main()

```

### gemini/main.py

```python
import google.generativeai as genai
import os
from dotenv import load_dotenv
from fastapi import FastAPI, File, UploadFile, Form
from fastapi.responses import JSONResponse
import uvicorn
from typing import Optional
from PIL import Image

load_dotenv()
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
model = genai.GenerativeModel("gemini-1.5-flash")
app = FastAPI()

@app.post("/generate")
async def generate_content(
    text: Optional[str] = Form(None),
    image: Optional[UploadFile] = File(None)
):
    content = []

    if text:
        content.append(text)

    if image:
        temp_file = f"temp_{image.filename}"
        with open(temp_file, "wb") as buffer:
            buffer.write(await image.read())
        img = Image.open(temp_file)
        content.append(img)
        os.remove(temp_file)

    if not content:
        return JSONResponse(status_code=400, content={"error": "No input provided"})

    response = model.generate_content(content)
    return {"response": response.text}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

```

### api/index.py

```python
from fastapi import FastAPI, HTTPException, UploadFile, File, Depends
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from typing import Optional, List
from PIL import Image, ImageDraw, ImageFont
import markdown
from bs4 import BeautifulSoup
import requests
from io import BytesIO
import base64
import os
import PyPDF2
import uvicorn
from moviepy.editor import ImageClip, AudioFileClip, concatenate_videoclips
import json
import uuid
import shutil

app = FastAPI(docs_url="/api/docs", openapi_url="/api/openapi.json")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Mount static folder
STATIC_FOLDER = "static"
if not os.path.exists(STATIC_FOLDER):
    os.makedirs(STATIC_FOLDER)

app.mount("/static", StaticFiles(directory=STATIC_FOLDER), name="static")

class SlideRequest(BaseModel):
    title: str
    content: str
    image_url: Optional[str] = None
    image_subtitle: Optional[str] = None


@app.post("/api/create_slide")
async def create_slide_endpoint(slide_request: SlideRequest):
    try:
        slide = create_slide(
            slide_request.title,
            slide_request.content,
            slide_request.image_url,
            slide_request.image_subtitle,
        )

        # Convert the slide to base64
        buffered = BytesIO()
        slide.save(buffered, format="PNG")
        img_str = base64.b64encode(buffered.getvalue()).decode()

        return {"slide_image": img_str}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


class PDFBase64Request(BaseModel):
    pdf_base64: str


@app.post("/api/extract_pdf_text")
async def extract_pdf_text(request: PDFBase64Request):
    try:
        # Decode the base64 PDF
        pdf_content = base64.b64decode(request.pdf_base64)

        # Create a PDF reader object
        pdf_file = BytesIO(pdf_content)

        try:
            pdf_reader = PyPDF2.PdfReader(pdf_file, strict=False)
        except PyPDF2.errors.PdfReadError as e:
            print(f"PyPDF2 error: {str(e)}")
            raise HTTPException(status_code=400, detail=f"Invalid PDF file: {str(e)}")

        # Initialize an empty string to store all the text
        all_text = ""

        # Iterate through all pages and extract text
        for page in pdf_reader.pages:
            all_text += page.extract_text()

        # Prepare the response
        response = {
            "total_pages": len(pdf_reader.pages),
            "total_characters": len(all_text),
            "extracted_text": all_text[
                :1000
            ],  # Return first 1000 characters as a sample
            "full_text": all_text,
        }

        return response
    except Exception as e:
        print(f"Unexpected error: {str(e)}")
        raise HTTPException(status_code=500, detail=f"Error processing PDF: {str(e)}")

@app.post("/api/create_slide")
async def create_slide_endpoint(slide_request: SlideRequest):
    try:
        slide = create_slide(
            slide_request.title,
            slide_request.content,
            slide_request.image_url,
            slide_request.image_subtitle,
        )

        # Convert the slide to base64
        buffered = BytesIO()
        slide.save(buffered, format="PNG")
        img_str = base64.b64encode(buffered.getvalue()).decode()

        return {"slide_image": img_str}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

def get_default_font(size):
    return ImageFont.load_default().font_variant(size=size)

def create_slide(title, content, image_url=None, image_subtitle=None, width=800, height=600):
    print(f"Creating slide: {title}")
    try:
        slide = Image.new('RGB', (width, height), color='white')
        draw = ImageDraw.Draw(slide)

        # Drastically increase font sizes
        title_font_size = 60
        content_font_size = 50
        subtitle_font_size = 40

        title_font = get_default_font(title_font_size)
        content_font = get_default_font(content_font_size)
        subtitle_font = get_default_font(subtitle_font_size)

        # Draw title
        title_width = width - 40
        title_lines = wrap_text(title, title_font, title_width)
        y_offset = 20
        for line in title_lines:
            draw.text((20, y_offset), line, font=title_font, fill="black")
            y_offset += title_font_size + 5

        # Parse and draw content
        html = markdown.markdown(content)
        soup = BeautifulSoup(html, "html.parser")

        content_width = width - 40
        y_offset += 20

        for element in soup.find_all(["p", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol"]):
            if element.name == "p":
                lines = wrap_text(element.text, content_font, content_width)
                for line in lines:
                    draw.text((20, y_offset), line, font=content_font, fill="black")
                    y_offset += content_font_size + 5
            elif element.name.startswith("h"):
                header_size = content_font_size + 10
                header_font = get_default_font(header_size)
                header_lines = wrap_text(element.text, header_font, content_width)
                for line in header_lines:
                    draw.text((20, y_offset), line, font=header_font, fill="black")
                    y_offset += header_size + 5
            elif element.name in ["ul", "ol"]:
                for li in element.find_all("li"):
                    bullet_lines = wrap_text("• " + li.text, content_font, content_width - 20)
                    for line in bullet_lines:
                        draw.text((40, y_offset), line, font=content_font, fill="black")
                        y_offset += content_font_size + 5

        # Add image if provided (scaled down)
        if image_url:
            tr
[truncated — 4962 more characters]
```

### frontend/app/layout.tsx

```typescript
import { GeistSans } from "geist/font/sans";
import { ThemeProvider } from "next-themes";
import "./globals.css";
import Sidebar from "@/components/sidebar";
import { headers } from 'next/headers';

const defaultUrl = process.env.VERCEL_URL
  ? `https://${process.env.VERCEL_URL}`
  : "http://localhost:3000";

export const metadata = {
  metadataBase: new URL(defaultUrl),
  title: "LectureGen",
  description: "Transform your research papers into engaging video lectures",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const headersList = headers();
  const pathname = headersList.get("x-invoke-path") || "";

  return (
    <html lang="en" className={GeistSans.className} suppressHydrationWarning>
      <body className="bg-background text-foreground">
        <ThemeProvider
          attribute="class"
          defaultTheme="light"
          enableSystem
          disableTransitionOnChange
        >
          <div className="min-h-screen w-full bg-white text-black dark:bg-background dark:text-foreground bg-graph-paper dark:bg-none">
            <div className="flex">
              <Sidebar initialPathname={pathname} />
              <main className="flex-1 overflow-y-auto">
                {children}
              </main>
            </div>
          </div>
        </ThemeProvider>
      </body>
    </html>
  );
}

```

### frontend/app/page.tsx

```typescript
'use client'
import { useState } from 'react'
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Card, CardContent } from "@/components/ui/card"
import { BookOpen, Video, Zap, Lock, ChevronRight, FileText } from "lucide-react"
import Link from "next/link"
import Image from "next/image"
import { NextPage } from 'next'
import { useTheme } from "next-themes"
import { useRouter } from 'next/navigation'

interface Feature {
  icon: React.ElementType;
  title: string;
  description: string;
}

interface Example {
  title: string;
  journal: string;
  videoLength: string;
  views: string;
  paperPreview: string;
  videoThumbnail: string;
}

const LandingPage: NextPage = () => {
  const [email, setEmail] = useState<string>('')
  const { theme } = useTheme()
  const router = useRouter()

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    if (email) {
      router.push(`/sign-up?username=${encodeURIComponent(email)}`)
    }
  }

  const features: Feature[] = [
    { icon: BookOpen, title: "Upload Paper", description: "Simply upload your research paper in PDF format." },
    { icon: Zap, title: "AI Processing", description: "Our AI analyzes and extracts key information from the paper." },
    { icon: Video, title: "Generate Video", description: "A clear, engaging video lecture is automatically created." }
  ]

  const examples: Example[] = [
    { 
      title: "Quantum Computing Breakthroughs", 
      journal: "Nature Physics", 
      videoLength: "15:23", 
      views: "12.5K", 
      paperPreview: "https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fs41567-023-02214-0/MediaObjects/41567_2023_2214_Fig1_HTML.png",
      videoThumbnail: "https://img.youtube.com/vi/JhHMJCUmq28/0.jpg"
    },
    { 
      title: "Advancements in CRISPR Technology", 
      journal: "Cell", 
      videoLength: "18:47", 
      views: "9.8K", 
      paperPreview: "https://ars.els-cdn.com/content/image/1-s2.0-S0092867422015725-gr1.jpg",
      videoThumbnail: "https://img.youtube.com/vi/4YKFw2KZA5o/0.jpg"
    },
    { 
      title: "Dark Matter Detection Methods", 
      journal: "Astrophysical Journal", 
      videoLength: "20:11", 
      views: "7.2K", 
      paperPreview: "https://iopscience.iop.org/article/10.3847/1538-4357/ac4973/meta/assets/images/large/apjac4973f1_hr.jpg",
      videoThumbnail: "https://img.youtube.com/vi/QAa2O_8wBUQ/0.jpg"
    },
    { 
      title: "Neural Networks in Climate Modeling", 
      journal: "Science", 
      videoLength: "16:39", 
      views: "11.3K", 
      paperPreview: "https://www.science.org/cms/10.1126/science.abi9167/asset/3bfb0eb7-54f4-4c4f-b4c9-63f7c2d1d3fb/assets/graphic/373_1503_f3.jpeg",
      videoThumbnail: "https://img.youtube.com/vi/Ck3FuTzZvhI/0.jpg"
    }
  ]

  return (
    <div className={`min-h-screen w-full ${
      theme === 'dark' 
        ? 'bg-background text-foreground' 
        : 'bg-white text-black bg-graph-paper bg-graph-paper'
    }`}>
      <header className={`w-full px-4 py-6 ${theme === 'dark' ? 'bg-background' : 'bg-white/80'}`}>
        <div className="max-w-7xl mx-auto flex justify-between items-center">
          <div className="flex items-center space-x-2">
            <BookOpen className="w-8 h-8 text-primary" />
            <span className="text-xl font-bold">LectureGen</span>
          </div>
          <nav className="space-x-4 flex items-center">
            <Button variant="outline" asChild>
              <Link href="/sign-in">Log In</Link>
            </Button>
            <Button asChild>
              <Link href="/sign-up">Sign Up</Link>
            </Button>
          </nav>
        </div>
      </header>

      <main>
        <section className={`w-full px-4 py-24 ${
          theme === 'dark' ? '' : 'bg-white/80'
        }`}>
          <div className="max-w-7xl mx-auto text-center">
            <h1 className="text-4xl font-bold mb-6">Transform Your Research with AI</h1>
            <p className="text-xl mb-12">Unlock the power of your academic papers with our AI-driven analysis tool</p>
            <div className="flex justify-center mb-12">
              <div className="relative w-64 h-64">
                <div className={`absolute inset-0 ${theme === 'dark' ? 'bg-primary/20' : 'bg-primary/10'} rounded-full animate-pulse`}></div>
                <div className={`absolute inset-4 ${theme === 'dark' ? 'bg-primary/40' : 'bg-primary/20'} rounded-full animate-pulse animation-delay-500`}></div>
                <div className={`absolute inset-8 ${theme === 'dark' ? 'bg-primary/60' : 'bg-primary/30'} rounded-full animate-pulse animation-delay-1000`}></div>
                <div className="absolute inset-0 flex items-center justify-center">
                  <BookOpen className="w-16 h-16 text-primary" />
                </div>
                <div className="absolute top-1/2 left-full transform -translate-y-1/2 w-16 h-1 bg-primary"></div>
                <div className="absolute top-1/2 left-[calc(100%+4rem)] transform -translate-y-1/2">
                  <Video className="w-16 h-16 text-primary" />
                </div>
              </div>
            </div>
            <div className="max-w-md mx-auto">
              <form onSubmit={handleSubmit} className="flex space-x-2">
                <Input 
                  type="email" 
                  placeholder="Enter your email" 
                  value={email} 
                  onChange={(e) => setEmail(e.target.value)}
                  className={`flex-grow ${theme === 'dark' ? 'bg-input' : 'bg-gray-100'}`}
                />
                <Button type="submit">Get Started</Button>
              </form>
            </div>
          </div>
        </section>

        <section className={`w-full py-24 ${
          theme === 'dark' ? 'bg-muted' : 'bg-gray-100/80'
        }`}>
          <div className="max-w-7xl mx-auto px-4">
            <h2 className="text-3xl font-bold mb-12 text-center"
[truncated — 5215 more characters]
```

[120 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]