Skip to content

Commit d548224

Browse files
Merge pull request #176 from ltctceplrm/comicvine
Added support for comics through Comic Vine API
2 parents acd4086 + 0a4c308 commit d548224

File tree

9 files changed

+175
-35
lines changed

9 files changed

+175
-35
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,12 @@ Now you select the result you want and the plugin will cast it's magic and creat
108108
- music releases
109109
- wiki articles
110110
- books
111+
- manga
112+
- comics
111113
112114
### Currently supported APIs:
113115
116+
114117
| Name | Description | Supported formats | Authentification | Rate limiting | SFW filter support |
115118
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
116119
| [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 |
@@ -121,6 +124,7 @@ Now you select the result you want and the plugin will cast it's magic and creat
121124
| [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 |
122125
| [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/). NOTE: As of September 2024 the API key is no longer free so consider using Giant Bomb or steam instead | 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 |
123126
| [Giant Bomb](https://www.giantbomb.com) | The Giant Bomb API offers metadata for games for all platforms | games | Yes, by making an account [here](https://www.giantbomb.com/login-signup/) | API requests are limited to 200 requests per resource, per hour. In addition, they implement velocity detection to prevent malicious use. If too many requests are made per second, you may receive temporary blocks to resources. | No |
127+
| 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
124128
125129
#### Notes
126130
@@ -160,6 +164,10 @@ Now you select the result you want and the plugin will cast it's magic and creat
160164
- [Giant Bomb](https://www.giantbomb.com)
161165
- you can find this ID in the URL
162166
- e.g. for "Dota 2" the URL looks like this `https://www.giantbomb.com/dota-2/3030-32887/` so the ID is `3030-32887`
167+
- [Comic Vine](https://www.comicvine.gamespot.com)
168+
- you can find this ID in the URL
169+
- e.g. for "Boule & Bill" the URL looks like this `https://comicvine.gamespot.com/boule-bill/4050-70187/` so the ID is `4050-70187`
170+
- Please note that only volumes can be added, not separate issues.
163171
164172
### Problems, unexpected behavior or improvement suggestions?
165173

src/api/apis/ComicVineAPI.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { requestUrl } from 'obsidian';
2+
import { ComicMangaModel } from 'src/models/ComicMangaModel';
3+
import type MediaDbPlugin from '../../main';
4+
import type { MediaTypeModel } from '../../models/MediaTypeModel';
5+
import { MediaType } from '../../utils/MediaType';
6+
import { APIModel } from '../APIModel';
7+
8+
export class ComicVineAPI extends APIModel {
9+
plugin: MediaDbPlugin;
10+
11+
constructor(plugin: MediaDbPlugin) {
12+
super();
13+
14+
this.plugin = plugin;
15+
this.apiName = 'ComicVineAPI';
16+
this.apiDescription = 'A free API for comic books.';
17+
this.apiUrl = 'https://comicvine.gamespot.com/api';
18+
this.types = [MediaType.ComicManga];
19+
}
20+
21+
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
22+
console.log(`MDB | api "${this.apiName}" queried by Title`);
23+
24+
const searchUrl = `${this.apiUrl}/search/?api_key=${this.plugin.settings.ComicVineKey}&format=json&resources=volume&query=${encodeURIComponent(title)}`;
25+
const fetchData = await requestUrl({
26+
url: searchUrl,
27+
});
28+
// console.debug(fetchData);
29+
if (fetchData.status !== 200) {
30+
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
31+
}
32+
33+
const data = await fetchData.json;
34+
// console.debug(data);
35+
const ret: MediaTypeModel[] = [];
36+
for (const result of data.results) {
37+
ret.push(
38+
new ComicMangaModel({
39+
title: result.name,
40+
englishTitle: result.name,
41+
year: result.start_year,
42+
dataSource: this.apiName,
43+
id: `4050-${result.id}`,
44+
publishers: result.publisher?.name ?? [],
45+
}),
46+
);
47+
}
48+
49+
return ret;
50+
}
51+
52+
async getById(id: string): Promise<MediaTypeModel> {
53+
console.log(`MDB | api "${this.apiName}" queried by ID`);
54+
55+
const searchUrl = `${this.apiUrl}/volume/${encodeURIComponent(id)}/?api_key=${this.plugin.settings.ComicVineKey}&format=json`;
56+
const fetchData = await requestUrl({
57+
url: searchUrl,
58+
});
59+
60+
console.debug(fetchData);
61+
62+
if (fetchData.status !== 200) {
63+
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
64+
}
65+
66+
const data = await fetchData.json;
67+
// console.debug(data);
68+
const result = data.results;
69+
70+
return new ComicMangaModel({
71+
type: MediaType.ComicManga,
72+
title: result.name,
73+
englishTitle: result.name,
74+
alternateTitles: result.aliases,
75+
plot: result.deck,
76+
year: result.start_year ?? '',
77+
dataSource: this.apiName,
78+
url: result.site_detail_url,
79+
id: `4050-${result.id}`,
80+
81+
authors: result.people?.map((x: any) => x.name) ?? [],
82+
chapters: result.count_of_issues,
83+
image: result.image?.original_url ?? '',
84+
85+
released: true,
86+
publishers: result.publisher?.name ?? [],
87+
publishedFrom: result.start_year ?? 'unknown',
88+
publishedTo: 'unknown',
89+
status: result.status,
90+
91+
userData: {
92+
read: false,
93+
lastRead: '',
94+
personalRating: 0,
95+
},
96+
});
97+
}
98+
}

src/api/apis/MALAPIManga.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type MediaDbPlugin from '../../main';
2-
import { MangaModel } from '../../models/MangaModel';
2+
import { ComicMangaModel } from '../../models/ComicMangaModel';
33
import type { MediaTypeModel } from '../../models/MediaTypeModel';
44
import { MediaType } from '../../utils/MediaType';
55
import { APIModel } from '../APIModel';
@@ -15,7 +15,7 @@ export class MALAPIManga extends APIModel {
1515
this.apiName = 'MALAPI Manga';
1616
this.apiDescription = 'A free API for Manga. Some results may take a long time to load.';
1717
this.apiUrl = 'https://jikan.moe/';
18-
this.types = [MediaType.Manga];
18+
this.types = [MediaType.ComicManga];
1919
this.typeMappings = new Map<string, string>();
2020
this.typeMappings.set('manga', 'manga');
2121
this.typeMappings.set('manhwa', 'manhwa');
@@ -45,7 +45,7 @@ export class MALAPIManga extends APIModel {
4545
for (const result of data.data) {
4646
const type = this.typeMappings.get(result.type?.toLowerCase());
4747
ret.push(
48-
new MangaModel({
48+
new ComicMangaModel({
4949
subType: type,
5050
title: result.title,
5151
plot: result.synopsis,
@@ -69,8 +69,8 @@ export class MALAPIManga extends APIModel {
6969
status: result.status,
7070

7171
userData: {
72-
watched: false,
73-
lastWatched: '',
72+
read: false,
73+
lastRead: '',
7474
personalRating: 0,
7575
},
7676
}),
@@ -95,7 +95,7 @@ export class MALAPIManga extends APIModel {
9595
const result = data.data;
9696

9797
const type = this.typeMappings.get(result.type?.toLowerCase());
98-
return new MangaModel({
98+
return new ComicMangaModel({
9999
subType: type,
100100
title: result.title,
101101
englishTitle: result.title_english ?? result.title,
@@ -114,13 +114,14 @@ export class MALAPIManga extends APIModel {
114114
image: result.images?.jpg?.image_url ?? '',
115115

116116
released: true,
117+
publishers: result.serializations?.map((x: any) => x.name) ?? [],
117118
publishedFrom: new Date(result.published?.from).toLocaleDateString() ?? 'unknown',
118119
publishedTo: new Date(result.published?.to).toLocaleDateString() ?? 'unknown',
119120
status: result.status,
120121

121122
userData: {
122-
watched: false,
123-
lastWatched: '',
123+
read: false,
124+
lastRead: '',
124125
personalRating: 0,
125126
},
126127
});

src/main.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { OMDbAPI } from './api/apis/OMDbAPI';
1111
import { OpenLibraryAPI } from './api/apis/OpenLibraryAPI';
1212
import { SteamAPI } from './api/apis/SteamAPI';
1313
import { WikipediaAPI } from './api/apis/WikipediaAPI';
14+
import { ComicVineAPI } from './api/apis/ComicVineAPI';
1415
import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal';
1516
import type { MediaTypeModel } from './models/MediaTypeModel';
1617
import { PropertyMapper } from './settings/PropertyMapper';
@@ -53,6 +54,7 @@ export default class MediaDbPlugin extends Plugin {
5354
this.apiManager.registerAPI(new SteamAPI(this));
5455
this.apiManager.registerAPI(new BoardGameGeekAPI(this));
5556
this.apiManager.registerAPI(new OpenLibraryAPI(this));
57+
this.apiManager.registerAPI(new ComicVineAPI(this));
5658
this.apiManager.registerAPI(new MobyGamesAPI(this));
5759
this.apiManager.registerAPI(new GiantBombAPI(this));
5860
// this.apiManager.registerAPI(new LocGovAPI(this)); // TODO: parse data
@@ -549,17 +551,18 @@ export default class MediaDbPlugin extends Plugin {
549551
}
550552

551553
const validOldMetadata: MediaTypeModelObj = metadata as unknown as MediaTypeModelObj;
554+
console.debug(`MDB | validOldMetadata`, validOldMetadata);
552555

553556
const oldMediaTypeModel = this.mediaTypeManager.createMediaTypeModelFromMediaType(validOldMetadata, validOldMetadata.type);
554-
// console.debug(oldMediaTypeModel);
557+
console.debug(`MDB | oldMediaTypeModel created`, oldMediaTypeModel);
555558

556559
let newMediaTypeModel = await this.apiManager.queryDetailedInfoById(validOldMetadata.id, validOldMetadata.dataSource);
557560
if (!newMediaTypeModel) {
558561
return;
559562
}
560563

561564
newMediaTypeModel = Object.assign(oldMediaTypeModel, newMediaTypeModel.getWithOutUserData());
562-
// console.debug(newMediaTypeModel);
565+
console.debug(`MDB | newMediaTypeModel after merge`, newMediaTypeModel);
563566

564567
if (onlyMetadata) {
565568
await this.createMediaDbNoteFromModel(newMediaTypeModel, { attachFile: activeFile, folder: activeFile.parent ?? undefined, openNote: true });

src/models/MangaModel.ts renamed to src/models/ComicMangaModel.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import type { ModelToData } from '../utils/Utils';
33
import { mediaDbTag, migrateObject } from '../utils/Utils';
44
import { MediaTypeModel } from './MediaTypeModel';
55

6-
export type MangaData = ModelToData<MangaModel>;
6+
export type ComicMangaData = ModelToData<ComicMangaModel>;
77

8-
export class MangaModel extends MediaTypeModel {
8+
export class ComicMangaModel extends MediaTypeModel {
99
plot: string;
1010
alternateTitles: string[];
1111
genres: string[];
@@ -17,16 +17,17 @@ export class MangaModel extends MediaTypeModel {
1717

1818
released: boolean;
1919
status: string;
20+
publishers: string[];
2021
publishedFrom: string;
2122
publishedTo: string;
2223

2324
userData: {
24-
watched: boolean;
25-
lastWatched: string;
25+
read: boolean;
26+
lastRead: string;
2627
personalRating: number;
2728
};
2829

29-
constructor(obj: MangaData) {
30+
constructor(obj: ComicMangaData) {
3031
super();
3132

3233
this.plot = '';
@@ -40,12 +41,13 @@ export class MangaModel extends MediaTypeModel {
4041

4142
this.released = false;
4243
this.status = '';
44+
this.publishers = [];
4345
this.publishedFrom = '';
4446
this.publishedTo = '';
4547

4648
this.userData = {
47-
watched: false,
48-
lastWatched: '',
49+
read: false,
50+
lastRead: '',
4951
personalRating: 0,
5052
};
5153

@@ -59,11 +61,11 @@ export class MangaModel extends MediaTypeModel {
5961
}
6062

6163
getTags(): string[] {
62-
return [mediaDbTag, 'manga', 'light-novel'];
64+
return [mediaDbTag, 'manga', 'light-novel', 'comicbook'];
6365
}
6466

6567
getMediaType(): MediaType {
66-
return MediaType.Manga;
68+
return MediaType.ComicManga;
6769
}
6870

6971
getSummary(): string {

src/settings/PropertyMapper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ export class PropertyMapper {
6262
return obj;
6363
}
6464

65+
if (obj.type === 'manga') {
66+
obj.type = 'comicManga';
67+
console.debug(`MDB | updated metadata type`, obj.type);
68+
}
6569
if (MEDIA_TYPES.contains(obj.type as any)) {
6670
return obj;
6771
}

0 commit comments

Comments
 (0)