Skip to main content

Overview

TrackGeek provides comprehensive media tracking across 6 media types, each powered by industry-leading external APIs. The system automatically fetches, caches, and refreshes media data to ensure accurate and up-to-date information.

Supported Media Types

TrackGeek supports tracking across six distinct media categories:

Anime

Japanese animation series and movies

Manga

Japanese comics and graphic novels

TV Shows

Television series and episodes

Movies

Feature films and cinema releases

Games

Video games across all platforms

Books

Books and literary works

Data Source Integration

Each media type is powered by a specialized external API that provides comprehensive metadata, imagery, and relationships.

Anime & Manga: Jikan API

API Base URL: https://api.jikan.moe/v4
The Jikan service fetches comprehensive anime data including:
  • Basic Info: Title, type, source, episodes, status, rating
  • Temporal Data: Aired dates, duration, season, year, broadcast schedule
  • Metadata: Synopsis, background, rank, popularity
  • Media: Trailer videos, promotional videos, music videos, episode previews
  • Production: Producers, licensors, studios
  • Classification: Genres, explicit genres, themes, demographics
  • Characters: Character profiles with voice actors and roles
  • Staff: Production staff with positions
  • Relationships: Related anime/manga entries
  • External Links: Official and fan sites
Source: src/shared/infra/integrations/jikan.service.ts:111-266
The Jikan service fetches comprehensive manga data including:
  • Basic Info: Title, type, chapters, volumes, status, publishing state
  • Temporal Data: Published dates, rank, popularity
  • Metadata: Synopsis
  • Credits: Authors and serializations
  • Classification: Genres, explicit genres, themes, demographics
  • Characters: Character profiles with roles
  • Relationships: Related manga/anime entries
  • External Links: Official and fan sites
Source: src/shared/infra/integrations/jikan.service.ts:268-349

TV Shows & Movies: TMDB API

API Base URL: https://api.themoviedb.org/3
The TMDB service fetches comprehensive movie data including:
  • Identification: TMDB ID, IMDB ID
  • Basic Info: Title, original title, original language, tagline
  • Media: Poster, backdrop, videos (trailers, teasers, behind-the-scenes)
  • Financial: Budget, revenue
  • Release: Release date, status
  • Production: Production companies, production countries, spoken languages
  • Collection: Belongs to collection (for franchises)
  • Credits: Full cast and crew with roles and profile images
  • Metadata: Overview, popularity, runtime, genres, homepage
Source: src/shared/infra/integrations/tmdb.service.ts:116-226
The TMDB service fetches comprehensive TV show data including:
  • Identification: TMDB ID
  • Basic Info: Name, original name, original language, tagline, type
  • Media: Poster, backdrop
  • Production: Created by, production companies, production countries, networks
  • Status: In production, status, languages
  • Episodes: Number of episodes, episode runtime
  • Seasons: Full season and episode breakdown with:
    • Season number, name, air date, poster
    • Individual episodes with names, overviews, air dates, still images
  • Air Dates: First air date, last air date, last episode to air, next episode to air
  • Credits: Full cast and crew with roles and profile images
  • Metadata: Popularity, homepage, origin country, genres
Source: src/shared/infra/integrations/tmdb.service.ts:228-377

Games: IGDB API

API Base URL: https://api.igdb.com/v4
IGDB uses Twitch OAuth2 for authentication. Access tokens are cached and automatically refreshed before expiration.
The IGDB service fetches extensive game data including:
  • Basic Info: Name, slug, summary, storyline
  • Release: First release date, release dates by platform
  • Media: Cover art, screenshots, artworks, videos
  • Classification: Genres, themes, keywords, player perspectives, game modes
  • Ratings: Age ratings with organizations and synopses
  • Companies: Involved companies (developers, publishers, porters, supporting)
  • Platforms: Supported platforms
  • Multiplayer: Detailed multiplayer modes (co-op, LAN, split-screen, online)
  • Localization: Game localizations and language support
  • Relationships:
    • Similar games
    • Parent game, forks, ports
    • Expansions, DLCs, standalone expansions
    • Remakes, remasters
    • Bundles
  • Collections: Game collections and franchises
  • Technical: Game engines, game status, game type
  • Alternative Names: Alternative titles and comments
  • External Games: Cross-references to other platforms
Source: src/shared/infra/integrations/igdb.service.ts:138-557

Books: Hardcover API

API Base URL: https://api.hardcover.app/v1/graphql
The Hardcover service fetches comprehensive book data using GraphQL:
  • Basic Info: Title, subtitle, alternative titles, slug, state
  • Content: Description, headline, pages, audio seconds
  • Release: Release date, release year
  • Classification: Book category ID, literary type ID, compilation status
  • Editions:
    • Default audio edition
    • Default cover edition
    • Default ebook edition
    • Default physical edition
    • Up to 30 edition variants
  • Media: Cover images for all editions
  • Series: Featured book series information
  • Links: External links to retailers and reviews
  • Canonical: Canonical book reference
  • Metadata: Curation status, editions count
Source: src/shared/infra/integrations/hardcover.service.ts:83-263

Database Schema

Each media type has a dedicated database model that stores the fetched data locally.
model Anime {
  id               String   @id @default(uuid())
  malId            Int      @unique
  url              String
  imageUrl         String?
  trailer          Json?
  title            String
  titles           Json?
  type             String?
  source           String?
  numberOfEpisodes Int?
  status           String?
  aired            Json?
  duration         String?
  rating           String?
  rank             Int?
  popularity       Int?
  synopsis         String?
  background       String?
  season           String?
  year             Int?
  broadcast        Json?
  producers        Json?
  licensors        Json?
  studios          Json?
  genres           Json?
  explicitGenres   Json?
  themes            Json?
  demographics     Json?
  relations         Json?
  theme            Json?
  external          Json?
  characters        Json?
  cast             Json?
  videos            Json?
  lastRefreshedAt  DateTime @default(now())
  createdAt        DateTime @default(now())
  updatedAt        DateTime @updatedAt

  animeWatches    AnimeWatch[]
  animeProgresses AnimeProgress[]
  animeReviews    AnimeReview[]
  favorites       Favorite[]
  listItems       ListItem[]
  comments        Comment[]
}
Source: prisma/schema.prisma:356-401

Data Flow Architecture

The media tracking system follows a consistent pattern across all media types:

Search Flow

  1. Client Request: User searches for media by query string
  2. Cache Check: Check if search results are cached (24-hour TTL)
  3. External API Call: If not cached, fetch from external API
  4. Cache Results: Store results for 24 hours
  5. Return Data: Send search results to client
Example (src/modules/anime/anime.service.ts:30-32):
async searchAnimes(searchAnimeDto: SearchAnimeDto) {
  return this.integrationsService.jikan.searchAnimes(searchAnimeDto.query);
}

Fetch Flow

  1. Client Request: User requests specific media by ID
  2. Cache Check: Check if media is cached (6-hour TTL)
  3. Database Check: If not cached, check database
  4. External API Call: If not in database, fetch from external API
  5. Store in Database: Save fetched data with lastRefreshedAt timestamp
  6. Cache Data: Cache for 6 hours
  7. Return Data: Send media details to client
Example (src/modules/anime/anime.service.ts:34-56):
async getAnimeById(id: number) {
  const cachedAnime = await this.cacheService.get<Anime>(
    this.cacheKeys.animeById.prefix(id)
  );

  if (cachedAnime) {
    return cachedAnime;
  }

  let anime = await this.databaseService.anime.findUnique({
    where: { malId: id },
  });

  if (!anime) {
    const jikanAnime = await this.integrationsService.jikan.getAnimeById(id);
    anime = await this.databaseService.anime.create({
      data: jikanAnime,
    });
  }

  await this.cacheService.set(
    this.cacheKeys.animeById.prefix(id),
    anime,
    this.cacheKeys.animeById.expiration
  );

  return anime;
}

Refresh System

TrackGeek implements an intelligent refresh system to keep media data up-to-date without overwhelming external APIs.

Refresh Interval

Media data can be refreshed once every 24 hours per item.
Constant Definition (src/shared/constants/refresh-interval.ts:1):
export const REFRESH_INTERVAL_MS = 3600 * 24 * 1000; // 24 hours

Refresh Flow

  1. Refresh Request: User or system triggers refresh for specific media
  2. Existence Check: Verify media exists in database
  3. Time Check: Ensure 24 hours have passed since lastRefreshedAt
  4. Cache Invalidation: Delete cached entry if exists
  5. External API Call: Fetch latest data from external API
  6. Database Update: Update media record with new data and timestamp
  7. Re-cache: Cache fresh data for 6 hours
Example (src/modules/anime/anime.service.ts:58-87):
async refreshAnime(refreshAnimeDto: RefreshAnimeDto) {
  const anime = await this.databaseService.anime.findUnique({
    where: { malId: refreshAnimeDto.id },
  });

  if (!anime) {
    throw new AppException(ERROR_CODES.ANIME_NOT_FOUND);
  }

  if (Date.now() - anime.lastRefreshedAt.getTime() < REFRESH_INTERVAL_MS) {
    throw new AppException(ERROR_CODES.ANIME_ALREADY_REFRESHED);
  }

  if (await this.cacheService.exists(this.cacheKeys.animeById.prefix(anime.malId))) {
    await this.cacheService.delete(this.cacheKeys.animeById.prefix(anime.malId));
  }

  const jikanAnime = await this.integrationsService.jikan.getAnimeById(anime.malId);

  await this.databaseService.anime.update({
    where: { malId: refreshAnimeDto.id },
    data: jikanAnime,
  });

  await this.cacheService.set(
    this.cacheKeys.animeById.prefix(anime.malId),
    anime,
    this.cacheKeys.animeById.expiration,
  );
}

Error Handling

  • ANIME_NOT_FOUND: Media doesn’t exist in database
  • ANIME_ALREADY_REFRESHED: Less than 24 hours since last refresh
  • JIKAN_SERVICE_UNAVAILABLE: External API is down or rate-limited
  • TMDB_SERVICE_UNAVAILABLE: TMDB API is unavailable
  • IGDB_SERVICE_UNAVAILABLE: IGDB API is unavailable
  • HARDCOVER_SERVICE_UNAVAILABLE: Hardcover API is unavailable

Caching Strategy

TrackGeek uses a multi-tier caching strategy for optimal performance:
Cache TypeTTLUse Case
Search Results24 hoursSearch queries remain consistent
Media Details6 hoursBalance freshness with performance
External API Data24 hoursReduce API calls to external services
Cache Key Pattern:
  • Anime: anime:id:{malId}
  • Manga: manga:id:{malId}
  • Movie: movie:id:{tmdbId}
  • TV Show: tvshow:id:{tmdbId}
  • Game: game:id:{igdbId}
  • Book: book:id:{hardcoverId}

Best Practices

Cache First

Always check cache before hitting the database or external APIs

Respect Rate Limits

Use the 24-hour refresh interval to avoid overwhelming external APIs

Handle Failures

Implement proper error handling for external API failures

Store Efficiently

Use JSON fields for complex nested data from external APIs

Build docs developers (and LLMs) love