Skip to content

Latest commit

 

History

History
1328 lines (1025 loc) · 42.2 KB

File metadata and controls

1328 lines (1025 loc) · 42.2 KB

HTTP API Documentation

This document describes the FBC Uploader REST API endpoints. All endpoints return JSON responses unless otherwise specified. The API implements token-based authentication and TUS protocol for resumable file uploads.

Note: All API routes are prefixed with /api. For deployment and environment configuration, see README.md.


Table of Contents


Authentication

Admin Authentication

Admin-only endpoints require a Bearer token in the Authorization header:

Authorization: Bearer <admin_api_key>

The admin API key is configured via FBC_ADMIN_API_KEY environment variable or auto-generated and stored in {config_path}/secret.key.

Public Endpoints

The following endpoints are always public:

  • GET /health
  • GET /api/tokens/{token}/info
  • POST /api/uploads/initiate?token=...
  • All TUS protocol endpoints (/api/uploads/{id}/tus)
  • GET /api/metadata/
  • POST /api/metadata/validate
  • GET /api/notice/

When FBC_ALLOW_PUBLIC_DOWNLOADS=1, download endpoints also become public without requiring admin authentication.


Global Notes

  • Content-Type

    • Requests with JSON body should include Content-Type: application/json
    • TUS protocol PATCH requests require Content-Type: application/offset+octet-stream
    • Responses typically include Content-Type: application/json, unless returning a file
  • Status Codes

    • 200 OK - Request successful
    • 201 Created - Resource created successfully
    • 204 No Content - Request successful, no content to return
    • 4xx - Client errors (bad request, unauthorized, forbidden, not found)
    • 5xx - Server errors
  • Error Responses When an error occurs, responses follow this structure:

    {
      "detail": "Error description"
    }

    or for validation errors:

    {
      "detail": {
        "field": "field_name",
        "message": "Error message"
      }
    }
  • Datetime Format All datetime values are in UTC with ISO 8601 format including timezone info.


Endpoints

GET /health

Health check endpoint to verify the service is running.

Authentication: None

Response (200):

{
  "status": "ok"
}

POST /api/tokens/

Create a new upload token.

Authentication: Required (Admin)

Request Body:

{
  "max_uploads": 5,
  "max_size_bytes": 104857600,
  "expiry_datetime": "2025-12-24T00:00:00Z",
  "allowed_mime": ["application/pdf", "video/*"]
}

Fields:

  • max_uploads (integer, optional): Maximum number of uploads allowed (default: 1; min: 1)
  • max_size_bytes (integer, required): Maximum file size in bytes (> 0)
  • expiry_datetime (datetime, optional): Token expiration date (defaults to current time + FBC_DEFAULT_TOKEN_TTL_HOURS)
  • allowed_mime (array of strings, optional): Allowed MIME types with wildcard support (e.g., video/*). Empty = all types allowed

Response (201):

{
  "token": "upload-token-string",
  "download_token": "fbc_download-token-string",
  "upload_url": "http://localhost:8000/api/uploads/initiate?token=upload-token-string",
  "expires_at": "2025-12-24T00:00:00Z",
  "max_uploads": 5,
  "max_size_bytes": 104857600,
  "allowed_mime": ["application/pdf", "video/*"]
}

GET /api/tokens/

List all upload tokens.

Authentication: Required (Admin)

Query Parameters:

  • skip (integer, optional): Number of records to skip (default: 0)
  • limit (integer, optional): Maximum records to return (default: 10)

Response (200):

{
  "tokens": [
    {
      "token": "upload-token-string",
      "download_token": "fbc_download-token-string",
      "expires_at": "2025-12-24T00:00:00Z",
      "uploads_used": 2,
      "max_uploads": 5,
      "max_size_bytes": 104857600,
      "allowed_mime": ["application/pdf"],
      "disabled": false,
      "created_at": "2025-12-23T00:00:00Z",
      "remaining_uploads": 3
    }
  ],
  "total": 1
}

GET /api/tokens/{token_value}

Get detailed information about a specific token.

Authentication: None

Path Parameters:

  • token_value (string): Upload token or download token

Response (200):

{
  "token": "upload-token-string",
  "download_token": "fbc_download-token-string",
  "expires_at": "2025-12-24T00:00:00Z",
  "uploads_used": 0,
  "max_uploads": 1,
  "max_size_bytes": 104857600,
  "allowed_mime": ["application/pdf"],
  "disabled": false,
  "created_at": "2025-12-23T00:00:00Z",
  "remaining_uploads": 1
}

Error Responses:

  • 404 Not Found - Token does not exist

PATCH /api/tokens/{token_value}

Update an existing token.

Authentication: Required (Admin)

Path Parameters:

  • token_value (string): Upload token or download token

Request Body:

{
  "max_uploads": 10,
  "max_size_bytes": 209715200,
  "expiry_datetime": "2025-12-31T23:59:59Z",
  "allowed_mime": ["image/*", "video/*"],
  "disabled": false
}

Fields (all optional):

  • max_uploads (integer): Update maximum uploads
  • max_size_bytes (integer): Update maximum file size
  • expiry_datetime (datetime): Update expiration date
  • allowed_mime (array): Update allowed MIME types
  • disabled (boolean): Enable or disable the token

Response (200):

{
  "token": "upload-token-string",
  "download_token": "fbc_download-token-string",
  "expires_at": "2025-12-31T23:59:59Z",
  "uploads_used": 0,
  "max_uploads": 10,
  "max_size_bytes": 209715200,
  "allowed_mime": ["image/*", "video/*"],
  "disabled": false,
  "created_at": "2025-12-23T00:00:00Z",
  "remaining_uploads": 10
}

Error Responses:

  • 404 Not Found - Token does not exist

DELETE /api/tokens/{token_value}

Delete a token and its associated uploads.

Authentication: Required (Admin)

Path Parameters:

  • token_value (string): Upload token or download token

Query Parameters:

  • delete_files (boolean, optional): Whether to delete physical files (default: false)

Response (204): No content

Error Responses:

  • 404 Not Found - Token does not exist

Notes:

  • Deletes the token and all associated upload records
  • Physical files are deleted from storage if delete_files=true
  • This operation cannot be undone

GET /api/tokens/{token_value}/info

Get public token information including uploads.

Authentication: None

Path Parameters:

  • token_value (string): The upload token

Response (200):

{
  "token": "upload-token-string",
  "download_token": "fbc_download-token-string",
  "remaining_uploads": 1,
  "max_uploads": 1,
  "max_size_bytes": 104857600,
  "max_chunk_bytes": 94371840,
  "allowed_mime": ["application/pdf"],
  "expires_at": "2025-12-24T00:00:00Z",
  "allow_public_downloads": false,
  "uploads": [
    {
      "public_id": "rT72ZKGMPdldiEmA9eDI7kik",
      "filename": "document.pdf",
      "ext": "pdf",
      "mimetype": "application/pdf",
      "size_bytes": 1024000,
      "meta_data": {"title": "My Document"},
      "upload_length": 1024000,
      "upload_offset": 1024000,
      "recommended_chunk_bytes": 94371840,
      "status": "completed",
      "created_at": "2025-12-23T12:00:00Z",
      "completed_at": "2025-12-23T12:01:00Z",
      "stream_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/stream",
      "thumbnail_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/thumbnail",
      "download_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik",
      "upload_url": "http://localhost:8000/api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus"
    }
  ]
}

Fields:

  • remaining_uploads: Number of uploads still available
  • max_chunk_bytes: Maximum chunk size for TUS uploads (from FBC_MAX_CHUNK_BYTES)
  • allow_public_downloads: Whether public downloads are enabled
  • uploads: Array of upload records
  • recommended_chunk_bytes: Server-selected TUS chunk size to reuse for checksum-verified resume operations
  • stream_url: Inline media URL for browser playback when the upload is completed
  • thumbnail_url: Preview image URL for embeds and thumbnail-first media UIs

Upload Status Values:

  • initiated - Upload created but no data uploaded yet
  • in_progress - Upload in progress
  • completed - Upload complete

Error Responses:

  • 404 Not Found - Token does not exist

GET /api/tokens/{token_value}/uploads

List all uploads for a specific token.

Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)

Path Parameters:

  • token_value (string): Upload token or download token

Response (200):

[
  {
    "public_id": "rT72ZKGMPdldiEmA9eDI7kik",
    "filename": "document.pdf",
    "ext": "pdf",
    "mimetype": "application/pdf",
    "size_bytes": 1024000,
    "meta_data": {"title": "My Document"},
    "upload_length": 1024000,
    "upload_offset": 1024000,
    "recommended_chunk_bytes": 94371840,
     "status": "completed",
      "created_at": "2025-12-23T12:00:00Z",
      "completed_at": "2025-12-23T12:01:00Z",
      "stream_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/stream",
      "thumbnail_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/thumbnail",
      "download_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik",
      "upload_url": "http://localhost:8000/api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus"
    }
]

Error Responses:

  • 404 Not Found - Token does not exist

GET /api/tokens/{download_token}/uploads/{upload_id}

Get metadata information about a completed upload.

Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)

Path Parameters:

  • download_token (string): The download token
  • upload_id (string): The upload record public ID (random string)

Response (200):

{
  "public_id": "rT72ZKGMPdldiEmA9eDI7kik",
  "filename": "document.pdf",
  "ext": "pdf",
  "mimetype": "application/pdf",
  "size_bytes": 1024000,
  "meta_data": {
    "title": "My Document"
  },
  "upload_length": 1024000,
  "upload_offset": 1024000,
  "recommended_chunk_bytes": 94371840,
  "status": "completed",
  "created_at": "2025-01-01T12:00:00Z",
  "completed_at": "2025-01-01T12:05:00Z",
  "stream_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/stream",
  "thumbnail_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/thumbnail",
  "upload_url": "http://localhost:8000/api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus",
  "download_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/download"
}

Error Responses:

  • 404 Not Found - Download token or upload not found
  • 409 Conflict - Upload not yet completed

GET /api/tokens/{download_token}/uploads/{upload_id}/stream

HEAD is also supported.

Stream a completed file inline for browser playback.

Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)

Path Parameters:

  • download_token (string): The download token
  • upload_id (string): The upload record public ID (random string)

Response (200): Returns the file with headers:

  • Content-Type: Original file MIME type or application/octet-stream
  • Content-Disposition: inline; filename="original-filename.ext"
  • Content-Length: File size in bytes

Error Responses:

  • 404 Not Found - Download token or upload not found
  • 409 Conflict - Upload not yet completed

Notes:

  • Use this endpoint for HTML <video> and <audio> playback
  • Use /download when you want attachment-style download behavior

GET /api/tokens/{download_token}/uploads/{upload_id}/subtitles

List external subtitle tracks that match a completed upload.

Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)

Path Parameters:

  • download_token (string): The download token
  • upload_id (string): The upload record public ID (random string)

Response (200):

{
  "subtitles": [
    {
      "source_format": "vtt",
      "delivery_format": "vtt",
      "renderer": "native",
      "url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/subtitles/vtt"
    },
    {
      "source_format": "ass",
      "delivery_format": "ass",
      "renderer": "assjs",
      "url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik/subtitles/ass"
    }
  ]
}

Notes:

  • This endpoint only returns tracks discovered under FBC_SUBTITLE_PATH.
  • A subtitle filename containing the upload ID is preferred over name-based matches. No particular prefix or bracket format is required around the ID.
  • Name-based discovery first uses the uploaded filename, then optionally tries title and broadcast_date metadata when those values are available.
  • Discovery results are cached per upload for FBC_SUBTITLE_CACHE_TTL_SECONDS, including cases where no subtitles are found.
  • Results are ordered by renderer preference: .vtt, then .srt, then .ass.
  • .srt files are exposed here with source_format: "srt", delivery_format: "vtt", and renderer: "native".
  • Duplicate matches for the same stem and extension are treated as ambiguous and omitted.

Error Responses:

  • 404 Not Found - Download token or upload not found
  • 409 Conflict - Upload not yet completed

GET /api/tokens/{download_token}/uploads/{upload_id}/subtitles/{source_format}

HEAD is also supported.

Return the selected subtitle content for a completed upload.

Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)

Path Parameters:

  • download_token (string): The download token
  • upload_id (string): The upload record public ID (random string)
  • source_format (string): One of vtt, srt, or ass

Response (200):

  • vtt: Content-Type: text/vtt
  • srt: Converted on the fly and returned as Content-Type: text/vtt
  • ass: Returned as Content-Type: text/x-ssa

Error Responses:

  • 404 Not Found - Download token, upload, or subtitle not found
  • 409 Conflict - Upload not yet completed

Notes:

  • The share page uses vtt and converted srt as native browser subtitle tracks.
  • .ass subtitles are intended for ASS.js-based browser rendering.
  • Subtitle discovery is cached per upload, so newly added or removed subtitle files may take up to FBC_SUBTITLE_CACHE_TTL_SECONDS to appear.

GET /api/tokens/{download_token}/uploads/{upload_id}/preview.mp4

HEAD is also supported.

Return a short MP4 preview clip for bot embeds when one has been generated.

Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)

Path Parameters:

  • download_token (string): The download token
  • upload_id (string): The upload record public ID (random string)

Response (200): Returns the file with headers:

  • Content-Type: video/mp4
  • Content-Disposition: inline; filename="..."

Error Responses:

  • 404 Not Found - Download token, upload, or preview not found
  • 409 Conflict - Upload not yet completed

Notes:

  • Bot embed metadata may use this endpoint instead of the full stream for large videos that meet the preview size threshold.
  • Setting FBC_EMBED_PREVIEW_MIN_SIZE_BYTES=0 disables preview sidecars and keeps embed metadata on the original /stream URL.
  • If no preview sidecar exists, embed metadata falls back to the original /stream URL.

GET /api/tokens/{download_token}/uploads/{upload_id}/thumbnail

HEAD is also supported.

Return a preview image for a completed upload.

Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)

Path Parameters:

  • download_token (string): The download token
  • upload_id (string): The upload record public ID (random string)

Response (200): Returns an image with headers:

  • Content-Type: image/jpeg
  • Content-Disposition: inline; filename="..."

Error Responses:

  • 404 Not Found - Download token or upload not found
  • 409 Conflict - Upload not yet completed

Notes:

  • Video uploads return a generated sidecar thumbnail when available
  • The same endpoint is used by social embeds and the thumbnail-first share page UI

GET /api/tokens/{download_token}/uploads/{upload_id}/download

HEAD is also supported.

Download a completed file.

Authentication: Required (Admin, or public if FBC_ALLOW_PUBLIC_DOWNLOADS=1)

Path Parameters:

  • download_token (string): The download token
  • upload_id (string): The upload record public ID (random string)

Response (200): Returns the file with headers:

  • Content-Type: Original file MIME type or application/octet-stream
  • Content-Disposition: attachment; filename="original-filename.ext"
  • Content-Length: File size in bytes

Error Responses:

  • 404 Not Found - Download token or upload not found
  • 409 Conflict - Upload not yet completed

POST /api/tokens/{download_token}/uploads/{upload_id}/watch

Create a temporary Watch Party for a completed video or audio upload.

Authentication: Valid download token in the path; requires FBC_ALLOW_PUBLIC_DOWNLOADS=1

Path Parameters:

  • download_token (string): Download token for the shared file
  • upload_id (string): Public ID of the completed upload

Request: No request body.

Response (200):

HTTP/1.1 200 OK
Content-Type: application/json
{
  "room_id": "room-id-string",
  "host_key": "creator-only-secret",
  "invite_path": "/f/fbc_download-token?upload=upload-id-string&room=room-id-string"
}

Fields:

  • room_id (string): Temporary room identifier used by the WebSocket URL
  • host_key (string): One-time creator key; keep it private and never include it in an invite URL
  • invite_path (string): Relative share-page path for guests

Error Responses:

  • 403 Forbidden - Public downloads are disabled
  • 404 Not Found - Download token or upload not found
  • 409 Conflict - Upload is not completed
  • 415 Unsupported Media Type - Upload is not video or audio
  • 503 Service Unavailable - In-memory room capacity has been reached

Notes:

  • The download token must be valid, enabled, unexpired, belong to the upload, and reference an existing file.
  • Rooms are stored in the current server process and expire after inactivity.
  • Connect to the returned room with WS /api/watch/{room_id}/ws.

DELETE /api/watch/{room_id}

Discard an unclaimed Watch Party room when no WebSocket participant has joined.

Authentication: Send the creator key in the X-Watch-Host-Key request header. The key is never accepted in the query string or path.

Response (204): The room was discarded.

Invalid, already-claimed, or occupied rooms return 404 Not Found without changing the room.


WS /api/watch/{room_id}/ws

Connect to a Watch Party and synchronize playback for its upload.

Authentication: Send the download token and upload public ID in the first JSON join message. The creator also sends host_key.

Path Parameters:

  • room_id (string): Room ID returned by the Watch Party creation endpoint

Request: No request body. Send the join JSON message immediately after the WebSocket connection is established.

Connection:

GET /api/watch/room-id-string/ws HTTP/1.1
Connection: Upgrade
Upgrade: websocket

The creator sends this message after connecting:

{
  "type": "join",
  "download_token": "fbc_download-token",
  "upload_id": "upload-id-string",
  "host_key": "creator-only-secret"
}

Guests omit host_key:

{
  "type": "join",
  "download_token": "fbc_download-token",
  "upload_id": "upload-id-string"
}

Server Messages:

Type Fields Meaning
ready participant_id, role, participant_count, participant_version, version, sync_required, host host_key Join accepted; role is host or guest; a host receives its rotated key. A reconnecting host has sync_required: true and must apply the state at or above version before controlling playback
state version, anchor_position, paused, playback_rate, server_time, play_at, participant_count, participant_version Current server playback state. play_at is a Unix timestamp in server seconds for a scheduled play, or null otherwise.
participants participant_count, participant_version Participant count changed. Clients ignore messages with an older participant_version
promotion participant_id, version, host_key The indicated guest became host and receives its rotated key
host_status status, version waiting means the host disconnected and authoritative playback is paused; connected means the host recovered or a guest was promoted. Lifecycle version values are monotonic, so clients ignore older status events. Guests joining during reconnect grace receive the current waiting status
synced version The server accepted the recovering or promoted host's synchronization acknowledgement
pong client_time, server_time Response to a valid ping
error message Request or party error

Client Messages:

  • Host play commands are scheduled by the server 0.5 seconds in the future. The returned state is authoritative; clients use their server-clock offset to begin at play_at. Pause, seek, rate, and snapshot commands remain immediate.
  • Host playback commands use type play, pause, seek, rate, or snapshot and require a finite position in seconds.
  • A recovering or promoted host must first apply the authoritative state delivered after ready or promotion; clients must not send playback commands or snapshots until that synchronization is complete. A new host with sync_required: false may send its initial snapshot.
  • A recovering or promoted host acknowledges applied state with { "type": "synced", "version": <state version> }. The server rejects playback commands until that participant acknowledges the current authoritative version, and resends error plus state for a stale acknowledgement.
  • rate and snapshot require playback_rate from 0.25 through 4; snapshot also requires boolean paused.
  • Guests may send ping with numeric client_time or ready to request current state. Guests cannot control playback.
  • Example host command: { "type": "seek", "position": 120.5 }
  • Example ping: { "type": "ping", "client_time": 1735689600000 }

Authority and Limits:

  • The first valid join with the creation host_key establishes the host. Guests may join first without taking the host role.
  • If the host disconnects, the server anchors and pauses playback, broadcasts a waiting status and state, and keeps its host_key valid for a 15-second reconnect grace period. A reconnecting host receives and must apply that paused state before controlling playback. After grace expires, the longest-connected guest is promoted, receives a rotated host_key, and all participants receive the authoritative paused state. A late joiner receives the current projected state, including its paused state and playback rate.
  • A process allows at most 1000 rooms and at most 3 rooms per download token. A room allows at most 32 participants; before a host is established, one participant slot is reserved for the host. All messages are limited to 60 per two seconds per participant, and playback commands to 20 per two seconds per host. The room also expires when its download token expires.
  • Messages are limited to 8192 bytes; joins must arrive within 10 seconds. Never-joined rooms are retained for 60 seconds, and rooms expire after 30 minutes of inactivity. Participants that send nothing for 90 seconds are removed. Cleanup runs approximately every second in this process.

Close Codes:

  • 1000 - Normal client disconnect
  • 1001 - Server shutdown or room expiry
  • 1008 - Invalid join, missing/invalid credential, unknown room, or other policy violation during connection

Error Responses:

  • Invalid playback commands, guest control attempts, invalid positions/rates, malformed JSON, oversized messages, and rate-limit violations receive an error message while the connection remains open.
  • Invalid joins and join timeouts receive an error message and close with WebSocket code 1008.
  • Playback positions must be between 0 and 86400 seconds inclusive. The host reconnect grace period is approximately 15 seconds. An all-message rate-limit violation closes the socket; playback command errors are reported without closing it.
  • Disabling or deleting a token, or deleting an upload, invalidates matching Watch Party rooms and closes their sockets. Natural token expiry rejects new joins and cleanup closes existing connections.

Notes:

  • Room creation and WebSocket access require FBC_ALLOW_PUBLIC_DOWNLOADS=1; the associated upload must be completed playable media.
  • The host_key is only for the creator/ promoted host connection, is rotated after each successful host join or promotion, and must not be put in an invite URL.
  • Joining before the creator is allowed; that participant remains a guest until host promotion.

POST /api/uploads/initiate

Initiate a new file upload.

Authentication: Required via query parameter

Query Parameters:

  • token (string, required): The upload token

Request Body:

{
  "meta_data": {
    "title": "My Document",
    "category": "reports"
  },
  "filename": "document.pdf",
  "filetype": "application/pdf",
  "size_bytes": 1024000
}

Fields:

  • meta_data (object, required): Metadata fields validated against {config_path}/metadata.json schema
  • filename (string, optional): Original filename
  • filetype (string, optional): MIME type
  • size_bytes (integer, optional): Total file size in bytes (> 0)

Response (201):

{
  "upload_id": "rT72ZKGMPdldiEmA9eDI7kik",
  "upload_url": "http://localhost:8000/api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus",
  "download_url": "http://localhost:8000/api/tokens/fbc_token/uploads/rT72ZKGMPdldiEmA9eDI7kik",
  "meta_data": {
    "title": "My Document",
    "category": "reports"
  },
  "allowed_mime": ["application/pdf"],
  "remaining_uploads": 0,
  "recommended_chunk_bytes": 1024000
}
  • recommended_chunk_bytes: Server-selected TUS chunk size for this upload. Clients should use this value when sending checksum-verified PATCH requests and when resuming the upload.

Error Responses:

  • 404 Not Found - Token does not exist
  • 403 Forbidden - Token expired, disabled, or upload limit reached
  • 413 Content Too Large - File size exceeds token limit
  • 415 Unsupported Media Type - File type not allowed for this token
  • 422 Unprocessable Entity - Metadata validation error

OPTIONS /api/uploads/tus

Get TUS protocol capabilities.

Authentication: None

Response (204):

Headers:

Tus-Resumable: 1.0.0
Tus-Version: 1.0.0
Tus-Extension: creation,termination,checksum
Tus-Checksum-Algorithm: sha1,sha256

HEAD /api/uploads/{upload_id}/tus

Check upload status (TUS protocol).

Authentication: None

Path Parameters:

  • upload_id (string): The upload record public ID (random string)

Response (200):

Headers:

Upload-Offset: 512000
Upload-Length: 1024000
Tus-Resumable: 1.0.0

Error Responses:

  • 404 Not Found - Upload not found
  • 409 Conflict - Upload length unknown

PATCH /api/uploads/{upload_id}/tus

Upload file chunk (TUS protocol).

Authentication: None

Path Parameters:

  • upload_id (string): The upload record public ID (random string)

Required Headers:

  • Upload-Offset (integer): Current upload offset (must match server state)
  • Tus-Resumable (string): TUS protocol version (1.0.0)
  • Content-Type: Must be application/offset+octet-stream
  • Content-Length (integer, optional): Chunk size

Optional Headers:

  • Upload-Checksum (string): Per-chunk checksum in the form <algorithm> <base64-digest>

Request Body: Binary data (file chunk)

Max Chunk Size: Controlled by FBC_MAX_CHUNK_BYTES (default: 90MB)

Response (204):

Headers:

Upload-Offset: 1024000
Tus-Resumable: 1.0.0
Upload-Length: 1024000

Error Responses:

  • 404 Not Found - Upload not found
  • 409 Conflict - Upload length unknown or mismatched Upload-Offset
  • 460 Checksum Mismatch - Uploaded chunk checksum did not match Upload-Checksum
  • 413 Content Too Large - Chunk too large or upload exceeds declared length
  • 415 Unsupported Media Type - Invalid Content-Type or actual file mimetype doesn't match allowed types

Notes:

  • The upload is only finalized after POST /api/uploads/{upload_id}/complete
  • When Upload-Checksum is supplied, the chunk is verified before it is appended to the upload file
  • Server-side MIME type validation: Upon completion, the server detects the actual file type using libmagic and validates it against the token's allowed_mime restrictions. If validation fails, the upload is rejected and deleted (HTTP 415)
  • The detected MIME type replaces the client-provided value in the database
  • The uploads_used counter on the token is incremented upon initiation, not completion

DELETE /api/uploads/{upload_id}/tus

Delete an upload and its associated file (TUS protocol).

Authentication: None

Path Parameters:

  • upload_id (string): The upload record public ID (random string)

Response (204): No content

Error Responses:

  • 404 Not Found - Upload not found

Notes:

  • Deletes both the database record and the physical file
  • This operation cannot be undone

DELETE /api/uploads/{upload_id}/cancel

Cancel an in-progress upload and restore the token slot.

Authentication: Required via query parameter

Path Parameters:

  • upload_id (string): The upload record public ID (random string)

Query Parameters:

  • token (string, required): The upload token

Response (200):

{
  "message": "Upload cancelled successfully",
  "remaining_uploads": 5
}

Error Responses:

  • 404 Not Found - Upload or token not found
  • 409 Conflict - Upload already completed

Notes:

  • Restores the upload slot to the token (increments remaining_uploads)
  • Deletes the physical file if it exists
  • Can only cancel uploads that are not completed

POST /api/uploads/{upload_id}/complete

Mark an upload as complete.

Authentication: None

Path Parameters:

  • upload_id (string): The upload record public ID (random string)

Response (200):

{
  "public_id": "rT72ZKGMPdldiEmA9eDI7kik",
  "filename": "document.pdf",
  "ext": "pdf",
  "mimetype": "application/pdf",
  "size_bytes": 1024000,
  "meta_data": {"title": "My Document"},
  "upload_length": 1024000,
  "upload_offset": 1024000,
  "status": "completed",
  "created_at": "2025-12-23T12:00:00Z",
  "completed_at": "2025-12-23T12:01:00Z",
  "download_url": null,
  "upload_url": null
}

Error Responses:

  • 403 Forbidden - Associated upload token expired or disabled
  • 404 Not Found - Upload or associated token not found
  • 409 Conflict - Upload has not received all declared bytes
  • 415 Unsupported Media Type - Detected file type is not allowed

GET /api/metadata/

Get the metadata schema configuration.

Authentication: None

Response (200):

{
  "fields": [
    {
      "key": "title",
      "label": "Title",
      "type": "string",
      "required": true,
      "minLength": 3,
      "maxLength": 100
    },
    {
      "key": "category",
      "label": "Category",
      "type": "select",
      "required": false,
      "options": ["reports", "images", "videos"],
      "allowCustom": false
    }
  ]
}

Field Schema:

Each field object can have:

  • key (string, required): Field identifier
  • label (string, optional): Human-readable label
  • type (string, required): Field type (string, text, number, integer, boolean, date, datetime, select, multiselect)
  • required (boolean, optional): Whether field is mandatory
  • options (array, optional): Available options for select/multiselect
  • allowCustom (boolean, optional): Allow custom values not in options
  • minLength, maxLength (integer, optional): String length constraints
  • min, max (number, optional): Numeric value constraints
  • regex (string, optional): Regular expression pattern for string validation
  • default (any, optional): Default value used by the frontend when initializing the upload form
  • extract_regex (string, optional): Python regular expression used by /api/metadata/extract to prefill metadata from a filename

Notes:

  • Schema is loaded from {config_path}/metadata.json
  • extract_regex is interpreted with Python re syntax

POST /api/metadata/extract

Extract metadata values from a filename using the configured schema.

Authentication: None

Request Body:

{
  "filename": "240101 Example Show [youtube-dQw4w9WgXcQ].mp4"
}

Response (200):

{
  "metadata": {
    "broadcast_date": "2024-01-01",
    "title": "Example Show",
    "source": "youtube",
    "source_id": "dQw4w9WgXcQ"
  }
}

Extraction Rules:

  1. Fields without extract_regex are ignored
  2. The regex is matched against the provided filename with case-insensitive search
  3. Capture group 1 is used when present; otherwise the full match is used
  4. For date fields, named groups year, month, and day are combined into YYYY-MM-DD
  5. Two-digit years are normalized to 20xx

POST /api/metadata/validate

Validate metadata payload against the schema.

Authentication: None

Request Body:

{
  "metadata": {
    "title": "My Document",
    "category": "reports"
  }
}

or direct metadata object:

{
  "title": "My Document",
  "category": "reports"
}

Response (200):

{
  "metadata": {
    "title": "My Document",
    "category": "reports"
  }
}

Error Responses:

  • 422 Unprocessable Entity - Validation error
    {
      "detail": {
        "field": "title",
        "message": "Must be at least 3 characters"
      }
    }

Validation Rules:

  1. Type Coercion: Values are automatically converted to the expected type
  2. Required Fields: Missing required fields result in validation error
  3. String/Text: Length validated against minLength/maxLength, pattern validated against regex
  4. Number/Integer: Value validated against min/max
  5. Boolean: Accepts various formats (true, 1, "yes", "on" → true)
  6. Date/DateTime: Parsed from ISO format string
  7. Select: Value must be in options (unless allowCustom is true)
  8. Multiselect: Each value must be in options (unless allowCustom is true)

GET /api/notice/

Get the system notice (if configured).

Authentication: None

Response (200):

{
  "notice": "# Important Notice\n\nThe system will undergo maintenance..."
}

Fields:

  • notice (string or null): Markdown content from {config_path}/notice.md, or null if file doesn't exist

Notes:

  • Notice is displayed to users in the frontend
  • Supports full Markdown formatting

GET /api/admin/validate

Validate the admin API key.

Authentication: Required (Admin)

Response (200):

{
  "valid": true
}

Error Responses:

  • 401 Unauthorized - Invalid or missing API key

DELETE /api/admin/uploads/{upload_id}

Delete an upload record and its file (Admin only).

Authentication: Required (Admin)

Path Parameters:

  • upload_id (string): The upload record public ID (random string)

Response (204): No content

Error Responses:

  • 404 Not Found - Upload not found

Notes:

  • Deletes both the database record and the physical file
  • Does not restore the upload slot to the token
  • This operation cannot be undone

TUS Protocol

The API implements the TUS resumable upload protocol v1.0.0.

Supported Extensions:

  • creation: Create new uploads
  • termination: Delete uploads
  • checksum: Verify uploaded chunks with SHA-1 or SHA-256 checksums

Key Features:

  • Resumable: Upload can be paused and resumed from the same offset
  • Chunked: Large files can be split into smaller chunks
  • Verified: Each chunk is verified by checking the offset

Required Headers:

  • Tus-Resumable: 1.0.0 - Protocol version
  • Upload-Offset: <bytes> - Current upload position
  • Content-Type: application/offset+octet-stream - Required for PATCH

Response Headers:

  • Upload-Offset: Updated upload position after chunk
  • Upload-Length: Total file size (if known)
  • Tus-Resumable: Protocol version echo

Upload Flow

Typical upload flow:

  1. Create Token (Admin)

    POST /api/tokens
    Authorization: Bearer YOUR_API_KEY
    Content-Type: application/json
    
    {
      "max_uploads": 5,
      "max_size_bytes": 104857600,
      "allowed_mime": ["application/pdf"]
    }
  2. Get Token Info (Client)

    GET /api/tokens/{token}
  3. Initiate Upload (Client)

    POST /api/uploads/initiate?token={token}
    Content-Type: application/json
    
    {
      "meta_data": {"title": "My Document"},
      "filename": "document.pdf",
      "filetype": "application/pdf",
      "size_bytes": 1024000
    }
  4. Upload File Chunks (TUS Protocol)

    # Check current offset
    HEAD /api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus
    
    # Upload chunk
    PATCH /api/uploads/rT72ZKGMPdldiEmA9eDI7kik/tus
    Upload-Offset: 0
    Tus-Resumable: 1.0.0
    Content-Type: application/offset+octet-stream
    
    [binary data]
  5. Complete Upload

    POST /api/uploads/rT72ZKGMPdldiEmA9eDI7kik/complete
  6. Download File

    GET /api/tokens/{download_token}/uploads/rT72ZKGMPdldiEmA9eDI7kik
    Authorization: Bearer YOUR_API_KEY

Error Codes Reference

Code Meaning Common Causes
200 OK Request successful
201 Created Resource created successfully
204 No Content Request successful, no content to return
400 Bad Request Invalid request format or parameters
401 Unauthorized Missing or invalid admin credentials
403 Forbidden Token expired, disabled, or limit reached
404 Not Found Resource not found
409 Conflict Upload not ready, offset mismatch, or state conflict
413 Content Too Large File or chunk exceeds limit
415 Unsupported Media Type Invalid Content-Type or file type not allowed
422 Unprocessable Entity Validation error in request body
500 Internal Server Error Server-side error

Notes

  • All datetime values are in UTC with ISO 8601 format including timezone info
  • File paths are resolved and stored as absolute paths
  • Upload tokens are 18-character URL-safe strings
  • Download tokens are prefixed with fbc_ followed by 16-character URL-safe strings
  • Upload IDs (public_id) are 18-character URL-safe random strings (not sequential integers for security)
  • Metadata is stored as JSON in the database (meta_data column)
  • Use TUS when an upload may need to resume after an interruption or retry a failed chunk
  • Maximum chunk size is controlled by FBC_MAX_CHUNK_BYTES (default: 90MB)
  • Media remux eligibility is capped by FBC_MAX_REMUX_BYTES (default: 5GB)
  • Multimedia post-processing runs with up to FBC_POSTPROCESSING_WORKERS concurrent workers (default: 4)