rezka.ts 1.0.2 → 1.0.3
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/README.md +145 -58
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
# rezka
|
|
1
|
+
# rezka.ts
|
|
2
2
|
|
|
3
3
|
> Unofficial TypeScript API wrapper for the [HDRezka](https://rezka.ag) streaming service.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **No boilerplate** — top-level `search()
|
|
7
|
+
- **No boilerplate** — top-level `search()`, `load()`, `browse()` functions, no class instantiation required
|
|
8
8
|
- **Unified stream API** — one `.streams()` call for movies; fluent `.episode(s, e).streams()` for series
|
|
9
|
+
- **Rich metadata** — IMDb/KP ratings, actors, directors, genres, country, duration and more, parsed eagerly
|
|
10
|
+
- **Browse catalog** — list movies/series by type, filter (`last`, `popular`, `soon`) and page
|
|
11
|
+
- **Authentication** — `login()` returns a ready-to-use authenticated client
|
|
9
12
|
- **Consistent types** — all IDs are `number`, `type` is a proper union, not `string`
|
|
10
13
|
- **Smart defaults** — omit `translationId` to auto-use the first available track
|
|
11
14
|
- **TypeScript-first** — full type definitions, zero `any`
|
|
@@ -16,11 +19,11 @@
|
|
|
16
19
|
## Installation
|
|
17
20
|
|
|
18
21
|
```bash
|
|
19
|
-
npm install rezka
|
|
22
|
+
npm install rezka.ts
|
|
20
23
|
# or
|
|
21
|
-
yarn add rezka
|
|
24
|
+
yarn add rezka.ts
|
|
22
25
|
# or
|
|
23
|
-
pnpm add rezka
|
|
26
|
+
pnpm add rezka.ts
|
|
24
27
|
```
|
|
25
28
|
|
|
26
29
|
**Requires Node.js ≥ 18.**
|
|
@@ -30,8 +33,8 @@ pnpm add rezka-api
|
|
|
30
33
|
## Quick Start
|
|
31
34
|
|
|
32
35
|
```typescript
|
|
33
|
-
import { search, load } from 'rezka
|
|
34
|
-
// or: import rezka from 'rezka
|
|
36
|
+
import { search, load, browse } from 'rezka.ts';
|
|
37
|
+
// or: import rezka from 'rezka.ts'; then rezka.search(...) / rezka.load(...)
|
|
35
38
|
|
|
36
39
|
// 1. Search
|
|
37
40
|
const results = await search('mr robot');
|
|
@@ -40,19 +43,28 @@ console.log(results[0]);
|
|
|
40
43
|
|
|
41
44
|
// 2. Load media page — all metadata available synchronously
|
|
42
45
|
const media = await load(results[0].url);
|
|
43
|
-
console.log(media.title);
|
|
44
|
-
console.log(media.origTitle);
|
|
45
|
-
console.log(media.type);
|
|
46
|
-
console.log(media.
|
|
46
|
+
console.log(media.title); // "Мистер Робот"
|
|
47
|
+
console.log(media.origTitle); // "Mr. Robot"
|
|
48
|
+
console.log(media.type); // "series"
|
|
49
|
+
console.log(media.rating.imdb); // { score: 8.5, votes: 1234567 }
|
|
50
|
+
console.log(media.directors); // ['Sam Esmail']
|
|
51
|
+
console.log(media.actors); // ['Rami Malek', ...]
|
|
52
|
+
console.log(media.genres); // ['Thriller', 'Drama']
|
|
53
|
+
console.log(media.translations); // [{ id: 56, title: 'English' }, ...]
|
|
47
54
|
|
|
48
55
|
// 3a. Movie — get streams directly
|
|
49
|
-
const streams = await media.streams();
|
|
50
|
-
const streams = await media.streams(56);
|
|
56
|
+
const streams = await media.streams(); // uses first translation
|
|
57
|
+
const streams = await media.streams(56); // or pick a specific one
|
|
51
58
|
console.log(streams); // { '1080p': 'https://...m3u8', '720p': '...', ... }
|
|
52
59
|
|
|
53
60
|
// 3b. Series — fluent episode selector
|
|
54
61
|
const streams = await media.episode(1, 1).streams(); // S01E01, default translation
|
|
55
|
-
const streams = await media.episode(1, 3).streams(56); // S01E03,
|
|
62
|
+
const streams = await media.episode(1, 3).streams(56); // S01E03, specific translation
|
|
63
|
+
|
|
64
|
+
// 4. Browse catalog
|
|
65
|
+
const page = await browse({ type: 'movie', filter: 'popular' });
|
|
66
|
+
console.log(page.items[0].title);
|
|
67
|
+
console.log(page.hasNextPage); // → call with page: 2
|
|
56
68
|
```
|
|
57
69
|
|
|
58
70
|
---
|
|
@@ -92,12 +104,66 @@ const media = await load('https://rezka.ag/films/action/12345-inception.html');
|
|
|
92
104
|
|
|
93
105
|
---
|
|
94
106
|
|
|
107
|
+
### `browse(browseOptions?, clientOptions?)`
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
browse(browseOptions?: BrowseOptions, clientOptions?: RezkaOptions): Promise<BrowsePage>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Browse the HDRezka catalog — returns a paginated list of items.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { browse } from 'rezka.ts';
|
|
117
|
+
|
|
118
|
+
// Popular movies, page 1
|
|
119
|
+
const page = await browse({ type: 'movie', filter: 'popular' });
|
|
120
|
+
console.log(page.items); // BrowseItem[]
|
|
121
|
+
console.log(page.hasNextPage); // true → fetch page 2
|
|
122
|
+
|
|
123
|
+
// Latest series, page 2
|
|
124
|
+
const page2 = await browse({ type: 'series', filter: 'last', page: 2 });
|
|
125
|
+
|
|
126
|
+
// Specific genre URL
|
|
127
|
+
const comedy = await browse({ genreUrl: '/films/comedy/' });
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### `BrowseOptions`
|
|
131
|
+
|
|
132
|
+
| Option | Type | Description |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| `type` | `'movie' \| 'series' \| 'cartoon' \| 'anime'` | Content type to browse |
|
|
135
|
+
| `filter` | `'last' \| 'popular' \| 'soon' \| 'watching'` | Sort order |
|
|
136
|
+
| `page` | `number` | Page number, 1-based (default: `1`) |
|
|
137
|
+
| `genreUrl` | `string` | Direct genre path, e.g. `'/films/comedy/'` — overrides `type` |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
### `login(email, password, options?)`
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
login(email: string, password: string, options?: RezkaOptions): Promise<client>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Log in to HDRezka. Returns an **authenticated client** with session cookies baked in — use it like `createClient()`.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { login } from 'rezka.ts';
|
|
151
|
+
|
|
152
|
+
const client = await login('you@email.com', 'password');
|
|
153
|
+
const results = await client.search('inception');
|
|
154
|
+
const media = await client.load(results[0].url);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Throws** if credentials are incorrect.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
95
161
|
### `createClient(options)`
|
|
96
162
|
|
|
97
163
|
For cases where you need a **custom mirror, timeout, or shared configuration**. Returns an object with the same `search` and `load` methods bound to that config.
|
|
98
164
|
|
|
99
165
|
```typescript
|
|
100
|
-
import { createClient } from 'rezka
|
|
166
|
+
import { createClient } from 'rezka.ts';
|
|
101
167
|
|
|
102
168
|
const client = createClient({
|
|
103
169
|
baseUrl: 'https://my-mirror.com',
|
|
@@ -133,10 +199,19 @@ Returned by `load()`. All metadata is parsed eagerly and available as **readonly
|
|
|
133
199
|
| `title` | `string` | Localized title |
|
|
134
200
|
| `origTitle` | `string \| null` | Original (non-localized) title |
|
|
135
201
|
| `type` | `MediaType` | `'movie' \| 'series' \| 'animation' \| 'cartoon' \| 'anime' \| 'documentary' \| 'unknown'` |
|
|
136
|
-
| `year` | `string \| null` | Release year
|
|
202
|
+
| `year` | `string \| null` | Release year |
|
|
137
203
|
| `thumbnail` | `string \| null` | Poster image URL |
|
|
138
204
|
| `description` | `string \| null` | Plot description |
|
|
139
|
-
| `
|
|
205
|
+
| `rating` | `{ imdb: Rating \| null; kp: Rating \| null }` | IMDb and KinoPoisk scores |
|
|
206
|
+
| `slogan` | `string \| null` | Tagline / slogan |
|
|
207
|
+
| `country` | `string \| null` | Country of production |
|
|
208
|
+
| `quality` | `string \| null` | Video quality label (e.g. `'HDRip'`) |
|
|
209
|
+
| `ageRating` | `number \| null` | Age restriction as a number (e.g. `16`) |
|
|
210
|
+
| `duration` | `number \| null` | Runtime in minutes |
|
|
211
|
+
| `genres` | `string[]` | Genre names |
|
|
212
|
+
| `directors` | `string[]` | Director names |
|
|
213
|
+
| `actors` | `string[]` | Main cast names |
|
|
214
|
+
| `info` | `InfoRow[]` | Raw metadata table rows |
|
|
140
215
|
| `translations` | `Translation[]` | Available audio tracks — pass `.id` to `.streams()` |
|
|
141
216
|
| `seasons` | `Season[]` | Season list — empty array for movies |
|
|
142
217
|
|
|
@@ -149,7 +224,7 @@ episodes(seasonId: number): Episode[]
|
|
|
149
224
|
Get the episode list for a season. IDs come from `media.seasons`.
|
|
150
225
|
|
|
151
226
|
```typescript
|
|
152
|
-
const season = media.seasons[0];
|
|
227
|
+
const season = media.seasons[0]; // { id: 1, title: 'Season 1' }
|
|
153
228
|
const episodes = media.episodes(season.id); // [{ id, episodeId, seasonId, title }]
|
|
154
229
|
```
|
|
155
230
|
|
|
@@ -163,8 +238,8 @@ Select a specific episode. Returns an `EpisodeRef` synchronously (no network cal
|
|
|
163
238
|
|
|
164
239
|
```typescript
|
|
165
240
|
const ref = media.episode(1, 1);
|
|
166
|
-
const streams = await ref.streams();
|
|
167
|
-
const streams = await ref.streams(56);
|
|
241
|
+
const streams = await ref.streams(); // first available translation
|
|
242
|
+
const streams = await ref.streams(56); // specific translation ID
|
|
168
243
|
```
|
|
169
244
|
|
|
170
245
|
#### `media.streams(translationId?)`
|
|
@@ -188,40 +263,45 @@ const url1080 = streams['1080p'];
|
|
|
188
263
|
### Types
|
|
189
264
|
|
|
190
265
|
```typescript
|
|
191
|
-
type MediaType
|
|
192
|
-
type StreamUrls
|
|
266
|
+
type MediaType = 'movie' | 'series' | 'animation' | 'cartoon' | 'anime' | 'documentary' | 'unknown';
|
|
267
|
+
type StreamUrls = Record<string, string>; // quality → HLS URL
|
|
268
|
+
type BrowseFilter = 'last' | 'popular' | 'soon' | 'watching';
|
|
193
269
|
|
|
270
|
+
interface Rating { score: number; votes: number; }
|
|
194
271
|
interface SearchResult { url: string; title: string; type: MediaType; year: string; }
|
|
195
272
|
interface Translation { id: number; title: string; }
|
|
196
273
|
interface Season { id: number; title: string; }
|
|
197
274
|
interface Episode { id: number; episodeId: number; seasonId: number; title: string; }
|
|
198
275
|
interface InfoRow { key: string; value: string; }
|
|
199
276
|
interface EpisodeRef { streams(translationId?: number): Promise<StreamUrls>; }
|
|
277
|
+
interface BrowseItem { id: number; url: string; title: string; poster: string; type: MediaType; info: string; }
|
|
278
|
+
interface BrowsePage { items: BrowseItem[]; page: number; hasNextPage: boolean; }
|
|
279
|
+
interface BrowseOptions {
|
|
280
|
+
type?: 'movie' | 'series' | 'cartoon' | 'anime';
|
|
281
|
+
filter?: BrowseFilter;
|
|
282
|
+
page?: number;
|
|
283
|
+
genreUrl?: string;
|
|
284
|
+
}
|
|
200
285
|
```
|
|
201
286
|
|
|
202
287
|
---
|
|
203
288
|
|
|
204
|
-
## Full Example —
|
|
289
|
+
## Full Example — Series
|
|
205
290
|
|
|
206
291
|
```typescript
|
|
207
|
-
import { search, load } from 'rezka
|
|
292
|
+
import { search, load } from 'rezka.ts';
|
|
208
293
|
|
|
209
294
|
const results = await search('mr robot');
|
|
210
295
|
const media = await load(results[0].url);
|
|
211
296
|
|
|
212
|
-
console.log(`${media.title} (${media.origTitle})`);
|
|
213
|
-
console.log(`
|
|
214
|
-
console.log(`
|
|
215
|
-
|
|
216
|
-
// Translations
|
|
217
|
-
media.translations.forEach(t => console.log(`[${t.id}] ${t.title}`));
|
|
218
|
-
// [56] English
|
|
219
|
-
// [238] Русский
|
|
297
|
+
console.log(`${media.title} (${media.origTitle})`); // Мистер Робот (Mr. Robot)
|
|
298
|
+
console.log(`IMDb: ${media.rating.imdb?.score}`); // IMDb: 8.5
|
|
299
|
+
console.log(`Genres: ${media.genres.join(', ')}`);
|
|
300
|
+
console.log(`Directors: ${media.directors.join(', ')}`);
|
|
220
301
|
|
|
221
302
|
// Seasons & episodes
|
|
222
303
|
const season = media.seasons[0];
|
|
223
304
|
const episodes = media.episodes(season.id);
|
|
224
|
-
console.log(`${season.title}: ${episodes.length} episodes`);
|
|
225
305
|
|
|
226
306
|
// Stream S01E01 with the first available translation
|
|
227
307
|
const streams = await media.episode(season.id, episodes[0].episodeId).streams();
|
|
@@ -231,21 +311,38 @@ console.log('1080p:', streams['1080p']);
|
|
|
231
311
|
|
|
232
312
|
---
|
|
233
313
|
|
|
234
|
-
## Full Example —
|
|
314
|
+
## Full Example — Movie
|
|
235
315
|
|
|
236
316
|
```typescript
|
|
237
|
-
import { search, load } from 'rezka
|
|
317
|
+
import { search, load } from 'rezka.ts';
|
|
238
318
|
|
|
239
319
|
const results = await search('inception');
|
|
240
320
|
const media = await load(results[0].url);
|
|
241
321
|
|
|
242
|
-
|
|
243
|
-
|
|
322
|
+
console.log(`${media.duration} min, ${media.country}, ${media.ageRating}+`);
|
|
323
|
+
|
|
324
|
+
const streams = await media.streams(); // auto first translation
|
|
325
|
+
const streams = await media.streams(56); // specific translation
|
|
244
326
|
console.log(streams);
|
|
245
|
-
// { '1080p': 'https://...m3u8', '720p': '...', '
|
|
327
|
+
// { '1080p': 'https://...m3u8', '720p': '...', '4K': '...' }
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Full Example — Browse Catalog
|
|
246
333
|
|
|
247
|
-
|
|
248
|
-
|
|
334
|
+
```typescript
|
|
335
|
+
import { browse } from 'rezka.ts';
|
|
336
|
+
|
|
337
|
+
let page = 1;
|
|
338
|
+
while (true) {
|
|
339
|
+
const result = await browse({ type: 'series', filter: 'popular', page });
|
|
340
|
+
for (const item of result.items) {
|
|
341
|
+
console.log(`[${item.id}] ${item.title} ${item.info}`);
|
|
342
|
+
}
|
|
343
|
+
if (!result.hasNextPage) break;
|
|
344
|
+
page++;
|
|
345
|
+
}
|
|
249
346
|
```
|
|
250
347
|
|
|
251
348
|
---
|
|
@@ -253,32 +350,22 @@ const streams = await media.streams(56);
|
|
|
253
350
|
## Development
|
|
254
351
|
|
|
255
352
|
```bash
|
|
256
|
-
# Install dependencies
|
|
257
353
|
npm install
|
|
258
|
-
|
|
259
|
-
#
|
|
260
|
-
npm run build
|
|
261
|
-
|
|
262
|
-
# Run tests
|
|
263
|
-
npm test
|
|
264
|
-
|
|
265
|
-
# Watch mode
|
|
266
|
-
npm run test:watch
|
|
267
|
-
|
|
268
|
-
# Type-check
|
|
354
|
+
npm run build # CJS + ESM → dist/
|
|
355
|
+
npm test # 59 tests
|
|
269
356
|
npm run typecheck
|
|
270
357
|
```
|
|
271
358
|
|
|
272
359
|
### Project Structure
|
|
273
360
|
|
|
274
361
|
```
|
|
275
|
-
rezka
|
|
362
|
+
rezka.ts/
|
|
276
363
|
├── src/
|
|
277
|
-
│ ├── types.ts
|
|
278
|
-
│ ├── utils.ts
|
|
279
|
-
│ ├── http.ts
|
|
280
|
-
│ ├── Media.ts
|
|
281
|
-
│ └── index.ts
|
|
364
|
+
│ ├── types.ts # All TypeScript types and interfaces
|
|
365
|
+
│ ├── utils.ts # Stream URL decoder, parser, type detector
|
|
366
|
+
│ ├── http.ts # Axios client factory
|
|
367
|
+
│ ├── Media.ts # Media class — metadata + stream methods
|
|
368
|
+
│ └── index.ts # Public API: search(), load(), browse(), login(), createClient()
|
|
282
369
|
├── tests/
|
|
283
370
|
│ ├── utils.test.ts # Unit tests (decoder, parser, type detection)
|
|
284
371
|
│ └── rezka.test.ts # Integration tests with mocked HTTP
|