Files
mystuff_backend/API_DOCS.md
Lars Behrends 728ca893b1 Add API documentation and project README
Add comprehensive API_DOCS.md describing the Kyoo backend API (endpoints, request/response examples, image handling, authentication, error handling, CORS, logging) and a README.md with project overview, tech stack, quick start, API endpoints, data models, configuration and Docker instructions. Provides developers with usage examples, directory layout and troubleshooting steps for local deployment.
2026-04-12 03:00:17 +02:00

1113 lines
20 KiB
Markdown

# Kyoo Backend API Documentation
**Base URL**: `http://localhost:6400/api`
**Content-Type**: `application/json`
---
## Table of Contents
- [Response Format](#response-format)
- [Authentication](#authentication)
- [Endpoints](#endpoints)
- [Root](#root)
- [Documentation](#documentation)
- [Media](#media)
- [Series Episodes](#series-episodes)
- [Music Tracks](#music-tracks)
- [Cast](#cast)
- [Adult Cast](#adult-cast)
- [Images](#images)
- [Settings](#settings)
---
## Response Format
### Success Response
```json
{
"success": true,
"data": { ... }
}
```
### Error Response
```json
{
"success": false,
"error": "Error message"
}
```
### Paginated Response
```json
{
"success": true,
"data": {
"items": [ ... ],
"total": 100,
"page": 1,
"limit": 20,
"totalPages": 5
}
}
```
### HTTP Status Codes
- `200` - Success
- `201` - Created
- `400` - Bad Request
- `404` - Not Found
- `405` - Method Not Allowed
- `500` - Internal Server Error
---
## Authentication
Currently, the API does not require authentication. All endpoints are publicly accessible.
---
## Endpoints
### Root
#### Get API Information
**Endpoint**: `GET /api`
**Description**: Returns API version and available endpoints.
**Response**:
```json
{
"success": true,
"message": "Media API v1.0",
"endpoints": {
"GET /api/docs": "Automatische API-Dokumentation",
"GET /api/images/*": "Bilder abrufen",
"GET /api/media": "Alle Medien abrufen",
"GET /api/media/:id": "Ein Medium abrufen",
"POST /api/media": "Neues Medium erstellen",
"PUT /api/media/:id": "Medium aktualisieren",
"DELETE /api/media/:id": "Medium löschen",
"GET /api/cast": "Alle Cast-Mitglieder abrufen",
"GET /api/cast/:id": "Cast-Mitglied abrufen",
"GET /api/cast/:id/media": "Alle Medien eines Cast-Mitglieds abrufen",
"POST /api/cast": "Neues Cast-Mitglied erstellen",
"PUT /api/cast/:id": "Cast-Mitglied aktualisieren",
"DELETE /api/cast/:id": "Cast-Mitglied löschen",
"GET /api/settings": "Einstellungen abrufen",
"PUT /api/settings": "Einstellungen aktualisieren"
}
}
```
---
### Documentation
#### Get Auto-Generated Documentation
**Endpoint**: `GET /api/docs`
**Description**: Returns automatically generated API documentation.
**Response**:
```json
{
"success": true,
"data": { ... }
}
```
---
### Media
#### Get All Media
**Endpoint**: `GET /api/media`
**Query Parameters**:
- `page` (integer, optional) - Page number (default: 1)
- `limit` (integer, optional) - Items per page (default: 20)
- `category` (string, optional) - Filter by category (e.g., "movie", "tv", "music", "game")
- `type` (string, optional) - Filter by type (e.g., "Movie", "TV", "Album", "Game")
- `search` (string, optional) - Search in title and description
**Example Request**:
```bash
GET /api/media?page=1&limit=20&category=movie&search=action
```
**Response**:
```json
{
"success": true,
"data": {
"items": [
{
"id": 1,
"title": "Movie Title",
"cleanname": "movie-title",
"year": 2024,
"poster": "/images/movies/poster_xxx.webp",
"banner": "/images/movies/banner_xxx.webp",
"description": "Description text",
"rating": 8.5,
"category": "movie",
"type": "Movie",
"status": "released",
"aspectRatio": "16:9",
"runtime": 120,
"director": "Director Name",
"writer": "Writer Name",
"source": "netflix",
"releaseDate": "2024-01-01",
"createdAt": "2024-01-01 00:00:00",
"updatedAt": "2024-01-01 00:00:00"
}
],
"total": 100,
"page": 1,
"limit": 20,
"totalPages": 5
}
}
```
#### Get Single Media
**Endpoint**: `GET /api/media/:id`
**Path Parameters**:
- `id` (integer, required) - Media ID
**Example Request**:
```bash
GET /api/media/1
```
**Response**:
```json
{
"success": true,
"data": {
"id": 1,
"title": "Movie Title",
"cleanname": "movie-title",
"year": 2024,
"poster": "/images/movies/poster_xxx.webp",
"banner": "/images/movies/banner_xxx.webp",
"description": "Description text",
"rating": 8.5,
"category": "movie",
"type": "Movie",
"status": "released",
"aspectRatio": "16:9",
"runtime": 120,
"director": "Director Name",
"writer": "Writer Name",
"source": "netflix",
"releaseDate": "2024-01-01",
"genres": ["Action", "Drama"],
"tags": ["tag1", "tag2"],
"studios": ["Studio1"],
"staff": [
{
"id": 1,
"name": "Actor Name",
"photo": "/images/cast/photo_xxx.webp",
"role": "Actor",
"characterName": "Character",
"characterImage": "/images/characters/char_xxx.webp"
}
],
"createdAt": "2024-01-01 00:00:00",
"updatedAt": "2024-01-01 00:00:00"
}
}
```
#### Create Media
**Endpoint**: `POST /api/media`
**Request Body**:
```json
{
"title": "Movie Title",
"year": 2024,
"poster": "base64_encoded_image_or_url",
"banner": "base64_encoded_image_or_url",
"description": "Description text",
"rating": 8.5,
"category": "movie",
"type": "Movie",
"status": "released",
"aspectRatio": "16:9",
"runtime": 120,
"director": "Director Name",
"writer": "Writer Name",
"source": "netflix",
"releaseDate": "2024-01-01",
"genres": ["Action", "Drama"],
"tags": ["tag1", "tag2"],
"studios": ["Studio1"],
"staff": [
{
"id": 1,
"role": "Actor",
"characterName": "Character",
"characterImage": "base64_encoded_image_or_url"
}
]
}
```
**Note**: If `staff` includes a cast member by `name` instead of `id`, the cast member will be automatically created if it doesn't exist.
**Response** (201 Created):
```json
{
"success": true,
"data": {
"id": 1
}
}
```
**Response** (200 OK - if media already exists):
```json
{
"success": true,
"data": {
"id": 1,
"message": "Media already exists"
}
}
```
#### Update Media
**Endpoint**: `PUT /api/media/:id`
**Path Parameters**:
- `id` (integer, required) - Media ID
**Request Body**: Same as create, but all fields are optional.
**Response**:
```json
{
"success": true,
"data": {
"id": 1
}
}
```
#### Delete Media
**Endpoint**: `DELETE /api/media/:id`
**Path Parameters**:
- `id` (integer, required) - Media ID
**Response**:
```json
{
"success": true,
"message": "Media deleted successfully"
}
```
---
### Series Episodes
#### Get All Episodes
**Endpoint**: `GET /api/media/:id/episodes`
**Path Parameters**:
- `id` (integer, required) - Series media ID
**Query Parameters**:
- `season` (integer, optional) - Filter by season number
**Example Request**:
```bash
GET /api/media/1/episodes?season=1
```
**Response**:
```json
{
"success": true,
"data": {
"items": [
{
"id": 1,
"media_id": 1,
"season": 1,
"episode": 1,
"title": "Episode Title",
"overview": "Episode description",
"airDate": "2024-01-01",
"runtime": 45,
"poster": "/images/episodes/poster_xxx.webp"
}
]
}
}
```
#### Get Single Episode
**Endpoint**: `GET /api/media/:id/episodes/:episodeId`
**Path Parameters**:
- `id` (integer, required) - Series media ID
- `episodeId` (integer, required) - Episode ID
**Response**:
```json
{
"success": true,
"data": {
"id": 1,
"media_id": 1,
"season": 1,
"episode": 1,
"title": "Episode Title",
"overview": "Episode description",
"airDate": "2024-01-01",
"runtime": 45,
"poster": "/images/episodes/poster_xxx.webp"
}
}
```
#### Add Episode
**Endpoint**: `POST /api/media/:id/episodes`
**Path Parameters**:
- `id` (integer, required) - Series media ID
**Request Body**:
```json
{
"season": 1,
"episode": 1,
"title": "Episode Title",
"overview": "Episode description",
"airDate": "2024-01-01",
"runtime": 45,
"poster": "base64_encoded_image_or_url"
}
```
**Response** (201 Created):
```json
{
"success": true,
"data": {
"id": 1
}
}
```
#### Update Episode
**Endpoint**: `PUT /api/media/:id/episodes/:episodeId`
**Path Parameters**:
- `id` (integer, required) - Series media ID
- `episodeId` (integer, required) - Episode ID
**Request Body**: Same as add episode, but all fields are optional.
**Response**:
```json
{
"success": true,
"data": {
"id": 1
}
}
```
#### Delete Episode
**Endpoint**: `DELETE /api/media/:id/episodes/:episodeId`
**Path Parameters**:
- `id` (integer, required) - Series media ID
- `episodeId` (integer, required) - Episode ID
**Response**:
```json
{
"success": true,
"message": "Episode deleted successfully"
}
```
---
### Music Tracks
#### Get All Tracks
**Endpoint**: `GET /api/media/:id/tracks`
**Path Parameters**:
- `id` (integer, required) - Album media ID
**Response**:
```json
{
"success": true,
"data": {
"items": [
{
"id": 1,
"media_id": 1,
"track": 1,
"title": "Track Title",
"duration": 240,
"artists": ["Artist1", "Artist2"]
}
]
}
}
```
#### Get Single Track
**Endpoint**: `GET /api/media/:id/tracks/:trackId`
**Path Parameters**:
- `id` (integer, required) - Album media ID
- `trackId` (integer, required) - Track ID
**Response**:
```json
{
"success": true,
"data": {
"id": 1,
"media_id": 1,
"track": 1,
"title": "Track Title",
"duration": 240,
"artists": ["Artist1", "Artist2"]
}
}
```
#### Add Track
**Endpoint**: `POST /api/media/:id/tracks`
**Path Parameters**:
- `id` (integer, required) - Album media ID
**Request Body**:
```json
{
"track": 1,
"title": "Track Title",
"duration": 240,
"artists": ["Artist1", "Artist2"]
}
```
**Response** (201 Created):
```json
{
"success": true,
"data": {
"id": 1
}
}
```
#### Update Track
**Endpoint**: `PUT /api/media/:id/tracks/:trackId`
**Path Parameters**:
- `id` (integer, required) - Album media ID
- `trackId` (integer, required) - Track ID
**Request Body**: Same as add track, but all fields are optional.
**Response**:
```json
{
"success": true,
"data": {
"id": 1
}
}
```
#### Delete Track
**Endpoint**: `DELETE /api/media/:id/tracks/:trackId`
**Path Parameters**:
- `id` (integer, required) - Album media ID
- `trackId` (integer, required) - Track ID
**Response**:
```json
{
"success": true,
"message": "Track deleted successfully"
}
```
---
### Cast
#### Get All Cast
**Endpoint**: `GET /api/cast`
**Query Parameters**:
- `page` (integer, optional) - Page number (default: 1)
- `limit` (integer, optional) - Items per page (default: 20)
- `search` (string, optional) - Search by name
**Example Request**:
```bash
GET /api/cast?page=1&limit=20&search=john
```
**Response**:
```json
{
"success": true,
"data": {
"items": [
{
"id": 1,
"name": "Actor Name",
"cleanname": "actor-name",
"photo": "/images/cast/photo_xxx.webp",
"bio": "Biography text",
"birthDate": "1990-01-01",
"birthPlace": "City, Country",
"occupations": ["Actor", "Director"],
"filmography": [
{
"id": 1,
"title": "Movie Title",
"year": 2024,
"poster": "/images/movies/poster_xxx.webp",
"category": "movie",
"type": "Movie",
"role": "Actor",
"characterName": "Character"
}
],
"media_types": ["movie", "tv"],
"createdAt": "2024-01-01 00:00:00",
"updatedAt": "2024-01-01 00:00:00"
}
],
"total": 100,
"page": 1,
"limit": 20
}
}
```
#### Get Single Cast
**Endpoint**: `GET /api/cast/:id`
**Path Parameters**:
- `id` (integer, required) - Cast ID
**Response**:
```json
{
"success": true,
"data": {
"id": 1,
"name": "Actor Name",
"cleanname": "actor-name",
"photo": "/images/cast/photo_xxx.webp",
"bio": "Biography text",
"birthDate": "1990-01-01",
"birthPlace": "City, Country",
"occupations": ["Actor", "Director"],
"filmography": [
{
"id": 1,
"title": "Movie Title",
"year": 2024,
"poster": "/images/movies/poster_xxx.webp",
"category": "movie",
"type": "Movie",
"role": "Actor",
"characterName": "Character"
}
],
"adult_specifics": {
"id": 1,
"cast_id": 1,
"ethnicity": "Asian",
"hair_color": "Black",
"eye_color": "Brown",
"height": 170,
"weight": 60
},
"createdAt": "2024-01-01 00:00:00",
"updatedAt": "2024-01-01 00:00:00"
}
}
```
#### Get Cast Media
**Endpoint**: `GET /api/cast/:id/media`
**Path Parameters**:
- `id` (integer, required) - Cast ID
**Response**:
```json
{
"success": true,
"data": {
"items": [
{
"id": 1,
"title": "Movie Title",
"year": 2024,
"poster": "/images/movies/poster_xxx.webp",
"category": "movie",
"type": "Movie",
"role": "Actor",
"characterName": "Character"
}
]
}
}
```
#### Create Cast
**Endpoint**: `POST /api/cast`
**Request Body**:
```json
{
"name": "Actor Name",
"photo": "base64_encoded_image_or_url",
"bio": "Biography text",
"birthDate": "1990-01-01",
"birthPlace": "City, Country",
"occupations": ["Actor", "Director"]
}
```
**Response** (201 Created):
```json
{
"success": true,
"data": {
"id": 1
}
}
```
**Response** (200 OK - if cast already exists):
```json
{
"success": true,
"data": {
"id": 1,
"message": "Cast already exists"
}
}
```
#### Update Cast
**Endpoint**: `PUT /api/cast/:id`
**Path Parameters**:
- `id` (integer, required) - Cast ID
**Request Body**: Same as create, but all fields are optional.
**Response**:
```json
{
"success": true,
"data": {
"id": 1
}
}
```
#### Delete Cast
**Endpoint**: `DELETE /api/cast/:id`
**Path Parameters**:
- `id` (integer, required) - Cast ID
**Response**:
```json
{
"success": true,
"message": "Cast member deleted successfully"
}
```
---
### Adult Cast
#### Get All Adult Cast
**Endpoint**: `GET /api/cast/adult`
**Query Parameters**:
- `page` (integer, optional) - Page number (default: 1)
- `limit` (integer, optional) - Items per page (default: 20)
- `search` (string, optional) - Search by name
- `ethnicity` (string, optional) - Filter by ethnicity
- `hair_color` (string, optional) - Filter by hair color
**Example Request**:
```bash
GET /api/cast/adult?page=1&limit=20&ethnicity=Asian
```
**Response**:
```json
{
"success": true,
"data": {
"items": [
{
"id": 1,
"name": "Actor Name",
"cleanname": "actor-name",
"photo": "/images/cast/photo_xxx.webp",
"bio": "Biography text",
"birthDate": "1990-01-01",
"birthPlace": "City, Country",
"ethnicity": "Asian",
"hair_color": "Black",
"eye_color": "Brown",
"height": 170,
"weight": 60,
"createdAt": "2024-01-01 00:00:00",
"updatedAt": "2024-01-01 00:00:00"
}
],
"total": 100,
"page": 1,
"limit": 20
}
}
```
#### Get Single Adult Cast
**Endpoint**: `GET /api/cast/adult/:id`
**Path Parameters**:
- `id` (integer, required) - Cast ID
**Response**:
```json
{
"success": true,
"data": {
"id": 1,
"name": "Actor Name",
"cleanname": "actor-name",
"photo": "/images/cast/photo_xxx.webp",
"bio": "Biography text",
"birthDate": "1990-01-01",
"birthPlace": "City, Country",
"ethnicity": "Asian",
"hair_color": "Black",
"eye_color": "Brown",
"height": 170,
"weight": 60,
"createdAt": "2024-01-01 00:00:00",
"updatedAt": "2024-01-01 00:00:00"
}
}
```
#### Create Adult Cast
**Endpoint**: `POST /api/cast/adult`
**Request Body**:
```json
{
"name": "Actor Name",
"photo": "base64_encoded_image_or_url",
"bio": "Biography text",
"birthDate": "1990-01-01",
"birthPlace": "City, Country",
"ethnicity": "Asian",
"hair_color": "Black",
"eye_color": "Brown",
"height": 170,
"weight": 60
}
```
**Response** (201 Created):
```json
{
"success": true,
"data": {
"id": 1
}
}
```
#### Update Adult Cast
**Endpoint**: `PUT /api/cast/adult/:id`
**Path Parameters**:
- `id` (integer, required) - Cast ID
**Request Body**: Same as create, but all fields are optional.
**Response**:
```json
{
"success": true,
"data": {
"id": 1
}
}
```
#### Delete Adult Cast Specifics
**Endpoint**: `DELETE /api/cast/adult/:id`
**Path Parameters**:
- `id` (integer, required) - Cast ID
**Note**: This only deletes the adult-specific attributes, not the cast member itself.
**Response**:
```json
{
"success": true,
"message": "Adult specifics deleted successfully"
}
```
---
### Images
#### Serve Image
**Endpoint**: `GET /api/images/*`
**Description**: Serves images directly from the storage. Images are organized by type (movies, games, cast, etc.).
**Example Requests**:
```bash
GET /api/images/movies/poster_xxx.webp
GET /api/images/cast/photo_xxx.webp
GET /api/images/episodes/poster_xxx.webp
```
**Response**: Binary image data (WebP format)
---
### Settings
#### Get Settings
**Endpoint**: `GET /api/settings`
**Description**: Retrieves application settings.
**Response**:
```json
{
"success": true,
"data": {
"id": 1,
"key": "value",
"updatedAt": "2024-01-01 00:00:00"
}
}
```
#### Update Settings
**Endpoint**: `PUT /api/settings`
**Request Body**:
```json
{
"key": "value"
}
```
**Response**:
```json
{
"success": true,
"data": {
"id": 1,
"key": "value",
"updatedAt": "2024-01-01 00:00:00"
}
}
```
---
## Image Handling
### Image Formats
Images can be provided in two ways:
1. **Base64-encoded string**: The API will automatically decode and save the image
2. **URL**: The API will store the URL as-is
### Image Storage
Images are stored in the following structure:
```
/images/
├── movies/
│ ├── poster_*.webp
│ └── banner_*.webp
├── games/
│ ├── poster_*.webp
│ └── banner_*.webp
├── cast/
│ └── photo_*.webp
├── characters/
│ └── char_*.webp
└── episodes/
└── poster_*.webp
```
### Image Processing
- All images are automatically converted to WebP format
- Old images are deleted when updating
- Images are served directly via the `/api/images/*` endpoint
---
## Search & Filtering
### Clean Names
The API uses "clean names" for deduplication. A clean name is generated from the title/name by:
- Converting to lowercase
- Replacing special characters with hyphens
- Removing leading/trailing hyphens
- Replacing multiple consecutive hyphens with a single hyphen
Example: "The Dark Knight" → "the-dark-knight"
### Deduplication
When creating media or cast members:
- The API checks if an item with the same clean name already exists
- If it exists, the existing ID is returned with a 200 status
- If it doesn't exist, a new item is created with a 201 status
---
## Error Handling
### Common Errors
#### 400 Bad Request
```json
{
"success": false,
"error": "Invalid JSON"
}
```
#### 404 Not Found
```json
{
"success": false,
"error": "Media not found"
}
```
#### 405 Method Not Allowed
```json
{
"success": false,
"error": "Method not allowed"
}
```
#### 500 Internal Server Error
```json
{
"success": false,
"error": "Error message"
}
```
---
## CORS
The API supports CORS with the following headers:
```
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type
```
---
## Rate Limiting
Currently, there is no rate limiting implemented.
---
## Logging
API logging can be enabled by setting the `API_LOGGING_ENABLED` environment variable to `true`.
Logs are written to:
- API requests/responses: `/var/www/html/logs/api.log`
- PHP errors: `/var/www/html/logs/php_error.log`