ichime-ts-api-client 1.0.0 → 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/.github/workflows/publish.yml +34 -0
- package/README.md +371 -29
- package/package.json +5 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Publish Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write # Required for OIDC
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- uses: pnpm/action-setup@v4
|
|
19
|
+
with:
|
|
20
|
+
version: 10
|
|
21
|
+
|
|
22
|
+
- uses: actions/setup-node@v4
|
|
23
|
+
with:
|
|
24
|
+
node-version: '24'
|
|
25
|
+
registry-url: 'https://registry.npmjs.org'
|
|
26
|
+
cache: 'pnpm'
|
|
27
|
+
|
|
28
|
+
- run: pnpm install --frozen-lockfile
|
|
29
|
+
|
|
30
|
+
- run: pnpm build
|
|
31
|
+
|
|
32
|
+
- run: pnpm test run
|
|
33
|
+
|
|
34
|
+
- run: npm publish
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ichime-ts-api-client
|
|
2
2
|
|
|
3
|
-
TypeScript API client для работы с сайтом
|
|
3
|
+
TypeScript API client для работы с сайтом **anime365**
|
|
4
4
|
|
|
5
5
|
## Установка
|
|
6
6
|
|
|
@@ -8,59 +8,401 @@ TypeScript API client для работы с сайтом [smotret-anime.com](ht
|
|
|
8
8
|
pnpm add ichime-ts-api-client
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Быстрый старт
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import { ApiClient } from "ichime-ts-api-client";
|
|
14
|
+
import { HttpSession, ApiClient, WebClient } from "ichime-ts-api-client";
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
// Создаем сессию
|
|
17
|
+
const session = new HttpSession("https://smotret-anime.com");
|
|
17
18
|
|
|
18
|
-
//
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
limit: 10,
|
|
22
|
-
offset: 0,
|
|
23
|
-
});
|
|
19
|
+
// Используем публичный API
|
|
20
|
+
const apiClient = new ApiClient(session);
|
|
21
|
+
const series = await apiClient.listSeries({ query: "naruto" });
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
// Или веб-клиент с авторизацией
|
|
24
|
+
const webClient = new WebClient(session);
|
|
25
|
+
await webClient.login("username", "password");
|
|
26
|
+
const profile = await webClient.getProfile();
|
|
26
27
|
```
|
|
27
28
|
|
|
28
|
-
##
|
|
29
|
+
## Архитектура
|
|
30
|
+
|
|
31
|
+
Библиотека состоит из трех основных компонентов:
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
- **HttpSession** — базовый класс для HTTP запросов с поддержкой cookies
|
|
34
|
+
- **ApiClient** — клиент для публичного JSON API (`/api/*`)
|
|
35
|
+
- **WebClient** — клиент для веб-скрапинга HTML страниц (требует авторизации для большинства методов)
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
## HttpSession
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
Базовый класс для выполнения HTTP запросов с автоматическим управлением cookies.
|
|
35
40
|
|
|
36
41
|
```typescript
|
|
37
|
-
|
|
42
|
+
import { HttpSession } from "ichime-ts-api-client";
|
|
43
|
+
|
|
44
|
+
const session = new HttpSession("https://smotret-anime.com");
|
|
45
|
+
|
|
46
|
+
// Выполнить запрос
|
|
47
|
+
const response = await session.request("/api/series", {
|
|
48
|
+
method: "GET",
|
|
49
|
+
timeout: 15000, // таймаут в мс (по умолчанию 10000)
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Работа с cookies
|
|
53
|
+
await session.setCookie("name", "value");
|
|
54
|
+
const value = await session.getCookie("name");
|
|
55
|
+
const cookieString = await session.getCookieString();
|
|
38
56
|
```
|
|
39
57
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
58
|
+
## ApiClient
|
|
59
|
+
|
|
60
|
+
Клиент для работы с публичным JSON API. Не требует авторизации.
|
|
43
61
|
|
|
44
|
-
|
|
62
|
+
### Создание клиента
|
|
45
63
|
|
|
46
|
-
|
|
64
|
+
```typescript
|
|
65
|
+
import { HttpSession, ApiClient } from "ichime-ts-api-client";
|
|
47
66
|
|
|
48
|
-
|
|
67
|
+
const session = new HttpSession("https://smotret-anime.com");
|
|
68
|
+
const client = new ApiClient(session);
|
|
69
|
+
```
|
|
49
70
|
|
|
50
|
-
|
|
51
|
-
- `options.query` - поисковый запрос
|
|
52
|
-
- `options.limit` - количество результатов
|
|
53
|
-
- `options.offset` - смещение для пагинации
|
|
54
|
-
- `options.chips` - фильтры в виде объекта ключ-значение
|
|
55
|
-
- `options.myAnimeListId` - ID из MyAnimeList
|
|
71
|
+
### Методы
|
|
56
72
|
|
|
57
|
-
|
|
73
|
+
#### `listSeries(options?: ListSeriesOptions): Promise<Series[]>`
|
|
74
|
+
|
|
75
|
+
Поиск сериалов.
|
|
76
|
+
|
|
77
|
+
**Параметры:**
|
|
78
|
+
- `query` — поисковый запрос
|
|
79
|
+
- `limit` — количество результатов
|
|
80
|
+
- `offset` — смещение для пагинации
|
|
81
|
+
- `chips` — фильтры в виде объекта ключ-значение
|
|
82
|
+
- `myAnimeListId` — ID из MyAnimeList
|
|
58
83
|
|
|
59
84
|
```typescript
|
|
60
85
|
const series = await client.listSeries({
|
|
61
86
|
query: "attack on titan",
|
|
62
87
|
limit: 20,
|
|
63
88
|
});
|
|
89
|
+
|
|
90
|
+
// Поиск по MyAnimeList ID
|
|
91
|
+
const series = await client.listSeries({
|
|
92
|
+
myAnimeListId: 16498,
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### `getSeries(seriesId: number): Promise<SeriesFull>`
|
|
97
|
+
|
|
98
|
+
Получить полную информацию о сериале.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const series = await client.getSeries(12345);
|
|
102
|
+
console.log(series.titles, series.descriptions, series.genres);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### `listEpisodes(options?: ListEpisodesOptions): Promise<Episode[]>`
|
|
106
|
+
|
|
107
|
+
Получить список эпизодов.
|
|
108
|
+
|
|
109
|
+
**Параметры:**
|
|
110
|
+
- `seriesId` — ID сериала
|
|
111
|
+
- `limit` — количество результатов
|
|
112
|
+
- `offset` — смещение для пагинации
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const episodes = await client.listEpisodes({
|
|
116
|
+
seriesId: 12345,
|
|
117
|
+
limit: 50,
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### `getEpisode(episodeId: number): Promise<EpisodeFull>`
|
|
122
|
+
|
|
123
|
+
Получить полную информацию об эпизоде.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const episode = await client.getEpisode(67890);
|
|
127
|
+
console.log(episode.translations);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### `getTranslation(translationId: number): Promise<TranslationFull>`
|
|
131
|
+
|
|
132
|
+
Получить информацию о переводе.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
const translation = await client.getTranslation(111222);
|
|
136
|
+
console.log(translation.authorsSummary, translation.qualitySummary);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### `getTranslationEmbed(translationId: number): Promise<TranslationEmbed>`
|
|
140
|
+
|
|
141
|
+
Получить embed-информацию для воспроизведения перевода.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const embed = await client.getTranslationEmbed(111222);
|
|
145
|
+
console.log(embed.stream); // информация о потоках видео
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## WebClient
|
|
149
|
+
|
|
150
|
+
Клиент для работы с веб-страницами через HTML парсинг. Большинство методов требуют авторизации.
|
|
151
|
+
|
|
152
|
+
### Создание клиента
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { HttpSession, WebClient } from "ichime-ts-api-client";
|
|
156
|
+
|
|
157
|
+
const session = new HttpSession("https://smotret-anime.com");
|
|
158
|
+
const client = new WebClient(session);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Авторизация
|
|
162
|
+
|
|
163
|
+
#### `login(username: string, password: string): Promise<void>`
|
|
164
|
+
|
|
165
|
+
Авторизация на сайте.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
await client.login("user@example.com", "password123");
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Профиль
|
|
172
|
+
|
|
173
|
+
#### `getProfile(): Promise<Profile>`
|
|
174
|
+
|
|
175
|
+
Получить профиль текущего пользователя.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
const profile = await client.getProfile();
|
|
179
|
+
console.log(profile.id, profile.name, profile.avatarUrl);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Эпизоды
|
|
183
|
+
|
|
184
|
+
#### `getPersonalEpisodes(page: number): Promise<NewPersonalEpisode[]>`
|
|
185
|
+
|
|
186
|
+
Получить персональную ленту новых эпизодов (из списка отслеживаемых аниме).
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const episodes = await client.getPersonalEpisodes(1);
|
|
190
|
+
for (const ep of episodes) {
|
|
191
|
+
console.log(ep.seriesTitleRu, ep.episodeNumberLabel, ep.episodeUpdateType);
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### `getRecentEpisodes(page: number): Promise<NewRecentEpisode[]>`
|
|
196
|
+
|
|
197
|
+
Получить общую ленту недавно вышедших эпизодов.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
const episodes = await client.getRecentEpisodes(1);
|
|
201
|
+
for (const ep of episodes) {
|
|
202
|
+
console.log(ep.seriesTitleRu, ep.episodeNumberLabel, ep.episodeUploadedAt);
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### `markEpisodeAsWatched(translationId: number): Promise<void>`
|
|
207
|
+
|
|
208
|
+
Отметить эпизод как просмотренный.
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
await client.markEpisodeAsWatched(111222);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Список аниме
|
|
215
|
+
|
|
216
|
+
#### `getAnimeList(userId: number, category: AnimeListCategory): Promise<AnimeListEntry[]>`
|
|
217
|
+
|
|
218
|
+
Получить список аниме пользователя по категории.
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import type { AnimeListCategory } from "ichime-ts-api-client";
|
|
222
|
+
|
|
223
|
+
const watching = await client.getAnimeList(userId, "watching");
|
|
224
|
+
const completed = await client.getAnimeList(userId, "completed");
|
|
225
|
+
const onHold = await client.getAnimeList(userId, "onHold");
|
|
226
|
+
const dropped = await client.getAnimeList(userId, "dropped");
|
|
227
|
+
const planToWatch = await client.getAnimeList(userId, "planToWatch");
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
#### `getAnimeListEditableEntry(seriesId: number): Promise<AnimeListEditableEntry>`
|
|
231
|
+
|
|
232
|
+
Получить редактируемую запись из списка аниме.
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
const entry = await client.getAnimeListEditableEntry(12345);
|
|
236
|
+
console.log(entry.status, entry.episodesWatched, entry.score);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### `editAnimeListEntry(seriesId, score, episodes, status, comment): Promise<void>`
|
|
240
|
+
|
|
241
|
+
Редактировать запись в списке аниме.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { AnimeListEntryStatusNumericId } from "ichime-ts-api-client";
|
|
245
|
+
|
|
246
|
+
await client.editAnimeListEntry(
|
|
247
|
+
12345, // seriesId
|
|
248
|
+
8, // score (1-10)
|
|
249
|
+
5, // episodes watched
|
|
250
|
+
AnimeListEntryStatusNumericId.watching, // status
|
|
251
|
+
"Отличное аниме!" // comment
|
|
252
|
+
);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Моменты
|
|
256
|
+
|
|
257
|
+
#### `getMoments(page: number, sort?: MomentSorting): Promise<MomentPreview[]>`
|
|
258
|
+
|
|
259
|
+
Получить список моментов.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import type { MomentSorting } from "ichime-ts-api-client";
|
|
263
|
+
|
|
264
|
+
const moments = await client.getMoments(1, "popular");
|
|
265
|
+
for (const moment of moments) {
|
|
266
|
+
console.log(moment.momentTitle, moment.sourceDescription, moment.durationSeconds);
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
#### `getMomentsBySeries(seriesId: number, page: number): Promise<MomentPreview[]>`
|
|
271
|
+
|
|
272
|
+
Получить моменты по конкретному сериалу.
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
const moments = await client.getMomentsBySeries(12345, 1);
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### `getMomentDetails(momentId: number): Promise<MomentDetails>`
|
|
279
|
+
|
|
280
|
+
Получить детали момента.
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
const details = await client.getMomentDetails(99999);
|
|
284
|
+
console.log(details.seriesId, details.seriesTitle, details.episodeId);
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### `getMomentEmbed(momentId: number): Promise<MomentEmbed>`
|
|
288
|
+
|
|
289
|
+
Получить URL видео для момента.
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
const embed = await client.getMomentEmbed(99999);
|
|
293
|
+
console.log(embed.videoUrl);
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Типы
|
|
297
|
+
|
|
298
|
+
### API типы
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
import type {
|
|
302
|
+
// Сериалы
|
|
303
|
+
Series,
|
|
304
|
+
SeriesFull,
|
|
305
|
+
SeriesFullDescription,
|
|
306
|
+
SeriesFullGenre,
|
|
307
|
+
SeriesType,
|
|
308
|
+
Titles,
|
|
309
|
+
// Эпизоды
|
|
310
|
+
Episode,
|
|
311
|
+
EpisodeFull,
|
|
312
|
+
// Переводы
|
|
313
|
+
Translation,
|
|
314
|
+
TranslationFull,
|
|
315
|
+
TranslationEmbed,
|
|
316
|
+
TranslationEmbedStream,
|
|
317
|
+
} from "ichime-ts-api-client";
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Web типы
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
import type {
|
|
324
|
+
// Профиль
|
|
325
|
+
Profile,
|
|
326
|
+
// Эпизоды
|
|
327
|
+
NewPersonalEpisode,
|
|
328
|
+
NewRecentEpisode,
|
|
329
|
+
// Список аниме
|
|
330
|
+
AnimeListCategory,
|
|
331
|
+
AnimeListEntry,
|
|
332
|
+
AnimeListEditableEntry,
|
|
333
|
+
AnimeListEntryStatus,
|
|
334
|
+
EditAnimeListResult,
|
|
335
|
+
// Моменты
|
|
336
|
+
MomentPreview,
|
|
337
|
+
MomentDetails,
|
|
338
|
+
MomentEmbed,
|
|
339
|
+
MomentSorting,
|
|
340
|
+
VideoSource,
|
|
341
|
+
} from "ichime-ts-api-client";
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Утилиты для типов
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
import {
|
|
348
|
+
AnimeListCategoryNumericId,
|
|
349
|
+
AnimeListCategoryWebPath,
|
|
350
|
+
AnimeListEntryStatusNumericId,
|
|
351
|
+
animeListCategoryFromNumericId,
|
|
352
|
+
animeListEntryStatusFromNumericId,
|
|
353
|
+
} from "ichime-ts-api-client";
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## Вспомогательные функции
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
import {
|
|
360
|
+
parseApiDate,
|
|
361
|
+
isEmptyDate,
|
|
362
|
+
parseWebDate,
|
|
363
|
+
parseDurationString,
|
|
364
|
+
extractIdentifiersFromUrl,
|
|
365
|
+
} from "ichime-ts-api-client";
|
|
366
|
+
|
|
367
|
+
// Парсинг дат из API
|
|
368
|
+
const date = parseApiDate("2024-01-15 12:30:00");
|
|
369
|
+
|
|
370
|
+
// Проверка пустой даты
|
|
371
|
+
if (!isEmptyDate("0000-00-00 00:00:00")) {
|
|
372
|
+
// дата валидна
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Парсинг даты из веб-страницы
|
|
376
|
+
const webDate = parseWebDate("15 января 12:30");
|
|
377
|
+
|
|
378
|
+
// Парсинг длительности "1:30" -> 90 секунд
|
|
379
|
+
const seconds = parseDurationString("1:30");
|
|
380
|
+
|
|
381
|
+
// Извлечение ID из URL
|
|
382
|
+
const url = new URL("https://smotret-anime.com/catalog/1234-naruto/5678-episode-1");
|
|
383
|
+
const { seriesId, episodeId } = extractIdentifiersFromUrl(url);
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Обработка ошибок
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
import { ApiClientError, ApiError, WebClientError } from "ichime-ts-api-client";
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
await apiClient.getSeries(99999999);
|
|
393
|
+
} catch (error) {
|
|
394
|
+
if (error instanceof ApiClientError) {
|
|
395
|
+
console.error("API client error:", error.message);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
try {
|
|
400
|
+
await webClient.getProfile();
|
|
401
|
+
} catch (error) {
|
|
402
|
+
if (error instanceof WebClientError) {
|
|
403
|
+
console.error("Web client error:", error.message);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
64
406
|
```
|
|
65
407
|
|
|
66
408
|
## Разработка
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ichime-ts-api-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "TypeScript API client for smotret-anime.com",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": ">=24.0.0"
|
|
16
16
|
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/dimensi/ichime-ts-api-client"
|
|
20
|
+
},
|
|
17
21
|
"scripts": {
|
|
18
22
|
"build": "tsdown",
|
|
19
23
|
"test": "vitest",
|