push
This commit is contained in:
+494
@@ -0,0 +1,494 @@
|
||||
// OpenAPI 3.0 spec for the streamreverse REST API.
|
||||
// Served at GET /api.json and rendered by Swagger UI at GET /.
|
||||
|
||||
const spec = {
|
||||
openapi: '3.0.3',
|
||||
info: {
|
||||
title: 'bug.tv api',
|
||||
version: '1.0.0'
|
||||
},
|
||||
tags: [
|
||||
{ name: 'meta', description: 'service and provider metadata' },
|
||||
{ name: 'search', description: 'TMDB search and episode browsing' },
|
||||
{ name: 'details', description: 'TMDB media details' },
|
||||
{ name: 'streams', description: 'm3u8 stream lookup from providers' },
|
||||
],
|
||||
paths: {
|
||||
'/providers': {
|
||||
get: {
|
||||
tags: ['meta'],
|
||||
summary: 'List all providers and which are enabled',
|
||||
operationId: 'listProviders',
|
||||
responses: {
|
||||
200: {
|
||||
description: 'provider list',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
all: { type: 'array', items: { type: 'string' } },
|
||||
enabled: { type: 'array', items: { type: 'string' } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'/proxy/media': {
|
||||
get: {
|
||||
tags: ['meta'],
|
||||
summary: 'Proxy upstream media or m3u8 playlists with server-side headers',
|
||||
operationId: 'proxyMedia',
|
||||
parameters: [
|
||||
{
|
||||
name: 'url',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
},
|
||||
{
|
||||
name: 'h',
|
||||
in: 'query',
|
||||
required: false,
|
||||
description: 'JSON-encoded headers to send to the upstream URL',
|
||||
schema: { type: 'string' },
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Proxied upstream response. m3u8 playlist URIs are rewritten to use the proxy.',
|
||||
},
|
||||
400: { $ref: '#/components/responses/BadRequest' },
|
||||
502: { $ref: '#/components/responses/Upstream' },
|
||||
},
|
||||
},
|
||||
},
|
||||
'/search': {
|
||||
get: {
|
||||
tags: ['search'],
|
||||
summary: 'Search movies + TV via TMDB',
|
||||
operationId: 'search',
|
||||
parameters: [
|
||||
{
|
||||
name: 'q',
|
||||
in: 'query',
|
||||
required: true,
|
||||
description: 'search text',
|
||||
schema: { type: 'string' },
|
||||
example: 'dune',
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'up to 15 results',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string' },
|
||||
count: { type: 'integer' },
|
||||
results: { type: 'array', items: { $ref: '#/components/schemas/SearchResult' } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
400: { $ref: '#/components/responses/BadRequest' },
|
||||
502: { $ref: '#/components/responses/Upstream' },
|
||||
},
|
||||
},
|
||||
},
|
||||
'/trending': {
|
||||
get: {
|
||||
tags: ['search'],
|
||||
summary: 'Trending movies and TV via TMDB',
|
||||
operationId: 'getTrending',
|
||||
parameters: [
|
||||
{
|
||||
name: 'type',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string', enum: ['all', 'movie', 'tv'], default: 'all' },
|
||||
},
|
||||
{
|
||||
name: 'window',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string', enum: ['day', 'week'], default: 'week' },
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'up to 20 trending items',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
type: { type: 'string' },
|
||||
window: { type: 'string' },
|
||||
count: { type: 'integer' },
|
||||
results: { type: 'array', items: { $ref: '#/components/schemas/SearchResult' } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
400: { $ref: '#/components/responses/BadRequest' },
|
||||
502: { $ref: '#/components/responses/Upstream' },
|
||||
},
|
||||
},
|
||||
},
|
||||
'/movie/{id}': {
|
||||
get: {
|
||||
tags: ['details'],
|
||||
summary: 'Movie details (genres, rating, images, description)',
|
||||
description: 'Passes through the raw TMDB movie object, plus `poster_url`/`backdrop_url` convenience fields.',
|
||||
operationId: 'getMovieDetails',
|
||||
parameters: [{ $ref: '#/components/parameters/MovieId' }],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'TMDB movie object',
|
||||
content: { 'application/json': { schema: { $ref: '#/components/schemas/MediaDetails' } } },
|
||||
},
|
||||
404: { $ref: '#/components/responses/NotFound' },
|
||||
502: { $ref: '#/components/responses/Upstream' },
|
||||
},
|
||||
},
|
||||
},
|
||||
'/tv/{id}': {
|
||||
get: {
|
||||
tags: ['details'],
|
||||
summary: 'TV show details (genres, rating, images, description)',
|
||||
description: 'Passes through the raw TMDB tv object, plus `poster_url`/`backdrop_url` convenience fields.',
|
||||
operationId: 'getTvDetails',
|
||||
parameters: [{ $ref: '#/components/parameters/TvId' }],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'TMDB tv object',
|
||||
content: { 'application/json': { schema: { $ref: '#/components/schemas/MediaDetails' } } },
|
||||
},
|
||||
404: { $ref: '#/components/responses/NotFound' },
|
||||
502: { $ref: '#/components/responses/Upstream' },
|
||||
},
|
||||
},
|
||||
},
|
||||
'/tv/{id}/seasons': {
|
||||
get: {
|
||||
tags: ['search'],
|
||||
summary: 'List seasons for a TV show',
|
||||
operationId: 'getSeasons',
|
||||
parameters: [{ $ref: '#/components/parameters/TvId' }],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'season list',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
tmdb_id: { type: 'string' },
|
||||
count: { type: 'integer' },
|
||||
seasons: { type: 'array', items: { $ref: '#/components/schemas/Season' } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
400: { $ref: '#/components/responses/BadRequest' },
|
||||
502: { $ref: '#/components/responses/Upstream' },
|
||||
},
|
||||
},
|
||||
},
|
||||
'/tv/{id}/seasons/{season}': {
|
||||
get: {
|
||||
tags: ['search'],
|
||||
summary: 'List episodes for a season',
|
||||
operationId: 'getEpisodes',
|
||||
parameters: [
|
||||
{ $ref: '#/components/parameters/TvId' },
|
||||
{
|
||||
name: 'season',
|
||||
in: 'path',
|
||||
required: true,
|
||||
description: 'season number',
|
||||
schema: { type: 'integer' },
|
||||
example: 1,
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'episode list',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
tmdb_id: { type: 'string' },
|
||||
season: { type: 'integer' },
|
||||
count: { type: 'integer' },
|
||||
episodes: { type: 'array', items: { $ref: '#/components/schemas/Episode' } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
400: { $ref: '#/components/responses/BadRequest' },
|
||||
502: { $ref: '#/components/responses/Upstream' },
|
||||
},
|
||||
},
|
||||
},
|
||||
'/streams': {
|
||||
get: {
|
||||
tags: ['streams'],
|
||||
summary: 'Fetch m3u8 streams for a movie or TV episode',
|
||||
operationId: 'getStreams',
|
||||
description:
|
||||
'Scrapes every enabled provider in parallel (override with `providers`) and returns deduped, normalized, quality-ranked streams. ' +
|
||||
'For `type=tv` you must pass `season` and `episode`. Optional filters are applied before ranking.',
|
||||
parameters: [
|
||||
{
|
||||
name: 'tmdb',
|
||||
in: 'query',
|
||||
required: true,
|
||||
description: 'TMDB numeric id',
|
||||
schema: { type: 'integer' },
|
||||
example: 438631,
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
in: 'query',
|
||||
required: false,
|
||||
description: 'movie (default) or tv',
|
||||
schema: { type: 'string', enum: ['movie', 'tv'], default: 'movie' },
|
||||
},
|
||||
{
|
||||
name: 'season',
|
||||
in: 'query',
|
||||
required: false,
|
||||
description: 'season number (required when type=tv)',
|
||||
schema: { type: 'integer' },
|
||||
},
|
||||
{
|
||||
name: 'episode',
|
||||
in: 'query',
|
||||
required: false,
|
||||
description: 'episode number (required when type=tv)',
|
||||
schema: { type: 'integer' },
|
||||
},
|
||||
{
|
||||
name: 'providers',
|
||||
in: 'query',
|
||||
required: false,
|
||||
description: 'comma-separated provider names; defaults to the enabled set',
|
||||
schema: { type: 'string' },
|
||||
example: 'videasy,vidfast',
|
||||
},
|
||||
{
|
||||
name: 'minQuality',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string', enum: ['any', '480', '720', '1080', '2160'], default: 'any' },
|
||||
},
|
||||
{
|
||||
name: 'audioCodec',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string', enum: ['any', 'aac', 'ac3', 'dts', 'opus', 'mp3', 'flac'], default: 'any' },
|
||||
},
|
||||
{
|
||||
name: 'subtitles',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string', enum: ['any', 'required', 'lang'], default: 'any' },
|
||||
},
|
||||
{
|
||||
name: 'subtitleLang',
|
||||
in: 'query',
|
||||
required: false,
|
||||
description: 'two-letter lang code, used when subtitles=lang',
|
||||
schema: { type: 'string' },
|
||||
example: 'en',
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'stream list (best quality first)',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: { $ref: '#/components/schemas/StreamList' },
|
||||
},
|
||||
},
|
||||
},
|
||||
400: { $ref: '#/components/responses/BadRequest' },
|
||||
500: {
|
||||
description: 'no providers available / internal error',
|
||||
content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
parameters: {
|
||||
MovieId: {
|
||||
name: 'id',
|
||||
in: 'path',
|
||||
required: true,
|
||||
description: 'TMDB movie id',
|
||||
schema: { type: 'integer' },
|
||||
example: 438631,
|
||||
},
|
||||
TvId: {
|
||||
name: 'id',
|
||||
in: 'path',
|
||||
required: true,
|
||||
description: 'TMDB tv id',
|
||||
schema: { type: 'integer' },
|
||||
example: 1396,
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
BadRequest: {
|
||||
description: 'missing or invalid parameters',
|
||||
content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } },
|
||||
},
|
||||
NotFound: {
|
||||
description: 'media id not found on TMDB',
|
||||
content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } },
|
||||
},
|
||||
Upstream: {
|
||||
description: 'TMDB request failed',
|
||||
content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } },
|
||||
},
|
||||
},
|
||||
schemas: {
|
||||
Error: {
|
||||
type: 'object',
|
||||
properties: { error: { type: 'string' } },
|
||||
},
|
||||
SearchResult: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
tmdb_id: { type: 'integer', example: 438631 },
|
||||
type: { type: 'string', enum: ['movie', 'tv'] },
|
||||
name: { type: 'string', example: 'Dune' },
|
||||
year: { type: 'string', example: '2021' },
|
||||
poster: { type: 'string', format: 'uri' },
|
||||
overview: { type: 'string' },
|
||||
},
|
||||
},
|
||||
MediaDetails: {
|
||||
type: 'object',
|
||||
description: 'raw TMDB media object (movie or tv) with added poster_url/backdrop_url',
|
||||
properties: {
|
||||
id: { type: 'integer', example: 438631 },
|
||||
title: { type: 'string', description: 'movie title' },
|
||||
name: { type: 'string', description: 'tv show name' },
|
||||
overview: { type: 'string', description: 'description' },
|
||||
tagline: { type: 'string' },
|
||||
genres: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'integer' },
|
||||
name: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
vote_average: { type: 'number', description: 'rating, 0-10' },
|
||||
vote_count: { type: 'integer' },
|
||||
popularity: { type: 'number' },
|
||||
status: { type: 'string' },
|
||||
runtime: { type: 'integer', description: 'movie runtime in minutes' },
|
||||
number_of_seasons: { type: 'integer', description: 'tv' },
|
||||
number_of_episodes: { type: 'integer', description: 'tv' },
|
||||
release_date: { type: 'string', format: 'date', description: 'movie' },
|
||||
first_air_date: { type: 'string', format: 'date', description: 'tv' },
|
||||
poster_path: { type: 'string' },
|
||||
backdrop_path: { type: 'string' },
|
||||
poster_url: { type: 'string', format: 'uri', description: 'full url (original size)' },
|
||||
backdrop_url: { type: 'string', format: 'uri', description: 'full url (original size)' },
|
||||
images: {
|
||||
type: 'object',
|
||||
description:
|
||||
'all available images from TMDB (posters, backdrops, logos, profiles); every item includes `url` with the full image url',
|
||||
},
|
||||
},
|
||||
additionalProperties: true,
|
||||
},
|
||||
Season: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
season_number: { type: 'integer' },
|
||||
name: { type: 'string' },
|
||||
episode_count: { type: 'integer' },
|
||||
air_date: { type: 'string', format: 'date' },
|
||||
},
|
||||
},
|
||||
Episode: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
episode_number: { type: 'integer' },
|
||||
name: { type: 'string' },
|
||||
air_date: { type: 'string', format: 'date' },
|
||||
},
|
||||
},
|
||||
Subtitle: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
url: { type: 'string', format: 'uri' },
|
||||
language: { type: 'string' },
|
||||
name: { type: 'string' },
|
||||
},
|
||||
},
|
||||
Stream: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
provider: { type: 'string', example: 'videasy' },
|
||||
name: { type: 'string' },
|
||||
url: { type: 'string', format: 'uri', description: 'the m3u8 (or direct) url' },
|
||||
quality: { type: 'string', example: '2160p' },
|
||||
pixels: { type: ['integer', 'null'], example: 2160 },
|
||||
audio: { type: ['string', 'null'], example: 'aac' },
|
||||
type: { type: 'string', example: 'm3u8' },
|
||||
headers: {
|
||||
type: 'object',
|
||||
description: 'http headers the url needs (e.g. Referer)',
|
||||
additionalProperties: { type: 'string' },
|
||||
},
|
||||
subtitles: { type: 'array', items: { $ref: '#/components/schemas/Subtitle' } },
|
||||
},
|
||||
},
|
||||
StreamList: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
tmdb_id: { type: 'integer' },
|
||||
type: { type: 'string', enum: ['movie', 'tv'] },
|
||||
season: { type: ['integer', 'null'] },
|
||||
episode: { type: ['integer', 'null'] },
|
||||
total: { type: 'integer', description: 'streams before filters' },
|
||||
matched: { type: 'integer', description: 'streams after filters' },
|
||||
streams: { type: 'array', items: { $ref: '#/components/schemas/Stream' } },
|
||||
providerErrors: {
|
||||
type: 'object',
|
||||
description: 'per-provider scrape errors',
|
||||
additionalProperties: { type: 'string' },
|
||||
},
|
||||
loadErrors: {
|
||||
type: 'object',
|
||||
description: 'per-provider load errors',
|
||||
additionalProperties: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = spec;
|
||||
Reference in New Issue
Block a user