@media-engine/providers 1.0.0 → 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.
Files changed (68) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +66 -135
  3. package/README.ru.md +72 -139
  4. package/dist/filmix-streaming/client.d.ts +36 -0
  5. package/dist/filmix-streaming/client.js +241 -0
  6. package/dist/filmix-streaming/client.js.map +1 -0
  7. package/dist/filmix-streaming/config.d.ts +42 -0
  8. package/dist/filmix-streaming/config.js +93 -0
  9. package/dist/filmix-streaming/config.js.map +1 -0
  10. package/dist/filmix-streaming/index.d.ts +4 -0
  11. package/dist/filmix-streaming/index.js +48 -0
  12. package/dist/filmix-streaming/index.js.map +1 -0
  13. package/dist/filmix-streaming/mapping.d.ts +3 -0
  14. package/dist/filmix-streaming/mapping.js +109 -0
  15. package/dist/filmix-streaming/mapping.js.map +1 -0
  16. package/dist/filmix-streaming/matching.d.ts +4 -0
  17. package/dist/filmix-streaming/matching.js +41 -0
  18. package/dist/filmix-streaming/matching.js.map +1 -0
  19. package/dist/imdb-dataset/sqlite-import.js +5 -2
  20. package/dist/imdb-dataset/sqlite-import.js.map +1 -1
  21. package/dist/index.d.ts +4 -0
  22. package/dist/index.js +4 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/package-version.d.ts +2 -2
  25. package/dist/package-version.js +1 -1
  26. package/dist/rutube-streaming/client.d.ts +18 -0
  27. package/dist/rutube-streaming/client.js +106 -0
  28. package/dist/rutube-streaming/client.js.map +1 -0
  29. package/dist/rutube-streaming/config.d.ts +26 -0
  30. package/dist/rutube-streaming/config.js +57 -0
  31. package/dist/rutube-streaming/config.js.map +1 -0
  32. package/dist/rutube-streaming/index.d.ts +4 -0
  33. package/dist/rutube-streaming/index.js +38 -0
  34. package/dist/rutube-streaming/index.js.map +1 -0
  35. package/dist/rutube-streaming/mapping.d.ts +4 -0
  36. package/dist/rutube-streaming/mapping.js +32 -0
  37. package/dist/rutube-streaming/mapping.js.map +1 -0
  38. package/dist/rutube-streaming/matching.d.ts +2 -0
  39. package/dist/rutube-streaming/matching.js +37 -0
  40. package/dist/rutube-streaming/matching.js.map +1 -0
  41. package/dist/shared/safe-fetch.d.ts +2 -0
  42. package/dist/shared/safe-fetch.js +29 -3
  43. package/dist/shared/safe-fetch.js.map +1 -1
  44. package/dist/veoveo-streaming/client.d.ts +28 -0
  45. package/dist/veoveo-streaming/client.js +212 -0
  46. package/dist/veoveo-streaming/client.js.map +1 -0
  47. package/dist/veoveo-streaming/config.d.ts +32 -0
  48. package/dist/veoveo-streaming/config.js +67 -0
  49. package/dist/veoveo-streaming/config.js.map +1 -0
  50. package/dist/veoveo-streaming/index.d.ts +4 -0
  51. package/dist/veoveo-streaming/index.js +46 -0
  52. package/dist/veoveo-streaming/index.js.map +1 -0
  53. package/dist/veoveo-streaming/mapping.d.ts +3 -0
  54. package/dist/veoveo-streaming/mapping.js +121 -0
  55. package/dist/veoveo-streaming/mapping.js.map +1 -0
  56. package/dist/videohub-streaming/client.d.ts +33 -0
  57. package/dist/videohub-streaming/client.js +208 -0
  58. package/dist/videohub-streaming/client.js.map +1 -0
  59. package/dist/videohub-streaming/config.d.ts +34 -0
  60. package/dist/videohub-streaming/config.js +73 -0
  61. package/dist/videohub-streaming/config.js.map +1 -0
  62. package/dist/videohub-streaming/index.d.ts +4 -0
  63. package/dist/videohub-streaming/index.js +53 -0
  64. package/dist/videohub-streaming/index.js.map +1 -0
  65. package/dist/videohub-streaming/mapping.d.ts +3 -0
  66. package/dist/videohub-streaming/mapping.js +84 -0
  67. package/dist/videohub-streaming/mapping.js.map +1 -0
  68. package/package.json +7 -2
@@ -0,0 +1,36 @@
1
+ import { type ProviderContext } from "@media-engine/core";
2
+ import type { FilmixStreamingConfig } from "./config.js";
3
+ export interface FilmixPostSummary {
4
+ id: number;
5
+ title: string;
6
+ originalTitle?: string;
7
+ year: number;
8
+ section: number;
9
+ altName?: string;
10
+ type: "movie" | "series";
11
+ }
12
+ export interface FilmixStream {
13
+ translation: string;
14
+ link: string;
15
+ qualities: number[];
16
+ }
17
+ export interface FilmixEpisode extends FilmixStream {
18
+ number: number;
19
+ }
20
+ export interface FilmixSeason {
21
+ number: number;
22
+ translations: Array<{
23
+ name: string;
24
+ episodes: FilmixEpisode[];
25
+ }>;
26
+ }
27
+ export interface FilmixPost extends FilmixPostSummary {
28
+ movies: FilmixStream[];
29
+ seasons: FilmixSeason[];
30
+ }
31
+ export declare function searchFilmixPosts(config: FilmixStreamingConfig, title: string, context: ProviderContext): Promise<FilmixPostSummary[]>;
32
+ export declare function loadFilmixPost(config: FilmixStreamingConfig, postId: number, context: ProviderContext): Promise<FilmixPost | null>;
33
+ export declare function parseFilmixSearchResponse(provider: string, value: unknown, limit: number): FilmixPostSummary[];
34
+ export declare function parseFilmixPost(provider: string, value: unknown, limits: Pick<FilmixStreamingConfig, "seasonLimit" | "translationLimit" | "episodeLimit">): FilmixPost;
35
+ export declare function createFilmixSourceUrl(config: Pick<FilmixStreamingConfig, "siteBaseUrl">, post: FilmixPostSummary): string | undefined;
36
+ export declare function resolveFilmixQualityLink(link: string, quality: number): string | undefined;
@@ -0,0 +1,241 @@
1
+ import { ProviderError } from "@media-engine/core";
2
+ import { fetchJson, getProviderHttpStatus } from "../shared/index.js";
3
+ const SERIES_SECTION = 7;
4
+ const BRACKETED_QUALITIES = /_\[([0-9,]+)\]\.mp4(?=$|[?#])/u;
5
+ const PRINTF_QUALITY = /_%s\.mp4(?=$|[?#])/u;
6
+ export async function searchFilmixPosts(config, title, context) {
7
+ const url = createApiUrl(config, "search");
8
+ url.searchParams.set("story", title);
9
+ const payload = await fetchJson({
10
+ provider: config.name,
11
+ url,
12
+ context,
13
+ fetch: config.fetch,
14
+ rateLimitGate: config.rateLimitGate,
15
+ maxResponseBytes: config.maxResponseBytes,
16
+ init: { headers: createHeaders(config) },
17
+ });
18
+ return parseFilmixSearchResponse(config.name, payload, config.searchResultLimit);
19
+ }
20
+ export async function loadFilmixPost(config, postId, context) {
21
+ const url = createApiUrl(config, `post/${postId}`);
22
+ try {
23
+ const payload = await fetchJson({
24
+ provider: config.name,
25
+ url,
26
+ context,
27
+ fetch: config.fetch,
28
+ rateLimitGate: config.rateLimitGate,
29
+ maxResponseBytes: config.maxResponseBytes,
30
+ init: { headers: createHeaders(config) },
31
+ });
32
+ return parseFilmixPost(config.name, payload, config);
33
+ }
34
+ catch (error) {
35
+ if (getProviderHttpStatus(error) === 404)
36
+ return null;
37
+ throw error;
38
+ }
39
+ }
40
+ export function parseFilmixSearchResponse(provider, value, limit) {
41
+ if (!Array.isArray(value))
42
+ throw invalidResponse(provider);
43
+ const posts = value.slice(0, limit).flatMap((entry) => {
44
+ const post = parsePostSummary(entry);
45
+ return post ? [post] : [];
46
+ });
47
+ if (value.length > 0 && posts.length === 0)
48
+ throw invalidResponse(provider);
49
+ return posts;
50
+ }
51
+ export function parseFilmixPost(provider, value, limits) {
52
+ const summary = parsePostSummary(value);
53
+ if (!summary || !isRecord(value) || !isRecord(value.player_links)) {
54
+ throw invalidResponse(provider);
55
+ }
56
+ const movieValue = value.player_links.movie;
57
+ const playlistValue = value.player_links.playlist;
58
+ const movies = parseMovies(movieValue, limits.translationLimit);
59
+ const seasons = parseSeasons(playlistValue, limits.seasonLimit, limits.translationLimit, limits.episodeLimit);
60
+ if (summary.type === "movie" && movieValue !== undefined && !Array.isArray(movieValue)) {
61
+ throw invalidResponse(provider);
62
+ }
63
+ if (summary.type === "series" && playlistValue !== undefined && !isRecord(playlistValue)) {
64
+ throw invalidResponse(provider);
65
+ }
66
+ if ((Array.isArray(movieValue) && movieValue.length > 0 && movies.length === 0) ||
67
+ (isRecord(playlistValue) && Object.keys(playlistValue).length > 0 && seasons.length === 0)) {
68
+ throw invalidResponse(provider);
69
+ }
70
+ return { ...summary, movies, seasons };
71
+ }
72
+ export function createFilmixSourceUrl(config, post) {
73
+ if (!post.altName || !/^[\p{L}\p{N}_-]{1,300}$/u.test(post.altName))
74
+ return undefined;
75
+ const kind = post.type === "series" ? "seria" : "film";
76
+ return new URL(`${kind}/${post.id}-${post.altName}.html`, `${config.siteBaseUrl}/`).href;
77
+ }
78
+ function createApiUrl(config, path) {
79
+ const url = new URL(`${config.baseUrl}/${path}`);
80
+ url.searchParams.set("app_lang", "ru_RU");
81
+ url.searchParams.set("user_dev_id", config.deviceId);
82
+ url.searchParams.set("user_dev_name", "media-engine");
83
+ url.searchParams.set("user_dev_vendor", "media-engine");
84
+ url.searchParams.set("user_dev_os", "node");
85
+ url.searchParams.set("user_dev_apk", "2.2.13");
86
+ if (config.token)
87
+ url.searchParams.set("user_dev_token", config.token);
88
+ return url;
89
+ }
90
+ function parsePostSummary(value) {
91
+ if (!isRecord(value))
92
+ return undefined;
93
+ const id = readInteger(value.id, 1, Number.MAX_SAFE_INTEGER);
94
+ const title = readRequiredString(value.title, 500);
95
+ const originalTitle = readNullableString(value.original_title, 500);
96
+ const year = readInteger(value.year, 1_800, 3_000);
97
+ const section = readInteger(value.section, 0, 10_000);
98
+ const altName = readNullableString(value.alt_name, 500);
99
+ if (id === undefined ||
100
+ !title ||
101
+ originalTitle === false ||
102
+ year === undefined ||
103
+ section === undefined ||
104
+ altName === false) {
105
+ return undefined;
106
+ }
107
+ return {
108
+ id,
109
+ title,
110
+ ...(originalTitle ? { originalTitle } : {}),
111
+ year,
112
+ section,
113
+ ...(altName ? { altName } : {}),
114
+ type: section === SERIES_SECTION ? "series" : "movie",
115
+ };
116
+ }
117
+ function parseMovies(value, limit) {
118
+ if (!Array.isArray(value))
119
+ return [];
120
+ return value.slice(0, limit).flatMap((entry) => {
121
+ const stream = parseStream(entry, undefined);
122
+ return stream ? [stream] : [];
123
+ });
124
+ }
125
+ function parseSeasons(value, seasonLimit, translationLimit, episodeLimit) {
126
+ if (!isRecord(value))
127
+ return [];
128
+ return Object.entries(value)
129
+ .slice(0, seasonLimit)
130
+ .flatMap(([seasonKey, seasonValue]) => {
131
+ const number = readPositiveKey(seasonKey);
132
+ if (number === undefined || !isRecord(seasonValue))
133
+ return [];
134
+ const translations = Object.entries(seasonValue)
135
+ .slice(0, translationLimit)
136
+ .flatMap(([name, episodesValue]) => {
137
+ const translation = name.trim().slice(0, 500);
138
+ if (!translation)
139
+ return [];
140
+ const episodes = parseEpisodes(episodesValue, translation, episodeLimit);
141
+ return episodes.length > 0 ? [{ name: translation, episodes }] : [];
142
+ });
143
+ return translations.length > 0 ? [{ number, translations }] : [];
144
+ })
145
+ .sort((left, right) => left.number - right.number);
146
+ }
147
+ function parseEpisodes(value, translation, limit) {
148
+ if (Array.isArray(value)) {
149
+ return value.slice(0, limit).flatMap((entry, index) => {
150
+ const stream = parseStream(entry, translation);
151
+ return stream ? [{ ...stream, number: index + 1 }] : [];
152
+ });
153
+ }
154
+ if (!isRecord(value))
155
+ return [];
156
+ return Object.entries(value)
157
+ .slice(0, limit)
158
+ .flatMap(([episodeKey, entry]) => {
159
+ const number = readPositiveKey(episodeKey);
160
+ const stream = parseStream(entry, translation);
161
+ return number !== undefined && stream ? [{ ...stream, number }] : [];
162
+ })
163
+ .sort((left, right) => left.number - right.number);
164
+ }
165
+ function parseStream(value, fallbackTranslation) {
166
+ if (!isRecord(value))
167
+ return undefined;
168
+ const translationValue = readNullableString(value.translation, 500);
169
+ const translation = translationValue || fallbackTranslation;
170
+ const link = readRequiredString(value.link, 8_192);
171
+ const explicitQualities = parseExplicitQualities(value.qualities);
172
+ if (translationValue === false || !translation || !link || explicitQualities === false) {
173
+ return undefined;
174
+ }
175
+ const qualities = explicitQualities ?? parseLinkQualities(link);
176
+ return { translation, link, qualities };
177
+ }
178
+ function parseExplicitQualities(value) {
179
+ if (value === undefined || value === null)
180
+ return undefined;
181
+ if (!Array.isArray(value))
182
+ return false;
183
+ const qualities = value.flatMap((quality) => {
184
+ const parsed = readInteger(quality, 1, 10_000);
185
+ return parsed === undefined ? [] : [parsed];
186
+ });
187
+ return value.length > 0 && qualities.length === 0 ? false : [...new Set(qualities)];
188
+ }
189
+ function parseLinkQualities(link) {
190
+ const match = BRACKETED_QUALITIES.exec(link);
191
+ if (!match?.[1])
192
+ return [];
193
+ return [
194
+ ...new Set(match[1].split(",").flatMap((value) => {
195
+ const parsed = Number(value);
196
+ return Number.isSafeInteger(parsed) && parsed > 0 ? [parsed] : [];
197
+ })),
198
+ ];
199
+ }
200
+ export function resolveFilmixQualityLink(link, quality) {
201
+ if (!Number.isSafeInteger(quality) || quality <= 0)
202
+ return undefined;
203
+ if (BRACKETED_QUALITIES.test(link)) {
204
+ return link.replace(BRACKETED_QUALITIES, `_${quality}.mp4`);
205
+ }
206
+ if (PRINTF_QUALITY.test(link)) {
207
+ return link.replace(PRINTF_QUALITY, `_${quality}.mp4`);
208
+ }
209
+ return undefined;
210
+ }
211
+ function createHeaders(config) {
212
+ return { Accept: "application/json", "User-Agent": config.userAgent };
213
+ }
214
+ function readInteger(value, min, max) {
215
+ return Number.isSafeInteger(value) && Number(value) >= min && Number(value) <= max
216
+ ? Number(value)
217
+ : undefined;
218
+ }
219
+ function readPositiveKey(value) {
220
+ return /^(?:[1-9]\d*)$/u.test(value) ? readInteger(Number(value), 1, 100_000) : undefined;
221
+ }
222
+ function readRequiredString(value, maxLength) {
223
+ return typeof value === "string" ? value.trim().slice(0, maxLength) || undefined : undefined;
224
+ }
225
+ function readNullableString(value, maxLength) {
226
+ if (value === null || value === undefined)
227
+ return undefined;
228
+ return typeof value === "string" ? value.trim().slice(0, maxLength) || undefined : false;
229
+ }
230
+ function isRecord(value) {
231
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
232
+ }
233
+ function invalidResponse(provider) {
234
+ return new ProviderError({
235
+ provider,
236
+ code: "PROVIDER_INVALID_RESPONSE",
237
+ message: `Provider "${provider}" returned an invalid Filmix response.`,
238
+ retryable: false,
239
+ });
240
+ }
241
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/filmix-streaming/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { StreamingProviderCapabilities } from "@media-engine/core";
2
+ import { ProviderRateLimitGate, type ProviderFetch } from "../shared/index.js";
3
+ export interface FilmixStreamingProviderOptions {
4
+ name?: string;
5
+ version?: string;
6
+ baseUrl?: string;
7
+ siteBaseUrl?: string;
8
+ fetch?: ProviderFetch;
9
+ maxResponseBytes?: number;
10
+ searchResultLimit?: number;
11
+ seasonLimit?: number;
12
+ translationLimit?: number;
13
+ episodeLimit?: number;
14
+ linkTtlMs?: number;
15
+ deviceId?: string;
16
+ /** User-owned Filmix device token. Authenticated requests require an HTTPS API endpoint. */
17
+ token?: string;
18
+ /** Explicit local-only opt-in for Filmix endpoints that transmit the token over plain HTTP. */
19
+ allowInsecureHttpToken?: boolean;
20
+ userAgent?: string;
21
+ now?: () => number;
22
+ }
23
+ export interface FilmixStreamingConfig {
24
+ name: string;
25
+ baseUrl: string;
26
+ siteBaseUrl: string;
27
+ fetch: ProviderFetch;
28
+ rateLimitGate: ProviderRateLimitGate;
29
+ maxResponseBytes: number;
30
+ searchResultLimit: number;
31
+ seasonLimit: number;
32
+ translationLimit: number;
33
+ episodeLimit: number;
34
+ linkTtlMs: number;
35
+ deviceId: string;
36
+ token?: string;
37
+ maxQuality: 480 | 720;
38
+ userAgent: string;
39
+ now: () => number;
40
+ }
41
+ export declare function createFilmixConfig(options: FilmixStreamingProviderOptions): FilmixStreamingConfig;
42
+ export declare function createFilmixCapabilities(): StreamingProviderCapabilities;
@@ -0,0 +1,93 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { MEDIA_ENGINE_DEFAULT_USER_AGENT } from "../package-version.js";
3
+ import { ProviderRateLimitGate } from "../shared/index.js";
4
+ import { resolveBoundedIntegerOption } from "../shared/options.js";
5
+ import { createHardenedProviderFetch } from "../shared/safe-fetch.js";
6
+ const DEFAULT_PROVIDER_NAME = "filmix-streaming";
7
+ const DEFAULT_BASE_URL = "http://filmixapp.cyou/api/v2";
8
+ const DEFAULT_SITE_BASE_URL = "https://filmix.my";
9
+ const DEFAULT_MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
10
+ const DEFAULT_SEARCH_RESULT_LIMIT = 50;
11
+ const DEFAULT_SEASON_LIMIT = 100;
12
+ const DEFAULT_TRANSLATION_LIMIT = 50;
13
+ const DEFAULT_EPISODE_LIMIT = 500;
14
+ const DEFAULT_LINK_TTL_MS = 15 * 60_000;
15
+ export function createFilmixConfig(options) {
16
+ const name = normalizeProviderName(options.name ?? DEFAULT_PROVIDER_NAME);
17
+ const baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_BASE_URL, "baseUrl");
18
+ const token = normalizeToken(options.token);
19
+ if (token && new URL(baseUrl).protocol !== "https:" && options.allowInsecureHttpToken !== true) {
20
+ throw new TypeError("Filmix streaming token requires an HTTPS baseUrl unless allowInsecureHttpToken is explicitly enabled.");
21
+ }
22
+ return {
23
+ name,
24
+ baseUrl,
25
+ siteBaseUrl: normalizeBaseUrl(options.siteBaseUrl ?? DEFAULT_SITE_BASE_URL, "siteBaseUrl"),
26
+ fetch: options.fetch ?? createHardenedProviderFetch({ provider: name, maxRedirects: 2 }),
27
+ rateLimitGate: new ProviderRateLimitGate(),
28
+ maxResponseBytes: resolveBoundedIntegerOption(options.maxResponseBytes, DEFAULT_MAX_RESPONSE_BYTES, "Filmix streaming maxResponseBytes", 1_024, 16 * 1024 * 1024),
29
+ searchResultLimit: resolveBoundedIntegerOption(options.searchResultLimit, DEFAULT_SEARCH_RESULT_LIMIT, "Filmix streaming searchResultLimit", 1, 100),
30
+ seasonLimit: resolveBoundedIntegerOption(options.seasonLimit, DEFAULT_SEASON_LIMIT, "Filmix streaming seasonLimit", 1, 500),
31
+ translationLimit: resolveBoundedIntegerOption(options.translationLimit, DEFAULT_TRANSLATION_LIMIT, "Filmix streaming translationLimit", 1, 100),
32
+ episodeLimit: resolveBoundedIntegerOption(options.episodeLimit, DEFAULT_EPISODE_LIMIT, "Filmix streaming episodeLimit", 1, 2_000),
33
+ linkTtlMs: resolveBoundedIntegerOption(options.linkTtlMs, DEFAULT_LINK_TTL_MS, "Filmix streaming linkTtlMs", 60_000, 2 * 60 * 60_000),
34
+ deviceId: normalizeDeviceId(options.deviceId ?? randomBytes(8).toString("hex")),
35
+ ...(token ? { token } : {}),
36
+ maxQuality: token ? 720 : 480,
37
+ userAgent: options.userAgent?.trim() || MEDIA_ENGINE_DEFAULT_USER_AGENT,
38
+ now: options.now ?? Date.now,
39
+ };
40
+ }
41
+ function normalizeToken(value) {
42
+ if (value === undefined)
43
+ return undefined;
44
+ const token = value.trim();
45
+ if (!token || token.length > 512 || /\s/u.test(token)) {
46
+ throw new TypeError("Filmix streaming token must contain 1 to 512 non-whitespace characters.");
47
+ }
48
+ return token;
49
+ }
50
+ export function createFilmixCapabilities() {
51
+ return {
52
+ mediaTypes: ["movie", "series"],
53
+ lookup: {
54
+ byTitle: true,
55
+ byExternalIds: [],
56
+ byEpisode: true,
57
+ },
58
+ features: ["mp4", "translations", "qualities", "episode_mapping"],
59
+ };
60
+ }
61
+ function normalizeProviderName(value) {
62
+ const name = value.trim();
63
+ if (!name)
64
+ throw new TypeError("Filmix streaming provider name is required.");
65
+ return name;
66
+ }
67
+ function normalizeDeviceId(value) {
68
+ const deviceId = value.trim().toLowerCase();
69
+ if (!/^[a-f0-9]{16}$/u.test(deviceId)) {
70
+ throw new TypeError("Filmix streaming deviceId must contain exactly 16 hexadecimal characters.");
71
+ }
72
+ return deviceId;
73
+ }
74
+ function normalizeBaseUrl(value, option) {
75
+ let url;
76
+ try {
77
+ url = new URL(value.trim());
78
+ }
79
+ catch (error) {
80
+ throw new TypeError(`Filmix streaming ${option} must be a valid HTTP(S) URL.`, {
81
+ cause: error,
82
+ });
83
+ }
84
+ if ((url.protocol !== "http:" && url.protocol !== "https:") ||
85
+ url.username !== "" ||
86
+ url.password !== "") {
87
+ throw new TypeError(`Filmix streaming ${option} must be a credential-free HTTP(S) URL.`);
88
+ }
89
+ url.search = "";
90
+ url.hash = "";
91
+ return url.href.replace(/\/$/u, "");
92
+ }
93
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/filmix-streaming/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { StreamingProvider } from "@media-engine/core";
2
+ import { type FilmixStreamingProviderOptions } from "./config.js";
3
+ export type { FilmixStreamingProviderOptions } from "./config.js";
4
+ export declare function filmixStreamingProvider(options?: FilmixStreamingProviderOptions): StreamingProvider;
@@ -0,0 +1,48 @@
1
+ import { rethrowIfProviderAborted } from "../shared/abort.js";
2
+ import { createFilmixSourceUrl, loadFilmixPost, searchFilmixPosts } from "./client.js";
3
+ import { createFilmixCapabilities, createFilmixConfig, } from "./config.js";
4
+ import { mapFilmixAvailability } from "./mapping.js";
5
+ import { matchesSelectedFilmixPost, selectFilmixPost } from "./matching.js";
6
+ export function filmixStreamingProvider(options = {}) {
7
+ const config = createFilmixConfig(options);
8
+ return {
9
+ name: config.name,
10
+ version: options.version,
11
+ kind: "streaming",
12
+ capabilities: createFilmixCapabilities(),
13
+ async getAvailability(query, context) {
14
+ if (query.providers && !query.providers.includes(config.name))
15
+ return null;
16
+ if (!canResolveQuery(query))
17
+ return null;
18
+ try {
19
+ const candidates = await searchFilmixPosts(config, query.title, context);
20
+ const candidate = selectFilmixPost(candidates, query);
21
+ if (!candidate)
22
+ return null;
23
+ const post = await loadFilmixPost(config, candidate.id, context);
24
+ if (!post || !matchesSelectedFilmixPost(post, candidate))
25
+ return null;
26
+ return mapFilmixAvailability(config.name, post, query, createFilmixSourceUrl(config, candidate), config.now(), config.linkTtlMs, config.maxQuality);
27
+ }
28
+ catch (error) {
29
+ rethrowIfProviderAborted(context, error);
30
+ throw error;
31
+ }
32
+ },
33
+ };
34
+ }
35
+ function canResolveQuery(query) {
36
+ if (!Boolean(query.title?.trim()) || !Number.isInteger(query.year))
37
+ return false;
38
+ if (query.type === "movie") {
39
+ return (query.seasonNumber === undefined &&
40
+ query.episodeNumber === undefined &&
41
+ query.absoluteEpisodeNumber === undefined);
42
+ }
43
+ return (query.type === "series" &&
44
+ Number.isInteger(query.seasonNumber) &&
45
+ Number.isInteger(query.episodeNumber) &&
46
+ query.absoluteEpisodeNumber === undefined);
47
+ }
48
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/filmix-streaming/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,EACL,wBAAwB,EACxB,kBAAkB,GAEnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAI5E,MAAM,UAAU,uBAAuB,CACrC,UAA0C,EAAE;IAE5C,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAE3C,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,WAAW;QACjB,YAAY,EAAE,wBAAwB,EAAE;QACxC,KAAK,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO;YAClC,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC3E,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YAEzC,IAAI,CAAC;gBACH,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,KAAM,EAAE,OAAO,CAAC,CAAC;gBAC1E,MAAM,SAAS,GAAG,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBACtD,IAAI,CAAC,SAAS;oBAAE,OAAO,IAAI,CAAC;gBAE5B,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;gBACjE,IAAI,CAAC,IAAI,IAAI,CAAC,yBAAyB,CAAC,IAAI,EAAE,SAAS,CAAC;oBAAE,OAAO,IAAI,CAAC;gBAEtE,OAAO,qBAAqB,CAC1B,MAAM,CAAC,IAAI,EACX,IAAI,EACJ,KAAK,EACL,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC,EACxC,MAAM,CAAC,GAAG,EAAE,EACZ,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,UAAU,CAClB,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,wBAAwB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;gBACzC,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,KAA0D;IACjF,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAEjF,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,OAAO,CACL,KAAK,CAAC,YAAY,KAAK,SAAS;YAChC,KAAK,CAAC,aAAa,KAAK,SAAS;YACjC,KAAK,CAAC,qBAAqB,KAAK,SAAS,CAC1C,CAAC;IACJ,CAAC;IAED,OAAO,CACL,KAAK,CAAC,IAAI,KAAK,QAAQ;QACvB,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,YAAY,CAAC;QACpC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC;QACrC,KAAK,CAAC,qBAAqB,KAAK,SAAS,CAC1C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { MediaAvailability } from "@media-engine/core";
2
+ import { type FilmixPost } from "./client.js";
3
+ export declare function mapFilmixAvailability(provider: string, post: FilmixPost, query: MediaAvailability["query"], sourceUrl: string | undefined, now: number, linkTtlMs: number, maxQuality?: 480 | 720): MediaAvailability | null;
@@ -0,0 +1,109 @@
1
+ import { normalizeProviderOutputUrl } from "../shared/index.js";
2
+ import { resolveFilmixQualityLink, } from "./client.js";
3
+ const QUALITY_ORDER = [720, 480];
4
+ const BLOCKED_TRANSLATION = /^(?:заблокировано|недоступно)(?:\s|$)/iu;
5
+ const RESTRICTION_VIDEO = /\/abuse_(?:\[[0-9,]+\]|%s|\d+)\.mp4$/iu;
6
+ export function mapFilmixAvailability(provider, post, query, sourceUrl, now, linkTtlMs, maxQuality = 480) {
7
+ const expiresAt = new Date(now + linkTtlMs).toISOString();
8
+ const options = post.type === "movie"
9
+ ? mapMovieOptions(provider, post, sourceUrl, expiresAt, maxQuality)
10
+ : mapEpisodeOptions(provider, post, query, sourceUrl, expiresAt, maxQuality);
11
+ if (options.length === 0)
12
+ return null;
13
+ const episodes = post.type === "series"
14
+ ? [
15
+ {
16
+ seasonNumber: query.seasonNumber,
17
+ episodeNumber: query.episodeNumber,
18
+ options,
19
+ },
20
+ ]
21
+ : undefined;
22
+ return {
23
+ query,
24
+ item: {
25
+ type: post.type,
26
+ title: post.title,
27
+ ...(post.originalTitle ? { originalTitle: post.originalTitle } : {}),
28
+ year: post.year,
29
+ },
30
+ ...(episodes ? { episodes } : {}),
31
+ options,
32
+ sourceProviders: [{ provider, ...(sourceUrl ? { url: sourceUrl } : {}) }],
33
+ checkedAt: new Date(now).toISOString(),
34
+ };
35
+ }
36
+ function mapMovieOptions(provider, post, sourceUrl, expiresAt, maxQuality) {
37
+ return uniqueOptions(post.movies.flatMap((stream, index) => {
38
+ return createOptions(provider, post.id, stream, index, sourceUrl, expiresAt, maxQuality);
39
+ }));
40
+ }
41
+ function mapEpisodeOptions(provider, post, query, sourceUrl, expiresAt, maxQuality) {
42
+ const season = post.seasons.filter((entry) => entry.number === query.seasonNumber);
43
+ if (season.length !== 1)
44
+ return [];
45
+ return uniqueOptions(season[0].translations.flatMap((translation, index) => {
46
+ const episodes = translation.episodes.filter((episode) => episode.number === query.episodeNumber);
47
+ if (episodes.length !== 1)
48
+ return [];
49
+ return createOptions(provider, post.id, episodes[0], index, sourceUrl, expiresAt, maxQuality, {
50
+ seasonNumber: season[0].number,
51
+ episodeNumber: episodes[0].number,
52
+ });
53
+ }));
54
+ }
55
+ function createOptions(provider, postId, stream, translationIndex, sourceUrl, expiresAt, maxQuality, episode) {
56
+ if (isRestrictedStream(stream))
57
+ return [];
58
+ const episodeKey = episode ? `:${episode.seasonNumber}:${episode.episodeNumber}` : "";
59
+ return QUALITY_ORDER.flatMap((quality) => {
60
+ if (quality > maxQuality || !stream.qualities.includes(quality))
61
+ return [];
62
+ const resolved = resolveFilmixQualityLink(stream.link, quality);
63
+ const url = normalizeProviderOutputUrl(resolved);
64
+ if (!url || new URL(url).pathname.toLowerCase().endsWith(".mp4") === false)
65
+ return [];
66
+ return [
67
+ {
68
+ id: `${provider}:${postId}${episodeKey}:${translationIndex}:${quality}`,
69
+ provider,
70
+ player: {
71
+ kind: "mp4",
72
+ label: "Filmix",
73
+ providerPlayerId: `${postId}${episodeKey}:${translationIndex}`,
74
+ },
75
+ translation: {
76
+ title: stream.translation,
77
+ type: "unknown",
78
+ },
79
+ quality: { label: `${quality}p`, height: quality },
80
+ ...(episode ? { episode } : {}),
81
+ access: { url },
82
+ availability: "available",
83
+ expiresAt,
84
+ ...(sourceUrl ? { sourceUrl } : {}),
85
+ },
86
+ ];
87
+ });
88
+ }
89
+ function isRestrictedStream(stream) {
90
+ if (BLOCKED_TRANSLATION.test(stream.translation.trim()))
91
+ return true;
92
+ try {
93
+ return RESTRICTION_VIDEO.test(new URL(stream.link).pathname);
94
+ }
95
+ catch {
96
+ return true;
97
+ }
98
+ }
99
+ function uniqueOptions(options) {
100
+ const seen = new Set();
101
+ return options.filter((option) => {
102
+ const key = `${option.translation?.title ?? ""}\u0000${option.access.url}`;
103
+ if (seen.has(key))
104
+ return false;
105
+ seen.add(key);
106
+ return true;
107
+ });
108
+ }
109
+ //# sourceMappingURL=mapping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapping.js","sourceRoot":"","sources":["../../src/filmix-streaming/mapping.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { MediaAvailability } from "@media-engine/core";
2
+ import type { FilmixPost, FilmixPostSummary } from "./client.js";
3
+ export declare function selectFilmixPost(posts: FilmixPostSummary[], query: MediaAvailability["query"]): FilmixPostSummary | undefined;
4
+ export declare function matchesSelectedFilmixPost(post: FilmixPost, selected: FilmixPostSummary): boolean;