Skip to content

m3sserstudi0s/swiparr

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

573 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Swiparr

Swiparr ๐Ÿฟ

Discover what to watch next, by yourself or together.

Swiparr turns the dreaded "what should we watch?" question into a fun, collaborative experience.
Like Tinder for movies, but smarter and works for groups.

License Docker Buy Me a Coffee


๐ŸŽฏ The Problem

The struggle is real: 30 minutes of "what should we watch?" that ends with watching the same show again. Swiparr fixes this by:

โœจ Turning discovery into a fun, game-like experience
๐Ÿค Finding content everyone actually wants to watch
โšก Making group decisions in minutes, not hours
๐ŸŒ Working with your existing media libraries OR standalone


โœจ Features

๐ŸŽฌ Content Discovery

  • Intuitive Swipe Interface - Browse movies with a familiar card-based design
  • Multi-Provider Support - Works with Jellyfin, Emby, Plex, or TMDB directly
  • Smart Matching - Automatically finds content everyone in your group will enjoy
  • Mobile-First - Optimized for phones, with desktop keyboard shortcuts
  • PWA Ready - Install as a web app for the best experience

๐Ÿ‘ฅ Built for Groups

  • Instant Sessions - Create or join in seconds, no complex setup
  • Flexible Match Rules - Choose "any two people" or "everyone must agree"
  • Session Controls - Limit likes, dislikes, or total matches
  • Watchlist Sync - Seamlessly save favorites back to your media server

๐Ÿ”ง Universal Compatibility

  • Jellyfin - Full native integration
  • Emby - Experimental support (improving)
  • Plex - Experimental support (improving)
  • TMDB - No media server required, works standalone

๐Ÿš€ Quick Start

Fastest: Swiparr Global

No setup, no server, no problem.

๐ŸŒ swiparr.com - Free to use, community-supported (hopefully)

Easiest: Deploy to Vercel

One-click deployment, perfect for personal or small group use:

Deploy with Vercel

Note: The automatic deployment workflow in Vercel uses the Turso integration by default as a database service provider. Free to set up, possible to swap out 1.

Vercel security note: AUTH_SECRET is auto-generated during the build (via scripts/ensure-auth-secret.cjs) and persisted in the database when not provided.

Full Control: Self-Host with Docker

Using Docker Compose (Recommended):

  1. Create docker-compose.yml:
services:
  swiparr:
    image: ghcr.io/m3sserstudi0s/swiparr:latest
    container_name: swiparr
    restart: unless-stopped
    environment:
      - PROVIDER=jellyfin  # or plex, emby, tmdb (or set PROVIDER_LOCK to "false")
      - JELLYFIN_URL=http://your-jellyfin:8096 # adjust to provider, none without server lock
    volumes:
      - ./swiparr-data:/app/data
    ports:
      - 4321:4321
  1. Run it:
docker compose up -d

Using Docker CLI:

docker run -d \
  --name swiparr \
  --restart unless-stopped \
  -p 4321:4321 \
  -v $(pwd)/swiparr-data:/app/data \
  -e PROVIDER=jellyfin \
  -e JELLYFIN_URL=http://your-jellyfin:8096 \
  ghcr.io/m3sserstudi0s/swiparr:latest
  1. Open http://localhost:4321

Docker security note: AUTH_SECRET is auto-generated on first boot (via scripts/ensure-auth-secret.cjs) and stored in the database when not provided.


โš™๏ธ Configuration Reference

Provider-Specific Settings

Choose one provider setup based on your needs:

Jellyfin Setup
PROVIDER=jellyfin
JELLYFIN_URL=http://your-jellyfin:8096              # Internal URL (required)
JELLYFIN_PUBLIC_URL=https://jellyfin.example.com    # Public URL (optional)
JELLYFIN_USE_WATCHLIST=false                         # Use Watchlist (plugin needed) vs Favorites (optional)
Emby Setup (Experimental)
PROVIDER=emby
EMBY_URL=http://your-emby:8096        # Internal URL (required)
EMBY_PUBLIC_URL=https://emby.example.com  # Public URL (optional)
Plex Setup (Experimental)
PROVIDER=plex
PLEX_URL=http://your-plex:32400       # Internal URL (required)
PLEX_PUBLIC_URL=https://plex.example.com # Public URL (optional)
TMDB Setup (No Server Required)
PROVIDER=tmdb
TMDB_ACCESS_TOKEN=your-tmdb-token     # API Read-Only Token (required)
TMDB_DEFAULT_REGION=SE                # Default region for availability/certifications (optional)

Security & Advanced Options

# Authentication
AUTH_SECRET=random-string-32-chars-min     # Auto-generated on boot/build when not provided. See Security & Privacy.
USE_SECURE_COOKIES=true                    # Required for HTTPS

# Application
PORT=4321                                  # Default port
HOSTNAME=0.0.0.0                          # Bind address
DATABASE_URL=file:/app/data/swiparr.db    # SQLite path or Turso URL
DATABASE_AUTH_TOKEN=your-token            # Required for Turso/Remote DB

# Base path (build-time only โ€” see Custom Base Path section)
# URL_BASE_PATH=/swipe

# Admin
ADMIN_USERNAME=your-username                      # Global auto-grant admin privileges
JELLYFIN_ADMIN_USERNAME=jelly-admin               # Provider-specific admin (overrides global)
PLEX_ADMIN_USERNAME=plex-admin                   # Provider-specific admin (overrides global)
EMBY_ADMIN_USERNAME=emby-admin                   # Provider-specific admin (overrides global)

# Security Headers
X_FRAME_OPTIONS=DENY                       # Frame control
CSP_FRAME_ANCESTORS=none                   # Embedding policy

# Network Safety
ALLOW_PRIVATE_PROVIDER_URLS=false          # Block private/LAN URLs for user-supplied providers (BYOP)
PLEX_IMAGE_ALLOWED_HOSTS=plex.example.com,*.plex.direct  # Optional extra image hosts

# BYOP Mode - Bring Your Own Provider
PROVIDER_LOCK=false                          # Let users choose and configure their own provider

# Misc
USE_ANALYTICS=false                          # Enable anonymous usage analytics (Vercel deployments)
ENABLE_DEBUG=false                           # Enable verbose debug logging and client-server error mapping
USE_STATIC_FILTERS=false                     # Skip dynamic filter fetching; use built-in genre/year/rating lists instead (useful for very large libraries where filter API calls time out)

Environment Variable Matrix

Variable Required? Default Description
PROVIDER โœณ๏ธ jellyfin Primary media provider (jellyfin, tmdb, plex, emby)
PROVIDER_LOCK โŒ true If true, users cannot change the provider at runtime
JELLYFIN_URL โœณ๏ธ - Internal URL of your Jellyfin server
JELLYFIN_PUBLIC_URL โŒ - Public URL of your Jellyfin server (for client-side access)
JELLYFIN_USE_WATCHLIST โŒ false Use Jellyfin Watchlist instead of Favorites
EMBY_URL โœณ๏ธ - Internal URL of your Emby server
EMBY_PUBLIC_URL โŒ - Public URL of your Emby server (for client-side access)
PLEX_URL โœณ๏ธ - Internal URL of your Plex server
PLEX_PUBLIC_URL โŒ - Public URL of your Plex server (for client-side access)
PLEX_TOKEN โŒ - Plex Admin/Access Token
TMDB_ACCESS_TOKEN โœณ๏ธ - TMDB API Read-Only Access Token
TMDB_DEFAULT_REGION โŒ SE Default TMDB region (ISO 3166-1) for streaming availability/certifications
AUTH_SECRET โŒ Auto-generated on boot/build Secret used for session encryption and guest lending token encryption (min 32 chars). See Security & Privacy.
USE_SECURE_COOKIES โŒ false Set to true for HTTPS deployments
DATABASE_URL โŒ file:/app/data/swiparr.db SQLite path or Turso URL 1
DATABASE_AUTH_TOKEN โŒ - Auth token for remote databases (e.g. Turso)
APP_PUBLIC_URL โŒ swiparr.com The public domain where the app is hosted
URL_BASE_PATH โŒ - Base path for subpath deployments (e.g. /swipe). Must be set at image build time โ€” see Custom Base Path.
ADMIN_USERNAME โŒ - Global admin username (overrides provider-specific) 2
JELLYFIN_ADMIN_USERNAME โŒ - Jellyfin-specific admin username 2
EMBY_ADMIN_USERNAME โŒ - Emby-specific admin username 2
PLEX_ADMIN_USERNAME โŒ - Plex-specific admin username 2
X_FRAME_OPTIONS โŒ DENY Security header: X-Frame-Options
CSP_FRAME_ANCESTORS โŒ none Security header: Content-Security-Policy frame-ancestors
ALLOW_PRIVATE_PROVIDER_URLS โŒ false Allow private/LAN provider URLs for BYOP user inputs
PLEX_IMAGE_ALLOWED_HOSTS โŒ - Extra allowlist for Plex image hosts (comma-separated). PLEX_URL/PLEX_PUBLIC_URL are allowed by default.
USE_ANALYTICS โŒ false Enable anonymous usage analytics (Vercel deployments)
ENABLE_DEBUG โŒ false Enable verbose debug logging and client-server error mapping
USE_STATIC_FILTERS โŒ false Skip dynamic filter fetching and use built-in genre/year/rating lists. Useful for very large libraries where filter API calls time out.

โœณ๏ธ = Required conditionally


Features (Deep dive)

Session Settings

When you create a session, customize it for your group:

Match Strategies
  • Two or More: Any two people liking the same content creates a match

    • Best for: Larger groups where majority rules
    • Finding: Quick results, more options
  • Unanimous: Everyone must like it for a match

    • Best for: Smaller groups wanting guaranteed crowd-pleasers
    • Finding: Fewer but higher-quality matches
Session Restrictions
  • Max Likes: Limit right swipes per person

    • Forces thoughtful, selective choices
    • Prevents mindless approval
  • Max Nopes: Limit left swipes per person

    • Stops serial negativity
    • Encourages open-mindedness
  • Max Matches: Auto-stop when you have enough options

    • Perfect for when you just need 3-4 solid picks
Guest Lending (Account Sharing)

How it works:

  1. Host enables "Guest Lending" in settings
  2. Guest joins session with just a name - no account needed
  3. Swiparr uses the host's credentials to fetch content
  4. Guest gets a unique ID, their swipes are tracked separately
  5. Guests cannot access host account or modify settings

Security note: Host credentials are stored server-side and encrypted at rest using AUTH_SECRET while Guest Lending is enabled. See "Generating AUTH_SECRET" in Security & Privacy.

Perfect for: Movie nights with friends who don't have media servers

Admin Role

Automatically Assigned: First user to log in for each provider becomes that provider's admin.

Manual Assignment: Set ADMIN_USERNAME (global) or [PROVIDER]_ADMIN_USERNAME (e.g., JELLYFIN_ADMIN_USERNAME) environment variables.

Admin Privileges:

  • Configure included media libraries for the provider
  • Manage global provider settings
  • Override session restrictions
  • Access admin dashboard (only for providers with authentication)

๐Ÿ”„ Provider Flexibility: Two Modes

Server Lock Mode

PROVIDER_LOCK=true

One provider, admin-controlled

  • Admin configures ONE provider in environment variables
  • All users automatically use this provider
  • Best for: Families, roommates, shared media servers

BYOP Mode

PROVIDER_LOCK=false

Bring Your Own Provider

  • Each user connects their own provider during onboarding
  • Users can switch providers anytime
  • Best for: Users with different media servers, and/or you have none

๐Ÿ”’ Security & Privacy

  • Generating AUTH_SECRET (optional):
# macOS and Linux
openssl rand -base64 32

Windows users can use https://generate-secret.vercel.app/32.

  • Encrypted Sessions: iron-session with secure, encrypted cookies
  • Encrypted Guest Lending Tokens: host access tokens are encrypted at rest when Guest Lending is enabled
  • Scoped Access: Guests can only swipe, no account access
  • Data Ownership: Self-hosted = your data stays on your server
  • Provider Isolation: No credential sharing in BYOP mode
  • CORS Protection: Configured for safe media server integration
  • Security Headers: X-Content-Type-Options, X-XSS-Protection, CSP, Referrer-Policy
  • Network Safety: Private/LAN provider URLs are blocked by default; enable via ALLOW_PRIVATE_PROVIDER_URLS
  • Mode Awareness: Env-configured providers are trusted when PROVIDER_LOCK=true; user-supplied URLs are checked

๐Ÿค Contributing

Swiparr is now open for contributions! ๐ŸŽ‰

How to Contribute

  1. Start with Discussion - Propose changes before coding
  2. Fork & Develop - After discussion approval
  3. Pull Request - With clear description and tests

Development Setup

git clone https://github.com/m3sserstudi0s/swiparr.git
cd swiparr
npm install
npm run dev          # Start dev server
npm run lint         # Check code style

Contribution Areas

  • Provider Integrations: Improve Emby/Plex support
  • UI/UX: Mobile responsiveness, accessibility
  • Performance: Optimize queries, bundle size
  • Documentation: Examples, guides, tutorials
  • Testing: Add test coverage (currently minimal)

First-time contributors welcome! Start with "good first issue" discussions.


๐Ÿ’š Support the Project

Swiparr is free, open source, and community-supported. Your contributions help:

  • โ˜• Buy Me a Coffee - Quick one-time support
  • ๐ŸŒŸ Star on GitHub - Show your support (it's free!)
  • ๐Ÿข Use swiparr.com - The hosted version includes infrastructure funding

All support directly funds development and infrastructure costs.


๐Ÿ“ž Community & Support

All support, questions, and discussions happen in GitHub Discussions:

Topic Link
โ“ Questions & Help Ask a Question
๐Ÿ’ก Feature Ideas Propose a Feature
๐Ÿ› Bug Reports Report a Bug
๐Ÿ™Œ General Chat Start a Discussion

๐Ÿณ Docker Advanced Topics

Custom Base Path

If you want to serve Swiparr under a subpath โ€” e.g. https://jellyfin.example.com/swipe/ โ€” you need to set URL_BASE_PATH at image build time, not as a runtime environment variable.

Why? Next.js bakes asset URLs (/_next/static/...) into the compiled output at build time. Setting a base path only at runtime can fix page routing but leaves all JS/CSS/image references pointing at the wrong path, breaking the app. The prefix must be known before the build.

The prebuilt image from ghcr.io does not support URL_BASE_PATH โ€” it is built without a base path. You must build your own image.

If you are not using compose and want to build the image

git clone https://github.com/m3sserstudi0s/swiparr.git
cd swiparr
docker build --build-arg URL_BASE_PATH=/swipe -t swiparr-custom .

Then use swiparr-custom as your image name in your docker run command.

If you are using compose, compose can build and manage the image for you at runtime

services:
  swiparr:
    pull_policy: build
    build:
      context: https://github.com/m3sserstudi0s/swiparr.git
      args:
        URL_BASE_PATH: /swipe
    container_name: swiparr
    restart: unless-stopped
    environment:
      - JELLYFIN_URL=http://jellyfin:8096
      - URL_BASE_PATH=/swipe       # must match the --build-arg value exactly
    volumes:
      - ./swiparr-data:/app/data
    ports:
      - 4321:4321

URL_BASE_PATH must also be passed as a runtime environment variable so the app generates correct internal links and auth redirects. It must match the --build-arg value exactly.

Step 3 โ€” Configure your reverse proxy

Forward requests for the subpath to the container. The app handles stripping the prefix internally โ€” do not strip it in the proxy.

Nginx:

location /swipe {
    proxy_pass http://swiparr:4321;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Traefik (Docker labels):

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.swiparr.rule=Host(`jellyfin.example.com`) && PathPrefix(`/swipe`)"
  - "traefik.http.routers.swiparr.tls=true"

Caddy:

handle /swipe* {
    reverse_proxy swiparr:4321
}

Reverse Proxy Configuration (root path)

Nginx Example:

location / {
    proxy_pass http://swiparr:4321;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Required Headers:

  • Host - Required for authentication
  • X-Forwarded-For - Client IP for logging
  • X-Forwarded-Proto - Protocol detection

Volume Management

volumes:
  - ./data:/app/data          # Database & cache
  - ./logs:/app/logs          # Optional: Persist logs

Environment File

Use --env-file .env with Docker for cleaner configuration management.


๐Ÿ“š Additional Resources

  • AGENTS.md - Developer guide and code standards (for contributors)
  • GitHub Releases - Detailed changelog for each version

๐Ÿ“„ License

MIT License - See LICENSE file for details

You're free to use, modify, and distribute Swiparr. Commercial use is permitted.


Made with โค๏ธ and late nights

๐ŸŒ Swiparr Global โ€ข โญ GitHub Repo โ€ข ๐Ÿ’ฌ Community

Footnotes

  1. Can be set to a local file (internal to container) OR external URL. Mostly relevant for Vercel deployments, which uses the Turso integration in the set-up workflow by default where these values are auto-generated and -injected. Can of course be swapped out with a database service provider of choice. โ†ฉ โ†ฉ2

  2. Only applicable for providers with authentication (Jellyfin, Plex, Emby). Admin role ownership is tracked per-provider. Defaults to the first user of that provider that logs in (if supported), or matching env vars. Admin capabilities are disabled for providers without built-in authentication (like TMDB). โ†ฉ โ†ฉ2 โ†ฉ3 โ†ฉ4

About

Swipe on what to watch next.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Sponsor this project

  •  

Packages

 
 
 

Contributors

Languages

โšก