@omss/framework 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +516 -0
- package/dist/controllers/content.controller.d.ts +37 -0
- package/dist/controllers/content.controller.d.ts.map +1 -0
- package/dist/controllers/content.controller.js +37 -0
- package/dist/controllers/content.controller.js.map +1 -0
- package/dist/controllers/health.controller.d.ts +11 -0
- package/dist/controllers/health.controller.d.ts.map +1 -0
- package/dist/controllers/health.controller.js +18 -0
- package/dist/controllers/health.controller.js.map +1 -0
- package/dist/controllers/proxy.controller.d.ts +17 -0
- package/dist/controllers/proxy.controller.d.ts.map +1 -0
- package/dist/controllers/proxy.controller.js +32 -0
- package/dist/controllers/proxy.controller.js.map +1 -0
- package/dist/core/cache.d.ts +29 -0
- package/dist/core/cache.d.ts.map +1 -0
- package/dist/core/cache.js +95 -0
- package/dist/core/cache.js.map +1 -0
- package/dist/core/errors.d.ts +27 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +42 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/server.d.ts +46 -0
- package/dist/core/server.d.ts.map +1 -0
- package/dist/core/server.js +210 -0
- package/dist/core/server.js.map +1 -0
- package/dist/core/types.d.ts +123 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +4 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +4 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +57 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/logger.d.ts +3 -0
- package/dist/middleware/logger.d.ts.map +1 -0
- package/dist/middleware/logger.js +34 -0
- package/dist/middleware/logger.js.map +1 -0
- package/dist/middleware/validation.d.ts +33 -0
- package/dist/middleware/validation.d.ts.map +1 -0
- package/dist/middleware/validation.js +93 -0
- package/dist/middleware/validation.js.map +1 -0
- package/dist/providers/base-provider.d.ts +91 -0
- package/dist/providers/base-provider.d.ts.map +1 -0
- package/dist/providers/base-provider.js +170 -0
- package/dist/providers/base-provider.js.map +1 -0
- package/dist/providers/provider-registry.d.ts +56 -0
- package/dist/providers/provider-registry.d.ts.map +1 -0
- package/dist/providers/provider-registry.js +182 -0
- package/dist/providers/provider-registry.js.map +1 -0
- package/dist/services/health.service.d.ts +16 -0
- package/dist/services/health.service.d.ts.map +1 -0
- package/dist/services/health.service.js +44 -0
- package/dist/services/health.service.js.map +1 -0
- package/dist/services/proxy.service.d.ts +56 -0
- package/dist/services/proxy.service.d.ts.map +1 -0
- package/dist/services/proxy.service.js +224 -0
- package/dist/services/proxy.service.js.map +1 -0
- package/dist/services/source.service.d.ts +57 -0
- package/dist/services/source.service.d.ts.map +1 -0
- package/dist/services/source.service.js +226 -0
- package/dist/services/source.service.js.map +1 -0
- package/dist/services/tmdb.service.d.ts +67 -0
- package/dist/services/tmdb.service.d.ts.map +1 -0
- package/dist/services/tmdb.service.js +237 -0
- package/dist/services/tmdb.service.js.map +1 -0
- package/package.json +90 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Open Media Streaming Foundation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# OMSS Example Backend
|
|
4
|
+
|
|
5
|
+
<img alt="40211688-73db-47bd-8153-30d1a416f576" src="https://github.com/user-attachments/assets/bc6d19dc-0d86-43e5-86cc-6e1d32bdab40" />
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
This is an extendable example implementation of the **[OMSS (Open Media Streaming Standard)](https://github.com/omss-spec/omss-spec)** backend server. It demonstrates how to build a compliant streaming media aggregation service that scrapes content from multiple providers and returns standardized responses. This is nearly a scraping framework. It handles most of the logic already for you. You just have to add the scraping logic!
|
|
9
|
+
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
## 🎯 What is OMSS?
|
|
13
|
+
|
|
14
|
+
OMSS is an open standard for streaming media aggregation. It provides a unified API for fetching movie and TV show streaming sources from multiple providers, with built-in proxy support, subtitle handling, and quality selection.
|
|
15
|
+
|
|
16
|
+
### Key Features
|
|
17
|
+
|
|
18
|
+
- ✅ **Standardized API**: Consistent response format across all providers
|
|
19
|
+
- ✅ **Multi-Provider Support**: Aggregate sources from multiple streaming providers
|
|
20
|
+
- ✅ **Built-in Proxy**: Automatic URL proxying with header forwarding
|
|
21
|
+
- ✅ **TMDB Integration**: Validation against The Movie Database
|
|
22
|
+
- ✅ **Caching Layer**: Redis or in-memory caching for performance
|
|
23
|
+
- ✅ **Type Safety**: Full TypeScript support
|
|
24
|
+
- ✅ **Provider Management**: Easy enable/disable, automatic discovery
|
|
25
|
+
- ✅ **Health Checks**: Monitor provider availability
|
|
26
|
+
- ✅ **Refresh API**: Force cache invalidation when needed
|
|
27
|
+
|
|
28
|
+
## 📋 Table of Contents
|
|
29
|
+
|
|
30
|
+
- [Installation](#installation)
|
|
31
|
+
- [Quick Start](#quick-start)
|
|
32
|
+
- [Configuration](#configuration)
|
|
33
|
+
- [Creating Custom Providers](#creating-custom-providers)
|
|
34
|
+
- [API Endpoints](#api-endpoints)
|
|
35
|
+
- [Environment Variables](#environment-variables)
|
|
36
|
+
- [Architecture](#architecture)
|
|
37
|
+
- [OMSS Compliance](#omss-compliance)
|
|
38
|
+
- [License](#license)
|
|
39
|
+
|
|
40
|
+
## 🚀 Installation
|
|
41
|
+
|
|
42
|
+
### Prerequisites
|
|
43
|
+
|
|
44
|
+
- Node.js 18.x or higher
|
|
45
|
+
- npm or yarn
|
|
46
|
+
- TMDB API Key ([Get one here](https://www.themoviedb.org/settings/api))
|
|
47
|
+
- (Optional) Redis server for caching
|
|
48
|
+
|
|
49
|
+
### Install Dependencies
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Setup Environment
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cp .env.example .env
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Edit `.env` and add your TMDB API key and modify other settings as needed.
|
|
62
|
+
|
|
63
|
+
## 🎬 Quick Start
|
|
64
|
+
|
|
65
|
+
### Basic Server Setup
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { OMSSServer } from './src';
|
|
69
|
+
import { ExampleProvider } from './src/providers/implementations/example-provider';
|
|
70
|
+
|
|
71
|
+
// Create server instance
|
|
72
|
+
const server = new OMSSServer({
|
|
73
|
+
name: 'My OMSS Backend',
|
|
74
|
+
version: '1.0.0',
|
|
75
|
+
host: 'localhost',
|
|
76
|
+
port: 3000,
|
|
77
|
+
cache: {
|
|
78
|
+
type: 'memory',
|
|
79
|
+
ttl: 7200,
|
|
80
|
+
},
|
|
81
|
+
tmdb: {
|
|
82
|
+
apiKey: process.env.TMDB_API_KEY,
|
|
83
|
+
cacheTTL: 86400,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Register providers
|
|
88
|
+
const registry = server.getRegistry();
|
|
89
|
+
registry.register(new ExampleProvider());
|
|
90
|
+
|
|
91
|
+
// or use the very cool auto-discovery feature
|
|
92
|
+
// registry.discoverProviders('./path/to/providerfolder');
|
|
93
|
+
// Note: this is relative to where you start the server.
|
|
94
|
+
|
|
95
|
+
// Start server
|
|
96
|
+
await server.start();
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Test the API
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Get movie sources
|
|
103
|
+
curl http://localhost:3000/v1/movies/550
|
|
104
|
+
|
|
105
|
+
# Get TV episode sources
|
|
106
|
+
curl http://localhost:3000/v1/tv/1399/1/1
|
|
107
|
+
|
|
108
|
+
# Health check
|
|
109
|
+
curl http://localhost:3000/v1/health
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## ⚙️ Configuration
|
|
113
|
+
|
|
114
|
+
### Server Configuration Options
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
interface OMSSConfig {
|
|
118
|
+
// Required: Server identification
|
|
119
|
+
name: string; // Your server name
|
|
120
|
+
version: string; // OMSS Spec version
|
|
121
|
+
|
|
122
|
+
// Optional: Network settings
|
|
123
|
+
host?: string; // Default: 'localhost'
|
|
124
|
+
port?: number; // Default: 3000
|
|
125
|
+
publicUrl?: string; // For reverse proxy setups
|
|
126
|
+
|
|
127
|
+
// Optional: Cache configuration
|
|
128
|
+
cache?: {
|
|
129
|
+
type: 'memory' | 'redis';
|
|
130
|
+
ttl?: number; // Default: 7200 (2 hours)
|
|
131
|
+
redis?: {
|
|
132
|
+
host: string;
|
|
133
|
+
port: number;
|
|
134
|
+
password?: string;
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Required: TMDB configuration
|
|
139
|
+
tmdb?: {
|
|
140
|
+
apiKey?: string; // Can also use TMDB_API_KEY env var
|
|
141
|
+
cacheTTL?: number; // Default: 86400 (24 hours)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Example Configurations
|
|
147
|
+
|
|
148
|
+
#### Development
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const server = new OMSSServer({
|
|
152
|
+
name: 'OMSS Dev Server',
|
|
153
|
+
version: '1.0.0',
|
|
154
|
+
host: 'localhost',
|
|
155
|
+
port: 3000,
|
|
156
|
+
cache: {
|
|
157
|
+
type: 'memory',
|
|
158
|
+
ttl: 3600,
|
|
159
|
+
},
|
|
160
|
+
tmdb: {
|
|
161
|
+
apiKey: process.env.TMDB_API_KEY,
|
|
162
|
+
cacheTTL: 86400,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### Production with Redis
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const server = new OMSSServer({
|
|
171
|
+
name: 'OMSS Production',
|
|
172
|
+
version: '1.0.0',
|
|
173
|
+
host: '0.0.0.0',
|
|
174
|
+
port: 3000,
|
|
175
|
+
publicUrl: 'https://api.mystream.com',
|
|
176
|
+
cache: {
|
|
177
|
+
type: 'redis',
|
|
178
|
+
ttl: 7200,
|
|
179
|
+
redis: {
|
|
180
|
+
host: process.env.REDIS_HOST || 'localhost',
|
|
181
|
+
port: parseInt(process.env.REDIS_PORT || '6379'),
|
|
182
|
+
password: process.env.REDIS_PASSWORD,
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
tmdb: {
|
|
186
|
+
apiKey: process.env.TMDB_API_KEY,
|
|
187
|
+
cacheTTL: 86400,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### Behind Reverse Proxy
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const server = new OMSSServer({
|
|
196
|
+
name: 'OMSS API',
|
|
197
|
+
version: '1.0.0',
|
|
198
|
+
host: '0.0.0.0',
|
|
199
|
+
port: 3000,
|
|
200
|
+
// This is the public URL clients will use
|
|
201
|
+
publicUrl: 'https://myapp.com/api',
|
|
202
|
+
cache: {
|
|
203
|
+
type: 'redis',
|
|
204
|
+
redis: {
|
|
205
|
+
host: 'redis.internal',
|
|
206
|
+
port: 6379,
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
tmdb: {
|
|
210
|
+
apiKey: process.env.TMDB_API_KEY,
|
|
211
|
+
cacheTTL: 86400,
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## 🔌 Creating Custom Providers
|
|
217
|
+
|
|
218
|
+
See the detailed [Provider Creation Guide](./examples/example-provider.ts) for a complete walkthrough.
|
|
219
|
+
|
|
220
|
+
### Quick Start with Auto-Discovery
|
|
221
|
+
|
|
222
|
+
The easiest way to add a new provider:
|
|
223
|
+
|
|
224
|
+
1. Create a directory for all of your provider files
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
touch src/providers/implementations/my-provider.ts
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
2. Implement the `BaseProvider` class (see example below) in each file.
|
|
231
|
+
|
|
232
|
+
3. In the Setup, use the `discoverProviders` method of the `ProviderRegistry` to load all providers from that directory:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
const registry = server.getRegistry();
|
|
236
|
+
registry.discoverProviders('./src/providers/implementations'); // relative to where you start the server from
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
4. That's it! The provider will be automatically discovered and registered when you start the server!
|
|
240
|
+
|
|
241
|
+
No imports, no manual registration needed!
|
|
242
|
+
|
|
243
|
+
### Minimal Provider Example
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { BaseProvider } from './src/providers/base-provider';
|
|
247
|
+
import { ProviderCapabilities, ProviderMediaObject, ProviderResult } from './src/core/types';
|
|
248
|
+
|
|
249
|
+
export class MyProvider extends BaseProvider {
|
|
250
|
+
// Required: Provider identification
|
|
251
|
+
readonly id = 'my-provider';
|
|
252
|
+
readonly name = 'My Provider';
|
|
253
|
+
readonly enabled = true;
|
|
254
|
+
|
|
255
|
+
// Required: Base URL and headers
|
|
256
|
+
readonly BASE_URL = 'https://provider.example.com';
|
|
257
|
+
readonly HEADERS = {
|
|
258
|
+
'User-Agent': 'Mozilla/5.0',
|
|
259
|
+
Referer: 'https://provider.example.com',
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// Required: Declare what this provider supports
|
|
263
|
+
readonly capabilities: ProviderCapabilities = {
|
|
264
|
+
supportedContentTypes: ['movies', 'tv'],
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// Implement movie scraping
|
|
268
|
+
async getMovieSources(media: ProviderMediaObject): Promise<ProviderResult> {
|
|
269
|
+
this.console.log('Fetching movie sources', media);
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
// Your scraping logic here
|
|
273
|
+
const streamUrl = await this.scrapeMovieUrl(media.tmdbId); // this is just some example function
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
sources: [
|
|
277
|
+
{
|
|
278
|
+
url: this.createProxyUrl(streamUrl, this.HEADERS),
|
|
279
|
+
type: 'hls',
|
|
280
|
+
quality: '1080p',
|
|
281
|
+
audioTracks: [
|
|
282
|
+
{
|
|
283
|
+
language: 'en',
|
|
284
|
+
label: 'English',
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
provider: {
|
|
288
|
+
id: this.id,
|
|
289
|
+
name: this.name,
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
subtitles: [],
|
|
294
|
+
diagnostics: [],
|
|
295
|
+
};
|
|
296
|
+
} catch (error) {
|
|
297
|
+
this.console.error('Failed to fetch sources', error, media);
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
sources: [],
|
|
301
|
+
subtitles: [],
|
|
302
|
+
diagnostics: [
|
|
303
|
+
{
|
|
304
|
+
code: 'PROVIDER_ERROR',
|
|
305
|
+
message: `${this.name} failed`,
|
|
306
|
+
field: '',
|
|
307
|
+
severity: 'error',
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Implement TV scraping
|
|
315
|
+
async getTVSources(media: ProviderMediaObject): Promise<ProviderResult> {
|
|
316
|
+
// Similar to getMovieSources but for TV
|
|
317
|
+
return { sources: [], subtitles: [], diagnostics: [] };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Optional: Custom health check
|
|
321
|
+
async healthCheck(): Promise<boolean> {
|
|
322
|
+
try {
|
|
323
|
+
const response = await fetch(this.BASE_URL);
|
|
324
|
+
return response.ok;
|
|
325
|
+
} catch {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Full Provider Example
|
|
333
|
+
|
|
334
|
+
See the detailed [Provider Creation Guide](./examples/example-provider.ts) for a complete walkthrough.
|
|
335
|
+
|
|
336
|
+
## 📡 API Endpoints
|
|
337
|
+
|
|
338
|
+
### GET `/v1/movies/:tmdbId`
|
|
339
|
+
|
|
340
|
+
Fetch streaming sources for a movie.
|
|
341
|
+
|
|
342
|
+
**Parameters:**
|
|
343
|
+
|
|
344
|
+
- `tmdbId` (path): TMDB movie ID
|
|
345
|
+
|
|
346
|
+
**Response:**
|
|
347
|
+
|
|
348
|
+
```json
|
|
349
|
+
{
|
|
350
|
+
"responseId": "uuid-v4",
|
|
351
|
+
"expiresAt": "2026-01-18T20:00:00.000Z",
|
|
352
|
+
"sources": [
|
|
353
|
+
{
|
|
354
|
+
"url": "/v1/proxy?data=...",
|
|
355
|
+
"type": "hls",
|
|
356
|
+
"quality": "1080p",
|
|
357
|
+
"audioTracks": [
|
|
358
|
+
{
|
|
359
|
+
"language": "en",
|
|
360
|
+
"label": "English"
|
|
361
|
+
}
|
|
362
|
+
],
|
|
363
|
+
"provider": {
|
|
364
|
+
"id": "vixsrc",
|
|
365
|
+
"name": "VixSrc"
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
],
|
|
369
|
+
"subtitles": [],
|
|
370
|
+
"diagnostics": []
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### GET `/v1/tv/:tmdbId/seasons/:season/episodes/:episode`
|
|
375
|
+
|
|
376
|
+
Fetch streaming sources for a TV episode.
|
|
377
|
+
|
|
378
|
+
**Parameters:**
|
|
379
|
+
|
|
380
|
+
- `tmdbId` (path): TMDB series ID
|
|
381
|
+
- `season` (path): Season number (0-99)
|
|
382
|
+
- `episode` (path): Episode number (1-9999)
|
|
383
|
+
|
|
384
|
+
**Response:** Same structure as movies endpoint
|
|
385
|
+
|
|
386
|
+
### GET `/v1/proxy`
|
|
387
|
+
|
|
388
|
+
Proxy streaming URLs with custom headers.
|
|
389
|
+
|
|
390
|
+
**Query Parameters:**
|
|
391
|
+
|
|
392
|
+
- `data` (required): URL-encoded JSON containing:
|
|
393
|
+
```json
|
|
394
|
+
{
|
|
395
|
+
"url": "https://stream.example.com/video.m3u8",
|
|
396
|
+
"headers": {
|
|
397
|
+
"Referer": "https://provider.example.com"
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### GET `/v1/refresh/:responseId`
|
|
403
|
+
|
|
404
|
+
Force refresh cached sources.
|
|
405
|
+
|
|
406
|
+
**Parameters:**
|
|
407
|
+
|
|
408
|
+
- `responseId` (path): Response ID from previous request
|
|
409
|
+
|
|
410
|
+
### GET `/v1/health`
|
|
411
|
+
|
|
412
|
+
Health check endpoint.
|
|
413
|
+
|
|
414
|
+
**Response:**
|
|
415
|
+
|
|
416
|
+
```json
|
|
417
|
+
{
|
|
418
|
+
"status": "healthy",
|
|
419
|
+
"version": "1.0.0",
|
|
420
|
+
"providers": {
|
|
421
|
+
"total": 1,
|
|
422
|
+
"enabled": 1
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
## 🌍 Environment Variables
|
|
428
|
+
|
|
429
|
+
```env
|
|
430
|
+
# Server Configuration
|
|
431
|
+
PORT=3000 # Port number for the server
|
|
432
|
+
HOST=0.0.0.0 # Use 'localhost' to restrict to local access
|
|
433
|
+
NODE_ENV=development # 'development' | 'production'
|
|
434
|
+
|
|
435
|
+
# TMDB Configuration
|
|
436
|
+
TMDB_API_KEY=your_tmdb_api_key_here
|
|
437
|
+
TMDB_CACHE_TTL=86400
|
|
438
|
+
|
|
439
|
+
# Cache Configuration
|
|
440
|
+
CACHE_TYPE=memory # 'memory' | 'redis'
|
|
441
|
+
|
|
442
|
+
# Redis Configuration (if using Redis cache)
|
|
443
|
+
REDIS_HOST=localhost # default Redis host
|
|
444
|
+
REDIS_PORT=6379 # default Redis port
|
|
445
|
+
REDIS_PASSWORD= # Redis password if required
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## 🏗️ Architecture
|
|
449
|
+
|
|
450
|
+
```
|
|
451
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
452
|
+
│ OMSS Server │
|
|
453
|
+
├─────────────────────────────────────────────────────────────┤
|
|
454
|
+
│ Controllers │
|
|
455
|
+
│ ├── ContentController (Movies/TV endpoints) │
|
|
456
|
+
│ ├── ProxyController (Streaming proxy) │
|
|
457
|
+
│ └── HealthController (Health checks) │
|
|
458
|
+
├─────────────────────────────────────────────────────────────┤
|
|
459
|
+
│ Services │
|
|
460
|
+
│ ├── SourceService (Aggregates provider results) │
|
|
461
|
+
│ ├── TMDBService (Validates against TMDB) │
|
|
462
|
+
│ └── ProxyService (Handles URL proxying) │
|
|
463
|
+
├─────────────────────────────────────────────────────────────┤
|
|
464
|
+
│ Provider Registry │
|
|
465
|
+
│ └── Manages all registered providers │
|
|
466
|
+
├─────────────────────────────────────────────────────────────┤
|
|
467
|
+
│ Providers (Implement BaseProvider) │
|
|
468
|
+
│ ├── VixSrcProvider │
|
|
469
|
+
│ ├── YourCustomProvider │
|
|
470
|
+
│ └── ... │
|
|
471
|
+
├─────────────────────────────────────────────────────────────┤
|
|
472
|
+
│ Cache Layer │
|
|
473
|
+
│ ├── MemoryCache (Development) │
|
|
474
|
+
│ └── RedisCache (Production) │
|
|
475
|
+
└─────────────────────────────────────────────────────────────┘
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## ✅ OMSS Compliance
|
|
479
|
+
|
|
480
|
+
This implementation follows the [OMSS Specification](https://github.com/omss-spec/omss-spec):
|
|
481
|
+
|
|
482
|
+
- ✅ **Standardized Response Format**: All responses follow OMSS schema
|
|
483
|
+
- ✅ **TMDB Validation**: All requests validated against TMDB
|
|
484
|
+
- ✅ **Proxy Support**: Required for all streaming URLs
|
|
485
|
+
- ✅ **Error Handling**: OMSS-compliant error responses
|
|
486
|
+
- ✅ **Source Identification**: Unique IDs for all sources
|
|
487
|
+
- ✅ **Audio Track Support**: Multiple audio tracks per source
|
|
488
|
+
- ✅ **Subtitle Support**: VTT/SRT subtitle formats
|
|
489
|
+
- ✅ **Quality Indicators**: Resolution-based quality tags
|
|
490
|
+
- ✅ **Provider Attribution**: Source provider identification
|
|
491
|
+
- ✅ **Diagnostics**: Detailed error/warning information
|
|
492
|
+
|
|
493
|
+
## 📚 Additional Resources
|
|
494
|
+
|
|
495
|
+
- [OMSS Specification](https://github.com/omss-spec/omss-spec)
|
|
496
|
+
- [Basic Server Example](./examples/basic-server.ts)
|
|
497
|
+
- [Provider Example](./examples/example-provider.ts)
|
|
498
|
+
- [TMDB API Documentation](https://developers.themoviedb.org/3)
|
|
499
|
+
|
|
500
|
+
## 🤝 Contributing
|
|
501
|
+
|
|
502
|
+
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
|
|
503
|
+
|
|
504
|
+
## 📄 License
|
|
505
|
+
|
|
506
|
+
MIT License - see [LICENSE](./LICENSE) file for details.
|
|
507
|
+
|
|
508
|
+
## 🙏 Acknowledgments
|
|
509
|
+
|
|
510
|
+
- [TMDB](https://www.themoviedb.org/) for their excellent API
|
|
511
|
+
- All provider maintainers
|
|
512
|
+
- OMSS specification contributors
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
**Note**: This is a reference implementation. Always ensure you have the legal right to stream content and comply with applicable copyright laws in your jurisdiction.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { FastifyRequest, FastifyReply } from 'fastify';
|
|
2
|
+
import { SourceService } from '../services/source.service';
|
|
3
|
+
interface MovieParams {
|
|
4
|
+
id: string;
|
|
5
|
+
}
|
|
6
|
+
interface TVParams {
|
|
7
|
+
id: string;
|
|
8
|
+
s: string;
|
|
9
|
+
e: string;
|
|
10
|
+
}
|
|
11
|
+
interface RefreshParams {
|
|
12
|
+
responseId: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class ContentController {
|
|
15
|
+
private sourceService;
|
|
16
|
+
constructor(sourceService: SourceService);
|
|
17
|
+
/**
|
|
18
|
+
* GET /v1/movies/:id
|
|
19
|
+
*/
|
|
20
|
+
getMovie(request: FastifyRequest<{
|
|
21
|
+
Params: MovieParams;
|
|
22
|
+
}>, reply: FastifyReply): Promise<never>;
|
|
23
|
+
/**
|
|
24
|
+
* GET /v1/tv/:id/seasons/:s/episodes/:e
|
|
25
|
+
*/
|
|
26
|
+
getTVEpisode(request: FastifyRequest<{
|
|
27
|
+
Params: TVParams;
|
|
28
|
+
}>, reply: FastifyReply): Promise<never>;
|
|
29
|
+
/**
|
|
30
|
+
* GET /v1/refresh/:responseId
|
|
31
|
+
*/
|
|
32
|
+
refreshSource(request: FastifyRequest<{
|
|
33
|
+
Params: RefreshParams;
|
|
34
|
+
}>, reply: FastifyReply): Promise<never>;
|
|
35
|
+
}
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=content.controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.controller.d.ts","sourceRoot":"","sources":["../../src/controllers/content.controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D,UAAU,WAAW;IACjB,EAAE,EAAE,MAAM,CAAC;CACd;AAED,UAAU,QAAQ;IACd,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACb;AAED,UAAU,aAAa;IACnB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,iBAAiB;IACd,OAAO,CAAC,aAAa;gBAAb,aAAa,EAAE,aAAa;IAEhD;;OAEG;IACG,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;QAAE,MAAM,EAAE,WAAW,CAAA;KAAE,CAAC,EAAE,KAAK,EAAE,YAAY;IAMpF;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE,cAAc,CAAC;QAAE,MAAM,EAAE,QAAQ,CAAA;KAAE,CAAC,EAAE,KAAK,EAAE,YAAY;IASrF;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,cAAc,CAAC;QAAE,MAAM,EAAE,aAAa,CAAA;KAAE,CAAC,EAAE,KAAK,EAAE,YAAY;CAK9F"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContentController = void 0;
|
|
4
|
+
class ContentController {
|
|
5
|
+
sourceService;
|
|
6
|
+
constructor(sourceService) {
|
|
7
|
+
this.sourceService = sourceService;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* GET /v1/movies/:id
|
|
11
|
+
*/
|
|
12
|
+
async getMovie(request, reply) {
|
|
13
|
+
const { id } = request.params;
|
|
14
|
+
const response = await this.sourceService.getMovieSources(id);
|
|
15
|
+
return reply.code(200).send(response);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* GET /v1/tv/:id/seasons/:s/episodes/:e
|
|
19
|
+
*/
|
|
20
|
+
async getTVEpisode(request, reply) {
|
|
21
|
+
const { id, s, e } = request.params;
|
|
22
|
+
const season = parseInt(s, 10);
|
|
23
|
+
const episode = parseInt(e, 10);
|
|
24
|
+
const response = await this.sourceService.getTVSources(id, season, episode);
|
|
25
|
+
return reply.code(200).send(response);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* GET /v1/refresh/:responseId
|
|
29
|
+
*/
|
|
30
|
+
async refreshSource(request, reply) {
|
|
31
|
+
const { responseId } = request.params;
|
|
32
|
+
await this.sourceService.refreshSource(responseId);
|
|
33
|
+
return reply.code(200).send({ status: 'OK' });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.ContentController = ContentController;
|
|
37
|
+
//# sourceMappingURL=content.controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.controller.js","sourceRoot":"","sources":["../../src/controllers/content.controller.ts"],"names":[],"mappings":";;;AAiBA,MAAa,iBAAiB;IACN;IAApB,YAAoB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAEpD;;OAEG;IACH,KAAK,CAAC,QAAQ,CAAC,OAAgD,EAAE,KAAmB;QAChF,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAC9D,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,OAA6C,EAAE,KAAmB;QACjF,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACpC,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAEhC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAC5E,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,OAAkD,EAAE,KAAmB;QACvF,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACtC,MAAM,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACnD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;CACJ;AAhCD,8CAgCC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FastifyRequest, FastifyReply } from 'fastify';
|
|
2
|
+
import { HealthService } from '../services/health.service';
|
|
3
|
+
export declare class HealthController {
|
|
4
|
+
private healthService;
|
|
5
|
+
constructor(healthService: HealthService);
|
|
6
|
+
/**
|
|
7
|
+
* GET / or /v1 or /v1/health
|
|
8
|
+
*/
|
|
9
|
+
getHealth(request: FastifyRequest, reply: FastifyReply): Promise<never>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=health.controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.controller.d.ts","sourceRoot":"","sources":["../../src/controllers/health.controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D,qBAAa,gBAAgB;IACb,OAAO,CAAC,aAAa;gBAAb,aAAa,EAAE,aAAa;IAEhD;;OAEG;IACG,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY;CAI/D"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HealthController = void 0;
|
|
4
|
+
class HealthController {
|
|
5
|
+
healthService;
|
|
6
|
+
constructor(healthService) {
|
|
7
|
+
this.healthService = healthService;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* GET / or /v1 or /v1/health
|
|
11
|
+
*/
|
|
12
|
+
async getHealth(request, reply) {
|
|
13
|
+
const health = this.healthService.getHealth();
|
|
14
|
+
return reply.code(200).send(health);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.HealthController = HealthController;
|
|
18
|
+
//# sourceMappingURL=health.controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.controller.js","sourceRoot":"","sources":["../../src/controllers/health.controller.ts"],"names":[],"mappings":";;;AAGA,MAAa,gBAAgB;IACL;IAApB,YAAoB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAEpD;;OAEG;IACH,KAAK,CAAC,SAAS,CAAC,OAAuB,EAAE,KAAmB;QACxD,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;CACJ;AAVD,4CAUC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FastifyRequest, FastifyReply } from 'fastify';
|
|
2
|
+
import { ProxyService } from '../services/proxy.service';
|
|
3
|
+
interface ProxyQuery {
|
|
4
|
+
data: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class ProxyController {
|
|
7
|
+
private proxyService;
|
|
8
|
+
constructor(proxyService: ProxyService);
|
|
9
|
+
/**
|
|
10
|
+
* GET /v1/proxy
|
|
11
|
+
*/
|
|
12
|
+
proxy(request: FastifyRequest<{
|
|
13
|
+
Querystring: ProxyQuery;
|
|
14
|
+
}>, reply: FastifyReply): Promise<never>;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=proxy.controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.controller.d.ts","sourceRoot":"","sources":["../../src/controllers/proxy.controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,UAAU,UAAU;IAChB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,eAAe;IACZ,OAAO,CAAC,YAAY;gBAAZ,YAAY,EAAE,YAAY;IAE9C;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE,cAAc,CAAC;QAAE,WAAW,EAAE,UAAU,CAAA;KAAE,CAAC,EAAE,KAAK,EAAE,YAAY;CAqBxF"}
|