Skip to content

Add My Drama List API #155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
## Obsidian Media DB Plugin

A plugin that can query multiple APIs for movies, series, anime, manga, games, music and wiki articles, and import them into your vault.
A plugin that can query multiple APIs for movies, series, asian dramas, anime, manga, games, music and wiki articles, and import them into your vault.

### Features

#### Search by Title

Search a movie, series, anime, game, music release or wiki article by its name across multiple APIs.
Search a movie, series, asian dramas, anime, game, music release or wiki article by its name across multiple APIs.

#### Search by ID

Expand Down Expand Up @@ -104,30 +104,37 @@ Now you select the result you want and the plugin will cast it's magic and creat

- movies (including specials)
- series (including OVAs)
- Asian Dramas and Movies (including specials)
- games
- music releases
- wiki articles
- books

### 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 movie, 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 |
| [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 |
| [Moby Games](https://www.mobygames.com) | The Moby Games API offers metadata for games for all platforms | games | Yes, by making an account [here](https://www.mobygames.com/user/register/) | API requests are limited to 360 per hour (one every ten seconds). In addition, requests should be made no more frequently than one per second. | No |
| 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 movie, 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 |
| [MyDramaList](https://mydramalist.com/) | MyDramaList offers metadata for Asian Dramas, movies, specials, etc. [Unofficial API](https://github.com/tbdsux/kuryana) is being used. | series, movies, specials | No | No | No |
| [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 |
| [Moby Games](https://www.mobygames.com) | The Moby Games API offers metadata for games for all platforms | games | Yes, by making an account [here](https://www.mobygames.com/user/register/) | API requests are limited to 360 per hour (one every ten seconds). In addition, requests should be made no more frequently than one per second. | No |

#### Notes

- [Jikan](https://jikan.moe/)

- 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"

- [MyDramaList](https://mydramalist.com/)
- [Unofficial API](https://github.com/tbdsux/kuryana/) is being used
- Ratelimit is unknown

#### Search by ID

- [Jikan](https://jikan.moe/)
Expand All @@ -142,6 +149,9 @@ Now you select the result you want and the plugin will cast it's magic and creat
- the ID you need is the ID of the movie or show on [IMDb](https://www.imdb.com)
- you can find this ID in the URL
- e.g. for "Rogue One" the URL looks like this `https://www.imdb.com/title/tt3748528/` so the ID is `tt3748528`
- [MyDramaList](https://mydramalist.com/)
- Id is in the URL
- e.g. for "The Roundup" the URL looks like this `https://mydramalist.com/29612-the-outlaws-2/` so the ID is `29612-the-outlaws-2`
- [MusicBrainz](https://musicbrainz.org/)
- the id of a release is not easily accessible, you are better off just searching by title
- [Wikipedia](https://en.wikipedia.org/wiki/Main_Page)
Expand Down
180 changes: 180 additions & 0 deletions src/api/apis/MyDramaListAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import MediaDbPlugin from 'src/main';
import { APIModel } from '../APIModel';
import { MediaType } from 'src/utils/MediaType';
import { MediaTypeModel } from 'src/models/MediaTypeModel';
import { MovieModel } from 'src/models/MovieModel';
import { SeriesModel } from 'src/models/SeriesModel';

export class MyDramaListAPI extends APIModel {
plugin: MediaDbPlugin;
apiDateFormat: string = 'DD MMM YYYY';

constructor(plugin: MediaDbPlugin) {
super();

this.plugin = plugin;
this.apiName = 'MyDramaListAPI';
this.apiDescription = 'A free API for Asian Movies and Dramas';
this.apiUrl = 'https://kuryana.vercel.app/';
this.types = [MediaType.Movie, MediaType.Series];
}

async searchByTitle(title: string): Promise<MediaTypeModel[]> {
console.log(`MDB | api "${this.apiName}" queried by Title`);

const searchUrl = `https://kuryana.vercel.app/search/q/${encodeURIComponent(title)}`;
const fetchData = await fetch(searchUrl);
// console.debug(fetchData);
if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
}

const data = await fetchData.json();
//console.debug(data);

const ret: MediaTypeModel[] = [];
for (const result of data.results.dramas) {
const type = result.type.toLowerCase();
if (type.contains('movie')) {
ret.push(
new MovieModel({
type: 'Movie',
subType: result.type,
title: result.title,
englishTitle: result.title,
year: result.year,
dataSource: this.apiName,
id: result.slug,
} as MovieModel),
);
} else if (type.contains('series') || type.contains('show') || type.contains('drama') || type.contains('special')) {
ret.push(
new SeriesModel({
type: 'Series',
subType: result.type,
title: result.title,
englishTitle: result.title,
year: result.year,
dataSource: this.apiName,
id: result.slug,
} as SeriesModel),
);
} else {
console.debug(`MDB | api "${this.apiName}" unsupported Type "${result.type}"`);
}
}
return ret;
}

async getById(id: string): Promise<MediaTypeModel> {
console.log(`MDB | api "${this.apiName}" queried by ID`);

const searchUrl = `https://kuryana.vercel.app/id/${encodeURIComponent(id)}`;
const fetchData = await fetch(searchUrl);
// console.debug(fetchData);

if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
}

const data = await fetchData.json();
// console.debug(data);

if (data.error) {
throw Error(`MDB | Received error from ${this.apiName}: ${data.title} ${data.info}`);
}

const result = data.data;
const type = result.details.type.toLowerCase();
if (type.contains('movie')) {
return new MovieModel({
type: 'Movie',
subType: result.details.type,
title: result.title,
englishTitle: result.title,
nativeTitle: result?.others?.native_title ?? '',
aliases: result?.others?.also_known_as ?? [],
year: result.details?.release_date?.split(',')[1]?.trim(),
dataSource: 'My Drama List',
url: `https://mydramalist.com/${id}`,
id: id,
country: result?.details?.country ?? '',
content_rating: result?.details?.content_rating ?? '',

plot: result.synopsis ?? '',
genres: result.others.genres ?? [],
mediaTags: result?.others?.tags ? result.others.tags.map((tag: string) => tag.replace(' (Vote or add tags)', '')) : [],
relatedContent: result.others.related_content ?? '',
director: result.others.director ? result.others.director : [],
writer: result.others.screenwriter ?? [],
studio: result.details.original_network ? [result.details.original_network] : [],
duration: result.details.duration?.replace(/[. ]/g, '') ?? '',
onlineRating: result.rating ? Number.parseFloat(result.rating) : '',
votes: Number.parseInt(result.details.score.split('(')[1].split(' ')[2].replace(',', '')) ?? '',
ranked: Number.parseInt(result.details.ranked.replace('#', '')) ?? '',
popularity: Number.parseInt(result.details.popularity.replace('#', '')) ?? '',
watchers: result.details.watchers ? Number.parseInt(result.details.watchers) : '',
actors: result.casts.map((cast: any) => cast.name) ?? [],
image: result.poster ?? '',

released: true,
streamingServices: [],
premiere: this.plugin.dateFormatter.format(result.details.release_date, this.apiDateFormat) ?? '',

userData: {
watched: false,
lastWatched: '',
personalRating: 0,
},
} as MovieModel);
} else if (type.contains('series') || type.contains('show') || type.contains('drama') || type.contains('special')) {
return new SeriesModel({
type: 'Series',
subType: result.details.type,
title: result.title,
nativeTitle: result?.others?.native_title ?? '',
aliases: result?.others?.also_known_as ?? [],
englishTitle: result.title,
year: result.details.aired?.split(',')[1]?.split('-')[0]?.trim(),
dataSource: 'My Drama List',
url: `https://mydramalist.com/${id}`,
id: id,
country: result?.details?.country ?? '',
content_rating: result?.details?.content_rating ?? '',

plot: result.synopsis ?? '',
genres: result.others.genres ?? [],
mediaTags: result?.others?.tags ? result.others.tags.map((tag: string) => tag.replace(' (Vote or add tags)', '')) : [],
relatedContent: result.others.related_content ?? '',
director: result.others.director ? result.others.director : [],
writer: result.others.screenwriter ?? [],
studio: [result.details.original_network],
episodes: result.details.episodes ?? 0,
duration: result.details.duration?.replace(/[. ]/g, '') ?? '',
onlineRating: result.rating ? Number.parseFloat(result.rating) : '',
actors: result.casts.map((cast: any) => cast.name) ?? [],
votes: Number.parseInt(result.details.score.split('(')[1].split(' ')[2].replace(',', '')) ?? '',
ranked: Number.parseInt(result.details.ranked.replace('#', '')) ?? '',
popularity: Number.parseInt(result.details.popularity.replace('#', '')) ?? '',
watchers: result.details.watchers ? Number.parseInt(result.details.watchers) : '',
image: result.poster ?? '',

released: true,
streamingServices: [],
airing: false,
airedFrom: this.plugin.dateFormatter.format(result.details.aired?.split('-')[0]?.trim(), this.apiDateFormat) ?? '',
airedTo: this.plugin.dateFormatter.format(result.details.aired?.split('-')[1]?.trim(), this.apiDateFormat) ?? '',

userData: {
watched: false,
lastWatched: '',
personalRating: 0,
},
} as SeriesModel);
} else {
console.debug(`MDB | api "${this.apiName}" unsupported Type "${result.details.type}"`);
}

return;
}
}
2 changes: 2 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { PropertyMapping, PropertyMappingModel } from './settings/PropertyMappin
import { ModalHelper, ModalResultCode, SearchModalOptions } from './utils/ModalHelper';
import { DateFormatter } from './utils/DateFormatter';
import { MediaType } from 'src/utils/MediaType';
import { MyDramaListAPI } from './api/apis/MyDramaListAPI';

export type Metadata = Record<string, unknown>;

Expand Down Expand Up @@ -58,6 +59,7 @@ export default class MediaDbPlugin extends Plugin {
this.apiManager.registerAPI(new BoardGameGeekAPI(this));
this.apiManager.registerAPI(new OpenLibraryAPI(this));
this.apiManager.registerAPI(new MobyGamesAPI(this));
this.apiManager.registerAPI(new MyDramaListAPI(this));
// this.apiManager.registerAPI(new LocGovAPI(this)); // TODO: parse data

this.mediaTypeManager = new MediaTypeManager();
Expand Down
Loading