Streaming platform Proof Of Concept.
- Python 3.11+
- Node.js
- FFmpeg (must be in your
PATH)
Pull and run the official Docker image:
docker pull richarddally/rtube:latest
docker run -p 5000:5000 richarddally/rtube:latestOr use docker-compose:
services:
rtube:
image: richarddally/rtube:latest
ports:
- "5000:5000"
volumes:
- ./data:/home/richard/data
environment:
- RTUBE_SECRET_KEY=your-secret-keyUsing uv (recommended):
uv synccd rtube/static
npm installConvert your MP4 video to HLS format:
python mp4_to_hls.pyThis can take some time depending on your CPU.
flask --app rtube runThen open http://127.0.0.1:5000 in your browser.
- HLS streaming with adaptive quality selection
- Keyboard shortcuts (hotkeys)
- Video markers support
- Timestamp sharing via URL parameter (
?t=120for 2 minutes) - Theater mode (wider view, press
Tor click button) - preference saved
- Upload and encode videos to HLS format
- Video visibility (public/private)
- Video deletion by owner or admin
- Thumbnail generation
- Video preview on hover (WebM clips)
- View count tracking
- Recommended videos sidebar
- Watch history with resume playback
- Post, edit, and delete comments on videos
- Threaded replies (nested comments)
- Soft delete with "[deleted]" placeholder
- Sort by newest or oldest
- YouTube-style collapsible replies
- Automatic URL detection and linking (urlize)
- Character limit (5000 characters)
- Create and manage custom playlists
- Add/remove videos from playlists
- Reorder videos within playlists
- Public playlist viewing
- Mark videos as favorites
- Quick access to favorite videos from profile
- Search videos by title, description, or author
- Results grouped by match type
Each video page includes a share button with two options:
- Copy link - Copies the video URL to clipboard
- Copy link at timestamp - Copies the URL with
?t=parameter set to current playback position
The button provides visual feedback when the URL is copied. Shared timestamp links automatically start playback at the specified time.
| Variable | Description | Default |
|---|---|---|
RTUBE_DATABASE_URL |
Database connection URL (PostgreSQL recommended for production) | sqlite:///rtube.db |
RTUBE_AUTH_DATABASE_URL |
Authentication database URL (separate for security) | sqlite:///rtube_auth.db |
RTUBE_SECRET_KEY |
Secret key for session security (generate a strong random key for production) | Auto-generated |
RTUBE_HTTPS |
Enable secure session cookies (true, 1, or yes when using HTTPS) |
false |
RTUBE_KEEP_ORIGINAL_VIDEO |
Keep original MP4 file after encoding (true, 1, or yes to enable) |
false |
RTUBE_INSTANCE_PATH |
Custom path for instance folder (sessions, secret key). Must be an absolute path. | instance/ |
RTUBE_OIDC_ENABLED |
Enable OIDC authentication (true, 1, or yes) |
false |
RTUBE_OIDC_CLIENT_ID |
OAuth2/OIDC client ID | - |
RTUBE_OIDC_CLIENT_SECRET |
OAuth2/OIDC client secret | - |
RTUBE_OIDC_DISCOVERY_URL |
OIDC discovery endpoint URL (.well-known/openid-configuration) |
- |
RTUBE_OIDC_SCOPES |
Space-separated OAuth2 scopes | openid profile email |
RTUBE_OIDC_USERNAME_CLAIM |
Claim to use for username | preferred_username |
RTUBE_OIDC_REDIRECT_URI |
Redirect URI for OIDC callback | http://127.0.0.1:5000/auth/oidc/callback |
REQUESTS_CA_BUNDLE |
Path to custom CA certificate bundle for OIDC provider (standard Python env var) | System CA bundle |
RTube includes a built-in authentication system with four user roles:
- Anonymous: Can view public videos only (not logged in)
- Viewer: Can view videos, create playlists, and add favorites, but cannot upload
- Uploader: Can view and upload videos, encode videos, manage own content
- Admin: Full access including user management, role changes, and moderation
Each user has a profile page accessible at /profile (own profile) or /profile/<username> (any authenticated user). Profiles display:
- Uploaded videos with thumbnails and view counts
- Posted comments with links to the videos
Administrators have access to the Admin dropdown menu which provides:
- List of all registered users with their roles
- Online/offline status based on recent activity
- Video, comment, playlist, and favorite counts per user
- Role management: change user roles (Viewer, Uploader, Admin)
- Direct links to user profiles for moderation
- Password change for admin account
- Scan for orphan encoded videos (HLS files not in database)
- Display available quality variants for each video
- Bulk import with automatic thumbnail generation
- Videos are imported as private by default
- List all videos with filters (visibility, owner, sort)
- Bulk actions: delete, make public, make private
- Quick access to video editing and viewing
- List videos missing preview clips
- Bulk generate WebM previews for hover display
- Platform statistics (videos, users, comments, views)
- Activity chart with configurable periods (30 days to 5 years)
- Storage usage breakdown
- Top videos, uploaders, and commenters
- Encoding job statistics
- Week-over-week comparisons
- Track all admin actions with timestamps
- Filter by action type or admin user
- Records IP addresses and action details
- Paginated view with full history
- Admins can edit any video (not just their own)
- Change video owner to any Uploader or Admin user
User sessions persist across server restarts. Sessions are stored server-side using Flask-Session with filesystem storage. The secret key is automatically generated and saved to instance/.secret_key on first run.
All media files are stored in the instance/ folder:
instance/videos/- HLS video files (.m3u8 and .ts segments)instance/thumbnails/- Video thumbnail imagesinstance/sessions/- User session datainstance/.secret_key- Persistent secret key
Use RTUBE_INSTANCE_PATH to customize the storage location.
On first startup, a default admin account is created:
- Username:
admin - Password:
admin
Important: Change this password immediately in production!
- Minimum 12 characters
- At least one uppercase letter (A-Z)
- At least one lowercase letter (a-z)
- At least one digit (0-9)
- At least one special character
- No common patterns or sequences
RTube supports Single Sign-On via OpenID Connect (OIDC). This works with any OIDC-compliant identity provider (Keycloak, Authentik, Azure AD, Okta, Google, etc.).
export RTUBE_OIDC_ENABLED=true
export RTUBE_OIDC_CLIENT_ID="rtube"
export RTUBE_OIDC_CLIENT_SECRET="your-client-secret"
export RTUBE_OIDC_DISCOVERY_URL="https://auth.example.com/realms/master/.well-known/openid-configuration"
export RTUBE_OIDC_SCOPES="openid profile email"
export RTUBE_OIDC_USERNAME_CLAIM="preferred_username"- User clicks "Sign in with SSO (OIDC)" on the login page
- User is redirected to the Identity Provider (IdP)
- After successful authentication, IdP redirects back to RTube
- RTube creates a local user account on first login (with
uploaderrole) - User is logged in
OIDC users can also use local credentials if they have a local account.
If your OIDC provider uses a self-signed certificate or a certificate signed by a private CA, you need to configure RTube to trust it. Flask-OIDC uses Python's requests library, which respects standard environment variables for CA certificates:
export REQUESTS_CA_BUNDLE=/path/to/custom-ca-bundle.pemCreating a combined CA bundle:
If you need to trust both system CAs and your custom CA:
# Linux/macOS
cat /etc/ssl/certs/ca-certificates.crt /path/to/your-ca.pem > /path/to/combined-ca-bundle.pem
export REQUESTS_CA_BUNDLE=/path/to/combined-ca-bundle.pem
# Or add your CA to the system trust store (requires root)
sudo cp your-ca.pem /usr/local/share/ca-certificates/your-ca.crt
sudo update-ca-certificatesDocker deployment:
When running RTube in Docker with a private CA:
FROM python:3.11-slim
COPY your-ca.pem /usr/local/share/ca-certificates/your-ca.crt
RUN update-ca-certificates
# ... rest of your DockerfileFor local development and testing, you can use one of these OIDC providers:
Authentik is an open-source identity provider that's easy to set up with Docker.
# Create docker-compose.yml
cat > docker-compose.yml << 'EOF'
services:
postgresql:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- database:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: authentik
POSTGRES_USER: authentik
POSTGRES_DB: authentik
redis:
image: redis:alpine
restart: unless-stopped
server:
image: ghcr.io/goauthentik/server:latest
restart: unless-stopped
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: authentik
AUTHENTIK_SECRET_KEY: "generate-a-random-secret-key-here"
ports:
- "9000:9000"
- "9443:9443"
depends_on:
- postgresql
- redis
worker:
image: ghcr.io/goauthentik/server:latest
restart: unless-stopped
command: worker
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: authentik
AUTHENTIK_SECRET_KEY: "generate-a-random-secret-key-here"
depends_on:
- postgresql
- redis
volumes:
database:
EOF
# Start Authentik
docker compose up -dThen:
- Open http://localhost:9000/if/flow/initial-setup/ to create admin account
- Create a new OAuth2/OIDC Provider in Admin > Providers
- Create an Application linked to this provider
- Configure RTube with the client credentials
docker run -p 8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:latest start-devThen:
- Open http://localhost:8080 and log in with admin/admin
- Create a new Realm (e.g., "rtube")
- Create a new Client with:
- Client ID:
rtube - Client authentication: ON
- Valid redirect URIs:
http://127.0.0.1:5000/auth/oidc/callback
- Client ID:
- Copy the client secret from Credentials tab
- Configure RTube:
export RTUBE_OIDC_ENABLED=true export RTUBE_OIDC_CLIENT_ID="rtube" export RTUBE_OIDC_CLIENT_SECRET="your-client-secret" export RTUBE_OIDC_DISCOVERY_URL="http://localhost:8080/realms/rtube/.well-known/openid-configuration"
For quick testing without a full IdP, you can use a mock OIDC server:
# Using Node.js
npx mock-oidc-server --port 9090
# Or using Python
pip install oidc-provider
python -m oidc_providerNote: Mock servers are for development only - never use in production!
RTube uses Flask-Migrate (Alembic) to manage database schema changes.
If you're setting up RTube for the first time, the database will be created automatically when you start the application. Then stamp the database to mark it as up-to-date:
flask --app rtube.app:create_app db stamp headAfter pulling new changes that include database migrations:
flask --app rtube.app:create_app db upgradeThe users table is stored in a separate auth database (rtube_auth.db or PostgreSQL). Flask-Migrate only manages the main database, so auth schema changes must be applied manually.
For role column (if upgrading from older version):
The role column should already exist with default value uploader. Valid roles are: viewer, uploader, admin.
When you modify the data models (models.py or models_auth.py):
-
Auto-generate a migration based on model changes:
flask --app rtube.app:create_app db migrate -m "Description of changes" -
Review the generated migration in
migrations/versions/before applying it. -
Apply the migration:
flask --app rtube.app:create_app db upgrade
| Command | Description |
|---|---|
flask db upgrade |
Apply all pending migrations |
flask db downgrade |
Revert the last migration |
flask db current |
Show current migration revision |
flask db history |
Show migration history |
flask db stamp head |
Mark database as up-to-date without running migrations |
Note: Always use --app rtube.app:create_app with Flask commands, or set the FLASK_APP environment variable:
export FLASK_APP=rtube.app:create_app # Linux/macOS
set FLASK_APP=rtube.app:create_app # WindowsRTube includes a GitHub Actions workflow to build and publish packages to PyPI and Docker Hub.
-
Configure Trusted Publisher on PyPI:
- Go to PyPI Publishing Settings
- Click "Add a new pending publisher" (for new projects) or configure on the project page (for existing projects)
- Fill in the form:
- PyPI Project Name:
rtube - Owner: your GitHub username or organization
- Repository name:
RTube - Workflow name:
publish.yml - Environment name:
pypi
- PyPI Project Name:
- Click "Add"
-
Create a GitHub Environment:
- Go to your repository on GitHub
- Navigate to Settings > Environments
- Click New environment
- Name:
pypi(must match the name configured on PyPI) - Add protection rules if desired (e.g., required reviewers)
No API tokens or secrets are required for PyPI - authentication is handled automatically via OIDC.
-
Create a Docker Hub Access Token:
- Log in to Docker Hub
- Go to Account Settings > Security > Access Tokens
- Click New Access Token
- Name:
github-actions(or any descriptive name) - Permissions: Read, Write, Delete
- Click Generate and copy the token
-
Add GitHub Secrets:
- Go to your repository on GitHub
- Navigate to Settings > Secrets and variables > Actions
- Add two secrets:
DOCKERHUB_USERNAME: Your Docker Hub usernameDOCKERHUB_TOKEN: The access token generated above
Option 1: Manual trigger with semantic version bump (recommended)
- Go to Actions > Build and Publish
- Click Run workflow
- Select the bump type:
PATCH(0.2.0 → 0.2.1) - Bug fixesMINOR(0.2.0 → 0.3.0) - New featuresMAJOR(0.2.0 → 1.0.0) - Breaking changes
- Click Run workflow
The workflow will automatically:
- Read the current version from
rtube/__version__.py - Bump the version based on your selection
- Update both
rtube/__version__.pyandpyproject.toml - Commit the changes and create a git tag (e.g.,
v0.3.0) - Build and publish to PyPI and Docker Hub
Option 2: Push a version tag manually
git tag v0.3.0
git push origin v0.3.0- Bump Version (manual trigger only): Updates version in source files, commits, and creates tag
- Build Wheel: Runs pytest, then creates wheel and sdist using
uv build - Build Docker: Builds Docker image using the wheel (validates image builds correctly)
- Publish to PyPI: Uploads packages using OIDC trusted publishing (only after Docker build succeeds)
- Push Docker: Pushes Docker image to Docker Hub (only after Docker build succeeds)
- Create GitHub Release: Creates a release with the built artifacts (only after PyPI and Docker Hub publish succeed)
- Rollback on failure: Logs errors and provides guidance if any step fails
- Download and install Git Large File Storage
- Track mp4 files
$ git lfs track "*.mp4" git add/commit/pushwill upload on GitHub LFS.