@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,33 @@
1
+ import { type MediaAvailability, type ProviderContext } from "@media-engine/core";
2
+ import type { VideoHubStreamingConfig } from "./config.js";
3
+ export interface VideoHubPlaylistItem {
4
+ vkId: string;
5
+ seasonNumber?: number;
6
+ episodeNumber?: number;
7
+ voiceStudio?: string;
8
+ voiceType?: string;
9
+ }
10
+ export interface VideoHubPlaylist {
11
+ title?: string;
12
+ isSerial: boolean;
13
+ items: VideoHubPlaylistItem[];
14
+ }
15
+ export interface VideoHubMp4Source {
16
+ url: string;
17
+ label: string;
18
+ height?: number;
19
+ }
20
+ export interface ResolvedVideoHubItem extends VideoHubPlaylistItem {
21
+ sources: VideoHubMp4Source[];
22
+ sourceUrl: string;
23
+ }
24
+ export declare function resolveVideoHubKinopoiskId(query: MediaAvailability["query"]): string | undefined;
25
+ export declare function loadVideoHubPlaylist(config: VideoHubStreamingConfig, kinopoiskId: string, context: ProviderContext, playbackUserAgent: string): Promise<{
26
+ playlist: VideoHubPlaylist;
27
+ sourceUrl: string;
28
+ }>;
29
+ export declare function resolveVideoHubItems(config: VideoHubStreamingConfig, playlist: VideoHubPlaylist, query: MediaAvailability["query"], context: ProviderContext, playbackUserAgent: string): Promise<ResolvedVideoHubItem[]>;
30
+ export declare function createVideoHubPlaylistUrl(baseUrl: string, kinopoiskId: string): URL;
31
+ export declare function createVideoHubVideoUrl(baseUrl: string, vkId: string): URL;
32
+ export declare function parseVideoHubPlaylist(provider: string, value: unknown, itemLimit: number): VideoHubPlaylist;
33
+ export declare function parseVideoHubSources(provider: string, value: unknown): VideoHubMp4Source[];
@@ -0,0 +1,208 @@
1
+ import { ProviderError } from "@media-engine/core";
2
+ import { fetchJson, normalizeProviderOutputUrl } from "../shared/index.js";
3
+ import { rethrowIfProviderAborted } from "../shared/abort.js";
4
+ const PLAYLIST_PUBLICATION_ID = "12";
5
+ const MAX_TITLE_LENGTH = 500;
6
+ const MAX_TRANSLATION_LENGTH = 200;
7
+ const MP4_SOURCE_FIELDS = [
8
+ { key: "mpeg4kUrl", label: "4K", height: 2160 },
9
+ { key: "mpeg2kUrl", label: "2K" },
10
+ { key: "mpegQhdUrl", label: "1440p", height: 1440 },
11
+ { key: "mpegFullHdUrl", label: "1080p", height: 1080 },
12
+ { key: "mpegHighUrl", label: "720p", height: 720 },
13
+ { key: "mpegMediumUrl", label: "480p", height: 480 },
14
+ { key: "mpegLowUrl", label: "360p", height: 360 },
15
+ { key: "mpegLowestUrl", label: "240p", height: 240 },
16
+ { key: "mpegTinyUrl", label: "144p", height: 144 },
17
+ ];
18
+ export function resolveVideoHubKinopoiskId(query) {
19
+ const kinopoisk = query.ids?.kinopoisk ?? query.kinopoisk;
20
+ return kinopoisk && /^[1-9]\d{0,11}$/u.test(kinopoisk) ? kinopoisk : undefined;
21
+ }
22
+ export async function loadVideoHubPlaylist(config, kinopoiskId, context, playbackUserAgent) {
23
+ const url = createVideoHubPlaylistUrl(config.baseUrl, kinopoiskId);
24
+ const payload = await fetchJson({
25
+ provider: config.name,
26
+ url,
27
+ context,
28
+ fetch: config.fetch,
29
+ rateLimitGate: config.rateLimitGate,
30
+ maxRetries: 0,
31
+ maxResponseBytes: config.maxPlaylistResponseBytes,
32
+ init: { headers: createHeaders(playbackUserAgent) },
33
+ });
34
+ return {
35
+ playlist: parseVideoHubPlaylist(config.name, payload, config.playlistItemLimit),
36
+ sourceUrl: url.href,
37
+ };
38
+ }
39
+ function createVideoLookupContext(config, context) {
40
+ return {
41
+ ...context,
42
+ timeoutMs: context.timeoutMs === undefined
43
+ ? config.videoLookupTimeoutMs
44
+ : Math.min(context.timeoutMs, config.videoLookupTimeoutMs),
45
+ };
46
+ }
47
+ export async function resolveVideoHubItems(config, playlist, query, context, playbackUserAgent) {
48
+ const selected = selectPlaylistItems(playlist, query).slice(0, config.videoLookupLimit);
49
+ if (selected.length === 0)
50
+ return [];
51
+ const resolved = new Array(selected.length);
52
+ let nextIndex = 0;
53
+ let firstError;
54
+ const worker = async () => {
55
+ while (nextIndex < selected.length) {
56
+ const index = nextIndex;
57
+ nextIndex += 1;
58
+ const item = selected[index];
59
+ try {
60
+ const result = await loadVideoHubSources(config, item.vkId, context, playbackUserAgent);
61
+ if (result.sources.length > 0)
62
+ resolved[index] = { ...item, ...result };
63
+ }
64
+ catch (error) {
65
+ rethrowIfProviderAborted(context, error);
66
+ firstError ??= error;
67
+ }
68
+ }
69
+ };
70
+ await Promise.all(Array.from({ length: Math.min(config.videoLookupConcurrency, selected.length) }, async () => worker()));
71
+ const available = resolved.filter((item) => item !== undefined);
72
+ if (available.length === 0 && firstError !== undefined)
73
+ throw firstError;
74
+ return available;
75
+ }
76
+ export function createVideoHubPlaylistUrl(baseUrl, kinopoiskId) {
77
+ const url = new URL("/api/v1/player/sv/playlist", `${baseUrl}/`);
78
+ url.searchParams.set("pub", PLAYLIST_PUBLICATION_ID);
79
+ url.searchParams.set("aggr", "kp");
80
+ url.searchParams.set("id", kinopoiskId);
81
+ return url;
82
+ }
83
+ export function createVideoHubVideoUrl(baseUrl, vkId) {
84
+ return new URL(`/api/v1/player/sv/video/${vkId}`, `${baseUrl}/`);
85
+ }
86
+ export function parseVideoHubPlaylist(provider, value, itemLimit) {
87
+ if (!isRecord(value) || typeof value.isSerial !== "boolean" || !Array.isArray(value.items)) {
88
+ throw invalidResponse(provider, "playlist");
89
+ }
90
+ const isSerial = value.isSerial;
91
+ const items = value.items.slice(0, itemLimit).flatMap((entry) => {
92
+ const item = parsePlaylistItem(entry, isSerial);
93
+ return item ? [item] : [];
94
+ });
95
+ if (value.items.length > 0 && items.length === 0)
96
+ throw invalidResponse(provider, "playlist");
97
+ const title = readString(value.titleName, MAX_TITLE_LENGTH);
98
+ return {
99
+ ...(title ? { title } : {}),
100
+ isSerial,
101
+ items,
102
+ };
103
+ }
104
+ export function parseVideoHubSources(provider, value) {
105
+ if (!isRecord(value) || !isRecord(value.sources))
106
+ throw invalidResponse(provider, "video");
107
+ const sources = [];
108
+ const seenUrls = new Set();
109
+ for (const field of MP4_SOURCE_FIELDS) {
110
+ const rawUrl = value.sources[field.key];
111
+ if (typeof rawUrl !== "string")
112
+ continue;
113
+ const url = normalizeHttpsOutputUrl(rawUrl);
114
+ if (!url || seenUrls.has(url))
115
+ continue;
116
+ seenUrls.add(url);
117
+ sources.push({
118
+ url,
119
+ label: field.label,
120
+ ...("height" in field ? { height: field.height } : {}),
121
+ });
122
+ }
123
+ return sources;
124
+ }
125
+ function selectPlaylistItems(playlist, query) {
126
+ if (query.type === "movie")
127
+ return playlist.isSerial ? [] : playlist.items;
128
+ if (!playlist.isSerial)
129
+ return [];
130
+ return playlist.items.filter((item) => item.seasonNumber === query.seasonNumber && item.episodeNumber === query.episodeNumber);
131
+ }
132
+ async function loadVideoHubSources(config, vkId, context, playbackUserAgent) {
133
+ const url = createVideoHubVideoUrl(config.baseUrl, vkId);
134
+ const payload = await fetchJson({
135
+ provider: config.name,
136
+ url,
137
+ context: createVideoLookupContext(config, context),
138
+ fetch: config.fetch,
139
+ rateLimitGate: config.rateLimitGate,
140
+ maxRetries: 0,
141
+ maxResponseBytes: config.maxVideoResponseBytes,
142
+ init: { headers: createHeaders(playbackUserAgent) },
143
+ });
144
+ return {
145
+ sources: parseVideoHubSources(config.name, payload),
146
+ sourceUrl: url.href,
147
+ };
148
+ }
149
+ function parsePlaylistItem(value, isSerial) {
150
+ if (!isRecord(value))
151
+ return undefined;
152
+ const vkId = readIdentifier(value.vkId);
153
+ if (!vkId)
154
+ return undefined;
155
+ const seasonNumber = isSerial ? readPositiveInteger(value.season) : undefined;
156
+ const episodeNumber = isSerial ? readPositiveInteger(value.episode) : undefined;
157
+ if (isSerial && (seasonNumber === undefined || episodeNumber === undefined))
158
+ return undefined;
159
+ const voiceStudio = readString(value.voiceStudio, MAX_TRANSLATION_LENGTH);
160
+ const voiceType = readString(value.voiceType, MAX_TRANSLATION_LENGTH);
161
+ return {
162
+ vkId,
163
+ ...(seasonNumber !== undefined ? { seasonNumber } : {}),
164
+ ...(episodeNumber !== undefined ? { episodeNumber } : {}),
165
+ ...(voiceStudio ? { voiceStudio } : {}),
166
+ ...(voiceType ? { voiceType } : {}),
167
+ };
168
+ }
169
+ function createHeaders(userAgent) {
170
+ return {
171
+ Accept: "application/json",
172
+ "User-Agent": userAgent,
173
+ };
174
+ }
175
+ function normalizeHttpsOutputUrl(value) {
176
+ const normalized = normalizeProviderOutputUrl(value.trim());
177
+ if (!normalized)
178
+ return undefined;
179
+ return new URL(normalized).protocol === "https:" ? normalized : undefined;
180
+ }
181
+ function readIdentifier(value) {
182
+ if (typeof value === "number" && Number.isSafeInteger(value) && value > 0)
183
+ return String(value);
184
+ return typeof value === "string" && /^[1-9]\d{0,19}$/u.test(value.trim())
185
+ ? value.trim()
186
+ : undefined;
187
+ }
188
+ function readPositiveInteger(value) {
189
+ return Number.isSafeInteger(value) && value > 0 ? value : undefined;
190
+ }
191
+ function readString(value, maxLength) {
192
+ if (typeof value !== "string")
193
+ return undefined;
194
+ const normalized = value.trim();
195
+ return normalized && normalized.length <= maxLength ? normalized : undefined;
196
+ }
197
+ function isRecord(value) {
198
+ return typeof value === "object" && value !== null && !Array.isArray(value);
199
+ }
200
+ function invalidResponse(provider, stage) {
201
+ return new ProviderError({
202
+ provider,
203
+ code: "PROVIDER_INVALID_RESPONSE",
204
+ message: `Provider "${provider}" returned an invalid VideoHUB ${stage} response.`,
205
+ retryable: false,
206
+ });
207
+ }
208
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/videohub-streaming/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { StreamingProviderCapabilities } from "@media-engine/core";
2
+ import { ProviderRateLimitGate, type ProviderFetch } from "../shared/index.js";
3
+ export interface VideoHubStreamingProviderOptions {
4
+ name?: string;
5
+ version?: string;
6
+ baseUrl?: string;
7
+ fetch?: ProviderFetch;
8
+ maxPlaylistResponseBytes?: number;
9
+ maxVideoResponseBytes?: number;
10
+ playlistItemLimit?: number;
11
+ videoLookupLimit?: number;
12
+ videoLookupConcurrency?: number;
13
+ videoLookupTimeoutMs?: number;
14
+ linkTtlMs?: number;
15
+ userAgent?: string;
16
+ now?: () => number;
17
+ }
18
+ export interface VideoHubStreamingConfig {
19
+ name: string;
20
+ baseUrl: string;
21
+ fetch: ProviderFetch;
22
+ rateLimitGate: ProviderRateLimitGate;
23
+ maxPlaylistResponseBytes: number;
24
+ maxVideoResponseBytes: number;
25
+ playlistItemLimit: number;
26
+ videoLookupLimit: number;
27
+ videoLookupConcurrency: number;
28
+ videoLookupTimeoutMs: number;
29
+ linkTtlMs: number;
30
+ userAgent: string;
31
+ now: () => number;
32
+ }
33
+ export declare function createVideoHubConfig(options: VideoHubStreamingProviderOptions): VideoHubStreamingConfig;
34
+ export declare function createVideoHubCapabilities(): StreamingProviderCapabilities;
@@ -0,0 +1,73 @@
1
+ import { MEDIA_ENGINE_DEFAULT_USER_AGENT } from "../package-version.js";
2
+ import { ProviderRateLimitGate } from "../shared/index.js";
3
+ import { resolveBoundedIntegerOption } from "../shared/options.js";
4
+ import { createHardenedProviderFetch } from "../shared/safe-fetch.js";
5
+ const DEFAULT_PROVIDER_NAME = "videohub-streaming";
6
+ const DEFAULT_BASE_URL = "https://plapi.cdnvideohub.com";
7
+ const DEFAULT_MAX_PLAYLIST_RESPONSE_BYTES = 2 * 1024 * 1024;
8
+ const DEFAULT_MAX_VIDEO_RESPONSE_BYTES = 256 * 1024;
9
+ const DEFAULT_PLAYLIST_ITEM_LIMIT = 1_000;
10
+ const DEFAULT_VIDEO_LOOKUP_LIMIT = 8;
11
+ const DEFAULT_VIDEO_LOOKUP_CONCURRENCY = 4;
12
+ const DEFAULT_VIDEO_LOOKUP_TIMEOUT_MS = 8_000;
13
+ const DEFAULT_LINK_TTL_MS = 5 * 60_000;
14
+ const DEFAULT_ADDRESS_ATTEMPT_TIMEOUT_MS = 3_500;
15
+ export function createVideoHubConfig(options) {
16
+ const name = normalizeProviderName(options.name ?? DEFAULT_PROVIDER_NAME);
17
+ return {
18
+ name,
19
+ baseUrl: normalizeBaseUrl(options.baseUrl ?? DEFAULT_BASE_URL),
20
+ fetch: options.fetch ??
21
+ createHardenedProviderFetch({
22
+ provider: name,
23
+ maxRedirects: 2,
24
+ addressAttemptTimeoutMs: DEFAULT_ADDRESS_ATTEMPT_TIMEOUT_MS,
25
+ }),
26
+ rateLimitGate: new ProviderRateLimitGate(),
27
+ maxPlaylistResponseBytes: resolveBoundedIntegerOption(options.maxPlaylistResponseBytes, DEFAULT_MAX_PLAYLIST_RESPONSE_BYTES, "VideoHUB streaming maxPlaylistResponseBytes", 1_024, 16 * 1024 * 1024),
28
+ maxVideoResponseBytes: resolveBoundedIntegerOption(options.maxVideoResponseBytes, DEFAULT_MAX_VIDEO_RESPONSE_BYTES, "VideoHUB streaming maxVideoResponseBytes", 1_024, 4 * 1024 * 1024),
29
+ playlistItemLimit: resolveBoundedIntegerOption(options.playlistItemLimit, DEFAULT_PLAYLIST_ITEM_LIMIT, "VideoHUB streaming playlistItemLimit", 1, 5_000),
30
+ videoLookupLimit: resolveBoundedIntegerOption(options.videoLookupLimit, DEFAULT_VIDEO_LOOKUP_LIMIT, "VideoHUB streaming videoLookupLimit", 1, 32),
31
+ videoLookupConcurrency: resolveBoundedIntegerOption(options.videoLookupConcurrency, DEFAULT_VIDEO_LOOKUP_CONCURRENCY, "VideoHUB streaming videoLookupConcurrency", 1, 8),
32
+ videoLookupTimeoutMs: resolveBoundedIntegerOption(options.videoLookupTimeoutMs, DEFAULT_VIDEO_LOOKUP_TIMEOUT_MS, "VideoHUB streaming videoLookupTimeoutMs", 500, 15_000),
33
+ linkTtlMs: resolveBoundedIntegerOption(options.linkTtlMs, DEFAULT_LINK_TTL_MS, "VideoHUB streaming linkTtlMs", 30_000, 60 * 60_000),
34
+ userAgent: options.userAgent?.trim() || MEDIA_ENGINE_DEFAULT_USER_AGENT,
35
+ now: options.now ?? Date.now,
36
+ };
37
+ }
38
+ export function createVideoHubCapabilities() {
39
+ return {
40
+ mediaTypes: ["movie", "series"],
41
+ lookup: {
42
+ byTitle: false,
43
+ byExternalIds: ["kinopoisk"],
44
+ byEpisode: true,
45
+ },
46
+ features: ["mp4", "translations", "qualities", "episode_mapping", "headers"],
47
+ };
48
+ }
49
+ function normalizeProviderName(value) {
50
+ const name = value.trim();
51
+ if (!name)
52
+ throw new TypeError("VideoHUB streaming provider name is required.");
53
+ return name;
54
+ }
55
+ function normalizeBaseUrl(value) {
56
+ let url;
57
+ try {
58
+ url = new URL(value.trim());
59
+ }
60
+ catch (error) {
61
+ throw new TypeError("VideoHUB streaming baseUrl must be a valid HTTPS URL.", {
62
+ cause: error,
63
+ });
64
+ }
65
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "") {
66
+ throw new TypeError("VideoHUB streaming baseUrl must be a credential-free HTTPS URL.");
67
+ }
68
+ url.pathname = "/";
69
+ url.search = "";
70
+ url.hash = "";
71
+ return url.href.replace(/\/$/u, "");
72
+ }
73
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/videohub-streaming/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { StreamingProvider } from "@media-engine/core";
2
+ import { type VideoHubStreamingProviderOptions } from "./config.js";
3
+ export type { VideoHubStreamingProviderOptions } from "./config.js";
4
+ export declare function videoHubStreamingProvider(options?: VideoHubStreamingProviderOptions): StreamingProvider;
@@ -0,0 +1,53 @@
1
+ import { rethrowIfProviderAborted } from "../shared/abort.js";
2
+ import { loadVideoHubPlaylist, resolveVideoHubItems, resolveVideoHubKinopoiskId, } from "./client.js";
3
+ import { createVideoHubCapabilities, createVideoHubConfig, } from "./config.js";
4
+ import { mapVideoHubAvailability } from "./mapping.js";
5
+ export function videoHubStreamingProvider(options = {}) {
6
+ const config = createVideoHubConfig(options);
7
+ return {
8
+ name: config.name,
9
+ version: options.version,
10
+ kind: "streaming",
11
+ capabilities: createVideoHubCapabilities(),
12
+ availabilityDependsOnPlaybackUserAgent: true,
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
+ const kinopoiskId = resolveVideoHubKinopoiskId(query);
19
+ if (!kinopoiskId)
20
+ return null;
21
+ const playbackUserAgent = resolvePlaybackUserAgent(context, config.userAgent);
22
+ try {
23
+ const { playlist, sourceUrl } = await loadVideoHubPlaylist(config, kinopoiskId, context, playbackUserAgent);
24
+ if ((query.type === "series") !== playlist.isSerial)
25
+ return null;
26
+ const items = await resolveVideoHubItems(config, playlist, query, context, playbackUserAgent);
27
+ return mapVideoHubAvailability(config.name, kinopoiskId, playlist.title, items, query, sourceUrl, config.now(), config.linkTtlMs, playbackUserAgent);
28
+ }
29
+ catch (error) {
30
+ rethrowIfProviderAborted(context, error);
31
+ throw error;
32
+ }
33
+ },
34
+ };
35
+ }
36
+ function resolvePlaybackUserAgent(context, fallback) {
37
+ const value = context.playbackUserAgent?.trim();
38
+ return value && value.length <= 512 && !/[\u0000-\u001f\u007f]/u.test(value) ? value : fallback;
39
+ }
40
+ function canResolveQuery(query) {
41
+ if (query.type !== "movie" && query.type !== "series")
42
+ return false;
43
+ if (query.absoluteEpisodeNumber !== undefined)
44
+ return false;
45
+ if (query.type === "movie") {
46
+ return query.seasonNumber === undefined && query.episodeNumber === undefined;
47
+ }
48
+ return (Number.isInteger(query.seasonNumber) &&
49
+ (query.seasonNumber ?? 0) > 0 &&
50
+ Number.isInteger(query.episodeNumber) &&
51
+ (query.episodeNumber ?? 0) > 0);
52
+ }
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/videohub-streaming/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { MediaAvailability } from "@media-engine/core";
2
+ import type { ResolvedVideoHubItem } from "./client.js";
3
+ export declare function mapVideoHubAvailability(provider: string, kinopoiskId: string, playlistTitle: string | undefined, items: ResolvedVideoHubItem[], query: MediaAvailability["query"], sourceUrl: string, now: number, linkTtlMs: number, playbackUserAgent: string): MediaAvailability | null;
@@ -0,0 +1,84 @@
1
+ export function mapVideoHubAvailability(provider, kinopoiskId, playlistTitle, items, query, sourceUrl, now, linkTtlMs, playbackUserAgent) {
2
+ const expiresAt = new Date(now + linkTtlMs).toISOString();
3
+ const options = uniqueOptions(items.flatMap((item) => item.sources.map((source) => createOption(provider, kinopoiskId, item, source, query, expiresAt, playbackUserAgent))));
4
+ if (options.length === 0)
5
+ return null;
6
+ const episodes = query.type === "series"
7
+ ? [
8
+ {
9
+ seasonNumber: query.seasonNumber,
10
+ episodeNumber: query.episodeNumber,
11
+ options,
12
+ },
13
+ ]
14
+ : undefined;
15
+ const ids = { ...query.ids, kinopoisk: kinopoiskId };
16
+ return {
17
+ query,
18
+ item: {
19
+ type: query.type,
20
+ title: playlistTitle ?? query.title,
21
+ year: query.year,
22
+ ids,
23
+ },
24
+ ...(episodes ? { episodes } : {}),
25
+ options,
26
+ sourceProviders: [{ provider, url: sourceUrl, ids }],
27
+ checkedAt: new Date(now).toISOString(),
28
+ };
29
+ }
30
+ function createOption(provider, kinopoiskId, item, source, query, expiresAt, playbackUserAgent) {
31
+ const episode = query.type === "series"
32
+ ? { seasonNumber: item.seasonNumber, episodeNumber: item.episodeNumber }
33
+ : undefined;
34
+ const translation = createTranslation(item.voiceStudio, item.voiceType);
35
+ const episodeKey = episode ? `:${episode.seasonNumber}:${episode.episodeNumber}` : "";
36
+ return {
37
+ id: `${provider}:${kinopoiskId}${episodeKey}:${item.vkId}:${source.label}`,
38
+ provider,
39
+ player: {
40
+ kind: "mp4",
41
+ label: "VideoHUB",
42
+ providerPlayerId: `${item.vkId}${episodeKey}`,
43
+ },
44
+ translation,
45
+ quality: {
46
+ label: source.label,
47
+ ...(source.height !== undefined ? { height: source.height } : {}),
48
+ },
49
+ ...(episode ? { episode } : {}),
50
+ access: { url: source.url, headers: { "User-Agent": playbackUserAgent } },
51
+ availability: "available",
52
+ expiresAt,
53
+ sourceUrl: item.sourceUrl,
54
+ };
55
+ }
56
+ function createTranslation(voiceStudio, voiceType) {
57
+ const title = voiceStudio || voiceType || "VideoHUB";
58
+ const normalizedType = (voiceType ?? title).toLocaleLowerCase();
59
+ const type = normalizedType.includes("субтит")
60
+ ? "subtitles"
61
+ : normalizedType.includes("дубляж")
62
+ ? "dub"
63
+ : normalizedType.includes("голос")
64
+ ? "voiceover"
65
+ : normalizedType.includes("оригинал")
66
+ ? "original"
67
+ : "unknown";
68
+ return {
69
+ title,
70
+ type,
71
+ ...(voiceStudio ? { team: voiceStudio } : {}),
72
+ };
73
+ }
74
+ function uniqueOptions(options) {
75
+ const seen = new Set();
76
+ return options.filter((option) => {
77
+ const key = `${option.translation?.title ?? ""}\u0000${option.quality?.label ?? ""}\u0000${option.access.url}`;
78
+ if (seen.has(key))
79
+ return false;
80
+ seen.add(key);
81
+ return true;
82
+ });
83
+ }
84
+ //# sourceMappingURL=mapping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapping.js","sourceRoot":"","sources":["../../src/videohub-streaming/mapping.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@media-engine/providers",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Provider implementations for Media Engine.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/Yaneart/media-engine#readme",
@@ -20,6 +20,7 @@
20
20
  "anime",
21
21
  "aniliberty",
22
22
  "ddbb",
23
+ "filmix",
23
24
  "hls",
24
25
  "kinobd",
25
26
  "cinemeta",
@@ -30,8 +31,12 @@
30
31
  "anilist",
31
32
  "bitsearch",
32
33
  "tvmaze",
34
+ "veoveo",
35
+ "videohub",
33
36
  "wikidata",
34
37
  "no-token",
38
+ "mp4",
39
+ "rutube",
35
40
  "torrent",
36
41
  "yts"
37
42
  ],
@@ -65,7 +70,7 @@
65
70
  "access": "public"
66
71
  },
67
72
  "dependencies": {
68
- "@media-engine/core": "1.0.0"
73
+ "@media-engine/core": "1.1.0"
69
74
  },
70
75
  "scripts": {
71
76
  "clean": "node ../../scripts/clean-package-dist.mjs",