Compare commits
2 Commits
prod
...
d8b61b4528
| Author | SHA1 | Date | |
|---|---|---|---|
| d8b61b4528 | |||
| 85ddd93757 |
142
README.md
142
README.md
@@ -1,33 +1,119 @@
|
||||
# Overview
|
||||
The VoD System is a powerful clip management platform designed to streamline how you handle your video content. Whether you're a content creator, streamer, or educator, VoD System lets you:
|
||||
# VoD System
|
||||
|
||||
- Upload clips effortlessly and securely.
|
||||
- Edit and trim videos with intuitive controls.
|
||||
- Compress files to specific file sizes.
|
||||
- Organize your clips for quick access and sharing.
|
||||
A self-hosted clip management and live streaming platform. Upload videos, trim and compress clips, save sections of a live stream, and organise everything through a clean web interface — all running locally under Docker.
|
||||
|
||||
This VoD system began as a small project back in my 5th year of secondary school, during lockdown. I wanted a quick way to send game clips to Discord, but the 8MB file size limit made it frustrating to manually tweak bitrates for every clip. To solve that, I wrote a simple Python script to automate the process. What started as a quick fix has since evolved into a full-fledged system — now built with a Spring Boot backend and a modern React/Vite frontend.
|
||||
This project started in secondary school during lockdown as a Python script to squeeze game clips under Discord's 8 MB file size limit. It has since grown into a full-stack application with a Spring Boot backend, a React/Vite/TypeScript frontend, and an nginx-rtmp sidecar for live streaming.
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|------------|---------------------------------------------------------|
|
||||
| Backend | Java 21, Spring Boot 3.4, Spring Security, JPA, FFmpeg |
|
||||
| Frontend | React 19, TypeScript, Vite, Tailwind CSS v4 |
|
||||
| Database | PostgreSQL 15 |
|
||||
| Streaming | nginx-rtmp |
|
||||
| Auth | Google OAuth 2.0 + JWT |
|
||||
| Runtime | Docker Compose |
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
- Docker & Docker Compose
|
||||
- JDK 21+
|
||||
- Node.js 16+
|
||||
- FFMPEG
|
||||
- Google Client keys
|
||||
## Steps
|
||||
1. Create your own two `.env` files based off the `example.env` files in the root and in the frontend folder
|
||||
2. Spin up docker container with `docker compose up`
|
||||
3. Run `./mvnw clean package` to build the jar
|
||||
4. Run `./mvnw spring-boot:run` to run the backend section
|
||||
5. Run `cd frontend && npm install && npm run dev` to build and run the frontend
|
||||
6. Endpoints should be available at 8080 (backend) and 5173 (frontend)
|
||||
|
||||
# Future Plans
|
||||
- **Format Handling**
|
||||
- Backend conversion of non-MP4 files via FFMPEG for broader format support
|
||||
- **Input Sources**
|
||||
- Support for local file uploads and YouTube imports
|
||||
- **Testing**
|
||||
- Unit tests, including coverage for FFMPEG-related functionality
|
||||
- **API Documentation**
|
||||
- Comprehensive and maintainable backend API docs
|
||||
- Docker and Docker Compose
|
||||
- A Google Cloud project with OAuth 2.0 credentials (Client ID and Secret)
|
||||
|
||||
That is all. The backend, frontend, database, and RTMP server all run as containers — no local JDK, Node, or FFmpeg install required.
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### 1. Configure the environment
|
||||
|
||||
Copy the example file and fill in the values:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Open `.env` and set the following:
|
||||
|
||||
```dotenv
|
||||
# OAuth credentials from your Google Cloud Console project
|
||||
GOOGLE_CLIENT_ID=
|
||||
GOOGLE_CLIENT_SECRET=
|
||||
|
||||
# A long random string used to sign JWTs — treat it like a password
|
||||
JWT_SECRET_KEY=
|
||||
|
||||
# Absolute paths on the host where persistent data will be stored
|
||||
POSTGRES_DATA=/path/to/postgres/data
|
||||
STREAM_DATA=/path/to/stream/data
|
||||
MEDIA_DATA=/path/to/media/data
|
||||
|
||||
# Database credentials — change these from the defaults before deploying
|
||||
POSTGRES_URL=jdbc:postgresql://postgres:5432/vodSystem
|
||||
POSTGRES_USER=myuser
|
||||
POSTGRES_PASSWORD=mypassword
|
||||
POSTGRES_DB=vodSystem
|
||||
|
||||
# The URLs the frontend will use to reach itself and the backend
|
||||
FRONTEND_URL=http://localhost:5173
|
||||
BACKEND_URL=http://localhost:8080
|
||||
```
|
||||
|
||||
### 2. Start the application
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
This builds and starts four containers: `vod_postgres`, `vod_backend`, `vod_frontend`, and `vod_rtmp`.
|
||||
|
||||
Once everything has started, the interfaces are available at:
|
||||
|
||||
- Frontend: [http://localhost:5173](http://localhost:5173)
|
||||
- Backend API: [http://localhost:8080/api/v1](http://localhost:8080/api/v1)
|
||||
- RTMP ingest: `rtmp://localhost:1935/live`
|
||||
|
||||
### 3. Authenticate
|
||||
|
||||
Log in through the frontend using your Google account. The backend exchanges the Google ID token for a short-lived JWT that is used on all subsequent API requests.
|
||||
|
||||
---
|
||||
|
||||
## Development Mode
|
||||
|
||||
For local backend or frontend development, a separate Compose file is provided that starts only the infrastructure services (PostgreSQL and the RTMP server), leaving the backend and frontend to run directly on the host:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.dev.yml up
|
||||
```
|
||||
|
||||
Then run the backend and frontend separately in your usual workflow.
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
A [Bruno](https://www.usebruno.com/) collection is included under `bruno/VoD-System/` with pre-configured requests covering all major endpoints. Import it directly into Bruno and point the `local` environment at `http://localhost:8080/api/v1`.
|
||||
|
||||
The collection is organised into four folders:
|
||||
|
||||
| Folder | Description |
|
||||
|----------|--------------------------------------------------|
|
||||
| Users | Google OAuth login flow, current user |
|
||||
| Media | Upload, compress, clip stream, save section |
|
||||
| Jobs | Poll job status, download completed output |
|
||||
| Streams | Stream history |
|
||||
|
||||
---
|
||||
|
||||
## Future Plans
|
||||
|
||||
- **Format handling** — backend conversion of non-MP4 files via FFmpeg for broader input format support
|
||||
- **Input sources** — YouTube imports and additional local upload options
|
||||
- **Testing** — unit and integration test coverage, including FFmpeg-related functionality
|
||||
- **API documentation** — comprehensive, auto-generated backend API docs
|
||||
|
||||
@@ -32,6 +32,7 @@ services:
|
||||
container_name: vod_frontend
|
||||
environment:
|
||||
VITE_API_URL: ${BACKEND_URL}
|
||||
VITE_FRONTEND_URL: ${FRONTEND_URL}
|
||||
VITE_GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import { defineConfig, loadEnv } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import flowbiteReact from "flowbite-react/plugin/vite";
|
||||
|
||||
export default defineConfig(({ mode }) => { // change to function form
|
||||
const env = loadEnv(mode, process.cwd(), '') // load all env vars
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
return {
|
||||
plugins: [react(), tailwindcss(), flowbiteReact()],
|
||||
preview: {
|
||||
port: 5173,
|
||||
@@ -15,7 +16,8 @@ export default defineConfig({
|
||||
port: 5173,
|
||||
strictPort: true,
|
||||
host: true,
|
||||
origin: "http://0.0.0.0:5173",
|
||||
origin: env.VITE_FRONTEND_URL || "http://0.0.0.0:5173", // use it here
|
||||
allowedHosts: true,
|
||||
proxy: {
|
||||
'/api/v1': {
|
||||
target: 'http://localhost:8080',
|
||||
@@ -24,4 +26,5 @@ export default defineConfig({
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -6,6 +6,7 @@ import com.ddf.vodsystem.entities.User;
|
||||
import com.ddf.vodsystem.exceptions.NotAuthenticated;
|
||||
import com.ddf.vodsystem.services.UserService;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -41,14 +42,15 @@ public class UserController {
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<APIResponse<Token>> login(@RequestBody Token token,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
String jwt = userService.login(token.token());
|
||||
|
||||
ResponseCookie cookie = ResponseCookie.from("token", jwt)
|
||||
.httpOnly(true)
|
||||
.maxAge(jwtExpiration / 1000)
|
||||
.sameSite("None")
|
||||
.secure(true)
|
||||
.sameSite(request.isSecure() ? "None" : "Lax")
|
||||
.secure(request.isSecure())
|
||||
.path("/")
|
||||
.build();
|
||||
|
||||
@@ -60,12 +62,13 @@ public class UserController {
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<APIResponse<Void>> logout(HttpServletResponse response) {
|
||||
public ResponseEntity<APIResponse<Void>> logout(HttpServletResponse response,
|
||||
HttpServletRequest request) {
|
||||
ResponseCookie cookie = ResponseCookie.from("token", "")
|
||||
.httpOnly(true)
|
||||
.maxAge(0)
|
||||
.sameSite("None")
|
||||
.secure(true)
|
||||
.sameSite(request.isSecure() ? "None" : "Lax")
|
||||
.secure(request.isSecure())
|
||||
.path("/")
|
||||
.build();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user