Match Uploader is a tool to help upload FRC match videos to YouTube in a fast, consistent manner.
Data source attributions:
To get started:
server/env/production.env.example
to server/env/production.env
and
fill in the values using the descriptions in Environment variables below.docker-compose.yaml
as needed (additional details in comments on the relevant lines):
port
property on the web
containervolumes
property on the web
and worker
containers
web
and worker
containers. In that case, you only have to update the volumes for the web
container, and the worker
container will use those volumes as well.docker compose up
to start the containers.
docker compose pull
before docker compose up
.worker
container log INFO: Worker connected and looking for jobs...
db
container log messages are safe to ignore:
database system is shut down
and server stopped
as long as there is activity after.ERROR: relation "graphile_worker.migrations" does not exist at character 93
(unsure why this is logged, but this is related to the worker library Match Uploader uses, Graphile Worker, and does not seem to be problematic)could not receive data from client: Connection reset by peer
worker
container may log Failed to read crontab file '/home/node/app/server/crontab'; cron is disabled
; you can ignore this.Event name
, Event TBA code
, (event code from The Blue Alliance),
Playoffs type
, Sandbox mode
(enable to test uploads without actually uploading to YouTube), and Video upload privacy
.
Sandbox mode
to Off
and Video upload privacy
to Public
.FRC Events API
if data for your event is not available on The Blue Alliance.localhost
or with a valid top-level domain due to Google OAuth2 app restrictions.videos
directory, which is where you'll place the video files for matches.Match Uploader expects a specific directory structure for your videos. When running Match Uploader in Docker, you can mount any directory (such as one that your video production software writes recordings to) as the videos volume (for specifics, see Docker volumes, below).
The expected directory structure is as follows:
videos/
├─ unlabeled/
│ ├─ Qualification 1.mp4
├─ $LABEL/
│ ├─ Qualification 1.mp4
[!TIP] The
.mp4
video extension is just an example. You can use any file type that YouTube supports.
A video label is an extra description for when you have multiple videos to upload for one match. Match Uploader will
include the video label in the middle of the video title, e.g., Qualification Match 1 - $LABEL - Event Name
.
What if I don't want a label in the video title? A video with no label is labeled unlabeled
(so you would put
videos that should be unlabeled in a directory called unlabeled
). This will title the video like
Qualification Match 1 - Event Name
.
After being uploaded, videos are moved to a directory called uploaded
within each label directory. (You don't need to
create the uploaded
directories; they'll get created automatically when needed.) For instance:
videos/
├─ unlabeled/
│ ├─ uploaded/
│ │ ├─ Qualification 1.mp4
| ├─ Qualification 2.mp4
├─ $LABEL/
│ ├─ uploaded/
│ │ ├─ Qualification 1.mp4
| ├─ Qualification 2.mp4
[!CAUTION] Don't mount your videos directory as a read-only Docker volume. Otherwise, the server won't be able to move videos to the
uploaded
directories.
The Docker Compose file actually runs 3 containers. You'll need all of them running to use Match Uploader. The containers should be started in this order (the default Docker Compose setup provided will handle this for you):
For simplicity, and to keep secrets out of docker-compose.yaml
, all 3 containers mentioned above will
pull environment variables from the server/env/production.env
file. As a result, not all containers use all the environment variables,
and you need to provide values for database information in two different environment variables. There are some additional environment variables
defined in the file that are not specified below; please leave those intact.
[!IMPORTANT]
You must define both thePOSTGRES_*
environment variables and theDB_CONNECTION_STRING
environment variable. Thedb
container uses thePOSTGRES_*
environment variables to set up the database configuration, while theweb
andworker
containers use theDB_CONNECTION_STRING
environment variable to connect to the database.
Variable | Description | Sample value |
---|---|---|
PORT |
Determines the port the backend server runs on inside of its container. (To change the external container port, you'd want to edit the port property on the web container in docker-compose.yaml .) |
Leave set to default: 8080 |
POSTGRES_DB |
Used by the db container. The name of the Postgres database to create. |
Leave set to default: match_uploader |
POSTGRES_USER |
Used by the db container. Determines the username of the user created to access the Postgres server, so you can put anything here as long as you use the same value in DB_CONNECTION_STRING . |
Leave set to default: match_uploader |
POSTGRES_PASSWORD |
Used by the db container. Determines the password of the user created to access the Postgres server, so you can put anything here as long as you use the same value in DB_CONNECTION_STRING . |
Pick any random string to use as a password |
DB_CONNECTION_STRING |
Used by the web and worker containers. Connection string to connect to the PostgreSQL server. |
Recommended value: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} (replace values with environment variables already defined; POSTGRES_HOST with the default Match Uploader Docker Compose configuration would be db ; POSTGRES_PORT should be the default Postgres port, 5432 ) |
WORKER_WEB_SERVER_URL |
Used by the worker container. The URL (include protocol, domain, and port) where the backend server can be reached from inside the worker container. This value is provided to Socket.IO to connect to the WebSocket server hosted out of the web container. |
Leave set to default: http://web:8080 |
The Docker Compose setup defaults to using the America/New_York
timezone. If your timezone is different, you'll need
to build the Dockerfile from scratch with the correct timezone (sorry this isn't easier!) and also update the TZ
and
PGTZ
environment variables for the Postgres container in docker-compose.yaml
.
There are 3 required Docker volumes for the web
and worker
containers:
/home/node/app/server/videos
/home/node/app/server/env
server/env/production.env.example
and fill in the values.
Descriptions of what you need to fill in are described above./home/node/app/server/settings
Examples of how to provide these volumes are in docker-compose.yaml
.
The Postgres container requires a volume to persist the database in. The default Docker Compose setup is set up so that Docker will create this volume for you.
Prior to v2.0, Match Uploader uploaded videos synchronously in an HTTP client. To add flexibility, v2.0 added a worker container that can asynchronously handle jobs such as video uploads.
From the client, you will notice very few changes with the worker in use. However, there are some important details to note:
With the worker, a video upload will now create a new job. Here are the possible statuses a job can have:
Status | Description |
---|---|
Pending | The job is waiting to be processed by the worker. (To ensure videos upload in the correct order, video uploads are processed serially in the order they are queued, one at a time.) |
Started | The worker is actively handling the job. Logs are visible via the worker container, e.g., docker compose logs --follow worker |
Failed (retryable) | An error occurred while handling the job. The job still has at least one more attempt remaining and will be retried. |
Completed | The job has finished successfully. |
Failed | An error occurred while handling the job. The job has no more attempts remaining and will not be retried. |
A normal job lifecycle is to go from Pending to Started to Completed.
Project organization:
server
contains all backend code (nodejs, Express, TypeScript)client
contains all frontend code (Vue3, Typescript)Install some baseline dependencies:
cd server && yarn run dev
cd client && yarn run dev
The server uses Prisma to manage our database. Check out the Prisma docs and server/prisma
for more information.
Migrations are automatically run when the server starts in the production Docker Compose setup. If you change the database as part of a change, be sure to include a migration that can run on startup to apply changes for existing users.
To trigger an automatic version increment and create releases, make sure the commit message when merging your PR starts with a type and scope, followed by a colon and a description (it's easiest to do this if you set your PR title correctly when you create it). For example:
type(scope): Brief description of changes
feat(youtube): Improve upload error handling
ci(github): Add automatic release workflow
docs(readme): Add release instructions
refactor(server): Move video upload to separate module
build(docker): Add docker-compose file
fix(client): Fix bug when uploading videos
The exact format required is based on the Angular commit message format. See
https://gist.github.com/brianclements/841ea7bffdb01346392c for more details. The type
must be one of the Angular
commit message format types. No specific value is required for scope
, though we may further define this as the project
matures.
The release process will trigger automatically once your PR is merged. A GitHub tag and release will be created, and a new production image will be pushed to GitHub Container Registry.