From d73591cf50c8ce786577bda64cbcb9390463f468 Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Thu, 23 Jul 2026 01:04:02 +0200 Subject: [PATCH 1/2] Replace Jikan with Tenrai --- README.md | 30 +++++++++---------- packages/obsidian/src/api/apis/MALAPI.ts | 6 ++-- packages/obsidian/src/api/apis/MALAPIManga.ts | 6 ++-- packages/schemas/src/fetchSchemas.sh | 4 +-- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 5a3e0902..0f5c6dcd 100644 --- a/README.md +++ b/README.md @@ -116,22 +116,22 @@ Now you select the result you want, and the plugin will cast its magic, creating ### Currently supported APIs: -| Name | Description | Supported formats | Authentification | Rate limiting | SFW filter support | -| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | -| [Jikan](https://jikan.moe/) | Jikan is an API that uses [My Anime List](https://myanimelist.net) and offers metadata for anime. | series, movies, specials, OVAs, manga, manwha, novels | No | 60 per minute and 3 per second | Yes | -| [OMDb](https://www.omdbapi.com/) | OMDb is an API that offers metadata for movies, series, and games. | series, movies, games | Yes, you can get a free key here [here](https://www.omdbapi.com/apikey.aspx) | 1000 per day | No | -| [TMDB](https://www.themoviedb.org/) | TMDB is a API that offers community editable metadata for movies and series. | series, movies | Yes, by making an account [here](https://www.themoviedb.org/signup) and getting your `API Read Access Token` (**not** `API Key`) [here](https://www.themoviedb.org/settings/api) | 50 per second | Yes | -| [MusicBrainz](https://musicbrainz.org/) | MusicBrainz is an API that offers information about music releases. | music releases | No | 50 per second | No | -| [Wikipedia](https://en.wikipedia.org/wiki/Main_Page) | The Wikipedia API allows access to all Wikipedia articles. | wiki articles | No | None | No | -| [Steam](https://store.steampowered.com/) | The Steam API offers information on all Steam games. | games | No | 10000 per day | No | -| [Open Library](https://openlibrary.org) | The OpenLibrary API offers metadata for books | books | No | Cover access is rate-limited when not using CoverID or OLID by max 100 requests/IP every 5 minutes. This plugin uses OLID, so there shouldn't be a rate limit. | No | -| Comic Vine | The Comic Vine API offers metadata for comic books | comicbooks | Yes, by making an account [here](https://comicvine.gamespot.com/login-signup/) and going to the [api section](https://comicvine.gamespot.com/api/) of the site | 200 requests per resource, per hour. There is also a velocity detection to prevent malicious use. If too many requests are made per second, you may receive temporary blocks to resources. | No | -| [VNDB](https://vndb.org/) | The VNDB API offers metadata for visual novels | games | No | 200 requests per 5 minutes | Yes | -| [Boardgame Geek](https://boardgamegeek.com) | The Boardgame Geek API offers metadata for boardgames | boardgames | Yes, by making an account [here](https://boardgamegeek.com/join/) and then [requesting an application token](https://boardgamegeek.com/applications) | Exact usage limits are still undetermined | No | +| Name | Description | Supported formats | Authentification | Rate limiting | SFW filter support | +| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | +| [Tenrai](https://tenrai.org/) | Tenrai is an API that uses [My Anime List](https://myanimelist.net) and offers metadata for anime. | series, movies, specials, OVAs, manga, manwha, novels | No | 60 per minute | Yes | +| [OMDb](https://www.omdbapi.com/) | OMDb is an API that offers metadata for movies, series, and games. | series, movies, games | Yes, you can get a free key here [here](https://www.omdbapi.com/apikey.aspx) | 1000 per day | No | +| [TMDB](https://www.themoviedb.org/) | TMDB is a API that offers community editable metadata for movies and series. | series, movies | Yes, by making an account [here](https://www.themoviedb.org/signup) and getting your `API Read Access Token` (**not** `API Key`) [here](https://www.themoviedb.org/settings/api) | 50 per second | Yes | +| [MusicBrainz](https://musicbrainz.org/) | MusicBrainz is an API that offers information about music releases. | music releases | No | 50 per second | No | +| [Wikipedia](https://en.wikipedia.org/wiki/Main_Page) | The Wikipedia API allows access to all Wikipedia articles. | wiki articles | No | None | No | +| [Steam](https://store.steampowered.com/) | The Steam API offers information on all Steam games. | games | No | 10000 per day | No | +| [Open Library](https://openlibrary.org) | The OpenLibrary API offers metadata for books | books | No | Cover access is rate-limited when not using CoverID or OLID by max 100 requests/IP every 5 minutes. This plugin uses OLID, so there shouldn't be a rate limit. | No | +| Comic Vine | The Comic Vine API offers metadata for comic books | comicbooks | Yes, by making an account [here](https://comicvine.gamespot.com/login-signup/) and going to the [api section](https://comicvine.gamespot.com/api/) of the site | 200 requests per resource, per hour. There is also a velocity detection to prevent malicious use. If too many requests are made per second, you may receive temporary blocks to resources. | No | +| [VNDB](https://vndb.org/) | The VNDB API offers metadata for visual novels | games | No | 200 requests per 5 minutes | Yes | +| [Boardgame Geek](https://boardgamegeek.com) | The Boardgame Geek API offers metadata for boardgames | boardgames | Yes, by making an account [here](https://boardgamegeek.com/join/) and then [requesting an application token](https://boardgamegeek.com/applications) | Exact usage limits are still undetermined | No | #### Notes -- [Jikan](https://jikan.moe/) +- [Tenrai](https://tenrai.org/) - sometimes the api is very slow; this is normal - you need to use the title the anime has on [My Anime List](https://myanimelist.net), which is in most cases the Japanese title - e.g. instead of "Demon Slayer" you have to search "Kimetsu no Yaiba" @@ -140,11 +140,11 @@ Now you select the result you want, and the plugin will cast its magic, creating #### Search by ID -- [Jikan](https://jikan.moe/) +- [Tenrai](https://tenrai.org/) - the ID you need is the ID of the anime on [My Anime List](https://myanimelist.net) - you can find this ID in the URL - e.g. for "Beyond the Boundary" the URL looks like this `https://myanimelist.net/anime/18153/Kyoukai_no_Kanata` so the ID is `18153` -- [Jikan Manga](https://jikan.moe/) +- [Tenrai Manga](https://tenrai.org/) - the ID you need is the ID of the manga on [My Anime List](https://myanimelist.net) - you can find this ID in the URL - e.g. for "All You Need Is Kill" the URL looks like this `https://myanimelist.net/manga/62887/All_You_Need_Is_Kill` so the ID is `62887` diff --git a/packages/obsidian/src/api/apis/MALAPI.ts b/packages/obsidian/src/api/apis/MALAPI.ts index 1461b970..5f287877 100644 --- a/packages/obsidian/src/api/apis/MALAPI.ts +++ b/packages/obsidian/src/api/apis/MALAPI.ts @@ -24,7 +24,7 @@ export class MALAPI extends APIModel { this.plugin = plugin; this.apiName = 'MALAPI'; this.apiDescription = 'A free API for Anime. Some results may take a long time to load.'; - this.apiUrl = 'https://jikan.moe/'; + this.apiUrl = 'https://tenrai.org/'; this.types = [MediaType.Movie, MediaType.Series]; this.typeMappings = new Map(); this.typeMappings.set('movie', 'movie'); @@ -36,7 +36,7 @@ export class MALAPI extends APIModel { async searchByTitle(title: string): Promise> { Logger.log(`MDB | api "${this.apiName}" queried by Title`); - const client = createClient({ baseUrl: 'https://api.jikan.moe/v4/' }); + const client = createClient({ baseUrl: 'https://api.tenrai.org/v1/' }); const response = await client.GET('/anime', { params: { @@ -111,7 +111,7 @@ export class MALAPI extends APIModel { async getById(id: string): Promise> { Logger.log(`MDB | api "${this.apiName}" queried by ID`); - const client = createClient({ baseUrl: 'https://api.jikan.moe/v4/' }); + const client = createClient({ baseUrl: 'https://api.tenrai.org/v1/' }); const response = await client.GET('/anime/{id}/full', { params: { diff --git a/packages/obsidian/src/api/apis/MALAPIManga.ts b/packages/obsidian/src/api/apis/MALAPIManga.ts index f864fa55..080708ad 100644 --- a/packages/obsidian/src/api/apis/MALAPIManga.ts +++ b/packages/obsidian/src/api/apis/MALAPIManga.ts @@ -23,7 +23,7 @@ export class MALAPIManga extends APIModel { this.plugin = plugin; this.apiName = 'MALAPI Manga'; this.apiDescription = 'A free API for Manga. Some results may take a long time to load.'; - this.apiUrl = 'https://jikan.moe/'; + this.apiUrl = 'https://tenrai.org/'; this.types = [MediaType.ComicManga]; this.typeMappings = new Map(); this.typeMappings.set('manga', 'manga'); @@ -38,7 +38,7 @@ export class MALAPIManga extends APIModel { async searchByTitle(title: string): Promise> { Logger.log(`MDB | api "${this.apiName}" queried by Title`); - const client = createClient({ baseUrl: 'https://api.jikan.moe/v4/' }); + const client = createClient({ baseUrl: 'https://api.tenrai.org/v1/' }); const response = await client.GET('/manga', { params: { @@ -109,7 +109,7 @@ export class MALAPIManga extends APIModel { async getById(id: string): Promise> { Logger.log(`MDB | api "${this.apiName}" queried by ID`); - const client = createClient({ baseUrl: 'https://api.jikan.moe/v4/' }); + const client = createClient({ baseUrl: 'https://api.tenrai.org/v1/' }); const response = await client.GET('/manga/{id}/full', { params: { diff --git a/packages/schemas/src/fetchSchemas.sh b/packages/schemas/src/fetchSchemas.sh index e5793eed..474e188e 100644 --- a/packages/schemas/src/fetchSchemas.sh +++ b/packages/schemas/src/fetchSchemas.sh @@ -2,8 +2,8 @@ # IMPORTANT: needs to be ran from this directory, otherwise the output files will be generated in the wrong place -# https://docs.api.jikan.moe/ -bun openapi-typescript https://raw.githubusercontent.com/jikan-me/jikan-rest/master/storage/api-docs/api-docs.json -o ./MALAPI.ts +# https://api.tenrai.org/documentation +bun openapi-typescript https://api.tenrai.org/documentation/openapi.json -o ./MALAPI.ts # https://www.giantbomb.com/forums/api-developers-3017/giant-bomb-openapi-specification-1901269/ bun openapi-typescript ./GiantBomb.json -o ./GiantBomb.ts From d8c02c1a6605dd5a4344c9aeb7d1117ac185f6e7 Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:14:31 +0200 Subject: [PATCH 2/2] Improve openlibrary api to reduce getting rate limited --- .../obsidian/src/api/apis/OpenLibraryAPI.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/obsidian/src/api/apis/OpenLibraryAPI.ts b/packages/obsidian/src/api/apis/OpenLibraryAPI.ts index 742a62be..6b58b5f4 100644 --- a/packages/obsidian/src/api/apis/OpenLibraryAPI.ts +++ b/packages/obsidian/src/api/apis/OpenLibraryAPI.ts @@ -9,7 +9,7 @@ import { MDBErrorKind, toMdbError } from 'packages/obsidian/src/utils/MDBError'; import { MediaType } from 'packages/obsidian/src/utils/MediaType'; import type { Result } from 'packages/obsidian/src/utils/result'; import { err, fromPromise, ok } from 'packages/obsidian/src/utils/result'; -import { obsidianFetch } from 'packages/obsidian/src/utils/Utils'; +import { obsidianFetch, contactEmail, mediaDbVersion, pluginName } from 'packages/obsidian/src/utils/Utils'; import type { paths } from 'packages/schemas/src/OpenLibrary'; type OpenLibraryIdKind = 'book' | 'search'; @@ -72,7 +72,10 @@ export class OpenLibraryAPI extends APIModel { private async fetchOpenLibraryJson(url: string, context: Record): Promise> { try { - const response = await obsidianFetch(new Request(`https://openlibrary.org${url}`)); + const request = new Request(`https://openlibrary.org${url}`); + const headers = new Headers(request.headers); + headers.set('User-Agent', `${pluginName}/${mediaDbVersion} (${contactEmail})`); + const response = await obsidianFetch(new Request(request, { headers })); if (!response.ok) { return err({ kind: MDBErrorKind.Api, @@ -97,10 +100,14 @@ export class OpenLibraryAPI extends APIModel { private async searchByOlid(olid: string): Promise> { const responseResult = await fromPromise( this.client.GET('/search.json', { + headers: { + 'User-Agent': `${pluginName}/${mediaDbVersion} (${contactEmail})`, + }, params: { query: { q: olid, fields: 'key,title,author_name,author_key,first_publish_year,cover_i,subject,number_of_pages,number_of_pages_median,description,ratings_average,isbn', + limit: 20, }, }, fetch: obsidianFetch, @@ -135,10 +142,14 @@ export class OpenLibraryAPI extends APIModel { const responseResult = await fromPromise( this.client.GET('/search.json', { + headers: { + 'User-Agent': `${pluginName}/${mediaDbVersion} (${contactEmail})`, + }, params: { query: { q: title, fields: 'key,title,author_name,first_publish_year,cover_i,subject,number_of_pages,number_of_pages_median,description,ratings_average,isbn', + limit: 20, }, }, fetch: obsidianFetch, @@ -276,10 +287,14 @@ export class OpenLibraryAPI extends APIModel { private async getBySearchQuery(query: string): Promise> { const responseResult = await fromPromise( this.client.GET('/search.json', { + headers: { + 'User-Agent': `${pluginName}/${mediaDbVersion} (${contactEmail})`, + }, params: { query: { q: query, fields: 'key,title,author_name,first_publish_year,cover_i,subject,number_of_pages,number_of_pages_median,description,ratings_average,isbn', + limit: 20, }, }, fetch: obsidianFetch,