TypeScript/ESM-обёртка над API smotret-anime.online (бывший anime365.ru): каталог аниме, переводы, эпизоды, ссылки на видео/субтитры и авторизация.
- Node.js 18 или новее (используется встроенный
fetch) - ESM (
"type": "module")
npm install anime365wrapperimport { Anime365API } from 'anime365wrapper';
const api = new Anime365API({ userAgent: 'MyApp/1.0' });
const series = await api.getSeriesList({ query: 'gate', limit: 10 });
const translations = await api.getTranslations({ feed: 'recent' });API всегда указывать userAgent — название сайта или программы, отправляемое в заголовке User-Agent.
- Ошибки приходят с HTTP 200. Тело ответа при ошибке —
{ "error": { "code": number, "message": string } }, статус при этом не меняется. Библиотека разбирает это сама и бросаетAnimeApiError— проверятьresponse.okбессмысленно, полагайтесь на try/catch. - Для полного сканирования используйте
afterId, а неoffset. При счёте на сотни тысяч записейoffsetработает медленно;feed: 'id'иfeed: 'all'поддерживают постраничный проход черезafterId(id последней полученной записи). subtitlesUrlизgetTranslationEmbed()бывает относительным (например/episodeTranslations/123.ass?willcache). Библиотека сама приводит его к абсолютному URL относительно домена API.- У
access_tokenнет срока действия — он действителен, пока не сменится пароль. Храните его как секрет (переменная окружения, секрет-хранилище), не коммитьте в репозиторий. - Домен API может меняться и блокироваться регионально — у сервиса несколько официальных зеркал (
smotret-anime.online,smotret-anime.app— рабочее зеркало для РФ,smotret-anime.org,anime365.ru,anime-365.ru). По умолчанию библиотека пробует их все по очереди при сетевых сбоях — см. «Зеркала и fallback» ниже.
new Anime365API(options?: {
baseUrl?: string | string[]; // по умолчанию DEFAULT_MIRRORS (все известные зеркала)
userAgent?: string; // по умолчанию 'Anime365Wrapper/2.0'
accessToken?: string; // если токен уже известен
timeoutMs?: number; // таймаут запроса, по умолчанию не ограничен
})По умолчанию (baseUrl не указан) используется список DEFAULT_MIRRORS — все известные домены anime365 в порядке приоритета. При сетевом сбое (недоступный домен, DNS, таймаут) библиотека автоматически пробует следующее зеркало из списка; ошибки самого API (AnimeApiError, например 404 или неверный пароль) fallback не запускают — домен ответил, значит проблема не в нём. Зеркало, на котором прошёл последний успешный запрос, запоминается и используется первым в следующий раз.
import { Anime365API, DEFAULT_MIRRORS } from 'anime365wrapper';
console.log(DEFAULT_MIRRORS); // ['https://smotret-anime.online/api', 'https://smotret-anime.app/api', ...]
const api = new Anime365API(); // fallback по всем зеркалам сразу из коробки
console.log(api.activeBaseUrl); // текущее рабочее зеркало
// baseUrl строкой — фиксированный домен без fallback (поведение как раньше)
const pinned = new Anime365API({ baseUrl: 'https://smotret-anime.online/api' });
// свой список зеркал
const custom = new Anime365API({ baseUrl: ['https://smotret-anime.app/api', 'https://anime365.ru/api'] });import { Anime365API } from 'anime365wrapper';
const api = new Anime365API({ userAgent: 'MyApp/1.0' });
const token = await api.login('user@example.com', 'password'); // сохраняется в this
const user = await api.getCurrentUser(); // требует токен
// либо переиспользовать уже полученный токен
api.setAccessToken(token);Получить access_token также можно вручную на сайте: api.socialLoginUrl (вход по email/паролю или через соцсети) и api.accessTokenPageUrl (страница с готовым токеном для авторизованного пользователя).
Тонкая обёртка, которая запоминает токен после login() и проксирует остальные методы Anime365API без дублирования его при каждом вызове:
import { UserSession } from 'anime365wrapper';
const session = new UserSession({ userAgent: 'MyApp/1.0' });
await session.login('user@example.com', 'password');
const translations = await session.getTranslations({ feed: 'recent' });
const user = await session.getCurrentUser();
// доступ к нижележащему клиенту, если нужен метод, не проксируемый UserSession
session.client.getAccessToken();getTranslations(query?: {
feed?: 'recent' | 'id' | 'all'; // recent — последние добавленные онгоинги, id/all — полный список (all включает неактивные)
afterId?: number; // для постраничного сканирования feed=id/all
seriesId?: number;
episodeId?: number;
fields?: string | string[]; // ограничить набор полей ответа
limit?: number;
offset?: number;
}): Promise<Translation[]>
getTranslationById(id: number): Promise<Translation>
getTranslationEmbed(id: number): Promise<EmbedTranslation> // ссылки на видео/субтитры, требует авторизацииgetSeriesList(query?: {
fields?: string | string[];
chips?: string | Chip[]; // расширенный фильтр каталога, см. buildChips() ниже
myAnimeListId?: number;
query?: string; // поиск по названию
pretty?: number;
limit?: number;
offset?: number;
}): Promise<Series[]>
getSeriesById(id: number): Promise<Series>getEpisodeById(id: number): Promise<Episode>getCurrentUser(): Promise<User> // требует access_tokenСписок допустимых полей и операторов сам API не публикует — их видно только на сайте (вкладка фильтров каталога) или в site.ccsData исходного кода страницы /catalog. buildChips() лишь механически собирает строку в формате, который использует сайт, ничего не проверяя:
import { Anime365API, buildChips } from 'anime365wrapper';
const chips = buildChips([
{ field: 'genre', operator: '@=', value: [8, 35] },
'genre_op=and',
]);
// chips === 'genre@=8,35;genre_op=and'
const api = new Anime365API();
const results = await api.getSeriesList({ chips });import { Anime365API, AnimeApiError, AnimeApiNetworkError } from 'anime365wrapper';
const api = new Anime365API();
try {
await api.getSeriesById(999999999);
} catch (error) {
if (error instanceof AnimeApiError) {
console.error(`API вернул ошибку ${error.code}: ${error.message}`); // 404: Series not found.
} else if (error instanceof AnimeApiNetworkError) {
console.error('Сеть недоступна или ответ не удалось разобрать:', error.message);
} else {
throw error;
}
}Экспортируются все модели ответов API:
import type {
Translation,
DownloadOption,
StreamOption,
EmbedTranslation,
Series,
Episode,
User,
LoginResponse,
ApiResponse,
} from 'anime365wrapper';В каталоге examples/ — рабочие скрипты: поиск аниме, лента последних переводов, полное сканирование по afterId, авторизация + получение embed-данных, расширенный фильтр через buildChips(). Запуск после сборки:
npm run build
npx tsx examples/search-series.ts gategit clone https://github.com/thedvxch/anime365wrapper.git
cd anime365wrapper
npm install
npm run build
npm test