@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
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ProviderRegistry } from '../providers/provider-registry';
|
|
2
|
+
import { CacheService } from '../core/cache';
|
|
3
|
+
import { SourceResponse, ResponseIdMapping } from '../core/types';
|
|
4
|
+
import { TMDBService } from '../services/tmdb.service';
|
|
5
|
+
export declare class SourceService {
|
|
6
|
+
private registry;
|
|
7
|
+
private cache;
|
|
8
|
+
private tmdbService;
|
|
9
|
+
private cacheTTL;
|
|
10
|
+
private tmdbValidator;
|
|
11
|
+
private responseIdMap;
|
|
12
|
+
constructor(registry: ProviderRegistry, cache: CacheService, tmdbService: TMDBService, cacheTTL?: {
|
|
13
|
+
sources: number;
|
|
14
|
+
subtitles: number;
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Get movie sources from all providers
|
|
18
|
+
*/
|
|
19
|
+
getMovieSources(tmdbId: string): Promise<SourceResponse>;
|
|
20
|
+
/**
|
|
21
|
+
* Get TV episode sources from all providers
|
|
22
|
+
*/
|
|
23
|
+
getTVSources(tmdbId: string, season: number, episode: number): Promise<SourceResponse>;
|
|
24
|
+
/**
|
|
25
|
+
* Refresh cached sources by responseId
|
|
26
|
+
*/
|
|
27
|
+
refreshSource(responseId: string): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Store responseId to cacheKey mapping
|
|
30
|
+
*/
|
|
31
|
+
private storeResponseIdMapping;
|
|
32
|
+
/**
|
|
33
|
+
* Cleanup expired responseId mappings
|
|
34
|
+
*/
|
|
35
|
+
private cleanupExpiredMappings;
|
|
36
|
+
/**
|
|
37
|
+
* Fetch results from all providers concurrently
|
|
38
|
+
*/
|
|
39
|
+
private fetchFromProviders;
|
|
40
|
+
/**
|
|
41
|
+
* Build final response from provider results
|
|
42
|
+
*/
|
|
43
|
+
private buildResponse;
|
|
44
|
+
/**
|
|
45
|
+
* Get mapping info for debugging
|
|
46
|
+
*/
|
|
47
|
+
getMappingInfo(responseId: string): ResponseIdMapping | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Get all mappings count
|
|
50
|
+
*/
|
|
51
|
+
getMappingsCount(): number;
|
|
52
|
+
/**
|
|
53
|
+
* Cleanup on service shutdown
|
|
54
|
+
*/
|
|
55
|
+
destroy(): void;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=source.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.service.d.ts","sourceRoot":"","sources":["../../src/services/source.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAkB,iBAAiB,EAAuB,MAAM,cAAc,CAAC;AAGtG,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,qBAAa,aAAa;IAKlB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,QAAQ;IAPpB,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,aAAa,CAA6C;gBAGtD,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,YAAY,EACnB,WAAW,EAAE,WAAW,EACxB,QAAQ;;;KAAsC;IAM1D;;OAEG;IACG,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA2C9D;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA6C5F;;OAEG;IACG,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBtD;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAK9B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAiB9B;;OAEG;YACW,kBAAkB;IAmDhC;;OAEG;IACH,OAAO,CAAC,aAAa;IA0BrB;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIjE;;OAEG;IACH,gBAAgB,IAAI,MAAM;IAI1B;;OAEG;IACH,OAAO,IAAI,IAAI;CAIlB"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SourceService = void 0;
|
|
4
|
+
const validation_1 = require("../middleware/validation");
|
|
5
|
+
const errors_1 = require("../core/errors");
|
|
6
|
+
const uuid_1 = require("uuid");
|
|
7
|
+
class SourceService {
|
|
8
|
+
registry;
|
|
9
|
+
cache;
|
|
10
|
+
tmdbService;
|
|
11
|
+
cacheTTL;
|
|
12
|
+
tmdbValidator;
|
|
13
|
+
responseIdMap = new Map();
|
|
14
|
+
constructor(registry, cache, tmdbService, cacheTTL = { sources: 7200, subtitles: 86400 }) {
|
|
15
|
+
this.registry = registry;
|
|
16
|
+
this.cache = cache;
|
|
17
|
+
this.tmdbService = tmdbService;
|
|
18
|
+
this.cacheTTL = cacheTTL;
|
|
19
|
+
setInterval(() => this.cleanupExpiredMappings(), 60 * 60 * 1000);
|
|
20
|
+
this.tmdbValidator = (0, validation_1.createTMDBValidator)(tmdbService);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get movie sources from all providers
|
|
24
|
+
*/
|
|
25
|
+
async getMovieSources(tmdbId) {
|
|
26
|
+
await this.tmdbValidator.validateMovie(tmdbId);
|
|
27
|
+
const cacheKey = `movie:${tmdbId}`;
|
|
28
|
+
// Check cache first
|
|
29
|
+
const cached = await this.cache.get(cacheKey);
|
|
30
|
+
if (cached) {
|
|
31
|
+
console.log(`[SourceService] Cache HIT for ${cacheKey}`);
|
|
32
|
+
return cached;
|
|
33
|
+
}
|
|
34
|
+
console.log(`[SourceService] Cache MISS for ${cacheKey}`);
|
|
35
|
+
// Build media object for providers
|
|
36
|
+
const media = await this.tmdbService.getMediaObject('movie', tmdbId);
|
|
37
|
+
// Try to get IMDB ID
|
|
38
|
+
media.imdbId = await this.tmdbService.getImdbId(tmdbId, 'movie');
|
|
39
|
+
// Fetch from providers
|
|
40
|
+
const results = await this.fetchFromProviders('movie', media);
|
|
41
|
+
const response = this.buildResponse(results);
|
|
42
|
+
// Throw error if no sources found
|
|
43
|
+
if (response.sources.length === 0) {
|
|
44
|
+
throw errors_1.OMSSErrors.noSourcesAvailable(tmdbId, this.registry.count);
|
|
45
|
+
}
|
|
46
|
+
// Store responseId mapping
|
|
47
|
+
this.storeResponseIdMapping(response.responseId, {
|
|
48
|
+
cacheKey,
|
|
49
|
+
type: 'movie',
|
|
50
|
+
tmdbId,
|
|
51
|
+
createdAt: Date.now(),
|
|
52
|
+
});
|
|
53
|
+
// Cache the response
|
|
54
|
+
await this.cache.set(cacheKey, response, this.cacheTTL.sources);
|
|
55
|
+
return response;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get TV episode sources from all providers
|
|
59
|
+
*/
|
|
60
|
+
async getTVSources(tmdbId, season, episode) {
|
|
61
|
+
await this.tmdbValidator.validateTVEpisode(tmdbId, season, episode);
|
|
62
|
+
const cacheKey = `tv:${tmdbId}:s${season}:e${episode}`;
|
|
63
|
+
// Check cache
|
|
64
|
+
const cached = await this.cache.get(cacheKey);
|
|
65
|
+
if (cached) {
|
|
66
|
+
console.log(`[SourceService] Cache HIT for ${cacheKey}`);
|
|
67
|
+
return cached;
|
|
68
|
+
}
|
|
69
|
+
console.log(`[SourceService] Cache MISS for ${cacheKey}`);
|
|
70
|
+
// Build media object for providers
|
|
71
|
+
const media = await this.tmdbService.getMediaObject('tv', tmdbId, season, episode);
|
|
72
|
+
// Try to get IMDB ID
|
|
73
|
+
media.imdbId = await this.tmdbService.getImdbId(tmdbId, 'tv');
|
|
74
|
+
// Fetch from providers
|
|
75
|
+
const results = await this.fetchFromProviders('tv', media);
|
|
76
|
+
const response = this.buildResponse(results);
|
|
77
|
+
// Throw error if no sources found
|
|
78
|
+
if (response.sources.length === 0) {
|
|
79
|
+
throw errors_1.OMSSErrors.noSourcesAvailable(`${tmdbId}/S${season}E${episode}`, this.registry.count);
|
|
80
|
+
}
|
|
81
|
+
// Store responseId mapping
|
|
82
|
+
this.storeResponseIdMapping(response.responseId, {
|
|
83
|
+
cacheKey,
|
|
84
|
+
type: 'tv',
|
|
85
|
+
tmdbId,
|
|
86
|
+
season,
|
|
87
|
+
episode,
|
|
88
|
+
createdAt: Date.now(),
|
|
89
|
+
});
|
|
90
|
+
// Cache the response
|
|
91
|
+
await this.cache.set(cacheKey, response, this.cacheTTL.sources);
|
|
92
|
+
return response;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Refresh cached sources by responseId
|
|
96
|
+
*/
|
|
97
|
+
async refreshSource(responseId) {
|
|
98
|
+
if (!/^[a-zA-Z0-9_-]{1,128}$/.test(responseId)) {
|
|
99
|
+
throw errors_1.OMSSErrors.invalidResponseId(responseId);
|
|
100
|
+
}
|
|
101
|
+
const mapping = this.responseIdMap.get(responseId);
|
|
102
|
+
if (!mapping) {
|
|
103
|
+
console.warn(`[SourceService] No mapping found for responseId: ${responseId}`);
|
|
104
|
+
throw errors_1.OMSSErrors.responseIdNotFound(responseId);
|
|
105
|
+
}
|
|
106
|
+
console.log(`[SourceService] Refreshing cache for ${mapping.cacheKey} (responseId: ${responseId})`);
|
|
107
|
+
await this.cache.delete(mapping.cacheKey);
|
|
108
|
+
this.responseIdMap.delete(responseId);
|
|
109
|
+
console.log(`[SourceService] Successfully refreshed cache for ${mapping.cacheKey}`);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Store responseId to cacheKey mapping
|
|
113
|
+
*/
|
|
114
|
+
storeResponseIdMapping(responseId, mapping) {
|
|
115
|
+
this.responseIdMap.set(responseId, mapping);
|
|
116
|
+
console.log(`[SourceService] Stored mapping: ${responseId} -> ${mapping.cacheKey}`);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Cleanup expired responseId mappings
|
|
120
|
+
*/
|
|
121
|
+
cleanupExpiredMappings() {
|
|
122
|
+
const now = Date.now();
|
|
123
|
+
const maxAge = this.cacheTTL.sources * 1000;
|
|
124
|
+
let cleaned = 0;
|
|
125
|
+
for (const [responseId, mapping] of this.responseIdMap.entries()) {
|
|
126
|
+
if (now - mapping.createdAt > maxAge) {
|
|
127
|
+
this.responseIdMap.delete(responseId);
|
|
128
|
+
cleaned++;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (cleaned > 0) {
|
|
132
|
+
console.log(`[SourceService] Cleaned up ${cleaned} expired responseId mapping(s)`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Fetch results from all providers concurrently
|
|
137
|
+
*/
|
|
138
|
+
async fetchFromProviders(type, media) {
|
|
139
|
+
const providers = this.registry.getProviders();
|
|
140
|
+
if (providers.length === 0) {
|
|
141
|
+
console.warn('[SourceService] No providers registered');
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
// Filter providers by capability
|
|
145
|
+
const supportedProviders = providers.filter((p) => p.capabilities.supportedContentTypes.includes(type === 'movie' ? 'movies' : 'tv'));
|
|
146
|
+
console.log(`[SourceService] Fetching from ${supportedProviders.length} provider(s) ` + `(${providers.length - supportedProviders.length} filtered out)`);
|
|
147
|
+
const promises = supportedProviders.map(async (provider) => {
|
|
148
|
+
try {
|
|
149
|
+
const startTime = Date.now();
|
|
150
|
+
let result;
|
|
151
|
+
if (type === 'movie') {
|
|
152
|
+
result = await provider.getMovieSources(media);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
result = await provider.getTVSources(media);
|
|
156
|
+
}
|
|
157
|
+
const duration = Date.now() - startTime;
|
|
158
|
+
console.log(`[SourceService] Provider '${provider.name}' returned ${result.sources.length} source(s) in ${duration}ms`);
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
console.error(`[SourceService] Provider '${provider.name}' failed:`, error);
|
|
163
|
+
return {
|
|
164
|
+
sources: [],
|
|
165
|
+
subtitles: [],
|
|
166
|
+
diagnostics: [
|
|
167
|
+
{
|
|
168
|
+
code: 'PROVIDER_ERROR',
|
|
169
|
+
message: `Provider '${provider.name}' failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
170
|
+
field: '',
|
|
171
|
+
severity: 'error',
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
const results = await Promise.allSettled(promises);
|
|
178
|
+
return results.filter((r) => r.status === 'fulfilled').map((r) => r.value);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Build final response from provider results
|
|
182
|
+
*/
|
|
183
|
+
buildResponse(results) {
|
|
184
|
+
const allSources = results.flatMap((r) => r.sources);
|
|
185
|
+
const allSubtitles = results.flatMap((r) => r.subtitles);
|
|
186
|
+
const allDiagnostics = results.flatMap((r) => r.diagnostics);
|
|
187
|
+
const failedProviders = results.filter((r) => r.sources.length === 0).length;
|
|
188
|
+
if (failedProviders > 0 && allSources.length > 0) {
|
|
189
|
+
allDiagnostics.push({
|
|
190
|
+
code: 'PARTIAL_SCRAPE',
|
|
191
|
+
message: `Only ${results.length - failedProviders} of ${results.length} providers returned results`,
|
|
192
|
+
field: '',
|
|
193
|
+
severity: 'warning',
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
const expiresAt = new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString();
|
|
197
|
+
return {
|
|
198
|
+
responseId: (0, uuid_1.v4)(),
|
|
199
|
+
expiresAt,
|
|
200
|
+
sources: allSources,
|
|
201
|
+
subtitles: allSubtitles,
|
|
202
|
+
diagnostics: allDiagnostics,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get mapping info for debugging
|
|
207
|
+
*/
|
|
208
|
+
getMappingInfo(responseId) {
|
|
209
|
+
return this.responseIdMap.get(responseId);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get all mappings count
|
|
213
|
+
*/
|
|
214
|
+
getMappingsCount() {
|
|
215
|
+
return this.responseIdMap.size;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Cleanup on service shutdown
|
|
219
|
+
*/
|
|
220
|
+
destroy() {
|
|
221
|
+
this.responseIdMap.clear();
|
|
222
|
+
console.log('[SourceService] Destroyed and cleared all mappings');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
exports.SourceService = SourceService;
|
|
226
|
+
//# sourceMappingURL=source.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.service.js","sourceRoot":"","sources":["../../src/services/source.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { CacheService } from '../core/cache';
|
|
2
|
+
import { ProviderMediaObject } from '../core/types';
|
|
3
|
+
export interface TMDBMovieResponse {
|
|
4
|
+
id: number;
|
|
5
|
+
title: string;
|
|
6
|
+
release_date: string;
|
|
7
|
+
status: 'Rumored' | 'Planned' | 'In Production' | 'Post Production' | 'Released' | 'Canceled';
|
|
8
|
+
adult: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface TMDBTVResponse {
|
|
11
|
+
id: number;
|
|
12
|
+
name: string;
|
|
13
|
+
first_air_date: string;
|
|
14
|
+
status: 'Returning Series' | 'Planned' | 'In Production' | 'Ended' | 'Canceled' | 'Pilot';
|
|
15
|
+
adult: boolean;
|
|
16
|
+
number_of_seasons: number;
|
|
17
|
+
seasons: Array<{
|
|
18
|
+
season_number: number;
|
|
19
|
+
episode_count: number;
|
|
20
|
+
air_date: string;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export interface TMDBSeasonResponse {
|
|
24
|
+
id: number;
|
|
25
|
+
season_number: number;
|
|
26
|
+
air_date: string;
|
|
27
|
+
episodes: Array<{
|
|
28
|
+
episode_number: number;
|
|
29
|
+
air_date: string | null;
|
|
30
|
+
name: string;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
export interface TMDBValidationResult {
|
|
34
|
+
exists: boolean;
|
|
35
|
+
released: boolean;
|
|
36
|
+
releaseDate?: string;
|
|
37
|
+
title?: string;
|
|
38
|
+
message?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare class TMDBService {
|
|
41
|
+
private cache;
|
|
42
|
+
private readonly baseUrl;
|
|
43
|
+
private readonly apiKey;
|
|
44
|
+
private readonly cacheTTL;
|
|
45
|
+
constructor(apiKey: string, cache: CacheService, cacheTTL?: number);
|
|
46
|
+
/**
|
|
47
|
+
* Validate a movie exists and has been released
|
|
48
|
+
*/
|
|
49
|
+
validateMovie(tmdbId: string): Promise<TMDBValidationResult>;
|
|
50
|
+
/**
|
|
51
|
+
* Validate a TV show exists and has aired
|
|
52
|
+
*/
|
|
53
|
+
validateTV(tmdbId: string): Promise<TMDBValidationResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Validate a specific TV episode exists and has aired
|
|
56
|
+
*/
|
|
57
|
+
validateTVEpisode(tmdbId: string, season: number, episode: number): Promise<TMDBValidationResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Get media object for provider
|
|
60
|
+
*/
|
|
61
|
+
getMediaObject(type: 'movie' | 'tv', tmdbId: string, season?: number, episode?: number): Promise<ProviderMediaObject>;
|
|
62
|
+
/**
|
|
63
|
+
* Get IMDB ID if available (cached)
|
|
64
|
+
*/
|
|
65
|
+
getImdbId(tmdbId: string, type: 'movie' | 'tv'): Promise<string | undefined>;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=tmdb.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmdb.service.d.ts","sourceRoot":"","sources":["../../src/services/tmdb.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,eAAe,GAAG,iBAAiB,GAAG,UAAU,GAAG,UAAU,CAAC;IAC9F,KAAK,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,kBAAkB,GAAG,SAAS,GAAG,eAAe,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;IAC1F,KAAK,EAAE,OAAO,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,KAAK,CAAC;QACX,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACN;AAED,MAAM,WAAW,kBAAkB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC;QACZ,cAAc,EAAE,MAAM,CAAC;QACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACN;AAED,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,WAAW;IAOhB,OAAO,CAAC,KAAK;IANjB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAG9B,MAAM,EAAE,MAAM,EACN,KAAK,EAAE,YAAY,EAC3B,QAAQ,SAAQ;IASpB;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAiDlE;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAiD/D;;OAEG;IACG,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAoFvG;;OAEG;IACG,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAuB3H;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAwBrF"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TMDBService = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
class TMDBService {
|
|
9
|
+
cache;
|
|
10
|
+
baseUrl = 'https://api.themoviedb.org/3';
|
|
11
|
+
apiKey;
|
|
12
|
+
cacheTTL;
|
|
13
|
+
constructor(apiKey, cache, cacheTTL = 86400 // 24 hours default
|
|
14
|
+
) {
|
|
15
|
+
this.cache = cache;
|
|
16
|
+
if (!apiKey || apiKey === 'your_tmdb_api_key_here') {
|
|
17
|
+
throw new Error('TMDB_API_KEY is not configured. Get one at https://www.themoviedb.org/settings/api');
|
|
18
|
+
}
|
|
19
|
+
this.apiKey = apiKey;
|
|
20
|
+
this.cacheTTL = cacheTTL;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Validate a movie exists and has been released
|
|
24
|
+
*/
|
|
25
|
+
async validateMovie(tmdbId) {
|
|
26
|
+
const cacheKey = `tmdb:movie:${tmdbId}`;
|
|
27
|
+
// Check cache first
|
|
28
|
+
const cached = await this.cache.get(cacheKey);
|
|
29
|
+
if (cached) {
|
|
30
|
+
return cached;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const response = await axios_1.default.get(`${this.baseUrl}/movie/${tmdbId}`, {
|
|
34
|
+
params: { api_key: this.apiKey },
|
|
35
|
+
timeout: 5000,
|
|
36
|
+
});
|
|
37
|
+
const movie = response.data;
|
|
38
|
+
const releaseDate = new Date(movie.release_date);
|
|
39
|
+
const now = new Date();
|
|
40
|
+
const released = releaseDate <= now && movie.status === 'Released';
|
|
41
|
+
const result = {
|
|
42
|
+
exists: true,
|
|
43
|
+
released,
|
|
44
|
+
releaseDate: movie.release_date,
|
|
45
|
+
title: movie.title,
|
|
46
|
+
message: released ? undefined : `Movie "${movie.title}" has not been released yet (release date: ${movie.release_date})`,
|
|
47
|
+
};
|
|
48
|
+
// Cache the result
|
|
49
|
+
await this.cache.set(cacheKey, result, this.cacheTTL);
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (axios_1.default.isAxiosError(error) && error.response?.status === 404) {
|
|
54
|
+
const result = {
|
|
55
|
+
exists: false,
|
|
56
|
+
released: false,
|
|
57
|
+
message: `Movie with TMDB ID ${tmdbId} does not exist`,
|
|
58
|
+
};
|
|
59
|
+
// Cache 404s for shorter time (1 hour)
|
|
60
|
+
await this.cache.set(cacheKey, result, 3600);
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
console.error('[TMDBService] Error validating movie:', error);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validate a TV show exists and has aired
|
|
69
|
+
*/
|
|
70
|
+
async validateTV(tmdbId) {
|
|
71
|
+
const cacheKey = `tmdb:tv:${tmdbId}`;
|
|
72
|
+
// Check cache first
|
|
73
|
+
const cached = await this.cache.get(cacheKey);
|
|
74
|
+
if (cached) {
|
|
75
|
+
return cached;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const response = await axios_1.default.get(`${this.baseUrl}/tv/${tmdbId}`, {
|
|
79
|
+
params: { api_key: this.apiKey },
|
|
80
|
+
timeout: 5000,
|
|
81
|
+
});
|
|
82
|
+
const tv = response.data;
|
|
83
|
+
const firstAirDate = new Date(tv.first_air_date);
|
|
84
|
+
const now = new Date();
|
|
85
|
+
const aired = firstAirDate <= now;
|
|
86
|
+
const result = {
|
|
87
|
+
exists: true,
|
|
88
|
+
released: aired,
|
|
89
|
+
releaseDate: tv.first_air_date,
|
|
90
|
+
title: tv.name,
|
|
91
|
+
message: aired ? undefined : `TV show "${tv.name}" has not aired yet (first air date: ${tv.first_air_date})`,
|
|
92
|
+
};
|
|
93
|
+
// Cache the result
|
|
94
|
+
await this.cache.set(cacheKey, result, this.cacheTTL);
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (axios_1.default.isAxiosError(error) && error.response?.status === 404) {
|
|
99
|
+
const result = {
|
|
100
|
+
exists: false,
|
|
101
|
+
released: false,
|
|
102
|
+
message: `TV show with TMDB ID ${tmdbId} does not exist`,
|
|
103
|
+
};
|
|
104
|
+
// Cache 404s for shorter time (1 hour)
|
|
105
|
+
await this.cache.set(cacheKey, result, 3600);
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
console.error('[TMDBService] Error validating TV show:', error);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Validate a specific TV episode exists and has aired
|
|
114
|
+
*/
|
|
115
|
+
async validateTVEpisode(tmdbId, season, episode) {
|
|
116
|
+
const cacheKey = `tmdb:tv:${tmdbId}:s${season}:e${episode}`;
|
|
117
|
+
// Check cache first
|
|
118
|
+
const cached = await this.cache.get(cacheKey);
|
|
119
|
+
if (cached) {
|
|
120
|
+
return cached;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
// First validate the TV show exists
|
|
124
|
+
const tvResult = await this.validateTV(tmdbId);
|
|
125
|
+
if (!tvResult.exists || !tvResult.released) {
|
|
126
|
+
return tvResult;
|
|
127
|
+
}
|
|
128
|
+
// Get season details
|
|
129
|
+
const response = await axios_1.default.get(`${this.baseUrl}/tv/${tmdbId}/season/${season}`, {
|
|
130
|
+
params: { api_key: this.apiKey },
|
|
131
|
+
timeout: 5000,
|
|
132
|
+
});
|
|
133
|
+
const seasonData = response.data;
|
|
134
|
+
const episodeData = seasonData.episodes.find((ep) => ep.episode_number === episode);
|
|
135
|
+
if (!episodeData) {
|
|
136
|
+
const result = {
|
|
137
|
+
exists: false,
|
|
138
|
+
released: false,
|
|
139
|
+
message: `Episode ${episode} does not exist in season ${season}`,
|
|
140
|
+
};
|
|
141
|
+
// Cache for 1 hour
|
|
142
|
+
await this.cache.set(cacheKey, result, 3600);
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
// Check if episode has aired
|
|
146
|
+
if (!episodeData.air_date) {
|
|
147
|
+
const result = {
|
|
148
|
+
exists: true,
|
|
149
|
+
released: false,
|
|
150
|
+
title: episodeData.name,
|
|
151
|
+
message: `Episode "${episodeData.name}" (S${season}E${episode}) does not have an air date yet`,
|
|
152
|
+
};
|
|
153
|
+
// Cache for 1 hour (might get updated soon)
|
|
154
|
+
await this.cache.set(cacheKey, result, 3600);
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
const airDate = new Date(episodeData.air_date);
|
|
158
|
+
const now = new Date();
|
|
159
|
+
const aired = airDate <= now;
|
|
160
|
+
const result = {
|
|
161
|
+
exists: true,
|
|
162
|
+
released: aired,
|
|
163
|
+
releaseDate: episodeData.air_date,
|
|
164
|
+
title: episodeData.name,
|
|
165
|
+
message: aired ? undefined : `Episode "${episodeData.name}" (S${season}E${episode}) has not aired yet (air date: ${episodeData.air_date})`,
|
|
166
|
+
};
|
|
167
|
+
// Cache the result
|
|
168
|
+
await this.cache.set(cacheKey, result, this.cacheTTL);
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
if (axios_1.default.isAxiosError(error) && error.response?.status === 404) {
|
|
173
|
+
const result = {
|
|
174
|
+
exists: false,
|
|
175
|
+
released: false,
|
|
176
|
+
message: `Season ${season} does not exist for TV show ${tmdbId}`,
|
|
177
|
+
};
|
|
178
|
+
// Cache 404s for 1 hour
|
|
179
|
+
await this.cache.set(cacheKey, result, 3600);
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
console.error('[TMDBService] Error validating episode:', error);
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get media object for provider
|
|
188
|
+
*/
|
|
189
|
+
async getMediaObject(type, tmdbId, season, episode) {
|
|
190
|
+
if (type === 'movie') {
|
|
191
|
+
const validation = await this.validateMovie(tmdbId);
|
|
192
|
+
return {
|
|
193
|
+
type: 'movie',
|
|
194
|
+
tmdbId,
|
|
195
|
+
title: validation.title,
|
|
196
|
+
releaseYear: validation.releaseDate ? new Date(validation.releaseDate).getFullYear().toString() : undefined,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
const validation = await this.validateTVEpisode(tmdbId, season, episode);
|
|
201
|
+
return {
|
|
202
|
+
type: 'tv',
|
|
203
|
+
tmdbId,
|
|
204
|
+
s: season,
|
|
205
|
+
e: episode,
|
|
206
|
+
title: validation.title,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Get IMDB ID if available (cached)
|
|
212
|
+
*/
|
|
213
|
+
async getImdbId(tmdbId, type) {
|
|
214
|
+
const cacheKey = `tmdb:imdb:${type}:${tmdbId}`;
|
|
215
|
+
const cached = await this.cache.get(cacheKey);
|
|
216
|
+
if (cached)
|
|
217
|
+
return cached;
|
|
218
|
+
try {
|
|
219
|
+
const endpoint = type === 'movie' ? 'movie' : 'tv';
|
|
220
|
+
const response = await axios_1.default.get(`${this.baseUrl}/${endpoint}/${tmdbId}/external_ids`, {
|
|
221
|
+
params: { api_key: this.apiKey },
|
|
222
|
+
timeout: 5000,
|
|
223
|
+
});
|
|
224
|
+
const imdbId = response.data.imdb_id;
|
|
225
|
+
if (imdbId) {
|
|
226
|
+
await this.cache.set(cacheKey, imdbId, this.cacheTTL);
|
|
227
|
+
}
|
|
228
|
+
return imdbId;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
console.error('[TMDBService] Failed to get IMDB ID:', error);
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
exports.TMDBService = TMDBService;
|
|
237
|
+
//# sourceMappingURL=tmdb.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmdb.service.js","sourceRoot":"","sources":["../../src/services/tmdb.service.ts"],"names":[],"mappings":"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"}
|