Files
neomovies-api/pkg/handlers/docs.go
Erno 30c48fbc50 fix: Use relative URL for OpenAPI spec to bypass Cloudflare
- Change SpecURL from absolute to relative path
- Fixes documentation loading on api.neomovies.ru
- Browser will request openapi.json from same domain
2025-10-18 21:19:57 +00:00

1876 lines
65 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handlers
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/MarceloPetrucio/go-scalar-api-reference"
)
type DocsHandler struct{}
func NewDocsHandler() *DocsHandler {
return &DocsHandler{}
}
func (h *DocsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.ServeDocs(w, r)
}
func (h *DocsHandler) RedirectToDocs(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/docs/", http.StatusMovedPermanently)
}
func (h *DocsHandler) GetOpenAPISpec(w http.ResponseWriter, r *http.Request) {
_ = determineBaseURL(r)
// Use relative server URL to inherit correct scheme/host from the browser/proxy
spec := getOpenAPISpecWithURL("/")
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, Origin, X-Requested-With")
json.NewEncoder(w).Encode(spec)
}
func (h *DocsHandler) ServeDocs(w http.ResponseWriter, r *http.Request) {
htmlContent, err := scalar.ApiReferenceHTML(&scalar.Options{
SpecURL: "/openapi.json",
CustomOptions: scalar.CustomOptions{
PageTitle: "Neo Movies API Documentation",
},
DarkMode: true,
})
if err != nil {
fmt.Printf("Error generating documentation: %v", err)
http.Error(w, fmt.Sprintf("Error generating documentation: %v", err), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
w.Header().Set("Pragma", "no-cache")
w.Header().Set("Expires", "0")
fmt.Fprintln(w, htmlContent)
}
func determineBaseURL(r *http.Request) string {
// Prefer proxy headers and request info over environment to avoid wrong scheme on platforms like Vercel
proto := ""
host := r.Host
if fwd := r.Header.Get("Forwarded"); fwd != "" {
for _, part := range strings.Split(fwd, ";") {
kv := strings.SplitN(strings.TrimSpace(part), "=", 2)
if len(kv) != 2 {
continue
}
key := strings.ToLower(strings.TrimSpace(kv[0]))
val := strings.Trim(strings.TrimSpace(kv[1]), "\"")
switch key {
case "proto":
if proto == "" {
proto = strings.ToLower(val)
}
case "host":
if val != "" {
host = val
}
}
}
}
if proto == "" {
if p := r.Header.Get("X-Forwarded-Proto"); p != "" {
proto = strings.ToLower(strings.TrimSpace(strings.Split(p, ",")[0]))
}
}
if xfh := r.Header.Get("X-Forwarded-Host"); xfh != "" {
host = strings.TrimSpace(strings.Split(xfh, ",")[0])
}
if proto == "" {
if r.TLS != nil {
proto = "https"
} else {
proto = "http"
}
}
if xfp := r.Header.Get("X-Forwarded-Port"); xfp != "" && !strings.Contains(host, ":") {
isDefault := (proto == "http" && xfp == "80") || (proto == "https" && xfp == "443")
if !isDefault {
host = host + ":" + xfp
}
}
return fmt.Sprintf("%s://%s", proto, host)
}
type OpenAPISpec struct {
OpenAPI string `json:"openapi"`
Info Info `json:"info"`
Servers []Server `json:"servers"`
Paths map[string]interface{} `json:"paths"`
Components Components `json:"components"`
}
type Info struct {
Title string `json:"title"`
Description string `json:"description"`
Version string `json:"version"`
Contact Contact `json:"contact"`
}
type Contact struct {
Name string `json:"name"`
URL string `json:"url"`
}
type Server struct {
URL string `json:"url"`
Description string `json:"description"`
}
type Components struct {
SecuritySchemes map[string]SecurityScheme `json:"securitySchemes"`
Schemas map[string]interface{} `json:"schemas"`
}
type SecurityScheme struct {
Type string `json:"type"`
Scheme string `json:"scheme,omitempty"`
BearerFormat string `json:"bearerFormat,omitempty"`
}
func getOpenAPISpecWithURL(baseURL string) *OpenAPISpec {
return &OpenAPISpec{
OpenAPI: "3.0.0",
Info: Info{
Title: "Neo Movies API",
Description: "Современный API для поиска фильмов и сериалов с интеграцией TMDB и поддержкой авторизации",
Version: "2.0.0",
Contact: Contact{
Name: "API Support",
URL: "https://github.com/your-username/neomovies-api-go",
},
},
Servers: []Server{
{
URL: baseURL,
Description: "Production server",
},
},
Paths: map[string]interface{}{
"/api/v1/health": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Health Check",
"description": "Проверка работоспособности API",
"tags": []string{"Health"},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "API работает корректно",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/APIResponse",
},
},
},
},
},
},
},
"/api/v1/search/multi": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Мультипоиск",
"description": "Поиск фильмов, сериалов и актеров",
"tags": []string{"Search"},
"parameters": []map[string]interface{}{
{
"name": "query",
"in": "query",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "Поисковый запрос",
},
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
"description": "Номер страницы",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Результаты поиска",
},
},
},
},
"/api/v1/categories": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Получить категории",
"description": "Получение списка категорий фильмов",
"tags": []string{"Categories"},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Список категорий",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{"$ref": "#/components/schemas/Category"},
},
},
},
},
},
},
},
"/api/v1/categories/{id}/movies": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Фильмы по категории",
"description": "Получение фильмов по категории",
"tags": []string{"Categories"},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "ID категории",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Фильмы категории",
},
},
},
},
"/api/v1/categories/{id}/media": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Медиа по категории",
"description": "Получение фильмов или сериалов по категории",
"tags": []string{"Categories"},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "ID категории",
},
{
"name": "type",
"in": "query",
"required": false,
"schema": map[string]interface{}{
"type": "string",
"enum": []string{"movie", "tv"},
"default": "movie",
},
"description": "Тип медиа: movie или tv",
},
{
"name": "page",
"in": "query",
"required": false,
"schema": map[string]interface{}{
"type": "integer",
"default": 1,
},
"description": "Номер страницы",
},
{
"name": "language",
"in": "query",
"required": false,
"schema": map[string]interface{}{
"type": "string",
"default": "ru-RU",
},
"description": "Язык ответа",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Медиа по категории",
},
},
},
},
"/api/v1/players/alloha/{imdb_id}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Плеер Alloha",
"description": "Получение плеера Alloha по IMDb ID",
"tags": []string{"Players"},
"parameters": []map[string]interface{}{
{
"name": "imdb_id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "IMDb ID фильма",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Данные плеера",
},
},
},
},
"/api/v1/players/lumex/{imdb_id}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Плеер Lumex",
"description": "Получение плеера Lumex по IMDb ID. Не поддерживает выбор сезона/серии - плеер работает напрямую с IMDb ID",
"tags": []string{"Players"},
"parameters": []map[string]interface{}{
{
"name": "imdb_id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "IMDb ID фильма или сериала (например, tt0133093)",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "HTML со встроенным Lumex плеером",
"content": map[string]interface{}{
"text/html": map[string]interface{}{},
},
},
},
},
},
"/api/v1/players/vibix/{imdb_id}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Vibix плеер по IMDb ID",
"description": "Возвращает HTML-страницу с iframe Vibix для указанного IMDb ID. Не поддерживает выбор сезона/серии - плеер работает напрямую с IMDb ID",
"tags": []string{"Players"},
"parameters": []map[string]interface{}{
{
"name": "imdb_id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "IMDb ID, например tt0133093",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "HTML со встроенным Vibix плеером",
"content": map[string]interface{}{
"text/html": map[string]interface{}{},
},
},
"404": map[string]interface{}{"description": "Фильм не найден"},
"503": map[string]interface{}{"description": "VIBIX_TOKEN не настроен"},
},
},
},
"/api/v1/players/vidsrc/{media_type}/{imdb_id}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Vidsrc плеер (английский)",
"description": "Возвращает HTML-страницу с iframe Vidsrc.to. Использует IMDb ID для фильмов и сериалов. Пример URL для фильма: https://vidsrc.to/embed/movie/tt1234567, для сериала: https://vidsrc.to/embed/tv/tt6385540/1/1",
"tags": []string{"Players"},
"parameters": []map[string]interface{}{
{
"name": "media_type",
"in": "path",
"required": true,
"schema": map[string]interface{}{"type": "string", "enum": []string{"movie", "tv"}},
"description": "Тип контента: movie (фильм) или tv (сериал)",
},
{
"name": "imdb_id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "IMDb ID, например tt6385540 (с префиксом tt)",
},
{
"name": "season",
"in": "query",
"required": false,
"schema": map[string]string{"type": "integer"},
"description": "Номер сезона (обязательно для TV)",
},
{
"name": "episode",
"in": "query",
"required": false,
"schema": map[string]string{"type": "integer"},
"description": "Номер серии (обязательно для TV)",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "HTML со встроенным Vidsrc плеером",
"content": map[string]interface{}{
"text/html": map[string]interface{}{},
},
},
"400": map[string]interface{}{"description": "Отсутствуют обязательные параметры"},
},
},
},
"/api/v1/players/vidlink/movie/{imdb_id}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Vidlink плеер для фильмов (английский)",
"description": "Возвращает HTML-страницу с iframe Vidlink.pro для фильмов. Использует IMDb ID. Пример URL: https://vidlink.pro/movie/tt1234567",
"tags": []string{"Players"},
"parameters": []map[string]interface{}{
{
"name": "imdb_id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "IMDb ID фильма, например tt1234567 (с префиксом tt)",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "HTML со встроенным Vidlink плеером",
"content": map[string]interface{}{
"text/html": map[string]interface{}{},
},
},
"400": map[string]interface{}{"description": "IMDb ID не указан"},
},
},
},
"/api/v1/players/vidlink/tv/{tmdb_id}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Vidlink плеер для сериалов (английский)",
"description": "Возвращает HTML-страницу с iframe Vidlink.pro для сериалов. Использует TMDB ID (без префикса tt). Пример URL: https://vidlink.pro/tv/94997/1/1",
"tags": []string{"Players"},
"parameters": []map[string]interface{}{
{
"name": "tmdb_id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "TMDB ID сериала, например 94997 (числовой идентификатор без префикса)",
},
{
"name": "season",
"in": "query",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "Номер сезона (обязательно)",
},
{
"name": "episode",
"in": "query",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "Номер серии (обязательно)",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "HTML со встроенным Vidlink плеером",
"content": map[string]interface{}{
"text/html": map[string]interface{}{},
},
},
"400": map[string]interface{}{"description": "Отсутствуют обязательные параметры (tmdb_id, season, episode)"},
},
},
},
"/api/v1/players/vidsrc-parse/{media_type}/{imdb_id}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Vidsrc плеер с парсингом (кастомный плеер)",
"description": "Возвращает HTML-страницу с кастомным Video.js плеером. Автоматически извлекает m3u8 ссылку из Vidsrc.to через клиентский парсинг в iframe. Использует IMDb ID для фильмов и сериалов.",
"tags": []string{"Players"},
"parameters": []map[string]interface{}{
{
"name": "media_type",
"in": "path",
"required": true,
"schema": map[string]interface{}{"type": "string", "enum": []string{"movie", "tv"}},
"description": "Тип контента: movie (фильм) или tv (сериал)",
},
{
"name": "imdb_id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "IMDb ID, например tt6385540 (с префиксом tt)",
},
{
"name": "season",
"in": "query",
"required": false,
"schema": map[string]string{"type": "integer"},
"description": "Номер сезона (обязательно для TV)",
},
{
"name": "episode",
"in": "query",
"required": false,
"schema": map[string]string{"type": "integer"},
"description": "Номер серии (обязательно для TV)",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "HTML с кастомным Video.js плеером и системой парсинга",
"content": map[string]interface{}{
"text/html": map[string]interface{}{},
},
},
"400": map[string]interface{}{"description": "Отсутствуют обязательные параметры"},
},
},
},
"/api/v1/players/vidlink-parse/movie/{imdb_id}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Vidlink плеер с парсингом для фильмов (кастомный)",
"description": "Возвращает HTML-страницу с кастомным Video.js плеером. Автоматически извлекает m3u8/mp4 ссылку из Vidlink.pro через клиентский парсинг. Использует IMDb ID для фильмов.",
"tags": []string{"Players"},
"parameters": []map[string]interface{}{
{
"name": "imdb_id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "IMDb ID фильма, например tt1234567 (с префиксом tt)",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "HTML с кастомным Video.js плеером и системой парсинга",
"content": map[string]interface{}{
"text/html": map[string]interface{}{},
},
},
"400": map[string]interface{}{"description": "IMDb ID не указан"},
},
},
},
"/api/v1/players/vidlink-parse/tv/{tmdb_id}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Vidlink плеер с парсингом для сериалов (кастомный)",
"description": "Возвращает HTML-страницу с кастомным Video.js плеером. Автоматически извлекает m3u8/mp4 ссылку из Vidlink.pro через клиентский парсинг. Использует TMDB ID для сериалов.",
"tags": []string{"Players"},
"parameters": []map[string]interface{}{
{
"name": "tmdb_id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "TMDB ID сериала, например 94997 (числовой идентификатор без префикса)",
},
{
"name": "season",
"in": "query",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "Номер сезона (обязательно)",
},
{
"name": "episode",
"in": "query",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "Номер серии (обязательно)",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "HTML с кастомным Video.js плеером и системой парсинга",
"content": map[string]interface{}{
"text/html": map[string]interface{}{},
},
},
"400": map[string]interface{}{"description": "Отсутствуют обязательные параметры (tmdb_id, season, episode)"},
},
},
},
"/api/v1/torrents/search/{imdbId}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Поиск торрентов",
"description": "Поиск торрентов по IMDB ID",
"tags": []string{"Torrents"},
"parameters": []map[string]interface{}{
{
"name": "imdbId",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "IMDB ID фильма или сериала",
},
{
"name": "type",
"in": "query",
"required": true,
"schema": map[string]interface{}{"type": "string", "enum": []string{"movie", "tv", "serial"}},
"description": "Тип контента: movie (фильм) или tv/serial (сериал)",
},
{
"name": "season",
"in": "query",
"required": false,
"schema": map[string]interface{}{"type": "integer"},
"description": "Номер сезона (для сериалов)",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Результаты поиска торрентов",
},
},
},
},
"/api/v1/reactions/{mediaType}/{mediaId}/counts": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Количество реакций",
"description": "Получение количества реакций для медиа",
"tags": []string{"Reactions"},
"parameters": []map[string]interface{}{
{
"name": "mediaType",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "Тип медиа (movie/tv)",
},
{
"name": "mediaId",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "ID медиа",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Количество реакций",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/ReactionCounts",
},
},
},
},
},
},
},
"/api/v1/images/{size}/{path}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Изображения",
"description": "Прокси для изображений TMDB",
"tags": []string{"Images"},
"parameters": []map[string]interface{}{
{
"name": "size",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "Размер изображения",
},
{
"name": "path",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "Путь к изображению",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Изображение",
"content": map[string]interface{}{
"image/*": map[string]interface{}{},
},
},
},
},
},
"/api/v1/auth/register": map[string]interface{}{
"post": map[string]interface{}{
"summary": "Регистрация пользователя",
"description": "Создание нового аккаунта пользователя",
"tags": []string{"Authentication"},
"requestBody": map[string]interface{}{
"required": true,
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/RegisterRequest",
},
},
},
},
"responses": map[string]interface{}{
"201": map[string]interface{}{
"description": "Пользователь успешно зарегистрирован",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/AuthResponse",
},
},
},
},
"409": map[string]interface{}{
"description": "Пользователь с таким email уже существует",
},
},
},
},
"/api/v1/auth/verify": map[string]interface{}{
"post": map[string]interface{}{
"tags": []string{"Authentication"},
"summary": "Подтверждение email",
"description": "Подтверждение email пользователя с помощью кода",
"requestBody": map[string]interface{}{
"required": true,
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
"required": []string{"email", "code"},
"properties": map[string]interface{}{
"email": map[string]interface{}{
"type": "string",
"format": "email",
"description": "Email пользователя",
"example": "user@example.com",
},
"code": map[string]interface{}{
"type": "string",
"description": "6-значный код верификации",
"example": "123456",
},
},
},
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Email успешно подтвержден",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"success": map[string]interface{}{
"type": "boolean",
},
"message": map[string]interface{}{
"type": "string",
},
},
},
},
},
},
"400": map[string]interface{}{
"description": "Неверный или истекший код",
},
},
},
},
"/api/v1/auth/resend-code": map[string]interface{}{
"post": map[string]interface{}{
"tags": []string{"Authentication"},
"summary": "Повторная отправка кода",
"description": "Повторная отправка кода верификации на email",
"requestBody": map[string]interface{}{
"required": true,
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
"required": []string{"email"},
"properties": map[string]interface{}{
"email": map[string]interface{}{
"type": "string",
"format": "email",
"description": "Email пользователя",
"example": "user@example.com",
},
},
},
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Код отправлен на email",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"success": map[string]interface{}{
"type": "boolean",
},
"message": map[string]interface{}{
"type": "string",
},
},
},
},
},
},
"400": map[string]interface{}{
"description": "Email уже подтвержден или пользователь не найден",
},
},
},
},
"/api/v1/auth/login": map[string]interface{}{
"post": map[string]interface{}{
"summary": "Авторизация пользователя",
"description": "Получение JWT токена для доступа к приватным эндпоинтам",
"tags": []string{"Authentication"},
"requestBody": map[string]interface{}{
"required": true,
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/LoginRequest",
},
},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Успешная авторизация",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/AuthResponse",
},
},
},
},
"401": map[string]interface{}{
"description": "Неверный email или пароль",
},
},
},
},
"/api/v1/auth/profile": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Получить профиль пользователя",
"description": "Получение информации о текущем пользователе",
"tags": []string{"Authentication"},
"security": []map[string][]string{
{"bearerAuth": []string{}},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Информация о пользователе",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/User",
},
},
},
},
},
},
"put": map[string]interface{}{
"summary": "Обновить профиль пользователя",
"description": "Обновление информации о пользователе",
"tags": []string{"Authentication"},
"security": []map[string][]string{
{"bearerAuth": []string{}},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Профиль успешно обновлен",
},
},
},
"delete": map[string]interface{}{
"summary": "Удалить аккаунт пользователя",
"description": "Полное и безвозвратное удаление аккаунта пользователя и всех связанных с ним данных (избранное, реакции)",
"tags": []string{"Authentication"},
"security": []map[string][]string{
{"bearerAuth": []string{}},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Аккаунт успешно удален",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"success": map[string]interface{}{"type": "boolean"},
"message": map[string]interface{}{"type": "string"},
},
},
},
},
},
"401": map[string]interface{}{
"description": "Неавторизованный запрос",
},
"500": map[string]interface{}{
"description": "Внутренняя ошибка сервера",
},
},
},
},
"/api/v1/movies/search": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Поиск фильмов",
"description": "Поиск фильмов по названию с поддержкой фильтров",
"tags": []string{"Movies"},
"parameters": []map[string]interface{}{
{
"name": "query",
"in": "query",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "Поисковый запрос",
},
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
"description": "Номер страницы",
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
"description": "Язык ответа",
},
{
"name": "year",
"in": "query",
"schema": map[string]string{"type": "integer"},
"description": "Год выпуска",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Результаты поиска фильмов",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/MovieSearchResponse",
},
},
},
},
},
},
},
"/api/v1/movies/popular": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Популярные фильмы",
"description": "Получение списка популярных фильмов",
"tags": []string{"Movies"},
"parameters": []map[string]interface{}{
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Список популярных фильмов",
},
},
},
},
"/api/v1/movies/{id}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Получить фильм по ID",
"description": "Подробная информация о фильме",
"tags": []string{"Movies"},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "ID фильма в TMDB",
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Информация о фильме",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/Movie",
},
},
},
},
},
},
},
"/api/v1/favorites": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Получить избранное",
"description": "Список избранных фильмов и сериалов пользователя",
"tags": []string{"Favorites"},
"security": []map[string][]string{
{"bearerAuth": []string{}},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Список избранного",
},
},
},
},
"/api/v1/favorites/{id}": map[string]interface{}{
"post": map[string]interface{}{
"summary": "Добавить в избранное",
"description": "Добавление фильма или сериала в избранное",
"tags": []string{"Favorites"},
"security": []map[string][]string{
{"bearerAuth": []string{}},
},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "ID медиа",
},
{
"name": "type",
"in": "query",
"required": false,
"schema": map[string]interface{}{
"type": "string",
"enum": []string{"movie", "tv"},
"default": "movie",
},
"description": "Тип медиа: movie или tv",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Добавлено в избранное",
},
},
},
"delete": map[string]interface{}{
"summary": "Удалить из избранного",
"description": "Удаление фильма или сериала из избранного",
"tags": []string{"Favorites"},
"security": []map[string][]string{
{"bearerAuth": []string{}},
},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "ID медиа",
},
{
"name": "type",
"in": "query",
"required": false,
"schema": map[string]interface{}{
"type": "string",
"enum": []string{"movie", "tv"},
"default": "movie",
},
"description": "Тип медиа: movie или tv",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Удалено из избранного",
},
},
},
},
"/api/v1/favorites/{id}/check": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Проверить избранное",
"description": "Проверка, находится ли медиа в избранном",
"tags": []string{"Favorites"},
"security": []map[string][]string{
{"bearerAuth": []string{}},
},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "ID медиа",
},
{
"name": "type",
"in": "query",
"required": false,
"schema": map[string]interface{}{
"type": "string",
"enum": []string{"movie", "tv"},
"default": "movie",
},
"description": "Тип медиа: movie или tv",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Статус избранного",
},
},
},
},
"/api/v1/movies/top-rated": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Топ рейтинг фильмов",
"description": "Получение списка фильмов с высоким рейтингом",
"tags": []string{"Movies"},
"parameters": []map[string]interface{}{
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Список фильмов с высоким рейтингом",
},
},
},
},
"/api/v1/movies/upcoming": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Скоро в прокате",
"description": "Получение списка фильмов, которые скоро выйдут в прокат",
"tags": []string{"Movies"},
"parameters": []map[string]interface{}{
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Список фильмов, которые скоро выйдут",
},
},
},
},
"/api/v1/movies/now-playing": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Сейчас в прокате",
"description": "Получение списка фильмов, которые сейчас в прокате",
"tags": []string{"Movies"},
"parameters": []map[string]interface{}{
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Список фильмов в прокате",
},
},
},
},
"/api/v1/movies/{id}/recommendations": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Рекомендации фильмов",
"description": "Получение рекомендаций фильмов на основе выбранного",
"tags": []string{"Movies"},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "ID фильма в TMDB",
},
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Рекомендуемые фильмы",
},
},
},
},
"/api/v1/movies/{id}/similar": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Похожие фильмы",
"description": "Получение похожих фильмов",
"tags": []string{"Movies"},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "ID фильма в TMDB",
},
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Похожие фильмы",
},
},
},
},
"/api/v1/tv/search": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Поиск сериалов",
"description": "Поиск сериалов по названию",
"tags": []string{"TV Series"},
"parameters": []map[string]interface{}{
{
"name": "query",
"in": "query",
"required": true,
"schema": map[string]string{"type": "string"},
"description": "Поисковый запрос",
},
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
"description": "Номер страницы",
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
"description": "Язык ответа",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Результаты поиска сериалов",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/TVSearchResponse",
},
},
},
},
},
},
},
"/api/v1/tv/popular": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Популярные сериалы",
"description": "Получение списка популярных сериалов",
"tags": []string{"TV Series"},
"parameters": []map[string]interface{}{
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Список популярных сериалов",
},
},
},
},
"/api/v1/tv/top-rated": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Топ рейтинг сериалов",
"description": "Получение списка сериалов с высоким рейтингом",
"tags": []string{"TV Series"},
"parameters": []map[string]interface{}{
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Список сериалов с высоким рейтингом",
},
},
},
},
"/api/v1/tv/on-the-air": map[string]interface{}{
"get": map[string]interface{}{
"summary": "В эфире",
"description": "Получение списка сериалов, которые сейчас в эфире",
"tags": []string{"TV Series"},
"parameters": []map[string]interface{}{
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Список сериалов в эфире",
},
},
},
},
"/api/v1/tv/airing-today": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Сегодня в эфире",
"description": "Получение списка сериалов, которые выходят сегодня",
"tags": []string{"TV Series"},
"parameters": []map[string]interface{}{
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Список сериалов, выходящих сегодня",
},
},
},
},
"/api/v1/tv/{id}": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Получить сериал по ID",
"description": "Подробная информация о сериале",
"tags": []string{"TV Series"},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "ID сериала в TMDB",
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Информация о сериале",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/TVSeries",
},
},
},
},
},
},
},
"/api/v1/tv/{id}/recommendations": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Рекомендации сериалов",
"description": "Получение рекомендаций сериалов на основе выбранного",
"tags": []string{"TV Series"},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "ID сериала в TMDB",
},
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Рекомендуемые сериалы",
},
},
},
},
"/api/v1/tv/{id}/similar": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Похожие сериалы",
"description": "Получение похожих сериалов",
"tags": []string{"TV Series"},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "ID сериала в TMDB",
},
{
"name": "page",
"in": "query",
"schema": map[string]string{"type": "integer", "default": "1"},
},
{
"name": "language",
"in": "query",
"schema": map[string]string{"type": "string", "default": "ru-RU"},
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Похожие сериалы",
},
},
},
},
"/api/v1/movies/{id}/external-ids": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Внешние идентификаторы фильма",
"description": "Получить внешние ID (IMDb, TVDB, Facebook и др.) для фильма по TMDB ID",
"tags": []string{"Movies"},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "ID фильма в TMDB",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Внешние идентификаторы фильма",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/ExternalIDs",
},
},
},
},
},
},
},
"/api/v1/tv/{id}/external-ids": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Внешние идентификаторы сериала",
"description": "Получить внешние ID (IMDb, TVDB, Facebook и др.) для сериала по TMDB ID",
"tags": []string{"TV Series"},
"parameters": []map[string]interface{}{
{
"name": "id",
"in": "path",
"required": true,
"schema": map[string]string{"type": "integer"},
"description": "ID сериала в TMDB",
},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Внешние идентификаторы сериала",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{
"$ref": "#/components/schemas/ExternalIDs",
},
},
},
},
},
},
},
"/api/v1/auth/google/login": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Google OAuth: начало",
"description": "Редирект на страницу авторизации Google",
"tags": []string{"Authentication"},
"responses": map[string]interface{}{
"302": map[string]interface{}{"description": "Redirect to Google"},
"400": map[string]interface{}{"description": "OAuth не сконфигурирован"},
},
},
},
"/api/v1/auth/google/callback": map[string]interface{}{
"get": map[string]interface{}{
"summary": "Google OAuth: коллбек",
"description": "Обработка кода авторизации и выдача JWT",
"tags": []string{"Authentication"},
"parameters": []map[string]interface{}{
{"name": "state", "in": "query", "required": true, "schema": map[string]string{"type": "string"}},
{"name": "code", "in": "query", "required": true, "schema": map[string]string{"type": "string"}},
},
"responses": map[string]interface{}{
"200": map[string]interface{}{
"description": "Успешная авторизация через Google",
"content": map[string]interface{}{
"application/json": map[string]interface{}{
"schema": map[string]interface{}{"$ref": "#/components/schemas/AuthResponse"},
},
},
},
"400": map[string]interface{}{"description": "Неверный state или ошибка обмена кода"},
},
},
},
},
Components: Components{
SecuritySchemes: map[string]SecurityScheme{
"bearerAuth": {
Type: "http",
Scheme: "bearer",
BearerFormat: "JWT",
},
},
Schemas: map[string]interface{}{
"APIResponse": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"success": map[string]string{"type": "boolean"},
"data": map[string]string{"type": "object"},
"message": map[string]string{"type": "string"},
"error": map[string]string{"type": "string"},
},
},
"RegisterRequest": map[string]interface{}{
"type": "object",
"required": []string{"email", "password", "name"},
"properties": map[string]interface{}{
"email": map[string]interface{}{
"type": "string",
"format": "email",
"example": "user@example.com",
},
"password": map[string]interface{}{
"type": "string",
"minLength": 6,
"example": "password123",
},
"name": map[string]interface{}{
"type": "string",
"example": "Иван Иванов",
},
},
},
"LoginRequest": map[string]interface{}{
"type": "object",
"required": []string{"email", "password"},
"properties": map[string]interface{}{
"email": map[string]interface{}{
"type": "string",
"format": "email",
"example": "user@example.com",
},
"password": map[string]interface{}{
"type": "string",
"example": "password123",
},
},
},
"AuthResponse": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"token": map[string]string{"type": "string"},
"user": map[string]interface{}{"$ref": "#/components/schemas/User"},
},
},
"User": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]string{"type": "string"},
"email": map[string]string{"type": "string"},
"name": map[string]string{"type": "string"},
"avatar": map[string]string{"type": "string"},
"favorites": map[string]interface{}{
"type": "array",
"items": map[string]string{"type": "string"},
},
"created_at": map[string]interface{}{
"type": "string",
"format": "date-time",
},
"updated_at": map[string]interface{}{
"type": "string",
"format": "date-time",
},
},
},
"Movie": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]string{"type": "integer"},
"title": map[string]string{"type": "string"},
"original_title": map[string]string{"type": "string"},
"overview": map[string]string{"type": "string"},
"poster_path": map[string]string{"type": "string"},
"backdrop_path": map[string]string{"type": "string"},
"release_date": map[string]string{"type": "string"},
"vote_average": map[string]string{"type": "number"},
"vote_count": map[string]string{"type": "integer"},
"popularity": map[string]string{"type": "number"},
"adult": map[string]string{"type": "boolean"},
"original_language": map[string]string{"type": "string"},
},
},
"MovieSearchResponse": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"page": map[string]string{"type": "integer"},
"results": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{"$ref": "#/components/schemas/Movie"},
},
"total_pages": map[string]string{"type": "integer"},
"total_results": map[string]string{"type": "integer"},
},
},
"TVSeries": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]string{"type": "integer"},
"name": map[string]string{"type": "string"},
"original_name": map[string]string{"type": "string"},
"overview": map[string]string{"type": "string"},
"poster_path": map[string]string{"type": "string"},
"backdrop_path": map[string]string{"type": "string"},
"first_air_date": map[string]string{"type": "string"},
"vote_average": map[string]string{"type": "number"},
"vote_count": map[string]string{"type": "integer"},
"popularity": map[string]string{"type": "number"},
"original_language": map[string]string{"type": "string"},
"number_of_seasons": map[string]string{"type": "integer"},
"number_of_episodes": map[string]string{"type": "integer"},
"status": map[string]string{"type": "string"},
},
},
"TVSearchResponse": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"page": map[string]string{"type": "integer"},
"results": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{"$ref": "#/components/schemas/TVSeries"},
},
"total_pages": map[string]string{"type": "integer"},
"total_results": map[string]string{"type": "integer"},
},
},
"Category": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]string{"type": "integer"},
"name": map[string]string{"type": "string"},
"description": map[string]string{"type": "string"},
},
},
"Player": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"url": map[string]string{"type": "string"},
"title": map[string]string{"type": "string"},
"quality": map[string]string{"type": "string"},
"type": map[string]string{"type": "string"},
},
},
"Torrent": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"title": map[string]string{"type": "string"},
"size": map[string]string{"type": "string"},
"seeds": map[string]string{"type": "integer"},
"peers": map[string]string{"type": "integer"},
"magnet": map[string]string{"type": "string"},
"hash": map[string]string{"type": "string"},
},
},
"Reaction": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"type": map[string]string{"type": "string"},
"count": map[string]string{"type": "integer"},
},
},
"ReactionCounts": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"like": map[string]string{"type": "integer"},
"dislike": map[string]string{"type": "integer"},
"love": map[string]string{"type": "integer"},
},
},
"ExternalIDs": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]string{"type": "integer"},
"imdb_id": map[string]string{"type": "string"},
"tvdb_id": map[string]string{"type": "integer"},
"wikidata_id": map[string]string{"type": "string"},
"facebook_id": map[string]string{"type": "string"},
"instagram_id": map[string]string{"type": "string"},
"twitter_id": map[string]string{"type": "string"},
},
},
"WebTorrentMetadata": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]string{"type": "integer"},
"title": map[string]string{"type": "string"},
"type": map[string]interface{}{
"type": "string",
"enum": []string{"movie", "tv"},
},
"year": map[string]string{"type": "integer"},
"posterPath": map[string]string{"type": "string"},
"backdropPath": map[string]string{"type": "string"},
"overview": map[string]string{"type": "string"},
"runtime": map[string]string{"type": "integer"},
"genres": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"$ref": "#/components/schemas/Genre",
},
},
"seasons": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"$ref": "#/components/schemas/SeasonMetadata",
},
},
"episodes": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"$ref": "#/components/schemas/EpisodeMetadata",
},
},
},
},
"SeasonMetadata": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"seasonNumber": map[string]string{"type": "integer"},
"name": map[string]string{"type": "string"},
"episodes": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"$ref": "#/components/schemas/EpisodeMetadata",
},
},
},
},
"EpisodeMetadata": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"episodeNumber": map[string]string{"type": "integer"},
"seasonNumber": map[string]string{"type": "integer"},
"name": map[string]string{"type": "string"},
"overview": map[string]string{"type": "string"},
"runtime": map[string]string{"type": "integer"},
"stillPath": map[string]string{"type": "string"},
},
},
"Genre": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]string{"type": "integer"},
"name": map[string]string{"type": "string"},
},
},
},
},
}
}