Skip to content

Commit 917f7ab

Browse files
Merge pull request #183 from ltctceplrm/api-toggle
Restored and updated the api toggle + added a download option for poster images
2 parents ce04d93 + 970432d commit 917f7ab

18 files changed

+219
-133
lines changed

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Obsidian Media DB Plugin
22

3-
A plugin that can query multiple APIs for movies, series, anime, manga, games, music and wiki articles, and import them into your vault.
3+
A plugin that can query multiple APIs for movies, series, anime, manga, books, games, music and wiki articles, and import them into your vault.
44

55
### Features
66

@@ -36,6 +36,10 @@ Available variables that can be used in template tags are the same variables fro
3636
3737
I also published my own templates [here](https://github.com/mProjectsCode/obsidian-media-db-templates).
3838
39+
#### Download poster images
40+
41+
Allows you to automatically download the poster images for a new media, ensuring offline access. The images are saved as `type_title (year)` e.g. `movie_The Perfect Storm (2000)` with a user chosen save location.
42+
3943
#### Metadata field customization
4044
4145
Allows you to rename the metadata fields this plugin generates through mappings.
@@ -113,7 +117,6 @@ Now you select the result you want and the plugin will cast it's magic and creat
113117
114118
### Currently supported APIs:
115119
116-
117120
| Name | Description | Supported formats | Authentification | Rate limiting | SFW filter support |
118121
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
119122
| [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 |
@@ -124,7 +127,7 @@ Now you select the result you want and the plugin will cast it's magic and creat
124127
| [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 |
125128
| [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 |
126129
| [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
130+
| 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 |
128131
129132
#### Notes
130133
@@ -149,6 +152,10 @@ Now you select the result you want and the plugin will cast it's magic and creat
149152
- e.g. for "Rogue One" the URL looks like this `https://www.imdb.com/title/tt3748528/` so the ID is `tt3748528`
150153
- [MusicBrainz](https://musicbrainz.org/)
151154
- the id of a release is not easily accessible, you are better off just searching by title
155+
- the search is generally for albums but you can have a more granular search like so:
156+
- search for albums by a specific `artist:"Lady Gaga" AND primarytype:"album"`
157+
- search for a specific album by a specific artist `artist:"Lady Gaga" AND primarytype:"album" AND releasegroup:"The Fame"`
158+
- search for a specific entry (song or album) by a specific `artist:"Lady Gaga" AND releasegroup:"Poker face"`
152159
- [Wikipedia](https://en.wikipedia.org/wiki/Main_Page)
153160
- [here](https://en.wikipedia.org/wiki/Wikipedia:Finding_a_Wikidata_ID) is a guide to finding the Wikipedia ID for an article
154161
- [Steam](https://store.steampowered.com/)

src/api/APIModel.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,19 @@ export abstract class APIModel {
1010
plugin!: MediaDbPlugin;
1111

1212
/**
13-
* This function should query the api and return a list of matches. The matches should be caped at 20.
13+
* This function should query the api and return a list of matches. The matches should be capped at 20.
1414
*
1515
* @param title the title to query for
1616
*/
1717
abstract searchByTitle(title: string): Promise<MediaTypeModel[]>;
1818

1919
abstract getById(id: string): Promise<MediaTypeModel>;
2020

21+
abstract getDisabledMediaTypes(): MediaType[];
22+
2123
hasType(type: MediaType): boolean {
22-
// if (
23-
// this.types.contains(type) &&
24-
// (Boolean((this.plugin.settings.apiToggle as any)?.[this.apiName]?.[type]) === true || (this.plugin.settings.apiToggle as any)?.[this.apiName]?.[type] === undefined)
25-
// ) {
26-
// return true;
27-
// }
28-
return this.types.contains(type);
24+
const disabledMediaTypes = this.getDisabledMediaTypes();
25+
return this.types.includes(type) && !disabledMediaTypes.includes(type);
2926
}
3027

3128
hasTypeOverlap(types: MediaType[]): boolean {

src/api/apis/BoardGameGeekAPI.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,7 @@ export class BoardGameGeekAPI extends APIModel {
117117
},
118118
});
119119
}
120+
getDisabledMediaTypes(): MediaType[] {
121+
return this.plugin.settings.BoardgameGeekAPI_disabledMediaTypes as MediaType[];
122+
}
120123
}

src/api/apis/ComicVineAPI.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,7 @@ export class ComicVineAPI extends APIModel {
9595
},
9696
});
9797
}
98+
getDisabledMediaTypes(): MediaType[] {
99+
return this.plugin.settings.ComicVineAPI_disabledMediaTypes as MediaType[];
100+
}
98101
}

src/api/apis/GiantBombAPI.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,7 @@ export class GiantBombAPI extends APIModel {
106106
},
107107
});
108108
}
109+
getDisabledMediaTypes(): MediaType[] {
110+
return this.plugin.settings.GiantBombAPI_disabledMediaTypes as MediaType[];
111+
}
109112
}

src/api/apis/MALAPI.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,7 @@ export class MALAPI extends APIModel {
195195

196196
throw new Error(`MDB | Unknown media type for id ${id}`);
197197
}
198+
getDisabledMediaTypes(): MediaType[] {
199+
return this.plugin.settings.MALAPI_disabledMediaTypes as MediaType[];
200+
}
198201
}

src/api/apis/MALAPIManga.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,7 @@ export class MALAPIManga extends APIModel {
126126
},
127127
});
128128
}
129+
getDisabledMediaTypes(): MediaType[] {
130+
return this.plugin.settings.MALAPIManga_disabledMediaTypes as MediaType[];
131+
}
129132
}

src/api/apis/MobyGamesAPI.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,7 @@ export class MobyGamesAPI extends APIModel {
107107
},
108108
});
109109
}
110+
getDisabledMediaTypes(): MediaType[] {
111+
return this.plugin.settings.MobyGamesAPI_disabledMediaTypes as MediaType[];
112+
}
110113
}

src/api/apis/MusicBrainzAPI.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,7 @@ export class MusicBrainzAPI extends APIModel {
9999
},
100100
});
101101
}
102+
getDisabledMediaTypes(): MediaType[] {
103+
return this.plugin.settings.MusicBrainzAPI_disabledMediaTypes as MediaType[];
104+
}
102105
}

src/api/apis/OMDbAPI.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export class OMDbAPI extends APIModel {
151151
duration: result.Runtime ?? 'unknown',
152152
onlineRating: Number.parseFloat(result.imdbRating ?? 0),
153153
actors: result.Actors?.split(', ') ?? [],
154-
image: result.Poster ?? '',
154+
image: result.Poster ? result.Poster.replace('_SX300', '_SX600') : '',
155155

156156
released: true,
157157
streamingServices: [],
@@ -181,7 +181,7 @@ export class OMDbAPI extends APIModel {
181181
duration: result.Runtime ?? 'unknown',
182182
onlineRating: Number.parseFloat(result.imdbRating ?? 0),
183183
actors: result.Actors?.split(', ') ?? [],
184-
image: result.Poster ?? '',
184+
image: result.Poster ? result.Poster.replace('_SX300', '_SX600') : '',
185185

186186
released: true,
187187
streamingServices: [],
@@ -209,7 +209,7 @@ export class OMDbAPI extends APIModel {
209209
publishers: [],
210210
genres: result.Genre?.split(', ') ?? [],
211211
onlineRating: Number.parseFloat(result.imdbRating ?? 0),
212-
image: result.Poster ?? '',
212+
image: result.Poster ? result.Poster.replace('_SX300', '_SX600') : '',
213213

214214
released: true,
215215
releaseDate: this.plugin.dateFormatter.format(result.Released, this.apiDateFormat) ?? 'unknown',
@@ -223,4 +223,8 @@ export class OMDbAPI extends APIModel {
223223

224224
throw new Error(`MDB | Unknown media type for id ${id}`);
225225
}
226+
227+
getDisabledMediaTypes(): MediaType[] {
228+
return this.plugin.settings.OMDbAPI_disabledMediaTypes as MediaType[];
229+
}
226230
}

src/api/apis/OpenLibraryAPI.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,7 @@ export class OpenLibraryAPI extends APIModel {
8989
},
9090
});
9191
}
92+
getDisabledMediaTypes(): MediaType[] {
93+
return this.plugin.settings.OpenLibraryAPI_disabledMediaTypes as MediaType[];
94+
}
9295
}

src/api/apis/SteamAPI.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { GameModel } from '../../models/GameModel';
44
import type { MediaTypeModel } from '../../models/MediaTypeModel';
55
import { MediaType } from '../../utils/MediaType';
66
import { APIModel } from '../APIModel';
7+
import { imageUrlExists } from '../../utils/Utils';
78

89
export class SteamAPI extends APIModel {
910
plugin: MediaDbPlugin;
@@ -85,6 +86,16 @@ export class SteamAPI extends APIModel {
8586

8687
// console.debug(result);
8788

89+
// Check if a poster version of the image exists, else use the header image
90+
const imageUrl = `https://steamcdn-a.akamaihd.net/steam/apps/${result.steam_appid}/library_600x900_2x.jpg`;
91+
const exists = await imageUrlExists(imageUrl);
92+
let finalimageurl;
93+
if (exists) {
94+
finalimageurl = imageUrl;
95+
} else {
96+
finalimageurl = result.header_image ?? '';
97+
}
98+
8899
return new GameModel({
89100
type: MediaType.Game,
90101
title: result.name,
@@ -98,7 +109,7 @@ export class SteamAPI extends APIModel {
98109
publishers: result.publishers,
99110
genres: result.genres?.map((x: any) => x.description) ?? [],
100111
onlineRating: Number.parseFloat(result.metacritic?.score ?? 0),
101-
image: result.header_image ?? '',
112+
image: finalimageurl ?? '',
102113

103114
released: !result.release_date?.coming_soon,
104115
releaseDate: this.plugin.dateFormatter.format(result.release_date?.date, this.apiDateFormat) ?? 'unknown',
@@ -109,4 +120,7 @@ export class SteamAPI extends APIModel {
109120
},
110121
});
111122
}
123+
getDisabledMediaTypes(): MediaType[] {
124+
return this.plugin.settings.SteamAPI_disabledMediaTypes as MediaType[];
125+
}
112126
}

src/api/apis/WikipediaAPI.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,7 @@ export class WikipediaAPI extends APIModel {
7979
userData: {},
8080
});
8181
}
82+
getDisabledMediaTypes(): MediaType[] {
83+
return this.plugin.settings.WikipediaAPI_disabledMediaTypes as MediaType[];
84+
}
8285
}

src/main.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { MarkdownView, Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder } from 'obsidian';
2+
import { requestUrl, normalizePath } from 'obsidian'; // Add requestUrl import
23
import type { MediaType } from 'src/utils/MediaType';
34
import { APIManager } from './api/APIManager';
45
import { BoardGameGeekAPI } from './api/apis/BoardGameGeekAPI';
@@ -219,13 +220,17 @@ export default class MediaDbPlugin extends Plugin {
219220
(await this.modalHelper.openSelectModal({ elements: apiSearchResults }, async selectModalData => {
220221
return await this.queryDetails(selectModalData.selected);
221222
})) ?? [];
222-
if (!selectResults) {
223+
if (!selectResults || selectResults.length < 1) {
223224
return;
224225
}
225226

226-
proceed = await this.modalHelper.openPreviewModal({ elements: selectResults }, async previewModalData => {
227+
const confirmed = await this.modalHelper.openPreviewModal({ elements: selectResults }, async previewModalData => {
227228
return previewModalData.confirmed;
228229
});
230+
if (!confirmed) {
231+
return;
232+
}
233+
break;
229234
}
230235

231236
await this.createMediaDbNotes(selectResults!);
@@ -249,13 +254,17 @@ export default class MediaDbPlugin extends Plugin {
249254
(await this.modalHelper.openSelectModal({ elements: apiSearchResults }, async selectModalData => {
250255
return await this.queryDetails(selectModalData.selected);
251256
})) ?? [];
252-
if (!selectResults) {
257+
if (!selectResults || selectResults.length < 1) {
253258
return;
254259
}
255260

256-
proceed = await this.modalHelper.openPreviewModal({ elements: selectResults }, async previewModalData => {
261+
const confirmed = await this.modalHelper.openPreviewModal({ elements: selectResults }, async previewModalData => {
257262
return previewModalData.confirmed;
258263
});
264+
if (!confirmed) {
265+
return;
266+
}
267+
break;
259268
}
260269

261270
await this.createMediaDbNotes(selectResults!);
@@ -307,6 +316,33 @@ export default class MediaDbPlugin extends Plugin {
307316

308317
options.openNote = this.settings.openNoteInNewTab;
309318

319+
if (mediaTypeModel.image && typeof mediaTypeModel.image === 'string' && mediaTypeModel.image.startsWith('http')) {
320+
if (this.settings.imageDownload) {
321+
try {
322+
const imageurl = mediaTypeModel.image;
323+
const imageext = imageurl.split('.').pop()?.split(/\#|\?/)[0] || 'jpg';
324+
const imagefileName = `${replaceIllegalFileNameCharactersInString(`${mediaTypeModel.type}_${mediaTypeModel.title} (${mediaTypeModel.year})`)}.${imageext}`;
325+
const imagepath = normalizePath(`${this.settings.imageFolder}/${imagefileName}`);
326+
327+
if (!this.app.vault.getAbstractFileByPath(this.settings.imageFolder)) {
328+
await this.app.vault.createFolder(this.settings.imageFolder);
329+
}
330+
331+
if (!this.app.vault.getAbstractFileByPath(imagepath)) {
332+
const response = await requestUrl({ url: imageurl, method: 'GET' });
333+
await this.app.vault.createBinary(imagepath, response.arrayBuffer);
334+
}
335+
336+
// Update model to use local image path
337+
mediaTypeModel.image = `[[${imagepath}]]`;
338+
} catch (e) {
339+
console.warn('MDB | Failed to download image:', e);
340+
}
341+
} else {
342+
mediaTypeModel.image = mediaTypeModel.image;
343+
}
344+
}
345+
310346
const fileContent = await this.generateMediaDbNoteContents(mediaTypeModel, options);
311347

312348
if (!options.folder) {

src/models/MediaTypeModel.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export abstract class MediaTypeModel {
99
dataSource: string;
1010
url: string;
1111
id: string;
12+
image?: string;
1213

1314
userData: object;
1415

@@ -21,6 +22,8 @@ export abstract class MediaTypeModel {
2122
this.dataSource = '';
2223
this.url = '';
2324
this.id = '';
25+
this.image = '';
26+
2427
this.userData = {};
2528
}
2629

0 commit comments

Comments
 (0)