@kitsuneislife/miau-index
v1.1.0
Published
A powerful TypeScript anime indexer that aggregates and unifies anime data from multiple sources including episodes, seasons, and torrents
Maintainers
Readme
🐱 Miau-Index
A powerful TypeScript anime indexer that aggregates and unifies anime data from multiple sources
📋 About
Miau-Index is a powerful TypeScript library designed to aggregate, unify, and normalize anime data from multiple external sources like MyAnimeList, AniList, Kitsu, and more. The project offers a robust and scalable architecture to manage complete anime information, including:
- 📺 Basic information (title, type, status, episodes)
- ⭐ Ratings and rankings from multiple sources
- 📝 Synopses and descriptions
- 🎭 Genres, themes, and demographics
- 🎬 Detailed seasons and episodes
- 👥 Characters, voice actors, and staff
- 🏢 Studios and producers
- 🔗 Anime relations (sequels, prequels, etc.)
- 🧲 NEW: Torrent indexing from Nyaa.si (optional extension)
✨ Features
🌟 Priority: Open and Free Sources
- Zero API Keys Required: Works perfectly without any configuration
- AniList: Public and completely free GraphQL API
- Kitsu: Public and completely free JSON:API
- MyAnimeList: OPTIONAL - only if you have/want to use an API key
- Automatic Preference: Prioritizes open sources by default
Core Features
- Data Unification: Combines information from multiple APIs to create a complete and accurate record
- Type-Safe: Fully typed with TypeScript for maximum safety
- Extensible: Modular architecture allows easy addition of new providers
- 🆕 Nyaa Extension: Optional torrent indexing with quality detection, metadata extraction, and smart filtering
Validation and Security
- Zod Schemas: Robust runtime validation for all data models
- Type Guards: Helper functions for type checking
- Error Handling: Custom error classes for better error management
- Input Sanitization: Protection against injection attacks
Performance
- Cache Service: In-memory caching system with configurable TTL
- Rate Limiting: Intelligent request rate control
- Retry Logic: Automatic retry with exponential backoff
- Timeout Configuration: Configurable request timeouts
Developer Experience
- Logging: Configurable logging system with multiple levels
- Helper Utilities: 15+ ready-to-use helper functions
- Comprehensive Tests: 113+ automated tests (98.3% passing)
- Full Documentation: Complete API documentation and guides
- Metrics & Observability: Built-in metrics tracking for monitoring
🚀 Installation
npm install @kitsuneislife/miau-indexOptional: Nyaa Torrent Extension
npm install @kitsuneislife/miau-index @kitsuneislife/nyaa📖 Basic Usage
import { MiauIndex, DataSource } from '@kitsuneislife/miau-index';
// Initialize the indexer
// Works WITHOUT API KEYS - uses open sources (AniList, Kitsu)
const miauIndex = new MiauIndex({
// All settings are optional!
malApiKey: 'your-mal-api-key', // OPTIONAL: only if you have MAL API key
enabledProviders: {
myAnimeList: true, // Ignored if no API key
aniList: true, // ✓ Open source - NO API key needed
kitsu: true, // ✓ Open source - NO API key needed
},
preferOpenSources: true, // Prioritize open sources (default: true)
enableLogging: true,
});
// Fetch specific anime from AniList
const anime = await miauIndex.fetchAnime([
{ source: DataSource.ANILIST, id: '5114' }
]);
console.log(anime.title.romaji); // "Fullmetal Alchemist: Brotherhood"
console.log(anime.ratings); // Ratings with score and votes
// Search anime by title (searches all providers)
const results = await miauIndex.searchAnime('Cowboy Bebop', 10);
// Get seasonal anime
const winterAnime = await miauIndex.getSeasonalAnime(2024, 'winter');
// Search in local repository
const localResults = await miauIndex.searchLocal('Naruto', 5);
// Check provider health
const health = await miauIndex.checkProviders();
console.log(health); // { ANILIST: true, MYANIMELIST: true, ... }
// Repository statistics
const stats = await miauIndex.getStats();
console.log(stats.totalAnime); // Total stored anime🆕 Nyaa Torrent Extension
import { MiauIndex } from '@kitsuneislife/miau-index';
import { TorrentQuality } from '@kitsuneislife/miau-index';
// Enable Nyaa extension
const miauIndex = new MiauIndex({
enableNyaa: true, // Enable torrent indexing
nyaaOptions: {
autoIndex: true,
minSeeders: 5,
preferredQuality: TorrentQuality.FULL_HD_1080p,
enableCache: true,
cacheTTL: 3600000, // 1 hour
timeout: 30000,
maxRetries: 3,
},
});
// Index torrents for an anime
const torrents = await miauIndex.indexTorrents(anime);
console.log(`Found ${torrents.length} torrents`);
// Get best torrent for an episode
const bestTorrent = await miauIndex.getBestTorrent(anime, 1);
console.log(bestTorrent.magnetLink);
// Search with filters
const filtered = await miauIndex.searchTorrents(anime, {
quality: TorrentQuality.FULL_HD_1080p,
minSeeders: 10,
});
// Get metrics
const metrics = miauIndex.getNyaaMetrics();
console.log(`Cache hit rate: ${metrics.cacheHitRate.toFixed(2)}%`);🌸 Extensão Nyaa (OPCIONAL)
Miau-Index inclui uma extensão TOTALMENTE OPCIONAL para indexar torrents de anime do Nyaa.si. Esta extensão usa o pacote @kitsuneislife/nyaa para buscar e associar torrents aos animes.
Instalação da Extensão
npm install @kitsuneislife/nyaaCaracterísticas da Extensão Nyaa
- 🔍 Indexação Automática: Busca torrents para animes e episódios
- 📊 Metadata Completa: Extrai qualidade, codec, idiomas, release group
- 🎯 Associação Inteligente: Liga torrents aos episódios corretos
- 🌐 Multi-Idioma: Suporte para áudio e legendas em vários idiomas
- 📦 Batches: Detecta e indexa torrents com múltiplos episódios
- ⚡ Filtros Avançados: Busca por qualidade, idioma, seeders, etc.
- 📈 Estatísticas: Analytics detalhados sobre torrents
Uso da Extensão Nyaa
import { MiauIndex, DataSource } from '@kitsuneislife/miau-index';
import { TorrentQuality, TorrentLanguage } from '@kitsuneislife/miau-index';
// Habilitar extensão Nyaa
const miauIndex = new MiauIndex({
enableNyaa: true, // ✓ ATIVA a extensão de torrents
nyaaOptions: {
autoIndex: true,
minSeeders: 5,
trustedOnly: false,
preferredQuality: TorrentQuality.FULL_HD_1080p,
preferredLanguages: [TorrentLanguage.JAPANESE, TorrentLanguage.ENGLISH],
},
});
// Verificar se está habilitada
console.log(miauIndex.isNyaaEnabled); // true
// Buscar anime
const anime = await miauIndex.fetchAnime([
{ source: DataSource.ANILIST, id: '21' } // One Piece
]);
// 1. Indexar todos os torrents do anime
const torrents = await miauIndex.indexTorrents(anime);
console.log(`${torrents.length} torrents encontrados`);
// 2. Indexar torrents de episódio específico
const ep1Torrents = await miauIndex.indexEpisodeTorrents(anime, 1);
// 3. Buscar torrents com filtros
const filtered = await miauIndex.searchTorrents(anime, {
quality: TorrentQuality.FULL_HD_1080p,
subtitleLanguage: TorrentLanguage.ENGLISH,
minSeeders: 10,
episodeNumber: 1,
});
// 4. Obter melhor torrent para episódio
const best = await miauIndex.getBestTorrent(anime, 1);
console.log(best?.magnetLink);
console.log(best?.metadata.quality);
console.log(best?.metadata.releaseGroup);
// 5. Estatísticas de torrents
const stats = await miauIndex.getTorrentStats(anime.id);
console.log(`Total: ${stats.totalTorrents}`);
console.log(`Média de seeders: ${stats.averageSeeders}`);
console.log(`Por qualidade:`, stats.byQuality);
console.log(`Por idioma:`, stats.byLanguage);
// 6. Atualizar informações do torrent (seeders/leechers)
const updated = await miauIndex.refreshTorrent(best.id);Tipos de Torrent
Qualidades
SD_480p: 480pHD_720p: 720pFULL_HD_1080p: 1080p (padrão)UHD_2160p/UHD_4K: 4K/2160pRAW: Raw (sem legendas)
Idiomas
JAPANESE: JaponêsENGLISH: InglêsPORTUGUESE_BR: Português (BR)SPANISH: EspanholFRENCH: FrancêsGERMAN: AlemãoITALIAN: ItalianoRUSSIAN: Russo
🎬 Episode & Season Fetching
Miau-Index v1.1.0+ supports detailed episode and season fetching from multiple providers with automatic organization and torrent association.
Features
- 📺 Complete Episode Metadata: Title, synopsis, duration, aired date, images
- 📅 Auto Season Organization: Automatically organizes episodes into seasons (13-episode heuristic)
- 🔗 Torrent Association: Links torrents to specific episodes
- 🌐 Multi-Provider Fallback: Tries multiple providers for episode data
- 🎯 Provider-Specific Data:
- AniList: GraphQL with airing schedule and streaming metadata
- Kitsu: Full REST API with detailed episode information
- Jikan: Complete MAL episode data with filler/recap flags
- MyAnimeList: Basic episode list from count
Usage
import { MiauIndex } from '@kitsuneislife/miau-index';
const miauIndex = new MiauIndex();
// Search for anime
const results = await miauIndex.searchAnime('Steins Gate');
const anime = results[0];
// Fetch episodes with metadata
const episodes = await miauIndex.getEpisodes(anime);
console.log(`Found ${episodes.length} episodes`);
// Display first episode info
const ep1 = episodes[0];
console.log(ep1.title); // "Episode 1: Prologue of the Beginning and End"
console.log(ep1.synopsis); // Episode description
console.log(ep1.duration); // 24 minutes
console.log(ep1.aired); // Date object
console.log(ep1.filler); // false
console.log(ep1.recap); // false
// Get anime with complete episode data
const completeData = await miauIndex.getAnimeWithEpisodes(anime.id);
console.log(completeData.anime);
console.log(completeData.episodes);
console.log(completeData.seasons); // Auto-organized seasons
// Seasons are automatically created
if (completeData.seasons) {
completeData.seasons.forEach(season => {
console.log(`Season ${season.seasonNumber}: ${season.episodes.length} episodes`);
});
}Episode-Torrent Integration
When using the Nyaa extension, episodes and torrents are automatically associated:
const miauIndex = new MiauIndex({
enableNyaa: true,
});
// Search and index
const results = await miauIndex.searchAnime('Cowboy Bebop');
const anime = results[0];
// Index torrents (creates episodes automatically)
await miauIndex.indexTorrents(anime);
// Get complete data with torrents
const data = await miauIndex.getAnimeWithEpisodes(anime.id);
// Associate torrents with episodes
await miauIndex.associateTorrentsWithEpisodes(anime.id);
// Get best torrent for specific episode
const bestForEp1 = await miauIndex.getBestTorrent(anime, 1);
console.log(`Best torrent for episode 1: ${bestForEp1?.title}`);Episode Model
interface Episode {
id: string; // Unique ID
animeId: string; // Parent anime ID
number: number; // Episode number
title?: string; // Episode title
synopsis?: string; // Episode description
duration?: number; // Duration in minutes
aired?: Date; // Air date
images?: { // Episode thumbnail
small?: string;
medium?: string;
large?: string;
original?: string;
};
filler?: boolean; // Is filler episode
recap?: boolean; // Is recap episode
externalIds: ExternalId[]; // Provider IDs
createdAt: Date;
updatedAt: Date;
}
interface AnimeSeason {
id: string;
animeId: string;
seasonNumber: number;
title?: string;
episodeCount: number;
episodes?: string[]; // Episode IDs
aired?: {
start?: Date;
end?: Date;
};
externalIds: ExternalId[];
createdAt: Date;
updatedAt: Date;
}🌸 Jikan Provider (FREE MyAnimeList Data)
Miau-Index v1.1.0+ includes the Jikan provider - a free, no-API-key provider for MyAnimeList data powered by Jikan API.
Features
- ✅ Zero Configuration: No API key required
- 🌐 Complete MAL Data: Full anime information from MyAnimeList
- 📺 Episode Support: Paginated episode fetching with filler/recap flags
- 📅 Seasonal Anime: Browse anime by season and year
- ⚡ Rate Limiting: Respects Jikan's 60 requests/minute limit
- 💾 24h Cache: Matches Jikan's caching strategy
Auto-Initialization
Jikan is automatically initialized by default:
const miauIndex = new MiauIndex(); // Jikan is ready to use!
// Search uses Jikan (among other providers)
const results = await miauIndex.searchAnime('Naruto');
// Fetch seasonal anime
const winter2023 = await miauIndex.getSeasonalAnime(2023, 'winter');Priority System
- AniList (open source, no key required)
- Kitsu (open source, no key required)
- Jikan (unofficial MAL, no key required) ← NEW
- MyAnimeList (official, requires API key, overrides Jikan)
// Without MAL API key: Uses Jikan for MAL data
const miauIndex = new MiauIndex();
// With MAL API key: Uses official MAL API (overrides Jikan)
const miauIndexOfficial = new MiauIndex({
malApiKey: 'your-mal-client-id',
});Jikan-Specific Features
import { JikanProvider } from '@kitsuneislife/miau-index';
// Use Jikan directly
const jikan = new JikanProvider();
// Fetch anime by MAL ID
const anime = await jikan.fetchAnimeById('1'); // Cowboy Bebop
// Search anime
const results = await jikan.searchAnime('One Piece', 10);
// Get seasonal anime
const summer2023 = await jikan.getSeasonalAnime(2023, 'summer');
// Fetch episodes (with pagination)
const episodes = await jikan.fetchEpisodes('anime-id', '21'); // One Piece episodes
// Check availability
const available = await jikan.isAvailable(); // trueRate Limiting & Caching
Jikan respects API limits and caches aggressively:
- Rate Limit: 55 requests/minute (stays under 60/min limit)
- Cache Duration: 24 hours (matches Jikan's server-side cache)
- Retry Logic: 3 retries with 2-second delay
- Timeout: 15 seconds per request
Example showing cache benefits:
const jikan = new JikanProvider();
// First call: Hits Jikan API (~200-500ms)
const anime1 = await jikan.fetchAnimeById('1');
// Second call: From cache (<1ms)
const anime2 = await jikan.fetchAnimeById('1');
console.log(anime1.id === anime2.id); // trueCHINESE: ChinêsKOREAN: CoreanoMULTI: Múltiplos idiomas
Tipos de Release
EPISODE: Episódio individualBATCH: Lote de episódiosSEASON: Temporada completaCOMPLETE: Série completaMOVIE: FilmeOVA: OVASPECIAL: Especial
Metadata Extraída
Cada torrent inclui metadata completa extraída do título:
- ✅ Qualidade (480p, 720p, 1080p, 4K)
- ✅ Codec (H.264, HEVC/H.265, AV1)
- ✅ Idiomas de áudio (Dual Audio detectado)
- ✅ Idiomas de legendas (Multi-sub detectado)
- ✅ Release group
- ✅ Tipo de release (episódio, batch, etc.)
- ✅ Range de episódios (para batches)
Exemplo Completo
Veja o arquivo examples/nyaa-extension.ts para exemplos completos de uso.
npx ts-node examples/nyaa-extension.ts⚙️ Configuração
Crie um arquivo .env baseado no .env.example:
# MyAnimeList API
MAL_CLIENT_ID=seu_client_id
MAL_CLIENT_SECRET=seu_client_secret
# AniList API
ANILIST_CLIENT_ID=seu_client_id
ANILIST_CLIENT_SECRET=seu_client_secret
# Kitsu API
KITSU_API_KEY=sua_api_key
# Configurações da aplicação
NODE_ENV=development
LOG_LEVEL=info
REPOSITORY_TYPE=memory
CACHE_ENABLED=true
CACHE_TTL=3600
RATE_LIMIT_ENABLED=true
RATE_LIMIT_RPM=60🏗️ Arquitetura
Estrutura de Diretórios
src/
├── config/ # Configurações da aplicação
├── models/ # Modelos de dados (Anime, Episode, People)
├── providers/ # Provedores de dados externos (MAL, AniList, Kitsu)
├── repositories/ # Camada de persistência
├── services/ # Serviços de lógica de negócio
├── types/ # Definições de tipos TypeScript
├── utils/ # Utilitários (logger, errors)
├── example.ts # Exemplo de uso
└── index.ts # Ponto de entrada principalComponentes Principais
Modelos
- Anime: Modelo principal com todas as informações unificadas
- Episode: Informações de episódios individuais
- AnimeSeason: Agrupamento de episódios por temporada
- Character: Personagens e dubladores
- Studio: Informações de estúdios
Provedores
- BaseAnimeProvider: Classe abstrata base para todos os provedores
- MyAnimeListProvider: Integração completa com MyAnimeList
- AniListProvider: Integração completa com AniList (GraphQL)
- KitsuProvider: Integração completa com Kitsu
Serviços
- AnimeUnificationService: Serviço principal que unifica dados de múltiplas fontes usando estratégias de consenso e prioridade
🔧 Desenvolvimento
Scripts Disponíveis
# Instalar dependências
npm install
# Compilar TypeScript
npm run build
# Executar exemplo
npm run dev
# Executar aplicação compilada
npm start
# Testes
npm test
npm run test:watch
# Linting
npm run lint
npm run lint:fix
# Formatação
npm run format
npm run format:checkAdicionar um Novo Provedor
- Crie uma nova classe que estende
BaseAnimeProvider - Implemente os métodos obrigatórios:
getSource(): Retorna oDataSourcefetchAnimeById(): Busca anime por ID externosearchAnime(): Busca animes por querygetSeasonalAnime(): Busca animes de uma temporada
import { BaseAnimeProvider } from './BaseProvider';
import { DataSource } from '../types/common';
export class NovoProvider extends BaseAnimeProvider {
constructor() {
super('https://api.exemplo.com');
}
getSource(): DataSource {
return DataSource.NOVO_PROVIDER;
}
async fetchAnimeById(externalId: string): Promise<Anime | null> {
// Implementação
}
// ... outros métodos
}- Registre o provedor no
MiauIndex:
const novoProvider = new NovoProvider();
miauIndex.registerProvider(novoProvider);📊 Tipos de Dados
AnimeType
- TV
- MOVIE
- OVA
- ONA
- SPECIAL
- MUSIC
AnimeStatus
- AIRING
- FINISHED
- NOT_YET_AIRED
- CANCELLED
DataSource
- MYANIMELIST
- ANILIST
- KITSU
- ANIDB
- TMDB
🤝 Contributing
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
Quick Start for Contributors
- Fork the repository
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/miau-index.git - Install dependencies:
npm install - Create a branch:
git checkout -b feature/my-feature - Make your changes and add tests
- Run tests:
npm test - Commit:
git commit -m "feat: add my feature" - Push:
git push origin feature/my-feature - Open a Pull Request
📝 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- MyAnimeList for anime data
- AniList for GraphQL API
- Kitsu for JSON:API
- Nyaa.si for torrent indexing
📮 Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
🔗 Links
Made with ❤️ by kitsuneislife
🧪 Testes
npm testOs testes estão configurados com Jest e incluem:
- Testes unitários para modelos
- Testes de integração para serviços
- Testes de providers
🤝 Contribuindo
Contribuições são bem-vindas! Por favor:
- Faça um fork do projeto
- Crie uma branch para sua feature (
git checkout -b feature/MinhaFeature) - Commit suas mudanças (
git commit -m 'Adiciona MinhaFeature') - Push para a branch (
git push origin feature/MinhaFeature) - Abra um Pull Request
📝 Licença
Este projeto está sob a licença MIT. Veja o arquivo LICENSE para mais detalhes.
🙏 Agradecimentos
- MyAnimeList
- AniList
- Kitsu
- Comunidade de anime
📧 Contato
Para dúvidas ou sugestões, abra uma issue no GitHub.
Feito com ❤️ e TypeScript
