# Project export: LearnIt

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: Learn through educational generative AI video content.
- Devpost: https://devpost.com/software/learnit-uzos68
- GitHub: https://github.com/leongmichael/learn-it
- Video: https://www.youtube.com/embed/tL_x_3Ti6wI?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — leongmichael (47 commits)

## Devpost submission (written by the team)

### What it does

LearnIt generates educational videos based on user prompts. LearnIt utilizes Google's Gemini 1.5 for script and video generation based on the user's prompts. To further aid in educating, DeepGram is used for audio narration to explain to users the concepts being taught on screen effectively. How it was built Manim - Powerful animation engine created by 3Blue1Brown used for video generation Google Gemini 1.5 - LLM used for script and video code generation using Manim DeepGram - SpeechAI used for script text-to-speech React - Javascript Frontend library for web app FastAPI - Python Backend library for server-side scripts Pydub - Python library for audio manipulation. Used to sync text-to-speech audio files to generated video Moviepy - Python library for video editing. Used to splice audio and video files together. Challenges ran into One challenge was having reliable Manim code to generate videos. Specifically, Gemini isn't always up-to-date with various libraries, Manim included. Therefore, if prompted to generate Manim code, it would generate non-functional code almost every time. To fix this issue, I fed up-to-date Manim documentation into the Gemini model to have it generate more accurate responses. This significantly increased the likelihood of generating usable code. One second challenge was generating a script that would sync up with the video. At the start, the script duration would be over 2x as long as the video duration. I tried to reduce the amount of unnecessary dialogue, but only slightly fixed the issue. Since the script duration would most always be longer than the video duration, given it wasn't already the same duration, I increased the speed of the text-to-speech audio outputs to align with the video duration. Though this further helped the issue, audio files with significantly longer durations would be sped up to the duration of low-duration videos, warping the quality of the audio. Due to the time constraints, I didn't manage to find a more viable solution. One last challenge was incorporating non-physics/math videos. Manim is meant for math-based animations. Therefore, it was not viable to have users generate videos on other subjects (history, economics, etc). In the future, I'd like to incorporate another engine/video/image generation to generate a larger variety of videos. Accomplishments proud of I've never used a LLM API before so I'm proud of what I was able to implement in such a short time-frame. I'm also proud to use more of a variety of different APIs/frameworks. I never considered using SpeechAI APIs prior to this but now that I've used DeepGram, I hope to continue using it for future projects. Furthermore, I've never used FastAPI before so I was glad to learn more web frameworks. What was learned As mentioned above, I learned to use LLM APIs, specifically Google's Gemini 1.5. Furthermore, I learned how to use DeepGram's text-to-speech API as well as FastAPI for backend development.

### What's next

LearnIt currently only utilizes Manim for video generation, so I'd like to incorporate other engines/video/image generation to generate a larger variety of videos. Furthermore, I'd like to be able for users to prompt live questions regarding outputted videos and create subsequent video responses (i.e. clarifying a misconception that was mentioned in the initial video). Finally, I hope to host this publically for people across the world to utilize and optimize learning.

## README (from the GitHub repository)

# LearnIt
Learn through generative AI video content. \
<img src="client/src/components/images/landingpage.png"  width="575" height="500"/>

## Setup / Installation

- [Install](https://nodejs.org/en/download) Node.js 18 or higher
- [Install](https://www.python.org/downloads/release/python-3110/) Python 3.11 or higher

In the `server` directory, you need the following yaml configuration file ```keys.yaml``` to save the Google Gemini and Deepgram API keys:

```
gemini: "KEY"
deepgram: "KEY"
```

## Available Scripts

NOTE: Scripts are directory dependent. Certain scripts will not work in other directories.

### `npm run electron-dev`
Runs the application frontend client in the development mode. Make sure to run in the `client` directory.

### `npm install`

Installs all packages listed in the `package.json` file in the directory you are in. Make sure to run in the `client` directory.

### `npm run format`

Formats all code using Prettier. Make sure to run in the `client` directory to format all JavaScript files. \
In VS Code, you can install the plugin [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to format code automatically when saving a file.

### ```source env/bin/activate```
Starts Python virtual environment. Make sure to run in the `server` directory. 

### ```deactivate```
Deactivates Python virtual environment. Make sure to run in the `server` directory.

### `pip install -r requirements.txt`

Installs all required dependencies to run the Python scripts. Make sure to run in the `server` directory.

## Detected evidence (automated analysis)

Indexed codebase: 5678 recognized source files, 78386 KB.
- C (language) — detected in the code
- C++ (language) — detected in the code
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 7815)

```
client/.gitignore
client/.prettierrc
client/package.json
client/public/electron.js
client/public/index.html
client/public/manifest.json
client/public/robots.txt
client/src/App.js
client/src/components/VideoPlayer.js
client/src/index.css
client/src/index.js
client/src/pages/Home.js
client/src/pages/NotFound.js
client/src/pages/Template.js
client/src/reportWebVitals.js
client/src/setupTests.js
client/videogen.py
README.md
server/.gitignore
server/documentation.txt
server/env/bin/activate
server/env/bin/activate.csh
server/env/bin/activate.fish
server/env/bin/Activate.ps1
server/env/bin/cygdb
server/env/bin/cython
server/env/bin/cythonize
server/env/bin/f2py
server/env/bin/httpx
server/env/bin/manim
server/env/bin/manimce
server/env/bin/markdown-it
server/env/bin/normalizer
server/env/bin/pip
server/env/bin/pip3
server/env/bin/pip3.11
server/env/bin/pygmentize
server/env/bin/pyrsa-decrypt
server/env/bin/pyrsa-encrypt
server/env/bin/pyrsa-keygen
server/env/bin/pyrsa-priv2pub
server/env/bin/pyrsa-sign
server/env/bin/pyrsa-verify
server/env/bin/python
server/env/bin/python3
server/env/bin/python3.11
server/env/bin/srt
server/env/bin/srt-deduplicate
server/env/bin/srt-fixed-timeshift
server/env/bin/srt-linear-timeshift
server/env/bin/srt-lines-matching
server/env/bin/srt-mux
server/env/bin/srt-normalise
server/env/bin/srt-play
server/env/bin/srt-process
server/env/bin/tqdm
server/env/bin/watchmedo
server/env/lib/python3.11/site-packages/_distutils_hack/__init__.py
server/env/lib/python3.11/site-packages/_distutils_hack/override.py
server/env/lib/python3.11/site-packages/_moderngl.py
server/env/lib/python3.11/site-packages/_yaml/__init__.py
server/env/lib/python3.11/site-packages/aenum-3.1.15.dist-info/INSTALLER
server/env/lib/python3.11/site-packages/aenum-3.1.15.dist-info/METADATA
server/env/lib/python3.11/site-packages/aenum-3.1.15.dist-info/RECORD
server/env/lib/python3.11/site-packages/aenum-3.1.15.dist-info/top_level.txt
server/env/lib/python3.11/site-packages/aenum-3.1.15.dist-info/WHEEL
server/env/lib/python3.11/site-packages/aenum/__init__.py
server/env/lib/python3.11/site-packages/aenum/_common.py
server/env/lib/python3.11/site-packages/aenum/_constant.py
server/env/lib/python3.11/site-packages/aenum/_enum.py
server/env/lib/python3.11/site-packages/aenum/_py2.py
server/env/lib/python3.11/site-packages/aenum/_py3.py
server/env/lib/python3.11/site-packages/aenum/_tuple.py
server/env/lib/python3.11/site-packages/aenum/CHANGES
server/env/lib/python3.11/site-packages/aenum/doc/aenum.rst
server/env/lib/python3.11/site-packages/aenum/LICENSE
server/env/lib/python3.11/site-packages/aenum/test_v3.py
server/env/lib/python3.11/site-packages/aenum/test_v37.py
server/env/lib/python3.11/site-packages/aenum/test.py
server/env/lib/python3.11/site-packages/aiofiles-24.1.0.dist-info/INSTALLER
server/env/lib/python3.11/site-packages/aiofiles-24.1.0.dist-info/licenses/LICENSE
server/env/lib/python3.11/site-packages/aiofiles-24.1.0.dist-info/licenses/NOTICE
server/env/lib/python3.11/site-packages/aiofiles-24.1.0.dist-info/METADATA
server/env/lib/python3.11/site-packages/aiofiles-24.1.0.dist-info/RECORD
server/env/lib/python3.11/site-packages/aiofiles-24.1.0.dist-info/WHEEL
server/env/lib/python3.11/site-packages/aiofiles/__init__.py
server/env/lib/python3.11/site-packages/aiofiles/base.py
server/env/lib/python3.11/site-packages/aiofiles/os.py
server/env/lib/python3.11/site-packages/aiofiles/ospath.py
server/env/lib/python3.11/site-packages/aiofiles/tempfile/__init__.py
server/env/lib/python3.11/site-packages/aiofiles/tempfile/temptypes.py
server/env/lib/python3.11/site-packages/aiofiles/threadpool/__init__.py
server/env/lib/python3.11/site-packages/aiofiles/threadpool/binary.py
server/env/lib/python3.11/site-packages/aiofiles/threadpool/text.py
server/env/lib/python3.11/site-packages/aiofiles/threadpool/utils.py
server/env/lib/python3.11/site-packages/aiohappyeyeballs-2.4.3.dist-info/INSTALLER
server/env/lib/python3.11/site-packages/aiohappyeyeballs-2.4.3.dist-info/LICENSE
server/env/lib/python3.11/site-packages/aiohappyeyeballs-2.4.3.dist-info/METADATA
server/env/lib/python3.11/site-packages/aiohappyeyeballs-2.4.3.dist-info/RECORD
server/env/lib/python3.11/site-packages/aiohappyeyeballs-2.4.3.dist-info/WHEEL
server/env/lib/python3.11/site-packages/aiohappyeyeballs/__init__.py
server/env/lib/python3.11/site-packages/aiohappyeyeballs/_staggered.py
server/env/lib/python3.11/site-packages/aiohappyeyeballs/impl.py
server/env/lib/python3.11/site-packages/aiohappyeyeballs/py.typed
server/env/lib/python3.11/site-packages/aiohappyeyeballs/types.py
server/env/lib/python3.11/site-packages/aiohappyeyeballs/utils.py
server/env/lib/python3.11/site-packages/aiohttp-3.10.10.dist-info/INSTALLER
server/env/lib/python3.11/site-packages/aiohttp-3.10.10.dist-info/LICENSE.txt
server/env/lib/python3.11/site-packages/aiohttp-3.10.10.dist-info/METADATA
server/env/lib/python3.11/site-packages/aiohttp-3.10.10.dist-info/RECORD
server/env/lib/python3.11/site-packages/aiohttp-3.10.10.dist-info/top_level.txt
server/env/lib/python3.11/site-packages/aiohttp-3.10.10.dist-info/WHEEL
server/env/lib/python3.11/site-packages/aiohttp/__init__.py
server/env/lib/python3.11/site-packages/aiohttp/_cparser.pxd
server/env/lib/python3.11/site-packages/aiohttp/_find_header.pxd
server/env/lib/python3.11/site-packages/aiohttp/_headers.pxi
server/env/lib/python3.11/site-packages/aiohttp/_helpers.pyi
server/env/lib/python3.11/site-packages/aiohttp/_helpers.pyx
server/env/lib/python3.11/site-packages/aiohttp/_http_parser.pyx
server/env/lib/python3.11/site-packages/aiohttp/_http_writer.pyx
[7695 more files omitted for size]
```

### Dependencies

- client/package.json: @emotion/react@^11.13.0, @emotion/styled@^11.13.0, @fontsource/roboto@^5.0.3, @googlemaps/google-maps-services-js@^3.4.0, @mui/icons-material@^5.16.6, @mui/material@^5.16.6, @mui/x-date-pickers@^7.10.0, @testing-library/jest-dom@^5.16.5, @testing-library/react@^13.4.0, @testing-library/user-event@^13.5.0, axios@^1.7.2, chart.js@^4.4.3, child_process@^1.0.2, concurrently@^9.0.1, cross-env@^7.0.3, dayjs@^1.11.11, deck.gl@^8.9.30, electron@^33.0.2, electron-builder@^25.1.8, electron-is-dev@^3.0.1, js-yaml@^4.1.0, lucide-react@^0.453.0, mapbox-gl@^2.15.0, moment@^2.30.1, moment-timezone@^0.5.44, papaparse@^5.4.1, prettier@^3.3.3, react@^18.2.0, react-chartjs-2@^5.2.0, react-dom@^18.2.0, react-dropzone@^14.2.3, react-map-gl@^7.1.6, react-router-dom@^6.14.0, react-scripts@^5.0.1, wait-on@^8.0.1, web-vitals@^2.1.4
- server/requirements.txt: aenum@==3.1.15, aiofiles@==24.1.0, aiohappyeyeballs@==2.4.3, aiohttp@==3.10.10, aiosignal@==1.3.1, annotated-types@==0.7.0, anyio@==4.6.2.post1, appnope@==0.1.4, asttokens@==2.4.1, attrs@==24.2.0, black@==22.12.0, cachetools@==5.5.0, casadi@==3.6.3, certifi@==2024.8.30, charset-normalizer@==3.4.0, click@==8.1.7, cloup@==3.0.5, comm@==0.2.2, contourpy@==1.2.0, cycler@==0.12.1, Cython@==3.0.11, dataclasses-json@==0.6.7, debugpy@==1.8.1, decorator@==4.4.2, deepgram-sdk@==3.7.4, deprecation@==2.1.0, distlib@==0.3.9, dnspython@==2.7.0, email_validator@==2.2.0, executing@==2.0.1, fastapi@==0.115.2, fastapi-cli@==0.0.5, filelock@==3.16.1, fonttools@==4.47.0, frozenlist@==1.4.1, glcontext@==3.0.0, google-ai-generativelanguage@==0.6.10, google-api-core@==2.21.0, google-api-python-client@==2.149.0, google-auth@==2.35.0, google-auth-httplib2@==0.2.0, google-generativeai@==0.8.3, googleapis-common-protos@==1.65.0, googlemaps@==4.10.0, grpcio@==1.67.0, grpcio-status@==1.67.0, h11@==0.14.0, httpcore@==1.0.6, httplib2@==0.22.0, httptools@==0.6.4, httpx@==0.27.2, idna@==3.10, imageio@==2.36.0, imageio-ffmpeg@==0.5.1, ipykernel@==6.29.4, ipython@==8.23.0, isosurfaces@==0.1.2, jedi@==0.19.1, Jinja2@==3.1.4, joblib@==1.4.2, jupyter_client@==8.6.1, jupyter_core@==5.7.2, kiwisolver@==1.4.5, manim@==0.18.1, ManimPango@==0.6.0, mapbox_earcut@==1.0.2, markdown-it-py@==3.0.0, MarkupSafe@==3.0.2, marshmallow@==3.23.0, matplotlib@==3.8.2, matplotlib-inline@==0.1.7, mdurl@==0.1.2, moderngl@==5.12.0, moderngl-window@==2.4.6, moviepy@==1.0.3, mplcursors@==0.5.3, multidict@==6.1.0, multipledispatch@==1.0.0, mypy-extensions@==1.0.0, nest-asyncio@==1.6.0, networkx@==3.4.1, numpy@==1.26.4, numpy-stl@==3.1.1, packaging@==24.1, pandas@==2.0.2, parso@==0.8.4, pathspec@==0.12.1, pexpect@==4.9.0, pillow@==11.0.0, platformdirs@==4.1.0, proglog@==0.1.10, prompt-toolkit@==3.0.43, propcache@==0.2.0, proto-plus@==1.24.0, protobuf@==5.28.2, psutil@==5.9.8, ptyprocess@==0.7.0, pure-eval@==0.2.2, pyasn1@==0.6.1, pyasn1_modules@==0.4.1, pycairo@==1.27.0, pydantic@==2.9.2, pydantic_core@==2.23.4, pydub@==0.25.1, pygame@==2.2.0, pyglet@==2.0.18, Pygments@==2.18.0, pyntcore@==2023.4.2.0, pyobjc-core@==10.3.1, pyobjc-framework-Cocoa@==10.3.1, PyOpenGL@==3.1.6, pyparsing@==3.2.0, PyQt5@==5.15.10, PyQt5-Qt5@==5.15.12, PyQt5-sip@==12.13.0, PyQt6@==6.4.0, PyQt6-Qt6@==6.6.1, PyQt6-sip@==13.6.0, pyqtgraph@==0.13.1, pyrr@==0.10.3, python-dateutil@==2.8.2, python-dotenv@==1.0.1, python-multipart@==0.0.12, python-utils@==3.8.1, pytz@==2024.1, PyYAML@==6.0.2, pyzmq@==26.0.0, qtmodern@==0.2.0, QtPy@==2.4.1, requests@==2.32.3, rich@==13.9.2, robotpy-wpinet@==2023.4.3.0, robotpy-wpiutil@==2023.4.3.0, rsa@==4.9, scikit-learn@==1.5.1, scipy@==1.14.1, screeninfo@==0.8.1, seaborn@==0.13.2, shellingham@==1.5.4, simplejson@==3.19.2, six@==1.16.0, skia-pathops@==0.8.0.post2, sniffio@==1.3.1, srt@==3.5.3, stack-data@==0.6.3, starlette@==0.40.0, svgelements@==1.9.6, tabulate@==0.9.0, threadpoolctl@==3.5.0, tk@==0.1.0, tornado@==6.4, tqdm@==4.66.5, traitlets@==5.14.2, transformations@==2024.6.1, typer@==0.12.5, typing_extensions@==4.12.2, typing-inspect@==0.9.0, tzdata@==2024.1, ucimlrepo@==0.0.7, uritemplate@==4.1.1, urllib3@==2.2.3, uvicorn@==0.32.0, uvloop@==0.21.0, virtualenv@==20.27.0, watchdog@==5.0.3, watchfiles@==0.24.0, wcwidth@==0.2.13, websockets@==13.1, yarl@==1.15.5

### Recent commits (newest first)

- ignore pycache
- read documentation from txt file
- change current working directory
- conver to local processes (#3)
- Convert to Electron Application (#2)
- Update README.md
- remove header
- adjust image width
- optimize readme
- remove temp files
- cleanup
- tuning 4
- tuning 3
- tuning 2
- model tuning
- remove unused
- cleanup
- cleanup
- default video
- misc styling

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

### server/env/lib/python3.11/site-packages/mapbox_earcut-1.0.2.dist-info/LICENSE.md

```markdown
The bindings to the earcut library are licensed under the following terms:

> ISC License
>
> Copyright (c) 2018, Samuel Kogler
>
> Permission to use, copy, modify, and/or distribute this software for any purpose
> with or without fee is hereby granted, provided that the above copyright notice
> and this permission notice appear in all copies.
>
> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
> REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
> FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
> INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
> OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
> THIS SOFTWARE.

For the license of the actual earcut library, see the license file in the include folder.
```

### server/env/lib/python3.11/site-packages/idna-3.10.dist-info/LICENSE.md

```markdown
BSD 3-Clause License

Copyright (c) 2013-2024, Kim Davies and contributors.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

```

### client/package.json

```
{
  "name": "client",
  "version": "0.1.0",
  "type": "module",
  "private": true,
  "dependencies": {
    "@emotion/react": "^11.13.0",
    "@emotion/styled": "^11.13.0",
    "@fontsource/roboto": "^5.0.3",
    "@googlemaps/google-maps-services-js": "^3.4.0",
    "@mui/icons-material": "^5.16.6",
    "@mui/material": "^5.16.6",
    "@mui/x-date-pickers": "^7.10.0",
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.7.2",
    "chart.js": "^4.4.3",
    "child_process": "^1.0.2",
    "dayjs": "^1.11.11",
    "deck.gl": "^8.9.30",
    "electron-is-dev": "^3.0.1",
    "js-yaml": "^4.1.0",
    "lucide-react": "^0.453.0",
    "mapbox-gl": "^2.15.0",
    "moment": "^2.30.1",
    "moment-timezone": "^0.5.44",
    "papaparse": "^5.4.1",
    "prettier": "^3.3.3",
    "react": "^18.2.0",
    "react-chartjs-2": "^5.2.0",
    "react-dom": "^18.2.0",
    "react-dropzone": "^14.2.3",
    "react-map-gl": "^7.1.6",
    "react-router-dom": "^6.14.0",
    "react-scripts": "^5.0.1",
    "web-vitals": "^2.1.4"
  },
  "main": "public/electron.js",
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "format": "prettier ./ --write",
    "format-check": "prettier ./ --check",
    "electron-dev": "concurrently \"cross-env BROWSER=none npm start\" \"wait-on http://localhost:3000 && electron .\"",
    "electron-pack": "npm run build && electron-builder -c.extraMetadata.main=build/electron.js",
    "preelectron-pack": "npm run build"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "concurrently": "^9.0.1",
    "cross-env": "^7.0.3",
    "electron": "^33.0.2",
    "electron-builder": "^25.1.8",
    "wait-on": "^8.0.1"
  },
  "build": {
    "appId": "com.example.electron-react",
    "files": [
      "build/**/*",
      "node_modules/**/*"
    ],
    "directories": {
      "buildResources": "assets"
    }
  }
}

```

### server/requirements.txt

```
aenum==3.1.15
aiofiles==24.1.0
aiohappyeyeballs==2.4.3
aiohttp==3.10.10
aiosignal==1.3.1
annotated-types==0.7.0
anyio==4.6.2.post1
appnope==0.1.4
asttokens==2.4.1
attrs==24.2.0
black==22.12.0
cachetools==5.5.0
casadi==3.6.3
certifi==2024.8.30
charset-normalizer==3.4.0
click==8.1.7
cloup==3.0.5
comm==0.2.2
contourpy==1.2.0
cycler==0.12.1
Cython==3.0.11
dataclasses-json==0.6.7
debugpy==1.8.1
decorator==4.4.2
deepgram-sdk==3.7.4
deprecation==2.1.0
distlib==0.3.9
dnspython==2.7.0
email_validator==2.2.0
executing==2.0.1
fastapi==0.115.2
fastapi-cli==0.0.5
filelock==3.16.1
fonttools==4.47.0
frozenlist==1.4.1
glcontext==3.0.0
google-ai-generativelanguage==0.6.10
google-api-core==2.21.0
google-api-python-client==2.149.0
google-auth==2.35.0
google-auth-httplib2==0.2.0
google-generativeai==0.8.3
googleapis-common-protos==1.65.0
googlemaps==4.10.0
grpcio==1.67.0
grpcio-status==1.67.0
h11==0.14.0
httpcore==1.0.6
httplib2==0.22.0
httptools==0.6.4
httpx==0.27.2
idna==3.10
imageio==2.36.0
imageio-ffmpeg==0.5.1
ipykernel==6.29.4
ipython==8.23.0
isosurfaces==0.1.2
jedi==0.19.1
Jinja2==3.1.4
joblib==1.4.2
jupyter_client==8.6.1
jupyter_core==5.7.2
kiwisolver==1.4.5
manim==0.18.1
ManimPango==0.6.0
mapbox_earcut==1.0.2
markdown-it-py==3.0.0
MarkupSafe==3.0.2
marshmallow==3.23.0
matplotlib==3.8.2
matplotlib-inline==0.1.7
mdurl==0.1.2
moderngl==5.12.0
moderngl-window==2.4.6
moviepy==1.0.3
mplcursors==0.5.3
multidict==6.1.0
multipledispatch==1.0.0
mypy-extensions==1.0.0
nest-asyncio==1.6.0
networkx==3.4.1
numpy==1.26.4
numpy-stl==3.1.1
packaging==24.1
pandas==2.0.2
parso==0.8.4
pathspec==0.12.1
pexpect==4.9.0
pillow==11.0.0
platformdirs==4.1.0
proglog==0.1.10
prompt-toolkit==3.0.43
propcache==0.2.0
proto-plus==1.24.0
protobuf==5.28.2
psutil==5.9.8
ptyprocess==0.7.0
pure-eval==0.2.2
pyasn1==0.6.1
pyasn1_modules==0.4.1
pycairo==1.27.0
pydantic==2.9.2
pydantic_core==2.23.4
pydub==0.25.1
pygame==2.2.0
pyglet==2.0.18
Pygments==2.18.0
pyntcore==2023.4.2.0
pyobjc-core==10.3.1
pyobjc-framework-Cocoa==10.3.1
PyOpenGL==3.1.6
pyparsing==3.2.0
PyQt5==5.15.10
PyQt5-Qt5==5.15.12
PyQt5-sip==12.13.0
PyQt6==6.4.0
PyQt6-Qt6==6.6.1
PyQt6-sip==13.6.0
pyqtgraph==0.13.1
pyrr==0.10.3
python-dateutil==2.8.2
python-dotenv==1.0.1
python-multipart==0.0.12
python-utils==3.8.1
pytz==2024.1
PyYAML==6.0.2
pyzmq==26.0.0
qtmodern==0.2.0
QtPy==2.4.1
requests==2.32.3
rich==13.9.2
robotpy-wpinet==2023.4.3.0
robotpy-wpiutil==2023.4.3.0
rsa==4.9
scikit-learn==1.5.1
scipy==1.14.1
screeninfo==0.8.1
seaborn==0.13.2
shellingham==1.5.4
simplejson==3.19.2
six==1.16.0
skia-pathops==0.8.0.post2
sniffio==1.3.1
srt==3.5.3
stack-data==0.6.3
starlette==0.40.0
svgelements==1.9.6
tabulate==0.9.0
threadpoolctl==3.5.0
tk==0.1.0
tornado==6.4
tqdm==4.66.5
traitlets==5.14.2
transformations==2024.6.1
typer==0.12.5
typing-inspect==0.9.0
typing_extensions==4.12.2
tzdata==2024.1
ucimlrepo==0.0.7
uritemplate==4.1.1
urllib3==2.2.3
uvicorn==0.32.0
uvloop==0.21.0
virtualenv==20.27.0
watchdog==5.0.3
watchfiles==0.24.0
wcwidth==0.2.13
websockets==13.1
yarl==1.15.5

```

### server/main.py

```python
import sys
from gemini import enter_prompt
from tts import tts
from videoediting import speed_up_audio, output_video, delete_media
import re
import time
import subprocess

def process_prompt(prompt):
    print("running")
    response = enter_prompt(prompt)

    script = ""
    code = ""

    find_code = re.search(r'"code":\s*"(.*?)"\s*}', response, re.DOTALL)
    if find_code:
        code = find_code.group(1)
        # Unescape the escaped quotes and backslashes
        code = code.encode().decode('unicode_escape')
    else:
        print("code not found")
    
    find_script = re.search(r'"script":\s*"(.*?)",\s*"code":', response, re.DOTALL)
    if find_script:
        script = find_script.group(1)
        # Unescape the escaped quotes and backslashes
        script = script.encode().decode('unicode_escape')
    else:
        print("script not found")


    # text to speech
    tts(script)

    # write generated code to python file
    with open('videogen.py', 'w') as file:
        file.write(code)

    # run terminal script to generate manim video
    try:
        command = "manim videogen.py GeneratedScene -ql"
        result = subprocess.run(command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        # Get the output and error (if any)
        output = result.stdout.decode()
        error = result.stderr.decode()

        print("Output:\n", output)
        if error:
            print("Error:\n", error)
    except subprocess.CalledProcessError as e:
        print(f"An error occurred: {e}")

    
    speed = speed_up_audio()
    time.sleep(0.5)
    output_video(speed)
    delete_media()
    
    return {"prompt": prompt}

if __name__ == "__main__":
    if len(sys.argv) > 1:
        prompt = sys.argv[1]
        process_prompt(prompt)

```

### client/src/index.js

```javascript
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.js";
import "./index.css";
import reportWebVitals from "./reportWebVitals.js";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

```

### client/src/App.js

```javascript
import { ThemeProvider, createTheme } from "@mui/material";
import { Suspense } from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";

import Home from "./pages/Home.js";
import NotFound from "./pages/NotFound.js";

const theme = createTheme({});

export default function App() {
  return (
    <ThemeProvider theme={theme}>
      <BrowserRouter>
        <Suspense>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/*" element={<NotFound />} />
          </Routes>
        </Suspense>
      </BrowserRouter>
    </ThemeProvider>
  );
}

```

### server/env/lib/python3.11/site-packages/rsa/cli.py

```python
#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

"""Commandline scripts.

These scripts are called by the executables defined in setup.py.
"""

import abc
import sys
import typing
import optparse

import rsa
import rsa.key
import rsa.pkcs1

HASH_METHODS = sorted(rsa.pkcs1.HASH_METHODS.keys())
Indexable = typing.Union[typing.Tuple, typing.List[str]]


def keygen() -> None:
    """Key generator."""

    # Parse the CLI options
    parser = optparse.OptionParser(
        usage="usage: %prog [options] keysize",
        description='Generates a new RSA key pair of "keysize" bits.',
    )

    parser.add_option(
        "--pubout",
        type="string",
        help="Output filename for the public key. The public key is "
        "not saved if this option is not present. You can use "
        "pyrsa-priv2pub to create the public key file later.",
    )

    parser.add_option(
        "-o",
        "--out",
        type="string",
        help="Output filename for the private key. The key is "
        "written to stdout if this option is not present.",
    )

    parser.add_option(
        "--form",
        help="key format of the private and public keys - default PEM",
        choices=("PEM", "DER"),
        default="PEM",
    )

    (cli, cli_args) = parser.parse_args(sys.argv[1:])

    if len(cli_args) != 1:
        parser.print_help()
        raise SystemExit(1)

    try:
        keysize = int(cli_args[0])
    except ValueError as ex:
        parser.print_help()
        print("Not a valid number: %s" % cli_args[0], file=sys.stderr)
        raise SystemExit(1) from ex

    print("Generating %i-bit key" % keysize, file=sys.stderr)
    (pub_key, priv_key) = rsa.newkeys(keysize)

    # Save public key
    if cli.pubout:
        print("Writing public key to %s" % cli.pubout, file=sys.stderr)
        data = pub_key.save_pkcs1(format=cli.form)
        with open(cli.pubout, "wb") as outfile:
            outfile.write(data)

    # Save private key
    data = priv_key.save_pkcs1(format=cli.form)

    if cli.out:
        print("Writing private key to %s" % cli.out, file=sys.stderr)
        with open(cli.out, "wb") as outfile:
            outfile.write(data)
    else:
        print("Writing private key to stdout", file=sys.stderr)
        sys.stdout.buffer.write(data)


class CryptoOperation(metaclass=abc.ABCMeta):
    """CLI callable that operates with input, output, and a key."""

    keyname = "public"  # or 'private'
    usage = "usage: %%prog [options] %(keyname)s_key"
    description = ""
    operation = "decrypt"
    operation_past = "decrypted"
    operation_progressive = "decrypting"
    input_help = "Name of the file to %(operation)s. Reads from stdin if " "not specified."
    output_help = (
        "Name of the file to write the %(operation_past)s file "
        "to. Written to stdout if this option is not present."
    )
    expected_cli_args = 1
    has_output = True

    key_class = rsa.PublicKey  # type: typing.Type[rsa.key.AbstractKey]

    def __init__(self) -> None:
        self.usage = self.usage % self.__class__.__dict__
        self.input_help = self.input_help % self.__class__.__dict__
        self.output_help = self.output_help % self.__class__.__dict__

    @abc.abstractmethod
    def perform_operation(
        self, indata: bytes, key: rsa.key.AbstractKey, cli_args: Indexable
    ) -> typing.Any:
        """Performs the program's operation.

        Implement in a subclass.

        :returns: the data to write to the output.
        """

    def __call__(self) -> None:
        """Runs the program."""

        (cli, cli_args) = self.parse_cli()

        key = self.read_key(cli_args[0], cli.keyform)

        indata = self.read_infile(cli.input)

        print(self.operation_progressive.title(), file=sys.stderr)
        outdata = self.perform_operation(indata, key, cli_args)

        if self.has_output:
            self.write_outfile(outdata, cli.output)

    def parse_cli(self) -> typing.Tuple[optparse.Values, typing.List[str]]:
        """Parse the CLI options

        :returns: (cli_opts, cli_args)
        """

        parser = optparse.OptionParser(usage=self.usage, description=self.description)

        parser.add_option("-i", "--input", type="string", help=self.input_help)

        if self.has_output:
            parser.add_option("-o", "--output", type="string", help=self.output_help)

        parser.add_option(
            "--keyform",
            help="Key format of the %s key - default PEM" % self.keyname,
            choices=("PEM", "DER"),
            default="PEM",
        )

        (cli, cli_args) = parser.parse_args(sys.argv[1:])

        if len(cli_args) != self.expected_cli_args:
            parser.print_help()
            raise SystemExit(1)

        return cli, cli_args

    def read_key(self, filename: str, keyform: str) -> rsa.key.AbstractKey:
        """Reads a public or private key."""

        print("Reading %s key from %s" % (self.keyname, filename), file=sys.stderr)
        with open(filename, "rb") as keyfile:
            keydata = keyfile.read()

        return self.key_class.load_pkcs1(keydata, keyform)

    def read_infile(self, inname: str) -> bytes:
        """Read the input file"""

        if inname:
       
[truncated — 4182 more characters]
```

### server/env/lib/python3.11/site-packages/tqdm/cli.py

```python
"""
Module version for monitoring CLI pipes (`... | python -m tqdm | ...`).
"""
import logging
import re
import sys
from ast import literal_eval as numeric
from textwrap import indent

from .std import TqdmKeyError, TqdmTypeError, tqdm
from .version import __version__

__all__ = ["main"]
log = logging.getLogger(__name__)


def cast(val, typ):
    log.debug((val, typ))
    if " or " in typ:
        for t in typ.split(" or "):
            try:
                return cast(val, t)
            except TqdmTypeError:
                pass
        raise TqdmTypeError(f"{val} : {typ}")

    # sys.stderr.write('\ndebug | `val:type`: `' + val + ':' + typ + '`.\n')
    if typ == 'bool':
        if (val == 'True') or (val == ''):
            return True
        if val == 'False':
            return False
        raise TqdmTypeError(val + ' : ' + typ)
    if typ == 'chr':
        if len(val) == 1:
            return val.encode()
        if re.match(r"^\\\w+$", val):
            return eval(f'"{val}"').encode()
        raise TqdmTypeError(f"{val} : {typ}")
    if typ == 'str':
        return val
    if typ == 'int':
        try:
            return int(val)
        except ValueError as exc:
            raise TqdmTypeError(f"{val} : {typ}") from exc
    if typ == 'float':
        try:
            return float(val)
        except ValueError as exc:
            raise TqdmTypeError(f"{val} : {typ}") from exc
    raise TqdmTypeError(f"{val} : {typ}")


def posix_pipe(fin, fout, delim=b'\\n', buf_size=256,
               callback=lambda float: None, callback_len=True):
    """
    Params
    ------
    fin  : binary file with `read(buf_size : int)` method
    fout  : binary file with `write` (and optionally `flush`) methods.
    callback  : function(float), e.g.: `tqdm.update`
    callback_len  : If (default: True) do `callback(len(buffer))`.
      Otherwise, do `callback(data) for data in buffer.split(delim)`.
    """
    fp_write = fout.write

    if not delim:
        while True:
            tmp = fin.read(buf_size)

            # flush at EOF
            if not tmp:
                getattr(fout, 'flush', lambda: None)()
                return

            fp_write(tmp)
            callback(len(tmp))
        # return

    buf = b''
    len_delim = len(delim)
    # n = 0
    while True:
        tmp = fin.read(buf_size)

        # flush at EOF
        if not tmp:
            if buf:
                fp_write(buf)
                if callback_len:
                    # n += 1 + buf.count(delim)
                    callback(1 + buf.count(delim))
                else:
                    for i in buf.split(delim):
                        callback(i)
            getattr(fout, 'flush', lambda: None)()
            return  # n

        while True:
            i = tmp.find(delim)
            if i < 0:
                buf += tmp
                break
            fp_write(buf + tmp[:i + len(delim)])
            # n += 1
            callback(1 if callback_len else (buf + tmp[:i]))
            buf = b''
            tmp = tmp[i + len_delim:]


# ((opt, type), ... )
RE_OPTS = re.compile(r'\n {4}(\S+)\s{2,}:\s*([^,]+)')
# better split method assuming no positional args
RE_SHLEX = re.compile(r'\s*(?<!\S)--?([^\s=]+)(\s+|=|$)')

# TODO: add custom support for some of the following?
UNSUPPORTED_OPTS = ('iterable', 'gui', 'out', 'file')

# The 8 leading spaces are required for consistency
CLI_EXTRA_DOC = r"""
    Extra CLI Options
    -----------------
    name  : type, optional
        TODO: find out why this is needed.
    delim  : chr, optional
        Delimiting character [default: '\n']. Use '\0' for null.
        N.B.: on Windows systems, Python converts '\n' to '\r\n'.
    buf_size  : int, optional
        String buffer size in bytes [default: 256]
        used when `delim` is specified.
    bytes  : bool, optional
        If true, will count bytes, ignore `delim`, and default
        `unit_scale` to True, `unit_divisor` to 1024, and `unit` to 'B'.
    tee  : bool, optional
        If true, passes `stdin` to both `stderr` and `stdout`.
    update  : bool, optional
        If true, will treat input as newly elapsed iterations,
        i.e. numbers to pass to `update()`. Note that this is slow
        (~2e5 it/s) since every input must be decoded as a number.
    update_to  : bool, optional
        If true, will treat input as total elapsed iterations,
        i.e. numbers to assign to `self.n`. Note that this is slow
        (~2e5 it/s) since every input must be decoded as a number.
    null  : bool, optional
        If true, will discard input (no stdout).
    manpath  : str, optional
        Directory in which to install tqdm man pages.
    comppath  : str, optional
        Directory in which to place tqdm completion.
    log  : str, optional
        CRITICAL|FATAL|ERROR|WARN(ING)|[default: 'INFO']|DEBUG|NOTSET.
"""


def main(fp=sys.stderr, argv=None):
    """
    Parameters (internal use only)
    ---------
    fp  : file-like object for tqdm
    argv  : list (default: sys.argv[1:])
    """
    if argv is None:
        argv = sys.argv[1:]
    try:
        log_idx = argv.index('--log')
    except ValueError:
        for i in argv:
            if i.startswith('--log='):
                logLevel = i[len('--log='):]
                break
        else:
            logLevel = 'INFO'
    else:
        # argv.pop(log_idx)
        # logLevel = argv.pop(log_idx)
        logLevel = argv[log_idx + 1]
    logging.basicConfig(level=getattr(logging, logLevel),
                        format="%(levelname)s:%(module)s:%(lineno)d:%(message)s")

    # py<3.13 doesn't dedent docstrings
    d = (tqdm.__doc__ if sys.version_info < (3, 13)
         else indent(tqdm.__doc__, "    ")) + CLI_EXTRA_DOC

    opt_types = dict(RE_OPTS.findall(d))
    # opt_types['delim'] = 'chr'

    for o in UNSUPPORTED_OPTS:
        opt_types.pop(o)

    log.debug(sorted(opt_types.items()))

    # d = RE_OPTS.sub(r'  --\1=<\1>  : \2', d)
    split = RE_OPTS.split(
[truncated — 5094 more characters]
```

### server/env/lib/python3.11/site-packages/markdown_it/main.py

```python
from __future__ import annotations

from collections.abc import Callable, Generator, Iterable, Mapping, MutableMapping
from contextlib import contextmanager
from typing import Any, Literal, overload

from . import helpers, presets
from .common import normalize_url, utils
from .parser_block import ParserBlock
from .parser_core import ParserCore
from .parser_inline import ParserInline
from .renderer import RendererHTML, RendererProtocol
from .rules_core.state_core import StateCore
from .token import Token
from .utils import EnvType, OptionsDict, OptionsType, PresetType

try:
    import linkify_it
except ModuleNotFoundError:
    linkify_it = None


_PRESETS: dict[str, PresetType] = {
    "default": presets.default.make(),
    "js-default": presets.js_default.make(),
    "zero": presets.zero.make(),
    "commonmark": presets.commonmark.make(),
    "gfm-like": presets.gfm_like.make(),
}


class MarkdownIt:
    def __init__(
        self,
        config: str | PresetType = "commonmark",
        options_update: Mapping[str, Any] | None = None,
        *,
        renderer_cls: Callable[[MarkdownIt], RendererProtocol] = RendererHTML,
    ):
        """Main parser class

        :param config: name of configuration to load or a pre-defined dictionary
        :param options_update: dictionary that will be merged into ``config["options"]``
        :param renderer_cls: the class to load as the renderer:
            ``self.renderer = renderer_cls(self)
        """
        # add modules
        self.utils = utils
        self.helpers = helpers

        # initialise classes
        self.inline = ParserInline()
        self.block = ParserBlock()
        self.core = ParserCore()
        self.renderer = renderer_cls(self)
        self.linkify = linkify_it.LinkifyIt() if linkify_it else None

        # set the configuration
        if options_update and not isinstance(options_update, Mapping):
            # catch signature change where renderer_cls was not used as a key-word
            raise TypeError(
                f"options_update should be a mapping: {options_update}"
                "\n(Perhaps you intended this to be the renderer_cls?)"
            )
        self.configure(config, options_update=options_update)

    def __repr__(self) -> str:
        return f"{self.__class__.__module__}.{self.__class__.__name__}()"

    @overload
    def __getitem__(self, name: Literal["inline"]) -> ParserInline:
        ...

    @overload
    def __getitem__(self, name: Literal["block"]) -> ParserBlock:
        ...

    @overload
    def __getitem__(self, name: Literal["core"]) -> ParserCore:
        ...

    @overload
    def __getitem__(self, name: Literal["renderer"]) -> RendererProtocol:
        ...

    @overload
    def __getitem__(self, name: str) -> Any:
        ...

    def __getitem__(self, name: str) -> Any:
        return {
            "inline": self.inline,
            "block": self.block,
            "core": self.core,
            "renderer": self.renderer,
        }[name]

    def set(self, options: OptionsType) -> None:
        """Set parser options (in the same format as in constructor).
        Probably, you will never need it, but you can change options after constructor call.

        __Note:__ To achieve the best possible performance, don't modify a
        `markdown-it` instance options on the fly. If you need multiple configurations
        it's best to create multiple instances and initialize each with separate config.
        """
        self.options = OptionsDict(options)

    def configure(
        self, presets: str | PresetType, options_update: Mapping[str, Any] | None = None
    ) -> MarkdownIt:
        """Batch load of all options and component settings.
        This is an internal method, and you probably will not need it.
        But if you will - see available presets and data structure
        [here](https://github.com/markdown-it/markdown-it/tree/master/lib/presets)

        We strongly recommend to use presets instead of direct config loads.
        That will give better compatibility with next versions.
        """
        if isinstance(presets, str):
            if presets not in _PRESETS:
                raise KeyError(f"Wrong `markdown-it` preset '{presets}', check name")
            config = _PRESETS[presets]
        else:
            config = presets

        if not config:
            raise ValueError("Wrong `markdown-it` config, can't be empty")

        options = config.get("options", {}) or {}
        if options_update:
            options = {**options, **options_update}  # type: ignore

        self.set(options)  # type: ignore

        if "components" in config:
            for name, component in config["components"].items():
                rules = component.get("rules", None)
                if rules:
                    self[name].ruler.enableOnly(rules)
                rules2 = component.get("rules2", None)
                if rules2:
                    self[name].ruler2.enableOnly(rules2)

        return self

    def get_all_rules(self) -> dict[str, list[str]]:
        """Return the names of all active rules."""
        rules = {
            chain: self[chain].ruler.get_all_rules()
            for chain in ["core", "block", "inline"]
        }
        rules["inline2"] = self.inline.ruler2.get_all_rules()
        return rules

    def get_active_rules(self) -> dict[str, list[str]]:
        """Return the names of all active rules."""
        rules = {
            chain: self[chain].ruler.get_active_rules()
            for chain in ["core", "block", "inline"]
        }
        rules["inline2"] = self.inline.ruler2.get_active_rules()
        return rules

    def enable(
        self, names: str | Iterable[str], ignoreInvalid: bool = False
    ) -> MarkdownIt:
        """Enable list or rules. (chainable)

        :param names: rule name or list of rule names to enable.
        :param ignoreInvalid: set `true` to ignore errors when rule not found.

        It will a
[truncated — 6772 more characters]
```

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