Streambert Anime Detection and AniList Metadata Switch: A Technical Deep Dive

Streambert automatically detects anime content by inspecting TMDB metadata for Japanese origin and animation genres, then switches to AniList for richer metadata and routes playback through anime-specific sources.

The truelockmc/streambert repository implements an intelligent content routing system that distinguishes anime from Western media. This Streambert anime detection AniList metadata switch ensures users receive culturally accurate metadata and appropriate streaming sources when browsing Japanese animation.

How Streambert Detects Anime Content

The Detection Logic in src/utils/api.js

At the heart of the detection system lies the isAnimeContent() function located in src/utils/api.js. This utility performs a three-factor validation against TMDB API responses to classify titles:

// src/utils/api.js
export const isAnimeContent = (item, details) => {
  const d = details || item;
  const lang = d.original_language;
  const countries = d.origin_country || [];
  const genreIds = d.genre_ids || (d.genres || []).map(g => g.id);
  const hasAnimation = genreIds.includes(16);
  return hasAnimation && (lang === "ja" || countries.includes("JP"));
};

The function evaluates three specific TMDB attributes:

  • original_language – Must equal "ja" (Japanese)
  • origin_country – Must include "JP" (Japan)
  • genre_ids – Must contain ID 16 (Animation genre)

Only when all conditions return true does Streambert classify the entry as anime, triggering the subsequent metadata switch.

Switching from TMDB to AniList Metadata

The AniList GraphQL Integration

Once isAnimeContent() returns true, Streambert abandons TMDB for descriptive metadata and queries the AniList GraphQL API. The request wrapper and query definitions reside alongside the detection logic in src/utils/api.js:

// src/utils/api.js
const ANILIST_API = "https://graphql.anilist.co";
const ANILIST_QUERY = `...`;   // GraphQL query omitted for brevity
export const fetchAnilistData = async (title, type = "ANIME", tmdbId = null) => { ... };

The fetchAnilistData() function accepts the original TMDB title (or TMDB ID) and returns enriched metadata including clean descriptions, specialized genres, ratings, and season structures that better reflect anime cataloging standards.

Caching Strategy for Performance

To minimize redundant network requests and respect AniList rate limits, Streambert implements a two-tier caching system:

  • AniList cache – Stored under the key streambert_anilistCache in localStorage. Loaded once via getAnilistCache() and flushed after writes through flushAnilistCache(). Entries automatically expire after 7 days.
  • Episode-group cache – Separate storage for TMDB episode groups under streambert_episodeGroupCache, sharing the same TTL and lazy-load patterns.

This caching layer ensures snappy UI performance when users repeatedly browse the same anime series.

Default Source Selection for Anime Playback

Streambert routes playback through different streaming sources based on the detection results. The constants defined immediately after the detection logic map these defaults:

// src/utils/api.js
export const ANIME_DEFAULT_SOURCE = "allmanga";
export const NON_ANIME_DEFAULT_SOURCE = "vidsrc";

When content passes the anime detection check, the UI selects ANIME_DEFAULT_SOURCE ("allmanga"); otherwise, it falls back to NON_ANIME_DEFAULT_SOURCE ("vidsrc"). The actual media URLs for the AllManga source are generated by the getSourceUrl() function in the same file, while the underlying AllManga fetching logic lives in src/ipc/allmanga.js.

Implementation Workflow

The typical integration flow when a user selects a title follows this sequence:

  1. TMDB Fetch – The UI component (in src/pages/TVPage.jsx or src/pages/MoviePage.jsx) retrieves base metadata from TMDB.
  2. Content Classification – isAnimeContent(item) evaluates the response.
  3. Metadata Enrichment – If anime is detected, the system calls fetchAnilistData(item.title, "ANIME", item.id) to fetch specialized metadata.
  4. Rendering & Playback – The UI renders AniList data (descriptions, genres, ratings) and configures playback using the AllManga source.

Practical Implementation Example

Below is a simplified React component demonstrating the detection and switch workflow:

import { isAnimeContent, fetchAnilistData, getSourceUrl } from '../utils/api';

async function loadTitleDetails(tmdbItem) {
  const anime = isAnimeContent(tmdbItem);
  let metadata = tmdbItem; // fallback to TMDB

  if (anime) {
    const anilist = await fetchAnilistData(
      tmdbItem.title?.original_name || tmdbItem.name,
      'ANIME',
      tmdbItem.id
    );
    if (anilist) metadata = anilist;
  }

  // Choose playback source
  const sourceId = anime ? 'allmanga' : 'vidsrc';
  const playbackUrl = getSourceUrl(sourceId, 'movie', tmdbItem.id);

  return { metadata, playbackUrl, anime };
}

Debugging the Cache

During development, you can manually clear the AniList cache to force fresh metadata fetches:

// In browser dev tools console
localStorage.removeItem('streambert_anilistCache');

Summary

  • Detection criteria – Streambert identifies anime through the isAnimeContent() function by checking for Japanese language ("ja"), Japanese origin country ("JP"), and Animation genre ID 16 in TMDB responses.
  • Metadata switching – Upon detection, the system invokes fetchAnilistData() from src/utils/api.js to query the AniList GraphQL API, replacing TMDB descriptions with anime-specific metadata.
  • Source routing – Anime content automatically uses the "allmanga" source via ANIME_DEFAULT_SOURCE, while non-anime defaults to "vidsrc".
  • Performance optimization – A 7-day TTL cache stored in localStorage under streambert_anilistCache prevents redundant API calls and accelerates repeat visits.
  • Implementation files – Core logic resides in src/utils/api.js, with UI integration in src/pages/TVPage.jsx and src/pages/MoviePage.jsx, and IPC handling in src/ipc/allmanga.js.

Frequently Asked Questions

How does Streambert determine if a title is anime?

Streambert evaluates three TMDB fields in the isAnimeContent() function: the original_language must be "ja" (Japanese), origin_country must include "JP", and genre_ids must contain 16 (Animation). All three conditions must be satisfied for the title to be classified as anime according to the source code in src/utils/api.js.

What metadata does AniList provide that TMDB doesn't?

AniList provides anime-specific categorization including precise season structures, native Japanese titles with proper romanization, specialized genre tags relevant to anime culture, and community-driven ratings that align with anime fan expectations. This metadata is retrieved via the fetchAnilistData() function when the anime detection trigger activates.

Where is the AniList cache stored?

The AniList cache persists in the browser's localStorage under the key streambert_anilistCache. The system loads this cache once at startup using getAnilistCache() and writes updates through flushAnilistCache(). Entries automatically expire after seven days to ensure metadata freshness.

Can I manually clear the AniList cache for debugging?

Yes. Open your browser's developer tools console and execute localStorage.removeItem('streambert_anilistCache'). This forces Streambert to fetch fresh metadata from AniList on the next anime title request, which is useful when verifying API responses or troubleshooting metadata inconsistencies.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →