# Project export: BooloTube

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 10.0
- Tagline: Using software to align reactions videos with original content, ensuring no copyright violations. Also allows users to watch the original content alongside reacted content.
- Devpost: https://devpost.com/software/boolotube
- GitHub: https://github.com/goblinrum/boolotube
- Demo: https://boolotube.reflex.run/
- Video: https://www.youtube.com/embed/EuWVGBBEtvM?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — Bill Hu (24 commits), Maanuj Vora (20 commits), goblinrum (19 commits)

## Devpost submission (written by the team)

### Inspiration

With the recent rise of content creators getting demonetized and DMCA takedowns, creators must be more careful than ever when uploading content to YouTube. We want to provide an uninterrupted experience for both creators and viewers watching reaction videos, so we designed a way for creators to still react to the videos that they love without getting demonetized. Viewers can now watch reactors and the videos they're reacting to side-by-side while still contributing views to both creators.

### What it does

The content creator captures their response to a video, ensuring not to include any content protected under the DMCA or the original content. The content creator utilizes this software to establish a correspondence of timestamps between their recorded reaction and the original material. For instance, should a content creator capture their reaction, and initiate their response at the 1:21 timestamp in their recorded material, they would correlate this to the 0:00 timestamp of the original video being commented on. For instance, should a content creator capture their reaction, and initiate their response at the 1:21 timestamp in their recorded material, they would correlate this to the 0:00 timestamp of the original video being commented on. The creator subsequently uploads their reaction video to platforms like YouTube (or any platform supporting video embedding) and associates it with the corresponding project generated using this application. During playback, viewers will observe the original content material synchronized to appear alongside the creator's reaction commencing at the 1:21 timestamp.

### How we built it

For the front-end and back-end, we used Reflex which uses Python and allowed us to insert in html/js For persistence, we stored data into cockroachDB which helps provide us reliability in the form of a PostgreSQL database We used GitHub as a version control system and used issues and pull requests to track progress on tasks and features development throughout the hackathon

### Challenges we ran into

We based our entire stack around Reflex, an emerging framework allowing developers to write full-stack code in "pure" Python. Since it's still a developing technology, many features normally found in front-end frameworks like React (such as being able to use Javascript and hooks), made it harder to adapt to the async nature of front-ends. Many new features are sparsely documented, so we had to rely on the expertise of Reflex's mentors during the hackathon to troubleshoot the many bugs that came up during development. We were testing the limits of this new framework as we were developing, leading to many instances of creative problem-solving to work around Reflex's current limitations. It was very new and challenging to wrap our heads around during front-end development in Python.

### Accomplishments we're proud of

We are most proud of our ability to take on a new platform and immediately start building a new project with so many unknowns. Given some very thorough planning and documentation reading, we were very proud of our ability to think on the spot and adapt to an ever-changing set of design limitations. We are also very proud that our product works and successfully achieves the original design goal in a simple and efficient manner.

### What we learned

We learned that having a good design and goal in mind from the start is very helpful, but being willing to pick up new things on the way can also provide an eye-opening experience. We also learned that it is important to ask important people for help whether they're the creators of the framework itself or other hackers that are running into the same problems as us. Working together not only as a team but as a community makes the best learning experience.

### What's next

We want to implement a Chrome extension that would enhance the user's ability to use our service by having a picture-in-picture video to play side by side with the original video allowing the user to experience a more involved viewer experience. Ideally, viewers don't even need to enter a site to enjoy their new viewing experience. Users might also want to have an opt-in (in favor of privacy) authentication system that allows users to log in and favorite/store videos. It would also be cool to experiment with other features such as supporting other video players and also integrating with currently published reaction videos or automatically timestamping using computer vision. Instructions to try it out! Reflex deployment is still buggy for our use case, so to see the side-by-side you need some tinkering with the deployment link. The base app is here and most functions should work, barring some small routing issues. The side by side demo can be seen here. You will need to navigate to another page like /search, then back to /viewer, and then hit refresh. The side-by-side should show up!

## README (from the GitHub repository)

# boolotube

boolotube is very boolo

it is an application that allows reaction video creators to release youtube content without worrying about DMCA infringement

the way it works is:

1. the creator records their reaction to the video without including the DMCA/OG content
2. the creator uses this application to map of timestamps from the reaction video to the OG content
   - for example, let's say I record my reaction video
   - I start reacting to the video at timestamp 1:21 on my reaction video
   - I map it to 0:00 of the video I'm reacting to
3. I upload the reaction video to youtube (or any other side with video embeds) and link the corresponding project created from this app
4. when watching the video, the OG content should pop up alongside my reaction at 1:21

Figma is [here](https://www.figma.com/file/EIl7wSWOZWOlxMg7MSMENj/BooloTube?type=design&node-id=0%3A1&mode=design&t=iAyaf7vB5pvYUqRO-1)

## API Functionality
### Models
```
project: {
  uuid: UUID
  my_reaction_url: String,
  original_video_url: String,
  createdAt: Time,
  mapped_timestamps: [Object]
}

timestamp_map: {
  uuid: UUID
  start_time_my_reaction: Integer (seconds from start)
  end_time_my_reaction: Integer,
  start_time_original: Integer,
  end_time_original: Integer,
}
```
### Endpoints
`POST /project`
- Creates a new project. Requires the 2 videos to be present
- Body:
- ```
    my_reaction_url: String,
    original_video_url: String,
  ```

`GET /project/:id`
- Gets the project given the ID
- Serves as the "permalink" to the project
- Returns both links, generates embeds for both
- Gets the list of matching timestamps

`POST /timestamp`
- Body:
- ```
  project_id: UUID
  start_time_my_reaction: Integer,
  end_time_my_reaction: Integer,
  start_time_original: Integer,
  end_time_originak: Integer,
  ```
- Creates a new timestamp attached to the corresponding object
- Does a few quick validations to check that the timestamps are logically correct

`GET /timestamp/:id`
- Optionally can be just a foreign key to project and the query returns all the nested data
- Gets relevant info about the timestamps

`DELETE /timestamp/:id`
- Deletes the timestamp object that holds the corresponding mapping

### Frontend

### Viewer interface
Simple 2 pane interface that contains 2 video embeds. Use something like [this](https://developers.google.com/youtube/iframe_api_reference) to determine the video playtime and play the original video given the mappings.

### Creator interface
Simple form to fill in the fields to create a new project. Once a project is created, allow the user in the current session only to add timestamps (for simplicity). Feel free to make a simple auth system to make each field editable. 


## Detected evidence (automated analysis)

Indexed codebase: 23 recognized source files, 45 KB.
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- PostgreSQL (technology) — detected in the code
- Python (language) — detected in the code
- Redis (technology) — detected in the code
- SQL (language) — detected in the code
- Next.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (28 of 28)

```
.env.example
.gitignore
.vscode/settings.json
BooloTube/__init__.py
BooloTube/api/project.py
BooloTube/api/timestamp.py
BooloTube/BooloTube.py
BooloTube/components/__init__.py
BooloTube/components/sidebar.py
BooloTube/database.sql
BooloTube/pages/__init__.py
BooloTube/pages/creator.py
BooloTube/pages/index.py
BooloTube/pages/search.py
BooloTube/pages/viewer.py
BooloTube/state.py
BooloTube/style.py
BooloTube/styles.py
BooloTube/templates/__init__.py
BooloTube/templates/template.py
Extension/background.js
Extension/manifest.json
Extension/project.html
Extension/project.js
README.md
requirements.txt
rxconfig.py
testing/test.html
```

### Dependencies

- requirements.txt: alembic@==1.12.1, anyio@==4.0.0, async-timeout@==4.0.3, bidict@==0.22.1, certifi@==2023.7.22, click@==8.1.7, cloudpickle@==2.2.1, colorama@==0.4.6, exceptiongroup@==1.1.3, fastapi@==0.96.1, greenlet@==3.0.1, gunicorn@==20.1.0, h11@==0.14.0, httpcore@==0.17.3, httpx@==0.24.1, idna@==3.4, install@==1.3.5, Jinja2@==3.1.2, Mako@==1.2.4, markdown-it-py@==3.0.0, MarkupSafe@==2.1.3, mdurl@==0.1.2, packaging@==23.2, pipdeptree@==2.13.0, platformdirs@==3.11.0, psutil@==5.9.6, psycopg@==3.1.12, psycopg-binary@==3.1.12, pydantic@==1.10.13, Pygments@==2.16.1, python-dotenv@==1.0.0, python-engineio@==4.8.0, python-multipart@==0.0.5, python-socketio@==5.10.0, redis@==4.6.0, reflex@==0.3.1, rich@==13.6.0, simple-websocket@==1.0.0, six@==1.16.0, sniffio@==1.3.0, SQLAlchemy@==1.4.41, sqlalchemy2-stubs@==0.0.2a35, sqlmodel@==0.0.8, starlette@==0.27.0, starlette-admin@==0.9.0, tabulate@==0.9.0, typer@==0.4.2, typing_extensions@==4.8.0, tzdata@==2023.3, uvicorn@==0.20.0, watchdog@==2.3.1, watchfiles@==0.19.0, websockets@==10.4, wrapt@==1.15.0, wsproto@==1.2.0

### Recent commits (newest first)

- Merge branch 'main' of https://github.com/goblinrum/boolotube
- link validation
- no controls
- polished view for side by side
- localstorage.removeItem instead
- Changed deleting logic
- Update creator.py
- bug 0
- Merge pull request #15 from goblinrum/side-by-side
- new stuff done
- Added spacers
- Fixed sidebar issue
- some design stuff
- Ability to see all timestamps and delete them
- Merge branch 'main' of https://github.com/goblinrum/boolotube
- Get all timestamps by project id
- remove template stuff
- Merge pull request #14 from goblinrum/search
- Merge branch 'main' of https://github.com/goblinrum/boolotube into search
- add search by secret_id

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

### requirements.txt

```
alembic==1.12.1
anyio==4.0.0
async-timeout==4.0.3
bidict==0.22.1
certifi==2023.7.22
click==8.1.7
cloudpickle==2.2.1
colorama==0.4.6
exceptiongroup==1.1.3
fastapi==0.96.1
greenlet==3.0.1
gunicorn==20.1.0
h11==0.14.0
httpcore==0.17.3
httpx==0.24.1
idna==3.4
install==1.3.5
Jinja2==3.1.2
Mako==1.2.4
markdown-it-py==3.0.0
MarkupSafe==2.1.3
mdurl==0.1.2
packaging==23.2
pipdeptree==2.13.0
platformdirs==3.11.0
psutil==5.9.6
psycopg==3.1.12
psycopg-binary==3.1.12
pydantic==1.10.13
Pygments==2.16.1
python-dotenv==1.0.0
python-engineio==4.8.0
python-multipart==0.0.5
python-socketio==5.10.0
redis==4.6.0
reflex==0.3.1
rich==13.6.0
simple-websocket==1.0.0
six==1.16.0
sniffio==1.3.0
SQLAlchemy==1.4.41
sqlalchemy2-stubs==0.0.2a35
sqlmodel==0.0.8
starlette==0.27.0
starlette-admin==0.9.0
tabulate==0.9.0
typer==0.4.2
typing_extensions==4.8.0
tzdata==2023.3
uvicorn==0.20.0
watchdog==2.3.1
watchfiles==0.19.0
websockets==10.4
wrapt==1.15.0
wsproto==1.2.0

```

### BooloTube/pages/index.py

```python
"""The home page of the app."""

from BooloTube import styles
from BooloTube.templates import template

import reflex as rx


@template(route="/", title="Home", image="/github.svg")
def index() -> rx.Component:
    """The home page.

    Returns:
        The UI for the home page.
    """
    with open("README.md", encoding="utf-8") as readme:
        content = readme.read()
    return rx.markdown(content, component_map=styles.markdown_style)

```

### rxconfig.py

```python
import reflex as rx
import os
from dotenv import load_dotenv

load_dotenv()

config = rx.Config(
    app_name="BooloTube",
    db_url=os.getenv('db_url')
)
```

### BooloTube/__init__.py

```python
"""Base template for Reflex."""

```

### BooloTube/state.py

```python
"""Base state for the app."""

import reflex as rx

class State(rx.State):

    @rx.var
    def project_id(self) -> str:
        return self.get_query_params().get('q', 'no pid')
        

    @rx.var
    def secret_id(self) -> str:
        return self.get_query_params().get('pid', 'no pid')

```

### Extension/project.html

```html
<!DOCTYPE html>
<html>

<head>
    <title>URL Sender</title>
    <script src="project.js"></script>
</head>

<body>
    <div>
        <label for="project">Enter Project Link:</label>
        <input type="text" id="project" /><br />
        <button id="submit">Submit</button>
    </div>
</body>

</html>
```

### BooloTube/BooloTube.py

```python
"""Welcome to Reflex!."""

from BooloTube import styles

# Import all the pages.
from BooloTube.pages import *

import reflex as rx
from .api.project import project_router
from .api.timestamp import timestamp_router

# Create the app and compile it.
app = rx.App(style=styles.base_style)
app.api.include_router(project_router)
app.api.include_router(timestamp_router)
app.compile()

```

### Extension/project.js

```javascript
document.addEventListener('DOMContentLoaded', function() {
  var submitButton = document.getElementById('submit');

  submitButton.addEventListener('click', function() {
    var project = document.getElementById('project').value;
    chrome.runtime.sendMessage({project: project, action: 'openUrl'}, function(response) {
      chrome.windows.create({
          url: response.data.original_video_url,
          type: 'popup', width: 400, height: 400,
      });
    });

  });
});

```

### BooloTube/database.sql

```sql
-- Initialize the projects table
CREATE TABLE IF NOT EXISTS projects (
    uuid TEXT PRIMARY KEY,
    my_reaction_url TEXT NOT NULL,
    original_video_url TEXT NOT NULL,
    createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    secret_id uuid DEFAULT uuid_generate_v1() NOT NULL
);

-- Initialize the timestamps table
CREATE TABLE IF NOT EXISTS timestamp_map (
    uuid TEXT PRIMARY KEY,
    project_id TEXT,
    start_time_my_reaction INTEGER,
    end_time_my_reaction INTEGER,
    start_time_original INTEGER,
    end_time_original INTEGER,
    FOREIGN KEY(project_id) REFERENCES projects(uuid)
);
```

### BooloTube/style.py

```python
shadow = "rgba(0, 0, 0, 0.15) 0px 2px 8px"
chat_margin = "20%"
message_style = dict(
    padding="1em",
    border_radius="5px",
    margin_y="0.5em",
    box_shadow=shadow,
    max_width="30em",
    display="inline-block",
)

# Set specific styles for questions and answers.
question_style = message_style | dict(
    bg="#F5EFFE", margin_left=chat_margin
)
answer_style = message_style | dict(
    bg="#DEEAFD", margin_right=chat_margin
)

# Styles for the action bar.
input_style = dict(
    border_width="1px", padding="1em", box_shadow=shadow
)
button_style = dict(bg="#CEFFEE", box_shadow=shadow)
```

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