update deps; make apis more type safe
This commit is contained in:
parent
9518ede41c
commit
292dcf5b49
46 changed files with 26911 additions and 550 deletions
17
automation/fetchSchemas.ts
Normal file
17
automation/fetchSchemas.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { $ } from 'utils/shellUtils';
|
||||
|
||||
async function fetchSchema() {
|
||||
// https://docs.api.jikan.moe/
|
||||
await $('bun openapi-typescript https://raw.githubusercontent.com/jikan-me/jikan-rest/master/storage/api-docs/api-docs.json -o ./src/api/schemas/MALAPI.ts');
|
||||
|
||||
// https://www.giantbomb.com/forums/api-developers-3017/giant-bomb-openapi-specification-1901269/
|
||||
await $('bun openapi-typescript ./src/api/schemas/GiantBomb.json -o ./src/api/schemas/GiantBomb.ts');
|
||||
|
||||
// https://www.omdbapi.com/swagger.json
|
||||
await $('bun openapi-typescript ./src/api/schemas/OMDb.json -o ./src/api/schemas/OMDb.ts');
|
||||
|
||||
// https://github.com/internetarchive/openlibrary-api/blob/main/swagger.yaml
|
||||
await $('bun openapi-typescript ./src/api/schemas/OpenLibrary.json -o ./src/api/schemas/OpenLibrary.ts');
|
||||
}
|
||||
|
||||
await fetchSchema();
|
||||
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
|
@ -7,7 +7,7 @@ import * as plugin_import from 'eslint-plugin-import';
|
|||
|
||||
export default tseslint.config(
|
||||
{
|
||||
ignores: ['npm/', 'node_modules/', 'exampleVault/', 'automation/', 'main.js', '*.svelte'],
|
||||
ignores: ['npm/', 'node_modules/', 'exampleVault/', 'automation/', 'main.js', '*.svelte', 'src/api/schemas/'],
|
||||
},
|
||||
{
|
||||
files: ['src/**/*.ts'],
|
||||
|
|
|
|||
34
package.json
34
package.json
|
|
@ -14,8 +14,8 @@
|
|||
"lint": "eslint --max-warnings=0 src/**",
|
||||
"lint:fix": "eslint --max-warnings=0 --fix src/**",
|
||||
"svelte-check": "svelte-check --compiler-warnings \"unused-export-let:ignore\"",
|
||||
"check": "bun run format:check && bun run tsc && bun run test",
|
||||
"check:fix": "bun run format && bun run tsc && bun run test",
|
||||
"check": "bun run format:check && bun run tsc && bun run lint && bun run svelte-check",
|
||||
"check:fix": "bun run format && bun run tsc && bun run lint:fix && bun run svelte-check",
|
||||
"release": "bun run automation/release.ts",
|
||||
"stats": "bun run automation/stats.ts"
|
||||
},
|
||||
|
|
@ -23,26 +23,28 @@
|
|||
"author": "Moritz Jung",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@happy-dom/global-registrator": "^18.0.1",
|
||||
"@lemons_dev/parsinom": "^0.0.12",
|
||||
"@happy-dom/global-registrator": "^14.12.3",
|
||||
"@types/bun": "^1.1.16",
|
||||
"builtin-modules": "^4.0.0",
|
||||
"esbuild": "^0.24.2",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@types/bun": "^1.2.19",
|
||||
"builtin-modules": "^5.0.0",
|
||||
"esbuild": "^0.25.8",
|
||||
"esbuild-plugin-copy-watch": "^2.3.1",
|
||||
"esbuild-svelte": "^0.8.2",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"esbuild-svelte": "^0.9.3",
|
||||
"eslint": "^9.32.0",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-only-warn": "^1.1.0",
|
||||
"obsidian": "latest",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"openapi-fetch": "^0.14.0",
|
||||
"openapi-typescript": "^7.8.0",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"string-argv": "^0.3.2",
|
||||
"svelte": "^5.17.5",
|
||||
"svelte-check": "^4.1.4",
|
||||
"svelte": "^5.38.0",
|
||||
"svelte-check": "^4.3.1",
|
||||
"svelte-preprocess": "^6.0.3",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.20.0"
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.39.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
|||
import { MediaType } from '../../utils/MediaType';
|
||||
import { APIModel } from '../APIModel';
|
||||
|
||||
// sadly no open api schema available
|
||||
|
||||
export class BoardGameGeekAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
|
||||
|
|
@ -118,6 +120,6 @@ export class BoardGameGeekAPI extends APIModel {
|
|||
});
|
||||
}
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.BoardgameGeekAPI_disabledMediaTypes as MediaType[];
|
||||
return this.plugin.settings.BoardgameGeekAPI_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */
|
||||
|
||||
import { requestUrl } from 'obsidian';
|
||||
import { ComicMangaModel } from 'src/models/ComicMangaModel';
|
||||
import type MediaDbPlugin from '../../main';
|
||||
|
|
@ -5,6 +7,8 @@ import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
|||
import { MediaType } from '../../utils/MediaType';
|
||||
import { APIModel } from '../APIModel';
|
||||
|
||||
// sadly no open api schema available
|
||||
|
||||
export class ComicVineAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
|
||||
|
|
@ -41,7 +45,7 @@ export class ComicVineAPI extends APIModel {
|
|||
year: result.start_year,
|
||||
dataSource: this.apiName,
|
||||
id: `4050-${result.id}`,
|
||||
publishers: result.publisher?.name ?? [],
|
||||
publishers: result.publisher?.name,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
@ -64,28 +68,32 @@ export class ComicVineAPI extends APIModel {
|
|||
}
|
||||
|
||||
const data = await fetchData.json;
|
||||
// console.debug(data);
|
||||
const result = data.results;
|
||||
|
||||
const authors = result.people as
|
||||
| {
|
||||
name: string;
|
||||
}[]
|
||||
| undefined;
|
||||
|
||||
return new ComicMangaModel({
|
||||
type: MediaType.ComicManga,
|
||||
title: result.name,
|
||||
englishTitle: result.name,
|
||||
alternateTitles: result.aliases,
|
||||
plot: result.deck,
|
||||
year: result.start_year ?? '',
|
||||
year: result.start_year,
|
||||
dataSource: this.apiName,
|
||||
url: result.site_detail_url,
|
||||
id: `4050-${result.id}`,
|
||||
|
||||
authors: result.people?.map((x: any) => x.name) ?? [],
|
||||
authors: authors?.map(x => x.name),
|
||||
chapters: result.count_of_issues,
|
||||
image: result.image?.original_url ?? '',
|
||||
image: result.image?.original_url,
|
||||
|
||||
released: true,
|
||||
publishers: result.publisher?.name ?? [],
|
||||
publishedFrom: result.start_year ?? 'unknown',
|
||||
publishedTo: 'unknown',
|
||||
publishers: result.publisher?.name,
|
||||
publishedFrom: result.start_year,
|
||||
status: result.status,
|
||||
|
||||
userData: {
|
||||
|
|
@ -96,6 +104,6 @@ export class ComicVineAPI extends APIModel {
|
|||
});
|
||||
}
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.ComicVineAPI_disabledMediaTypes as MediaType[];
|
||||
return this.plugin.settings.ComicVineAPI_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { requestUrl } from 'obsidian';
|
||||
import createClient from 'openapi-fetch';
|
||||
import { obsidianFetch } from 'src/utils/Utils';
|
||||
import type MediaDbPlugin from '../../main';
|
||||
import { GameModel } from '../../models/GameModel';
|
||||
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||
import { MediaType } from '../../utils/MediaType';
|
||||
import { APIModel } from '../APIModel';
|
||||
import type { paths } from '../schemas/GiantBomb';
|
||||
|
||||
export class GiantBombAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
|
|
@ -18,6 +20,7 @@ export class GiantBombAPI extends APIModel {
|
|||
this.apiUrl = 'https://www.giantbomb.com/api';
|
||||
this.types = [MediaType.Game];
|
||||
}
|
||||
|
||||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by Title`);
|
||||
|
||||
|
|
@ -25,35 +28,42 @@ export class GiantBombAPI extends APIModel {
|
|||
throw Error(`MDB | API key for ${this.apiName} missing.`);
|
||||
}
|
||||
|
||||
const searchUrl = `${this.apiUrl}/games?api_key=${this.plugin.settings.GiantBombKey}&filter=name:${encodeURIComponent(title)}&format=json`;
|
||||
const fetchData = await requestUrl({
|
||||
url: searchUrl,
|
||||
const client = createClient<paths>({ baseUrl: 'https://www.giantbomb.com/api/' });
|
||||
const response = await client.GET('/games', {
|
||||
params: {
|
||||
query: {
|
||||
api_key: this.plugin.settings.GiantBombKey,
|
||||
filter: `name:${encodeURIComponent(title)}`,
|
||||
format: 'json',
|
||||
limit: 20,
|
||||
},
|
||||
},
|
||||
fetch: obsidianFetch,
|
||||
});
|
||||
|
||||
// console.debug(fetchData);
|
||||
|
||||
if (fetchData.status === 401) {
|
||||
if (response.response.status === 401) {
|
||||
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
|
||||
}
|
||||
if (fetchData.status === 429) {
|
||||
if (response.response.status === 429) {
|
||||
throw Error(`MDB | Too many requests for ${this.apiName}, you've exceeded your API quota.`);
|
||||
}
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
if (response.response.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json;
|
||||
// console.debug(data);
|
||||
const data = response.data?.results;
|
||||
|
||||
const ret: MediaTypeModel[] = [];
|
||||
for (const result of data.results) {
|
||||
for (const result of data ?? []) {
|
||||
const year = result.original_release_date ? new Date(result.original_release_date).getFullYear().toString() : undefined;
|
||||
|
||||
ret.push(
|
||||
new GameModel({
|
||||
type: MediaType.Game,
|
||||
title: result.name,
|
||||
englishTitle: result.name,
|
||||
year: new Date(result.original_release_date).getFullYear().toString(),
|
||||
year: year,
|
||||
dataSource: this.apiName,
|
||||
id: result.guid,
|
||||
id: result.guid?.toString(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
@ -68,36 +78,79 @@ export class GiantBombAPI extends APIModel {
|
|||
throw Error(`MDB | API key for ${this.apiName} missing.`);
|
||||
}
|
||||
|
||||
const searchUrl = `${this.apiUrl}/game/${encodeURIComponent(id)}/?api_key=${this.plugin.settings.GiantBombKey}&format=json`;
|
||||
const fetchData = await requestUrl({
|
||||
url: searchUrl,
|
||||
const client = createClient<paths>({ baseUrl: 'https://www.giantbomb.com/api/' });
|
||||
const response = await client.GET('/game/{guid}', {
|
||||
params: {
|
||||
path: {
|
||||
guid: id,
|
||||
},
|
||||
query: {
|
||||
api_key: this.plugin.settings.GiantBombKey,
|
||||
format: 'json',
|
||||
},
|
||||
},
|
||||
fetch: obsidianFetch,
|
||||
});
|
||||
console.debug(fetchData);
|
||||
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
if (response.response.status === 401) {
|
||||
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
|
||||
}
|
||||
if (response.response.status === 429) {
|
||||
throw Error(`MDB | Too many requests for ${this.apiName}, you've exceeded your API quota.`);
|
||||
}
|
||||
if (response.response.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json;
|
||||
// console.debug(data);
|
||||
const result = data.results;
|
||||
const result = response.data?.results;
|
||||
|
||||
if (!result) {
|
||||
throw Error(`MDB | No results found for ID ${id} in ${this.apiName}.`);
|
||||
}
|
||||
|
||||
console.log(result);
|
||||
|
||||
// sadly the only OpenAPI definition I could find doesn't have the right types
|
||||
const year = result.original_release_date ? new Date(result.original_release_date).getFullYear().toString() : undefined;
|
||||
const developers = result.developers as
|
||||
| {
|
||||
name: string;
|
||||
}[]
|
||||
| undefined;
|
||||
const publishers = result.publishers as
|
||||
| {
|
||||
name: string;
|
||||
}[]
|
||||
| undefined;
|
||||
const genres = result.genres as
|
||||
| {
|
||||
name: string;
|
||||
}[]
|
||||
| undefined;
|
||||
const image = result.image as
|
||||
| {
|
||||
small_url: string;
|
||||
medium_url: string;
|
||||
super_url: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
return new GameModel({
|
||||
type: MediaType.Game,
|
||||
title: result.name,
|
||||
englishTitle: result.name,
|
||||
year: new Date(result.original_release_date).getFullYear().toString(),
|
||||
year: year,
|
||||
dataSource: this.apiName,
|
||||
url: result.site_detail_url,
|
||||
id: result.guid,
|
||||
developers: result.developers?.map((x: any) => x.name) ?? [],
|
||||
publishers: result.publishers?.map((x: any) => x.name) ?? [],
|
||||
genres: result.genres?.map((x: any) => x.name) ?? [],
|
||||
id: result.guid?.toString(),
|
||||
developers: developers?.map(x => x.name),
|
||||
publishers: publishers?.map(x => x.name),
|
||||
genres: genres?.map(x => x.name),
|
||||
onlineRating: 0,
|
||||
image: result.image?.super_url ?? '',
|
||||
image: image?.super_url,
|
||||
|
||||
released: true,
|
||||
releaseDate: result.original_release_date ?? 'unknown',
|
||||
releaseDate: result.original_release_date,
|
||||
|
||||
userData: {
|
||||
played: false,
|
||||
|
|
@ -107,6 +160,6 @@ export class GiantBombAPI extends APIModel {
|
|||
});
|
||||
}
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.GiantBombAPI_disabledMediaTypes as MediaType[];
|
||||
return this.plugin.settings.GiantBombAPI_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import createClient from 'openapi-fetch';
|
||||
import { isTruthy, obsidianFetch } from 'src/utils/Utils';
|
||||
import type MediaDbPlugin from '../../main';
|
||||
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||
import { MovieModel } from '../../models/MovieModel';
|
||||
import { SeriesModel } from '../../models/SeriesModel';
|
||||
import { MediaType } from '../../utils/MediaType';
|
||||
import { APIModel } from '../APIModel';
|
||||
import type { paths } from '../schemas/MALAPI';
|
||||
|
||||
export class MALAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
|
|
@ -28,30 +31,42 @@ export class MALAPI extends APIModel {
|
|||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by Title`);
|
||||
|
||||
const searchUrl = `https://api.jikan.moe/v4/anime?q=${encodeURIComponent(title)}&limit=20${this.plugin.settings.sfwFilter ? '&sfw' : ''}`;
|
||||
const client = createClient<paths>({ baseUrl: 'https://api.jikan.moe/v4/' });
|
||||
|
||||
const fetchData = await fetch(searchUrl);
|
||||
// console.debug(fetchData);
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
const response = await client.GET('/anime', {
|
||||
params: {
|
||||
query: {
|
||||
q: title,
|
||||
limit: 20,
|
||||
sfw: this.plugin.settings.sfwFilter ? true : false,
|
||||
},
|
||||
},
|
||||
fetch: obsidianFetch,
|
||||
});
|
||||
|
||||
if (response.error !== undefined) {
|
||||
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
|
||||
}
|
||||
const data = await fetchData.json();
|
||||
|
||||
// console.debug(data);
|
||||
const data = response.data?.data;
|
||||
|
||||
const ret: MediaTypeModel[] = [];
|
||||
|
||||
for (const result of data.data) {
|
||||
const type = this.typeMappings.get(result.type?.toLowerCase());
|
||||
for (const result of data ?? []) {
|
||||
const resType = result.type?.toLowerCase();
|
||||
const type = resType ? this.typeMappings.get(resType) : undefined;
|
||||
const year = result.year?.toString() ?? result.aired?.prop?.from?.year?.toString() ?? '';
|
||||
const id = result.mal_id?.toString();
|
||||
|
||||
if (type === undefined) {
|
||||
ret.push(
|
||||
new MovieModel({
|
||||
subType: '',
|
||||
title: result.title,
|
||||
englishTitle: result.title_english ?? result.title,
|
||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||
year,
|
||||
dataSource: this.apiName,
|
||||
id: result.mal_id,
|
||||
id,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
@ -61,9 +76,9 @@ export class MALAPI extends APIModel {
|
|||
subType: type,
|
||||
title: result.title,
|
||||
englishTitle: result.title_english ?? result.title,
|
||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||
year,
|
||||
dataSource: this.apiName,
|
||||
id: result.mal_id,
|
||||
id,
|
||||
}),
|
||||
);
|
||||
} else if (type === 'series' || type === 'ova') {
|
||||
|
|
@ -72,9 +87,9 @@ export class MALAPI extends APIModel {
|
|||
subType: type,
|
||||
title: result.title,
|
||||
englishTitle: result.title_english ?? result.title,
|
||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||
year,
|
||||
dataSource: this.apiName,
|
||||
id: result.mal_id,
|
||||
id,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
@ -86,41 +101,52 @@ export class MALAPI extends APIModel {
|
|||
async getById(id: string): Promise<MediaTypeModel> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||
|
||||
const searchUrl = `https://api.jikan.moe/v4/anime/${encodeURIComponent(id)}/full`;
|
||||
const fetchData = await fetch(searchUrl);
|
||||
const client = createClient<paths>({ baseUrl: 'https://api.jikan.moe/v4/' });
|
||||
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
const response = await client.GET('/anime/{id}/full', {
|
||||
params: {
|
||||
path: {
|
||||
id: id as unknown as number, // This is fine
|
||||
},
|
||||
},
|
||||
fetch: obsidianFetch,
|
||||
});
|
||||
|
||||
if (response.error !== undefined) {
|
||||
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json();
|
||||
// console.debug(data);
|
||||
const result = data.data;
|
||||
const result = response.data?.data;
|
||||
|
||||
if (result === undefined) {
|
||||
throw Error(`MDB | No data found for ID ${id} in ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const resType = result.type?.toLowerCase();
|
||||
const type = resType ? this.typeMappings.get(resType) : undefined;
|
||||
const year = result.year?.toString() ?? result.aired?.prop?.from?.year?.toString();
|
||||
const new_id = result.mal_id?.toString();
|
||||
|
||||
const type = this.typeMappings.get(result.type?.toLowerCase());
|
||||
if (type === undefined) {
|
||||
return new MovieModel({
|
||||
subType: '',
|
||||
subType: undefined,
|
||||
title: result.title,
|
||||
englishTitle: result.title_english ?? result.title,
|
||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||
year: year,
|
||||
dataSource: this.apiName,
|
||||
url: result.url,
|
||||
id: result.mal_id,
|
||||
id: new_id,
|
||||
|
||||
plot: result.synopsis,
|
||||
genres: result.genres?.map((x: any) => x.name) ?? [],
|
||||
director: [],
|
||||
writer: [],
|
||||
studio: result.studios?.map((x: any) => x.name).join(', ') ?? 'unknown',
|
||||
duration: result.duration ?? 'unknown',
|
||||
onlineRating: result.score ?? 0,
|
||||
actors: [],
|
||||
image: result.images?.jpg?.image_url ?? '',
|
||||
genres: result.genres?.map(x => x.name).filter(isTruthy),
|
||||
studio: result.studios?.map(x => x.name).filter(isTruthy),
|
||||
duration: result.duration,
|
||||
onlineRating: result.score,
|
||||
image: result.images?.jpg?.image_url,
|
||||
|
||||
released: true,
|
||||
premiere: this.plugin.dateFormatter.format(result.aired?.from, this.apiDateFormat) ?? 'unknown',
|
||||
streamingServices: result.streaming?.map((x: any) => x.name) ?? [],
|
||||
premiere: this.plugin.dateFormatter.format(result.aired?.from, this.apiDateFormat),
|
||||
streamingServices: result.streaming?.map(x => x.name).filter(isTruthy),
|
||||
|
||||
userData: {
|
||||
watched: false,
|
||||
|
|
@ -135,24 +161,21 @@ export class MALAPI extends APIModel {
|
|||
subType: type,
|
||||
title: result.title,
|
||||
englishTitle: result.title_english ?? result.title,
|
||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||
year: year,
|
||||
dataSource: this.apiName,
|
||||
url: result.url,
|
||||
id: result.mal_id,
|
||||
id: new_id,
|
||||
|
||||
plot: result.synopsis,
|
||||
genres: result.genres?.map((x: any) => x.name) ?? [],
|
||||
director: [],
|
||||
writer: [],
|
||||
studio: result.studios?.map((x: any) => x.name).join(', ') ?? 'unknown',
|
||||
duration: result.duration ?? 'unknown',
|
||||
onlineRating: result.score ?? 0,
|
||||
actors: [],
|
||||
image: result.images?.jpg?.image_url ?? '',
|
||||
genres: result.genres?.map(x => x.name).filter(isTruthy),
|
||||
studio: result.studios?.map(x => x.name).filter(isTruthy),
|
||||
duration: result.duration,
|
||||
onlineRating: result.score,
|
||||
image: result.images?.jpg?.image_url,
|
||||
|
||||
released: true,
|
||||
premiere: this.plugin.dateFormatter.format(result.aired?.from, this.apiDateFormat) ?? 'unknown',
|
||||
streamingServices: result.streaming?.map((x: any) => x.name) ?? [],
|
||||
premiere: this.plugin.dateFormatter.format(result.aired?.from, this.apiDateFormat),
|
||||
streamingServices: result.streaming?.map(x => x.name).filter(isTruthy),
|
||||
|
||||
userData: {
|
||||
watched: false,
|
||||
|
|
@ -165,24 +188,23 @@ export class MALAPI extends APIModel {
|
|||
subType: type,
|
||||
title: result.title,
|
||||
englishTitle: result.title_english ?? result.title,
|
||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||
year: year,
|
||||
dataSource: this.apiName,
|
||||
url: result.url,
|
||||
id: result.mal_id,
|
||||
id: new_id,
|
||||
|
||||
plot: result.synopsis,
|
||||
genres: result.genres?.map((x: any) => x.name) ?? [],
|
||||
writer: [],
|
||||
studio: result.studios?.map((x: any) => x.name) ?? [],
|
||||
genres: result.genres?.map(x => x.name).filter(isTruthy),
|
||||
studio: result.studios?.map(x => x.name).filter(isTruthy),
|
||||
episodes: result.episodes,
|
||||
duration: result.duration ?? 'unknown',
|
||||
onlineRating: result.score ?? 0,
|
||||
streamingServices: result.streaming?.map((x: any) => x.name) ?? [],
|
||||
image: result.images?.jpg?.image_url ?? '',
|
||||
duration: result.duration,
|
||||
onlineRating: result.score,
|
||||
streamingServices: result.streaming?.map(x => x.name).filter(isTruthy),
|
||||
image: result.images?.jpg?.image_url,
|
||||
|
||||
released: true,
|
||||
airedFrom: this.plugin.dateFormatter.format(result.aired?.from, this.apiDateFormat) ?? 'unknown',
|
||||
airedTo: this.plugin.dateFormatter.format(result.aired?.to, this.apiDateFormat) ?? 'unknown',
|
||||
airedFrom: this.plugin.dateFormatter.format(result.aired?.from, this.apiDateFormat),
|
||||
airedTo: this.plugin.dateFormatter.format(result.aired?.to, this.apiDateFormat),
|
||||
airing: result.airing,
|
||||
|
||||
userData: {
|
||||
|
|
@ -196,6 +218,6 @@ export class MALAPI extends APIModel {
|
|||
throw new Error(`MDB | Unknown media type for id ${id}`);
|
||||
}
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.MALAPI_disabledMediaTypes as MediaType[];
|
||||
return this.plugin.settings.MALAPI_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
import createClient from 'openapi-fetch';
|
||||
import { isTruthy, obsidianFetch } from 'src/utils/Utils';
|
||||
import type MediaDbPlugin from '../../main';
|
||||
import { ComicMangaModel } from '../../models/ComicMangaModel';
|
||||
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||
import { MediaType } from '../../utils/MediaType';
|
||||
import { APIModel } from '../APIModel';
|
||||
import type { paths } from '../schemas/MALAPI';
|
||||
|
||||
export class MALAPIManga extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
typeMappings: Map<string, string>;
|
||||
apiDateFormat: string = 'YYYY-MM-DDTHH:mm:ssZ'; // ISO
|
||||
|
||||
constructor(plugin: MediaDbPlugin) {
|
||||
super();
|
||||
|
|
@ -29,43 +33,55 @@ export class MALAPIManga extends APIModel {
|
|||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by Title`);
|
||||
|
||||
const searchUrl = `https://api.jikan.moe/v4/manga?q=${encodeURIComponent(title)}&limit=20${this.plugin.settings.sfwFilter ? '&sfw' : ''}`;
|
||||
const client = createClient<paths>({ baseUrl: 'https://api.jikan.moe/v4/' });
|
||||
|
||||
const fetchData = await fetch(searchUrl);
|
||||
// console.debug(fetchData);
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
const response = await client.GET('/manga', {
|
||||
params: {
|
||||
query: {
|
||||
q: title,
|
||||
limit: 20,
|
||||
sfw: this.plugin.settings.sfwFilter ? true : false,
|
||||
},
|
||||
},
|
||||
fetch: obsidianFetch,
|
||||
});
|
||||
|
||||
if (response.error !== undefined) {
|
||||
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
|
||||
}
|
||||
const data = await fetchData.json();
|
||||
|
||||
// console.debug(data);
|
||||
const data = response.data?.data;
|
||||
|
||||
const ret: MediaTypeModel[] = [];
|
||||
|
||||
for (const result of data.data) {
|
||||
const type = this.typeMappings.get(result.type?.toLowerCase());
|
||||
for (const result of data ?? []) {
|
||||
const resType = result.type?.toLowerCase();
|
||||
const type = resType ? this.typeMappings.get(resType) : undefined;
|
||||
const year = result.published?.prop?.from?.year?.toString() ?? '';
|
||||
const id = result.mal_id?.toString();
|
||||
|
||||
ret.push(
|
||||
new ComicMangaModel({
|
||||
subType: type,
|
||||
title: result.title,
|
||||
plot: result.synopsis,
|
||||
plot: result.synopsis ?? undefined,
|
||||
englishTitle: result.title_english ?? result.title,
|
||||
alternateTitles: result.titles?.map((x: any) => x.title) ?? [],
|
||||
year: result.year ?? result.published?.prop?.from?.year ?? '',
|
||||
alternateTitles: result.titles?.map(x => x.title).filter(isTruthy),
|
||||
year: year,
|
||||
dataSource: this.apiName,
|
||||
url: result.url,
|
||||
id: result.mal_id,
|
||||
id: id,
|
||||
|
||||
genres: result.genres?.map((x: any) => x.name) ?? [],
|
||||
authors: result.authors?.map((x: any) => x.name) ?? [],
|
||||
genres: result.genres?.map(x => x.name).filter(isTruthy),
|
||||
authors: result.authors?.map(x => x.name).filter(isTruthy),
|
||||
chapters: result.chapters,
|
||||
volumes: result.volumes,
|
||||
onlineRating: result.score ?? 0,
|
||||
image: result.images?.jpg?.image_url ?? '',
|
||||
onlineRating: result.score,
|
||||
image: result.images?.jpg?.image_url,
|
||||
|
||||
released: true,
|
||||
publishedFrom: new Date(result.published?.from).toLocaleDateString() ?? 'unknown',
|
||||
publishedTo: new Date(result.published?.to).toLocaleDateString() ?? 'unknown',
|
||||
publishedFrom: this.plugin.dateFormatter.format(result.published?.from, this.apiDateFormat),
|
||||
publishedTo: this.plugin.dateFormatter.format(result.published?.to, this.apiDateFormat),
|
||||
status: result.status,
|
||||
|
||||
userData: {
|
||||
|
|
@ -83,40 +99,53 @@ export class MALAPIManga extends APIModel {
|
|||
async getById(id: string): Promise<MediaTypeModel> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||
|
||||
const searchUrl = `https://api.jikan.moe/v4/manga/${encodeURIComponent(id)}/full`;
|
||||
const fetchData = await fetch(searchUrl);
|
||||
const client = createClient<paths>({ baseUrl: 'https://api.jikan.moe/v4/' });
|
||||
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
const response = await client.GET('/manga/{id}/full', {
|
||||
params: {
|
||||
path: {
|
||||
id: id as unknown as number, // This is fine
|
||||
},
|
||||
},
|
||||
fetch: obsidianFetch,
|
||||
});
|
||||
|
||||
if (response.error !== undefined) {
|
||||
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json();
|
||||
// console.debug(data);
|
||||
const result = data.data;
|
||||
const result = response.data?.data;
|
||||
|
||||
if (!result) {
|
||||
throw Error(`MDB | No data found for ID ${id} in ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const resType = result.type?.toLowerCase();
|
||||
const type = resType ? this.typeMappings.get(resType) : undefined;
|
||||
const year = result.published?.prop?.from?.year?.toString() ?? '';
|
||||
const new_id = result.mal_id?.toString();
|
||||
|
||||
const type = this.typeMappings.get(result.type?.toLowerCase());
|
||||
return new ComicMangaModel({
|
||||
subType: type,
|
||||
title: result.title,
|
||||
plot: result.synopsis ?? undefined,
|
||||
englishTitle: result.title_english ?? result.title,
|
||||
alternateTitles: result.titles?.map((x: any) => x.title) ?? [],
|
||||
year: result.year ?? result.published?.prop?.from?.year ?? '',
|
||||
alternateTitles: result.titles?.map(x => x.title).filter(isTruthy),
|
||||
year: year,
|
||||
dataSource: this.apiName,
|
||||
url: result.url,
|
||||
id: result.mal_id,
|
||||
id: new_id,
|
||||
|
||||
plot: (result.synopsis ?? 'unknown').replace(/"/g, "'") ?? 'unknown',
|
||||
genres: result.genres?.map((x: any) => x.name) ?? [],
|
||||
authors: result.authors?.map((x: any) => x.name) ?? [],
|
||||
genres: result.genres?.map(x => x.name).filter(isTruthy),
|
||||
authors: result.authors?.map(x => x.name).filter(isTruthy),
|
||||
chapters: result.chapters,
|
||||
volumes: result.volumes,
|
||||
onlineRating: result.score ?? 0,
|
||||
image: result.images?.jpg?.image_url ?? '',
|
||||
onlineRating: result.score,
|
||||
image: result.images?.jpg?.image_url,
|
||||
|
||||
released: true,
|
||||
publishers: result.serializations?.map((x: any) => x.name) ?? [],
|
||||
publishedFrom: new Date(result.published?.from).toLocaleDateString() ?? 'unknown',
|
||||
publishedTo: new Date(result.published?.to).toLocaleDateString() ?? 'unknown',
|
||||
publishedFrom: this.plugin.dateFormatter.format(result.published?.from, this.apiDateFormat),
|
||||
publishedTo: this.plugin.dateFormatter.format(result.published?.to, this.apiDateFormat),
|
||||
status: result.status,
|
||||
|
||||
userData: {
|
||||
|
|
@ -127,6 +156,6 @@ export class MALAPIManga extends APIModel {
|
|||
});
|
||||
}
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.MALAPIManga_disabledMediaTypes as MediaType[];
|
||||
return this.plugin.settings.MALAPIManga_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Notice } from 'obsidian';
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */
|
||||
|
||||
import { requestUrl } from 'obsidian';
|
||||
import type MediaDbPlugin from '../../main';
|
||||
import { GameModel } from '../../models/GameModel';
|
||||
|
|
@ -6,6 +7,10 @@ import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
|||
import { MediaType } from '../../utils/MediaType';
|
||||
import { APIModel } from '../APIModel';
|
||||
|
||||
// sadly no open api schema available
|
||||
|
||||
// TODO: maybe we should remove this API, as it can no longer be tested without paying for an API key
|
||||
|
||||
export class MobyGamesAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
apiDateFormat: string = 'YYYY-DD-MM';
|
||||
|
|
@ -19,6 +24,7 @@ export class MobyGamesAPI extends APIModel {
|
|||
this.apiUrl = 'https://api.mobygames.com/v1';
|
||||
this.types = [MediaType.Game];
|
||||
}
|
||||
|
||||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by Title`);
|
||||
|
||||
|
|
@ -108,6 +114,6 @@ export class MobyGamesAPI extends APIModel {
|
|||
});
|
||||
}
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.MobyGamesAPI_disabledMediaTypes as MediaType[];
|
||||
return this.plugin.settings.MobyGamesAPI_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,73 @@ import { MediaType } from '../../utils/MediaType';
|
|||
import { contactEmail, mediaDbVersion, pluginName } from '../../utils/Utils';
|
||||
import { APIModel } from '../APIModel';
|
||||
|
||||
// sadly no open api schema available
|
||||
|
||||
interface Tag {
|
||||
name: string;
|
||||
count: number;
|
||||
}
|
||||
interface Genre {
|
||||
name: string;
|
||||
count: number;
|
||||
id: string;
|
||||
disambiguation: string;
|
||||
}
|
||||
interface Release {
|
||||
id: string;
|
||||
'status-id': string;
|
||||
title: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
interface SearchResponse {
|
||||
id: string;
|
||||
'type-id': string;
|
||||
score: number;
|
||||
'primary-type-id': string;
|
||||
'artists-credit-id': string;
|
||||
count: number;
|
||||
title: string;
|
||||
'first-release-date': string;
|
||||
'primary-type': string;
|
||||
'artist-credit': {
|
||||
name: string;
|
||||
artist: {
|
||||
id: string;
|
||||
name: string;
|
||||
'short-name': string;
|
||||
};
|
||||
}[];
|
||||
releases: Release[];
|
||||
tags: Tag[];
|
||||
}
|
||||
|
||||
interface IdResponse {
|
||||
id: string;
|
||||
tags: Tag[];
|
||||
'primary-type-id': string;
|
||||
'artist-credit': {
|
||||
name: string;
|
||||
artist: {
|
||||
tags: Tag[];
|
||||
type: string;
|
||||
id: string;
|
||||
name: string;
|
||||
'short-name': string;
|
||||
country: string;
|
||||
};
|
||||
}[];
|
||||
title: string;
|
||||
genres: Genre[];
|
||||
'first-release-date': string;
|
||||
releases: Release[];
|
||||
'primary-type': string;
|
||||
rating: {
|
||||
value: number;
|
||||
'votes-count': number;
|
||||
};
|
||||
}
|
||||
|
||||
export class MusicBrainzAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
|
||||
|
|
@ -37,7 +104,9 @@ export class MusicBrainzAPI extends APIModel {
|
|||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json;
|
||||
const data = (await fetchData.json) as {
|
||||
'release-groups': SearchResponse[];
|
||||
};
|
||||
// console.debug(data);
|
||||
const ret: MediaTypeModel[] = [];
|
||||
|
||||
|
|
@ -53,7 +122,7 @@ export class MusicBrainzAPI extends APIModel {
|
|||
id: result.id,
|
||||
image: 'https://coverartarchive.org/release-group/' + result.id + '/front',
|
||||
|
||||
artists: result['artist-credit'].map((a: any) => a.name),
|
||||
artists: result['artist-credit'].map(a => a.name),
|
||||
subType: result['primary-type'],
|
||||
}),
|
||||
);
|
||||
|
|
@ -77,7 +146,7 @@ export class MusicBrainzAPI extends APIModel {
|
|||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const result = await fetchData.json;
|
||||
const result = (await fetchData.json) as IdResponse;
|
||||
|
||||
return new MusicReleaseModel({
|
||||
type: 'musicRelease',
|
||||
|
|
@ -89,8 +158,8 @@ export class MusicBrainzAPI extends APIModel {
|
|||
id: result.id,
|
||||
image: 'https://coverartarchive.org/release-group/' + result.id + '/front',
|
||||
|
||||
artists: result['artist-credit'].map((a: any) => a.name),
|
||||
genres: result.genres.map((g: any) => g.name),
|
||||
artists: result['artist-credit'].map(a => a.name),
|
||||
genres: result.genres.map(g => g.name),
|
||||
subType: result['primary-type'],
|
||||
rating: result.rating.value * 2,
|
||||
|
||||
|
|
@ -100,6 +169,6 @@ export class MusicBrainzAPI extends APIModel {
|
|||
});
|
||||
}
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.MusicBrainzAPI_disabledMediaTypes as MediaType[];
|
||||
return this.plugin.settings.MusicBrainzAPI_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Notice } from 'obsidian';
|
||||
import createClient from 'openapi-fetch';
|
||||
import { obsidianFetch } from 'src/utils/Utils';
|
||||
import type MediaDbPlugin from '../../main';
|
||||
import { GameModel } from '../../models/GameModel';
|
||||
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||
|
|
@ -6,6 +7,56 @@ import { MovieModel } from '../../models/MovieModel';
|
|||
import { SeriesModel } from '../../models/SeriesModel';
|
||||
import { MediaType } from '../../utils/MediaType';
|
||||
import { APIModel } from '../APIModel';
|
||||
import type { paths } from '../schemas/OMDb';
|
||||
|
||||
type SearchResponse =
|
||||
| {
|
||||
Response: 'True';
|
||||
totalResults: string;
|
||||
Search: {
|
||||
Title: string;
|
||||
Year: string;
|
||||
Poster: string;
|
||||
imdbID: string;
|
||||
Type: string;
|
||||
}[];
|
||||
}
|
||||
| {
|
||||
Response: 'False';
|
||||
Error: string;
|
||||
};
|
||||
|
||||
type IdResponse =
|
||||
| {
|
||||
Response: 'True';
|
||||
Title: string;
|
||||
Year: string;
|
||||
Rated: string;
|
||||
Released: string;
|
||||
Runtime: string;
|
||||
Genre: string;
|
||||
Director: string;
|
||||
Writer: string;
|
||||
Actors: string;
|
||||
Plot: string;
|
||||
Language: string;
|
||||
Country: string;
|
||||
Awards: string;
|
||||
Poster: string;
|
||||
Metascore: string;
|
||||
imdbRating: string;
|
||||
imdbVotes: string;
|
||||
imdbID: string;
|
||||
Type: string;
|
||||
DVD: string;
|
||||
BoxOffice: string;
|
||||
Production: string;
|
||||
Website: string;
|
||||
}
|
||||
| {
|
||||
Response: 'False';
|
||||
Error: string;
|
||||
};
|
||||
|
||||
export class OMDbAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
|
|
@ -33,24 +84,37 @@ export class OMDbAPI extends APIModel {
|
|||
throw new Error(`MDB | API key for ${this.apiName} missing.`);
|
||||
}
|
||||
|
||||
const searchUrl = `https://www.omdbapi.com/?s=${encodeURIComponent(title)}&apikey=${this.plugin.settings.OMDbKey}`;
|
||||
const fetchData = await fetch(searchUrl);
|
||||
const client = createClient<paths>({ baseUrl: 'https://www.omdbapi.com/' });
|
||||
|
||||
if (fetchData.status === 401) {
|
||||
const response = await client.GET('/?s', {
|
||||
params: {
|
||||
query: {
|
||||
s: title,
|
||||
apikey: this.plugin.settings.OMDbKey,
|
||||
},
|
||||
},
|
||||
fetch: obsidianFetch,
|
||||
});
|
||||
|
||||
if (response.response.status === 401) {
|
||||
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
|
||||
}
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
if (response.response.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json();
|
||||
const data = response.data as SearchResponse | undefined;
|
||||
|
||||
if (!data) {
|
||||
throw Error(`MDB | No data received from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
if (data.Response === 'False') {
|
||||
if (data.Error === 'Movie not found!') {
|
||||
return [];
|
||||
}
|
||||
|
||||
throw Error(`MDB | Received error from ${this.apiName}: \n${JSON.stringify(data, undefined, 4)}`);
|
||||
throw Error(`MDB | Received error from ${this.apiName}: ${data.Error}`);
|
||||
}
|
||||
if (!data.Search) {
|
||||
return [];
|
||||
|
|
@ -111,18 +175,30 @@ export class OMDbAPI extends APIModel {
|
|||
throw Error(`MDB | API key for ${this.apiName} missing.`);
|
||||
}
|
||||
|
||||
const searchUrl = `https://www.omdbapi.com/?i=${encodeURIComponent(id)}&apikey=${this.plugin.settings.OMDbKey}`;
|
||||
const fetchData = await fetch(searchUrl);
|
||||
const client = createClient<paths>({ baseUrl: 'https://www.omdbapi.com/' });
|
||||
|
||||
if (fetchData.status === 401) {
|
||||
const response = await client.GET('/?i', {
|
||||
params: {
|
||||
query: {
|
||||
i: id,
|
||||
apikey: this.plugin.settings.OMDbKey,
|
||||
},
|
||||
},
|
||||
fetch: obsidianFetch,
|
||||
});
|
||||
|
||||
if (response.response.status === 401) {
|
||||
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
|
||||
}
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
if (response.response.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const result = await fetchData.json();
|
||||
// console.debug(result);
|
||||
const result = response.data as IdResponse | undefined;
|
||||
|
||||
if (!result) {
|
||||
throw Error(`MDB | No data received from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
if (result.Response === 'False') {
|
||||
throw Error(`MDB | Received error from ${this.apiName}: ${result.Error}`);
|
||||
|
|
@ -130,7 +206,7 @@ export class OMDbAPI extends APIModel {
|
|||
|
||||
const type = this.typeMappings.get(result.Type.toLowerCase());
|
||||
if (type === undefined) {
|
||||
throw Error(`${result.type.toLowerCase()} is an unsupported type.`);
|
||||
throw Error(`${result.Type.toLowerCase()} is an unsupported type.`);
|
||||
}
|
||||
|
||||
if (type === 'movie') {
|
||||
|
|
@ -143,19 +219,17 @@ export class OMDbAPI extends APIModel {
|
|||
url: `https://www.imdb.com/title/${result.imdbID}/`,
|
||||
id: result.imdbID,
|
||||
|
||||
plot: result.Plot ?? '',
|
||||
genres: result.Genre?.split(', ') ?? [],
|
||||
director: result.Director?.split(', ') ?? [],
|
||||
writer: result.Writer?.split(', ') ?? [],
|
||||
studio: ['N/A'],
|
||||
duration: result.Runtime ?? 'unknown',
|
||||
plot: result.Plot,
|
||||
genres: result.Genre?.split(', '),
|
||||
director: result.Director?.split(', '),
|
||||
writer: result.Writer?.split(', '),
|
||||
duration: result.Runtime,
|
||||
onlineRating: Number.parseFloat(result.imdbRating ?? 0),
|
||||
actors: result.Actors?.split(', ') ?? [],
|
||||
image: result.Poster ? result.Poster.replace('_SX300', '_SX600') : '',
|
||||
actors: result.Actors?.split(', '),
|
||||
image: result.Poster.replace('_SX300', '_SX600'),
|
||||
|
||||
released: true,
|
||||
streamingServices: [],
|
||||
premiere: this.plugin.dateFormatter.format(result.Released, this.apiDateFormat) ?? 'unknown',
|
||||
premiere: this.plugin.dateFormatter.format(result.Released, this.apiDateFormat),
|
||||
|
||||
userData: {
|
||||
watched: false,
|
||||
|
|
@ -173,21 +247,18 @@ export class OMDbAPI extends APIModel {
|
|||
url: `https://www.imdb.com/title/${result.imdbID}/`,
|
||||
id: result.imdbID,
|
||||
|
||||
plot: result.Plot ?? '',
|
||||
genres: result.Genre?.split(', ') ?? [],
|
||||
writer: result.Writer?.split(', ') ?? [],
|
||||
plot: result.Plot,
|
||||
genres: result.Genre?.split(', '),
|
||||
writer: result.Writer?.split(', '),
|
||||
studio: [],
|
||||
episodes: 0,
|
||||
duration: result.Runtime ?? 'unknown',
|
||||
duration: result.Runtime,
|
||||
onlineRating: Number.parseFloat(result.imdbRating ?? 0),
|
||||
actors: result.Actors?.split(', ') ?? [],
|
||||
image: result.Poster ? result.Poster.replace('_SX300', '_SX600') : '',
|
||||
actors: result.Actors?.split(', '),
|
||||
image: result.Poster.replace('_SX300', '_SX600'),
|
||||
|
||||
released: true,
|
||||
streamingServices: [],
|
||||
airing: false,
|
||||
airedFrom: this.plugin.dateFormatter.format(result.Released, this.apiDateFormat) ?? 'unknown',
|
||||
airedTo: 'unknown',
|
||||
airedFrom: this.plugin.dateFormatter.format(result.Released, this.apiDateFormat),
|
||||
|
||||
userData: {
|
||||
watched: false,
|
||||
|
|
@ -205,14 +276,12 @@ export class OMDbAPI extends APIModel {
|
|||
url: `https://www.imdb.com/title/${result.imdbID}/`,
|
||||
id: result.imdbID,
|
||||
|
||||
developers: [],
|
||||
publishers: [],
|
||||
genres: result.Genre?.split(', ') ?? [],
|
||||
genres: result.Genre?.split(', '),
|
||||
onlineRating: Number.parseFloat(result.imdbRating ?? 0),
|
||||
image: result.Poster ? result.Poster.replace('_SX300', '_SX600') : '',
|
||||
image: result.Poster.replace('_SX300', '_SX600'),
|
||||
|
||||
released: true,
|
||||
releaseDate: this.plugin.dateFormatter.format(result.Released, this.apiDateFormat) ?? 'unknown',
|
||||
releaseDate: this.plugin.dateFormatter.format(result.Released, this.apiDateFormat),
|
||||
|
||||
userData: {
|
||||
played: false,
|
||||
|
|
@ -225,6 +294,6 @@ export class OMDbAPI extends APIModel {
|
|||
}
|
||||
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.OMDbAPI_disabledMediaTypes as MediaType[];
|
||||
return this.plugin.settings.OMDbAPI_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,26 @@
|
|||
import createClient from 'openapi-fetch';
|
||||
import { BookModel } from 'src/models/BookModel';
|
||||
import { obsidianFetch } from 'src/utils/Utils';
|
||||
import type MediaDbPlugin from '../../main';
|
||||
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||
import { MediaType } from '../../utils/MediaType';
|
||||
import { APIModel } from '../APIModel';
|
||||
import type { paths } from '../schemas/OpenLibrary';
|
||||
|
||||
interface SearchResponse {
|
||||
cover_i: number;
|
||||
has_fulltext: boolean;
|
||||
edition_count: number;
|
||||
title: string;
|
||||
author_name: string[];
|
||||
first_publish_year: number;
|
||||
key: string;
|
||||
|
||||
number_of_pages_median?: number;
|
||||
cover_edition_key?: string;
|
||||
isbn?: string[];
|
||||
ratings_average?: number;
|
||||
}
|
||||
|
||||
export class OpenLibraryAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
|
|
@ -20,14 +38,24 @@ export class OpenLibraryAPI extends APIModel {
|
|||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by Title`);
|
||||
|
||||
const searchUrl = `https://openlibrary.org/search.json?title=${encodeURIComponent(title)}`;
|
||||
const client = createClient<paths>({ baseUrl: 'https://openlibrary.org/' });
|
||||
|
||||
const fetchData = await fetch(searchUrl);
|
||||
// console.debug(fetchData);
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
const response = await client.GET('/search.json', {
|
||||
params: {
|
||||
query: {
|
||||
q: title,
|
||||
},
|
||||
},
|
||||
fetch: obsidianFetch,
|
||||
});
|
||||
|
||||
if (response.error !== undefined) {
|
||||
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
|
||||
}
|
||||
const data = await fetchData.json();
|
||||
|
||||
const data = response.data as {
|
||||
docs: SearchResponse[];
|
||||
};
|
||||
|
||||
// console.debug(data);
|
||||
|
||||
|
|
@ -37,11 +65,11 @@ export class OpenLibraryAPI extends APIModel {
|
|||
ret.push(
|
||||
new BookModel({
|
||||
title: result.title,
|
||||
englishTitle: result.title_english ?? result.title,
|
||||
year: result.first_publish_year,
|
||||
englishTitle: result.title,
|
||||
year: result.first_publish_year.toString(),
|
||||
dataSource: this.apiName,
|
||||
id: result.key,
|
||||
author: result.author_name ?? 'unknown',
|
||||
author: result.author_name.join(', '),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
@ -52,33 +80,47 @@ export class OpenLibraryAPI extends APIModel {
|
|||
async getById(id: string): Promise<MediaTypeModel> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||
|
||||
const searchUrl = `https://openlibrary.org/search.json?q=key:${encodeURIComponent(id)}`;
|
||||
const fetchData = await fetch(searchUrl);
|
||||
// console.debug(fetchData);
|
||||
const client = createClient<paths>({ baseUrl: 'https://openlibrary.org/' });
|
||||
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
const response = await client.GET('/search.json', {
|
||||
params: {
|
||||
query: {
|
||||
q: `key:${id}`,
|
||||
fields: 'key,title,author_name,number_of_pages_median,first_publish_year,isbn,ratings_score,first_sentence,title_suggest,rating*,cover_edition_key',
|
||||
},
|
||||
},
|
||||
fetch: obsidianFetch,
|
||||
});
|
||||
|
||||
if (response.error !== undefined) {
|
||||
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json();
|
||||
const data = response.data as {
|
||||
docs: SearchResponse[];
|
||||
};
|
||||
|
||||
// console.debug(data);
|
||||
const result = data.docs[0];
|
||||
|
||||
const pages = Number(result.number_of_pages_median);
|
||||
const isbn = Number((result.isbn ?? []).find((el: string) => el.length <= 10));
|
||||
const isbn13 = Number((result.isbn ?? []).find((el: string) => el.length == 13));
|
||||
|
||||
return new BookModel({
|
||||
title: result.title,
|
||||
year: result.first_publish_year,
|
||||
year: result.first_publish_year.toString(),
|
||||
dataSource: this.apiName,
|
||||
url: `https://openlibrary.org` + result.key,
|
||||
id: result.key,
|
||||
isbn: (result.isbn ?? []).find((el: string | any[]) => el.length <= 10) ?? 'unknown',
|
||||
isbn13: (result.isbn ?? []).find((el: string | any[]) => el.length == 13) ?? 'unknown',
|
||||
englishTitle: result.title_english ?? result.title,
|
||||
isbn: Number.isNaN(isbn) ? undefined : isbn,
|
||||
isbn13: Number.isNaN(isbn13) ? undefined : isbn13,
|
||||
englishTitle: result.title,
|
||||
|
||||
author: result.author_name ?? 'unknown',
|
||||
plot: result.description ?? 'unknown',
|
||||
pages: result.number_of_pages_median ?? 'unknown',
|
||||
onlineRating: Number.parseFloat(Number(result.ratings_average ?? 0).toFixed(2)),
|
||||
image: `https://covers.openlibrary.org/b/OLID/` + result.cover_edition_key + `-L.jpg`,
|
||||
author: result.author_name.join(', '),
|
||||
pages: Number.isNaN(pages) ? undefined : pages,
|
||||
onlineRating: result.ratings_average,
|
||||
image: result.cover_edition_key ? `https://covers.openlibrary.org/b/OLID/` + result.cover_edition_key + `-L.jpg` : undefined,
|
||||
|
||||
released: true,
|
||||
|
||||
|
|
@ -90,6 +132,6 @@ export class OpenLibraryAPI extends APIModel {
|
|||
});
|
||||
}
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.OpenLibraryAPI_disabledMediaTypes as MediaType[];
|
||||
return this.plugin.settings.OpenLibraryAPI_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,128 @@ import type MediaDbPlugin from '../../main';
|
|||
import { GameModel } from '../../models/GameModel';
|
||||
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||
import { MediaType } from '../../utils/MediaType';
|
||||
import { APIModel } from '../APIModel';
|
||||
import { imageUrlExists } from '../../utils/Utils';
|
||||
import { APIModel } from '../APIModel';
|
||||
|
||||
interface SearchResponse {
|
||||
appid: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
logo: string;
|
||||
}
|
||||
|
||||
type IdResponse = Record<
|
||||
string,
|
||||
{
|
||||
success: boolean;
|
||||
data: GameDetails;
|
||||
}
|
||||
>;
|
||||
|
||||
interface GameDetails {
|
||||
type: string;
|
||||
name: string;
|
||||
steam_appid: number;
|
||||
required_age: string;
|
||||
is_free: boolean;
|
||||
controller_support: string;
|
||||
dlc: number[];
|
||||
detailed_description: string;
|
||||
about_the_game: string;
|
||||
short_description: string;
|
||||
supported_languages: string;
|
||||
reviews: string;
|
||||
header_image: string;
|
||||
capsule_image: string;
|
||||
capsule_imagev5: string;
|
||||
website: string;
|
||||
pc_requirements: Requirements;
|
||||
mac_requirements: Requirements;
|
||||
linux_requirements: Requirements;
|
||||
legal_notice: string;
|
||||
drm_notice: string;
|
||||
developers: string[];
|
||||
publishers: string[];
|
||||
price_overview: PriceOverview;
|
||||
packages: number[];
|
||||
platforms: Platforms;
|
||||
metacritic?: {
|
||||
score: number;
|
||||
url: string;
|
||||
};
|
||||
categories: Category[];
|
||||
genres: Genre[];
|
||||
recommendations: {
|
||||
total: number;
|
||||
};
|
||||
achievements: {
|
||||
total: number;
|
||||
highlighted: Achievement[];
|
||||
};
|
||||
release_date: {
|
||||
coming_soon: boolean;
|
||||
date: string;
|
||||
};
|
||||
support_info: {
|
||||
url: string;
|
||||
email: string;
|
||||
};
|
||||
background: string;
|
||||
background_raw: string;
|
||||
content_descriptors: {
|
||||
ids: number[];
|
||||
notes: string;
|
||||
};
|
||||
ratings: Ratings;
|
||||
}
|
||||
|
||||
interface Requirements {
|
||||
minimum: string;
|
||||
recommended: string;
|
||||
}
|
||||
|
||||
interface PriceOverview {
|
||||
currency: string;
|
||||
initial: number;
|
||||
final: number;
|
||||
discount_percent: number;
|
||||
initial_formatted: string;
|
||||
final_formatted: string;
|
||||
}
|
||||
|
||||
interface Platforms {
|
||||
windows: boolean;
|
||||
mac: boolean;
|
||||
linux: boolean;
|
||||
}
|
||||
|
||||
interface Category {
|
||||
id: number;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface Genre {
|
||||
id: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface Achievement {
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
type Ratings = Record<
|
||||
string,
|
||||
{
|
||||
rating: string;
|
||||
descriptors: string;
|
||||
use_age_gate: string;
|
||||
required_age: string;
|
||||
rating_id?: string;
|
||||
banned?: string;
|
||||
rating_generated?: string;
|
||||
}
|
||||
>;
|
||||
|
||||
export class SteamAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
|
|
@ -35,7 +155,7 @@ export class SteamAPI extends APIModel {
|
|||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json;
|
||||
const data = (await fetchData.json) as SearchResponse[];
|
||||
|
||||
// console.debug(data);
|
||||
|
||||
|
|
@ -70,14 +190,13 @@ export class SteamAPI extends APIModel {
|
|||
}
|
||||
|
||||
// console.debug(await fetchData.json);
|
||||
const data = (await fetchData.json) as IdResponse;
|
||||
|
||||
let result: any;
|
||||
for (const [key, value] of Object.entries(await fetchData.json)) {
|
||||
// console.log(typeof key, key)
|
||||
// console.log(typeof id, id)
|
||||
let result: GameDetails | undefined = undefined;
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
// after some testing I found out that id is somehow a number despite that it's defined as string...
|
||||
if (key === String(id)) {
|
||||
result = (value as any).data;
|
||||
result = value.data;
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
|
|
@ -103,16 +222,16 @@ export class SteamAPI extends APIModel {
|
|||
year: new Date(result.release_date.date).getFullYear().toString(),
|
||||
dataSource: this.apiName,
|
||||
url: `https://store.steampowered.com/app/${result.steam_appid}`,
|
||||
id: result.steam_appid,
|
||||
id: result.steam_appid.toString(),
|
||||
|
||||
developers: result.developers,
|
||||
publishers: result.publishers,
|
||||
genres: result.genres?.map((x: any) => x.description) ?? [],
|
||||
onlineRating: Number.parseFloat(result.metacritic?.score ?? 0),
|
||||
image: finalimageurl ?? '',
|
||||
genres: result.genres?.map(x => x.description),
|
||||
onlineRating: result.metacritic?.score,
|
||||
image: finalimageurl,
|
||||
|
||||
released: !result.release_date?.coming_soon,
|
||||
releaseDate: this.plugin.dateFormatter.format(result.release_date?.date, this.apiDateFormat) ?? 'unknown',
|
||||
releaseDate: this.plugin.dateFormatter.format(result.release_date?.date, this.apiDateFormat),
|
||||
|
||||
userData: {
|
||||
played: false,
|
||||
|
|
@ -121,6 +240,6 @@ export class SteamAPI extends APIModel {
|
|||
});
|
||||
}
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.SteamAPI_disabledMediaTypes as MediaType[];
|
||||
return this.plugin.settings.SteamAPI_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,35 @@ import { WikiModel } from '../../models/WikiModel';
|
|||
import { MediaType } from '../../utils/MediaType';
|
||||
import { APIModel } from '../APIModel';
|
||||
|
||||
interface SearchResponse {
|
||||
query: {
|
||||
search: {
|
||||
title: string;
|
||||
pageid: number;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
interface IdResponse {
|
||||
query: {
|
||||
pages: Record<string, WikipediaPage>;
|
||||
};
|
||||
}
|
||||
|
||||
interface WikipediaPage {
|
||||
pageid: number;
|
||||
title: string;
|
||||
contentmodel: string;
|
||||
pagelanguage: string;
|
||||
pagelanguagehtmlcode: string;
|
||||
pagelanguagedir: string;
|
||||
touched: string; // ISO date string
|
||||
lastrevid: number;
|
||||
length: number;
|
||||
fullurl: string;
|
||||
editurl: string;
|
||||
canonicalurl: string;
|
||||
}
|
||||
export class WikipediaAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
apiDateFormat: string = 'YYYY-MM-DDTHH:mm:ssZ'; // ISO
|
||||
|
|
@ -29,7 +58,7 @@ export class WikipediaAPI extends APIModel {
|
|||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json();
|
||||
const data = (await fetchData.json()) as SearchResponse;
|
||||
console.debug(data);
|
||||
const ret: MediaTypeModel[] = [];
|
||||
|
||||
|
|
@ -41,7 +70,7 @@ export class WikipediaAPI extends APIModel {
|
|||
englishTitle: result.title,
|
||||
year: '',
|
||||
dataSource: this.apiName,
|
||||
id: result.pageid,
|
||||
id: result.pageid.toString(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
@ -59,27 +88,25 @@ export class WikipediaAPI extends APIModel {
|
|||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json();
|
||||
const data = (await fetchData.json()) as IdResponse;
|
||||
// console.debug(data);
|
||||
const result: any = Object.entries(data?.query?.pages)[0][1];
|
||||
const result = Object.values(data?.query?.pages)[0];
|
||||
|
||||
return new WikiModel({
|
||||
type: 'wiki',
|
||||
title: result.title,
|
||||
englishTitle: result.title,
|
||||
year: '',
|
||||
dataSource: this.apiName,
|
||||
url: result.fullurl,
|
||||
id: result.pageid,
|
||||
id: result.pageid.toString(),
|
||||
|
||||
wikiUrl: result.fullurl,
|
||||
lastUpdated: this.plugin.dateFormatter.format(result.touched, this.apiDateFormat) ?? undefined,
|
||||
lastUpdated: this.plugin.dateFormatter.format(result.touched, this.apiDateFormat),
|
||||
length: result.length,
|
||||
|
||||
userData: {},
|
||||
});
|
||||
}
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.WikipediaAPI_disabledMediaTypes as MediaType[];
|
||||
return this.plugin.settings.WikipediaAPI_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11226
src/api/schemas/GiantBomb.json
Normal file
11226
src/api/schemas/GiantBomb.json
Normal file
File diff suppressed because it is too large
Load diff
6454
src/api/schemas/GiantBomb.ts
Normal file
6454
src/api/schemas/GiantBomb.ts
Normal file
File diff suppressed because it is too large
Load diff
6761
src/api/schemas/MALAPI.ts
Normal file
6761
src/api/schemas/MALAPI.ts
Normal file
File diff suppressed because it is too large
Load diff
269
src/api/schemas/OMDb.json
Normal file
269
src/api/schemas/OMDb.json
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
{
|
||||
"openapi": "3.0.1",
|
||||
"info": {
|
||||
"title": "OMDb API",
|
||||
"description": "This API requires authorization, you can get a free key here: [http://omdbapi.com/apikey.aspx](http://omdbapi.com/apikey.aspx)",
|
||||
"termsOfService": "http://omdbapi.com/legal.htm",
|
||||
"contact": {
|
||||
"email": "bfritz@fadingsignal.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "CC BY-NC 4.0",
|
||||
"url": "https://creativecommons.org/licenses/by-nc/4.0/"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"externalDocs": {
|
||||
"description": "Find out more about Swagger",
|
||||
"url": "http://swagger.io"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://omdbapi.com/"
|
||||
},
|
||||
{
|
||||
"url": "https://omdbapi.com/"
|
||||
}
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"APIKeyQueryParam": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"name": "Title Parameter",
|
||||
"description": "e.g. ?t=title"
|
||||
},
|
||||
{
|
||||
"name": "ID Parameter",
|
||||
"description": "e.g. ?i=tt0000001"
|
||||
},
|
||||
{
|
||||
"name": "Search Parameter",
|
||||
"description": "e.g. ?s=title"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/?t": {
|
||||
"get": {
|
||||
"tags": ["Title Parameter"],
|
||||
"summary": "Returns the most popular match for a given title",
|
||||
"operationId": "getTitle",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "t",
|
||||
"in": "query",
|
||||
"description": "Title of movie or series",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"in": "query",
|
||||
"description": "Year of release",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"in": "query",
|
||||
"description": "Return movie or series",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["movie", "series"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "plot",
|
||||
"in": "query",
|
||||
"description": "Return short or full plot",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["short", "full"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "r",
|
||||
"in": "query",
|
||||
"description": "The response type to return",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["json", "xml"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"in": "query",
|
||||
"description": "JSONP callback name",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful operation",
|
||||
"content": {}
|
||||
},
|
||||
"401": {
|
||||
"description": "Not authenticated",
|
||||
"content": {}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"APIKeyQueryParam": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/?i": {
|
||||
"get": {
|
||||
"tags": ["ID Parameter"],
|
||||
"summary": "Returns a single result based on the ID provided",
|
||||
"operationId": "getId",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "i",
|
||||
"in": "query",
|
||||
"description": "A valid IMDb ID (e.g. tt0000001)",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "plot",
|
||||
"in": "query",
|
||||
"description": "Return short or full plot",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["short", "full"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "r",
|
||||
"in": "query",
|
||||
"description": "The response type to return",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["json", "xml"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"in": "query",
|
||||
"description": "JSONP callback name",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful operation",
|
||||
"content": {}
|
||||
},
|
||||
"401": {
|
||||
"description": "Not authenticated",
|
||||
"content": {}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"APIKeyQueryParam": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/?s": {
|
||||
"get": {
|
||||
"tags": ["Search Parameter"],
|
||||
"summary": "Returns an array of results for a given title",
|
||||
"operationId": "titleSearch",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "s",
|
||||
"in": "query",
|
||||
"description": "Title of movie or series",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"in": "query",
|
||||
"description": "Year of release",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"in": "query",
|
||||
"description": "Return movie or series",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["movie", "series"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "r",
|
||||
"in": "query",
|
||||
"description": "The response type to return",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["json", "xml"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"description": "Page number to return",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"in": "query",
|
||||
"description": "JSONP callback name",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful operation",
|
||||
"content": {}
|
||||
},
|
||||
"401": {
|
||||
"description": "Not authenticated",
|
||||
"content": {}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"APIKeyQueryParam": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"APIKeyQueryParam": {
|
||||
"type": "apiKey",
|
||||
"name": "apikey",
|
||||
"in": "query"
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-original-swagger-version": "2.0"
|
||||
}
|
||||
180
src/api/schemas/OMDb.ts
Normal file
180
src/api/schemas/OMDb.ts
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/**
|
||||
* This file was auto-generated by openapi-typescript.
|
||||
* Do not make direct changes to the file.
|
||||
*/
|
||||
|
||||
export interface paths {
|
||||
'/?t': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Returns the most popular match for a given title */
|
||||
get: operations['getTitle'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/?i': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Returns a single result based on the ID provided */
|
||||
get: operations['getId'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/?s': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Returns an array of results for a given title */
|
||||
get: operations['titleSearch'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
}
|
||||
export type webhooks = Record<string, never>;
|
||||
export interface components {
|
||||
schemas: never;
|
||||
responses: never;
|
||||
parameters: never;
|
||||
requestBodies: never;
|
||||
headers: never;
|
||||
pathItems: never;
|
||||
}
|
||||
export type $defs = Record<string, never>;
|
||||
export interface operations {
|
||||
getTitle: {
|
||||
parameters: {
|
||||
query: {
|
||||
/** @description Title of movie or series */
|
||||
t: string;
|
||||
/** @description Year of release */
|
||||
y?: number;
|
||||
/** @description Return movie or series */
|
||||
type?: 'movie' | 'series';
|
||||
/** @description Return short or full plot */
|
||||
plot?: 'short' | 'full';
|
||||
/** @description The response type to return */
|
||||
r?: 'json' | 'xml';
|
||||
/** @description JSONP callback name */
|
||||
callback?: string;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful operation */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Not authenticated */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
getId: {
|
||||
parameters: {
|
||||
query: {
|
||||
/** @description A valid IMDb ID (e.g. tt0000001) */
|
||||
i: string;
|
||||
/** @description Return short or full plot */
|
||||
plot?: 'short' | 'full';
|
||||
/** @description The response type to return */
|
||||
r?: 'json' | 'xml';
|
||||
/** @description JSONP callback name */
|
||||
callback?: string;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful operation */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Not authenticated */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
titleSearch: {
|
||||
parameters: {
|
||||
query: {
|
||||
/** @description Title of movie or series */
|
||||
s: string;
|
||||
/** @description Year of release */
|
||||
y?: number;
|
||||
/** @description Return movie or series */
|
||||
type?: 'movie' | 'series';
|
||||
/** @description The response type to return */
|
||||
r?: 'json' | 'xml';
|
||||
/** @description Page number to return */
|
||||
page?: number;
|
||||
/** @description JSONP callback name */
|
||||
callback?: string;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful operation */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Not authenticated */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
602
src/api/schemas/OpenLibrary.json
Normal file
602
src/api/schemas/OpenLibrary.json
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
{
|
||||
"components": {
|
||||
"schemas": {
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ValidationError"
|
||||
},
|
||||
"title": "Detail",
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"title": "HTTPValidationError",
|
||||
"type": "object"
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": "Location",
|
||||
"type": "array"
|
||||
},
|
||||
"msg": {
|
||||
"title": "Message",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"title": "Error Type",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["loc", "msg", "type"],
|
||||
"title": "ValidationError",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"description": "- These are still in development and may not be perfect\n- Contribute by proposing edits to [openapi.json](https://github.com/internetarchive/openlibrary/blob/master/static/openapi.json)\n- Please do not use our APIs for bulk downloads, see [dev center](https://openlibrary.org/developers/api)",
|
||||
"title": "Open Library API",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"openapi": "3.0.2",
|
||||
"paths": {
|
||||
"/api/books": {
|
||||
"get": {
|
||||
"operationId": "read_api_books_api_books_get",
|
||||
"parameters": [
|
||||
{
|
||||
"examples": {
|
||||
"isbn": {
|
||||
"value": "ISBN:0201558025"
|
||||
},
|
||||
"multiple": {
|
||||
"value": "ISBN:9781408113479,OCLC:420517"
|
||||
},
|
||||
"oclc": {
|
||||
"value": "OCLC:263296519"
|
||||
}
|
||||
},
|
||||
"in": "query",
|
||||
"name": "bibkeys",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Bibkeys",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Specifies the response format. Possible values are json and javascript. When not specified the format is javascript.",
|
||||
"in": "query",
|
||||
"name": "format",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"default": "json",
|
||||
"title": "Format",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "The name of the JavaScript function to call with the result. This is considered only when the format is javascript.",
|
||||
"in": "query",
|
||||
"name": "callback",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"title": "Callback"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Decides what information to provide for each matched bib_key. Possible values are viewapi and data. The default value is viewapi.",
|
||||
"in": "query",
|
||||
"name": "jscmd",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"default": "viewapi",
|
||||
"title": "Jscmd",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Validation Error"
|
||||
}
|
||||
},
|
||||
"summary": "Read Api Books",
|
||||
"tags": ["books"]
|
||||
}
|
||||
},
|
||||
"/api/volumes/brief/{key_type}/{value}.json": {
|
||||
"get": {
|
||||
"operationId": "read_api_volumes_brief_api_volumes_brief__key_type___value__json_get",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "key_type",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Key Type"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "value",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Value"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "callback",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"title": "Callback"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Validation Error"
|
||||
}
|
||||
},
|
||||
"summary": "Read Api Volumes Brief",
|
||||
"tags": ["books"]
|
||||
}
|
||||
},
|
||||
"/authors/{olid}.json": {
|
||||
"get": {
|
||||
"operationId": "read_authors_authors__olid__json_get",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "olid",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Olid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Validation Error"
|
||||
}
|
||||
},
|
||||
"summary": "Read Authors",
|
||||
"tags": ["authors"]
|
||||
}
|
||||
},
|
||||
"/authors/{olid}/works.json": {
|
||||
"get": {
|
||||
"operationId": "read_authors_works_authors__olid__works_json_get",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "olid",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Olid"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "limit",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"title": "Limit",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Validation Error"
|
||||
}
|
||||
},
|
||||
"summary": "Read Authors Works",
|
||||
"tags": ["authors"]
|
||||
}
|
||||
},
|
||||
"/books/{olid}": {
|
||||
"get": {
|
||||
"operationId": "read_books_books__olid__get",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "olid",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"example": "OL53924W",
|
||||
"title": "Olid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Validation Error"
|
||||
}
|
||||
},
|
||||
"summary": "Read Books",
|
||||
"tags": ["books"]
|
||||
}
|
||||
},
|
||||
"/covers/{key_type}/{value}-{size}.jpg": {
|
||||
"get": {
|
||||
"operationId": "read_covers_key_type_value_size_jpeg_covers__key_type___value___size__jpg_get",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "key_type",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Key Type"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "value",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Value"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "size",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Size"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Validation Error"
|
||||
}
|
||||
},
|
||||
"summary": "Read Covers Key Type Value Size Jpeg",
|
||||
"tags": ["covers"]
|
||||
}
|
||||
},
|
||||
"/isbn/{isbn}": {
|
||||
"get": {
|
||||
"operationId": "read_isbn_isbn__isbn__get",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "isbn",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Isbn"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Validation Error"
|
||||
}
|
||||
},
|
||||
"summary": "Read Isbn",
|
||||
"tags": ["books"]
|
||||
}
|
||||
},
|
||||
"/search.json": {
|
||||
"get": {
|
||||
"operationId": "read_search_json_search_json_get",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "q",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Q"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "page",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"title": "Page",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Validation Error"
|
||||
}
|
||||
},
|
||||
"summary": "Read Search Json",
|
||||
"tags": ["search"]
|
||||
}
|
||||
},
|
||||
"/search/authors.json": {
|
||||
"get": {
|
||||
"operationId": "read_search_authors_json_search_authors_json_get",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "q",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Q"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Validation Error"
|
||||
}
|
||||
},
|
||||
"summary": "Read Search Authors Json",
|
||||
"tags": ["search"]
|
||||
}
|
||||
},
|
||||
"/subjects/{subject}.json": {
|
||||
"get": {
|
||||
"operationId": "read_subjects_subjects__subject__json_get",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "subject",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Subject"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "details",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"default": false,
|
||||
"title": "Details",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Validation Error"
|
||||
}
|
||||
},
|
||||
"summary": "Read Subjects",
|
||||
"tags": ["subjects"]
|
||||
}
|
||||
},
|
||||
"/works/{olid}": {
|
||||
"get": {
|
||||
"operationId": "read_works_works__olid__get",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "olid",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"title": "Olid"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
},
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Validation Error"
|
||||
}
|
||||
},
|
||||
"summary": "Read Works",
|
||||
"tags": ["books"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"description": "Retrieve a specific work or edition by identifier",
|
||||
"externalDocs": {
|
||||
"description": "Find out more",
|
||||
"url": "https://openlibrary.org/dev/docs/api/books"
|
||||
},
|
||||
"name": "books"
|
||||
},
|
||||
{
|
||||
"description": "Retrieve an author and their works by author identifier",
|
||||
"externalDocs": {
|
||||
"description": "Find out more",
|
||||
"url": "https://openlibrary.org/dev/docs/api/authors"
|
||||
},
|
||||
"name": "authors"
|
||||
},
|
||||
{
|
||||
"description": "Search results for books, authors, and more",
|
||||
"externalDocs": {
|
||||
"description": "Find out more",
|
||||
"url": "https://openlibrary.org/dev/docs/api/search"
|
||||
},
|
||||
"name": "search"
|
||||
},
|
||||
{
|
||||
"description": "Fetch book covers by ISBN or Open Library identifier",
|
||||
"externalDocs": {
|
||||
"description": "Find out more",
|
||||
"url": "https://openlibrary.org/dev/docs/api/covers"
|
||||
},
|
||||
"name": "covers"
|
||||
},
|
||||
{
|
||||
"description": "Fetch books by subject name ",
|
||||
"externalDocs": {
|
||||
"description": "Find out more",
|
||||
"url": "https://openlibrary.org/dev/docs/api/subjects"
|
||||
},
|
||||
"name": "subjects"
|
||||
}
|
||||
]
|
||||
}
|
||||
578
src/api/schemas/OpenLibrary.ts
Normal file
578
src/api/schemas/OpenLibrary.ts
Normal file
|
|
@ -0,0 +1,578 @@
|
|||
/**
|
||||
* This file was auto-generated by openapi-typescript.
|
||||
* Do not make direct changes to the file.
|
||||
*/
|
||||
|
||||
export interface paths {
|
||||
'/api/books': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Read Api Books */
|
||||
get: operations['read_api_books_api_books_get'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/api/volumes/brief/{key_type}/{value}.json': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Read Api Volumes Brief */
|
||||
get: operations['read_api_volumes_brief_api_volumes_brief__key_type___value__json_get'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/authors/{olid}.json': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Read Authors */
|
||||
get: operations['read_authors_authors__olid__json_get'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/authors/{olid}/works.json': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Read Authors Works */
|
||||
get: operations['read_authors_works_authors__olid__works_json_get'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/books/{olid}': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Read Books */
|
||||
get: operations['read_books_books__olid__get'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/covers/{key_type}/{value}-{size}.jpg': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Read Covers Key Type Value Size Jpeg */
|
||||
get: operations['read_covers_key_type_value_size_jpeg_covers__key_type___value___size__jpg_get'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/isbn/{isbn}': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Read Isbn */
|
||||
get: operations['read_isbn_isbn__isbn__get'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/search.json': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Read Search Json */
|
||||
get: operations['read_search_json_search_json_get'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/search/authors.json': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Read Search Authors Json */
|
||||
get: operations['read_search_authors_json_search_authors_json_get'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/subjects/{subject}.json': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Read Subjects */
|
||||
get: operations['read_subjects_subjects__subject__json_get'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
'/works/{olid}': {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Read Works */
|
||||
get: operations['read_works_works__olid__get'];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
}
|
||||
export type webhooks = Record<string, never>;
|
||||
export interface components {
|
||||
schemas: {
|
||||
/** HTTPValidationError */
|
||||
HTTPValidationError: {
|
||||
/** Detail */
|
||||
detail?: components['schemas']['ValidationError'][];
|
||||
};
|
||||
/** ValidationError */
|
||||
ValidationError: {
|
||||
/** Location */
|
||||
loc: string[];
|
||||
/** Message */
|
||||
msg: string;
|
||||
/** Error Type */
|
||||
type: string;
|
||||
};
|
||||
};
|
||||
responses: never;
|
||||
parameters: never;
|
||||
requestBodies: never;
|
||||
headers: never;
|
||||
pathItems: never;
|
||||
}
|
||||
export type $defs = Record<string, never>;
|
||||
export interface operations {
|
||||
read_api_books_api_books_get: {
|
||||
parameters: {
|
||||
query: {
|
||||
bibkeys: string;
|
||||
/** @description Specifies the response format. Possible values are json and javascript. When not specified the format is javascript. */
|
||||
format?: string;
|
||||
/** @description The name of the JavaScript function to call with the result. This is considered only when the format is javascript. */
|
||||
callback?: unknown;
|
||||
/** @description Decides what information to provide for each matched bib_key. Possible values are viewapi and data. The default value is viewapi. */
|
||||
jscmd?: string;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['HTTPValidationError'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
read_api_volumes_brief_api_volumes_brief__key_type___value__json_get: {
|
||||
parameters: {
|
||||
query?: {
|
||||
callback?: unknown;
|
||||
};
|
||||
header?: never;
|
||||
path: {
|
||||
key_type: unknown;
|
||||
value: unknown;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['HTTPValidationError'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
read_authors_authors__olid__json_get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
olid: unknown;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['HTTPValidationError'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
read_authors_works_authors__olid__works_json_get: {
|
||||
parameters: {
|
||||
query?: {
|
||||
limit?: number;
|
||||
};
|
||||
header?: never;
|
||||
path: {
|
||||
olid: unknown;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['HTTPValidationError'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
read_books_books__olid__get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
olid: unknown;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['HTTPValidationError'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
read_covers_key_type_value_size_jpeg_covers__key_type___value___size__jpg_get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
key_type: unknown;
|
||||
value: unknown;
|
||||
size: unknown;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['HTTPValidationError'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
read_isbn_isbn__isbn__get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
isbn: unknown;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['HTTPValidationError'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
read_search_json_search_json_get: {
|
||||
parameters: {
|
||||
query: {
|
||||
q: unknown;
|
||||
page?: number;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['HTTPValidationError'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
read_search_authors_json_search_authors_json_get: {
|
||||
parameters: {
|
||||
query: {
|
||||
q: unknown;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['HTTPValidationError'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
read_subjects_subjects__subject__json_get: {
|
||||
parameters: {
|
||||
query?: {
|
||||
details?: boolean;
|
||||
};
|
||||
header?: never;
|
||||
path: {
|
||||
subject: unknown;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['HTTPValidationError'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
read_works_works__olid__get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
olid: unknown;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': unknown;
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['HTTPValidationError'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
50
src/main.ts
50
src/main.ts
|
|
@ -3,6 +3,7 @@ import { requestUrl, normalizePath } from 'obsidian'; // Add requestUrl import
|
|||
import type { MediaType } from 'src/utils/MediaType';
|
||||
import { APIManager } from './api/APIManager';
|
||||
import { BoardGameGeekAPI } from './api/apis/BoardGameGeekAPI';
|
||||
import { ComicVineAPI } from './api/apis/ComicVineAPI';
|
||||
import { GiantBombAPI } from './api/apis/GiantBombAPI';
|
||||
import { MALAPI } from './api/apis/MALAPI';
|
||||
import { MALAPIManga } from './api/apis/MALAPIManga';
|
||||
|
|
@ -12,9 +13,8 @@ import { OMDbAPI } from './api/apis/OMDbAPI';
|
|||
import { OpenLibraryAPI } from './api/apis/OpenLibraryAPI';
|
||||
import { SteamAPI } from './api/apis/SteamAPI';
|
||||
import { WikipediaAPI } from './api/apis/WikipediaAPI';
|
||||
import { ComicVineAPI } from './api/apis/ComicVineAPI';
|
||||
import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal';
|
||||
import { ConfirmOverwriteModal } from './modals/ConfirmOverwriteModal';
|
||||
import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal';
|
||||
import type { MediaTypeModel } from './models/MediaTypeModel';
|
||||
import { PropertyMapper } from './settings/PropertyMapper';
|
||||
import { PropertyMapping, PropertyMappingModel } from './settings/PropertyMapping';
|
||||
|
|
@ -123,7 +123,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
return false;
|
||||
}
|
||||
if (!checking) {
|
||||
this.updateActiveNote(false);
|
||||
void this.updateActiveNote(false);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
|
@ -136,7 +136,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
return false;
|
||||
}
|
||||
if (!checking) {
|
||||
this.updateActiveNote(true);
|
||||
void this.updateActiveNote(true);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
|
@ -150,7 +150,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
return false;
|
||||
}
|
||||
if (!checking) {
|
||||
this.createLinkWithSearchModal();
|
||||
void this.createLinkWithSearchModal();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
|
@ -213,7 +213,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
apiSearchResults = apiSearchResults.filter(x => types.contains(x.type));
|
||||
|
||||
let selectResults: MediaTypeModel[];
|
||||
let proceed: boolean = false;
|
||||
const proceed: boolean = false;
|
||||
|
||||
while (!proceed) {
|
||||
selectResults =
|
||||
|
|
@ -247,7 +247,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
}
|
||||
|
||||
let selectResults: MediaTypeModel[];
|
||||
let proceed: boolean = false;
|
||||
const proceed: boolean = false;
|
||||
|
||||
while (!proceed) {
|
||||
selectResults =
|
||||
|
|
@ -322,9 +322,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
|
||||
const fileContent = await this.generateMediaDbNoteContents(mediaTypeModel, options);
|
||||
|
||||
if (!options.folder) {
|
||||
options.folder = await this.mediaTypeManager.getFolder(mediaTypeModel, this.app);
|
||||
}
|
||||
options.folder ??= await this.mediaTypeManager.getFolder(mediaTypeModel, this.app);
|
||||
|
||||
const targetFile = await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent, options);
|
||||
|
||||
|
|
@ -347,7 +345,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
if (mediaTypeModel.image && typeof mediaTypeModel.image === 'string' && mediaTypeModel.image.startsWith('http')) {
|
||||
try {
|
||||
const imageUrl = mediaTypeModel.image;
|
||||
const imageExt = imageUrl.split('.').pop()?.split(/\#|\?/)[0] || 'jpg';
|
||||
const imageExt = imageUrl.split('.').pop()?.split(/#|\?/)[0] ?? 'jpg';
|
||||
const imageFileName = `${replaceIllegalFileNameCharactersInString(`${mediaTypeModel.type}_${mediaTypeModel.title} (${mediaTypeModel.year})`)}.${imageExt}`;
|
||||
const imagePath = normalizePath(`${this.settings.imageFolder}/${imageFileName}`);
|
||||
|
||||
|
|
@ -441,7 +439,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
|
||||
// Update updated front matter with entries from the old front matter, if it isn't defined in the new front matter
|
||||
Object.keys(previousMetadata).forEach(key => {
|
||||
const value = previousMetadata[key];
|
||||
const value: unknown = previousMetadata[key];
|
||||
|
||||
if (!frontMatter[key] && value) {
|
||||
frontMatter[key] = value;
|
||||
|
|
@ -450,17 +448,9 @@ export default class MediaDbPlugin extends Plugin {
|
|||
}
|
||||
|
||||
// Ensure that id, type, and dataSource are defined
|
||||
if (!frontMatter.id) {
|
||||
frontMatter.id = mediaTypeModel.id;
|
||||
}
|
||||
|
||||
if (!frontMatter.type) {
|
||||
frontMatter.type = mediaTypeModel.type;
|
||||
}
|
||||
|
||||
if (!frontMatter.dataSource) {
|
||||
frontMatter.dataSource = mediaTypeModel.dataSource;
|
||||
}
|
||||
frontMatter.id ??= mediaTypeModel.id;
|
||||
frontMatter.type ??= mediaTypeModel.type;
|
||||
frontMatter.dataSource ??= mediaTypeModel.dataSource;
|
||||
|
||||
if (this.settings.enableTemplaterIntegration && hasTemplaterPlugin(this.app)) {
|
||||
// Only support stringifyYaml for templater plugin
|
||||
|
|
@ -478,7 +468,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
||||
}
|
||||
|
||||
const attachFileMetadata: any = this.getMetadataFromFileCache(fileToAttach);
|
||||
const attachFileMetadata = this.getMetadataFromFileCache(fileToAttach);
|
||||
// TODO: better object merging
|
||||
fileMetadata = Object.assign(attachFileMetadata, fileMetadata);
|
||||
|
||||
|
|
@ -496,7 +486,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
||||
}
|
||||
|
||||
const templateMetadata: Metadata = this.getMetaDataFromFileContent(template);
|
||||
const templateMetadata = this.getMetaDataFromFileContent(template);
|
||||
// TODO: better object merging
|
||||
fileMetadata = Object.assign(templateMetadata, fileMetadata);
|
||||
|
||||
|
|
@ -522,7 +512,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
frontMatter = frontMatter.substring(4);
|
||||
frontMatter = frontMatter.substring(0, frontMatter.length - 3);
|
||||
|
||||
metadata = parseYaml(frontMatter);
|
||||
metadata = parseYaml(frontMatter) as Metadata;
|
||||
|
||||
if (!metadata) {
|
||||
metadata = {};
|
||||
|
|
@ -645,11 +635,11 @@ export default class MediaDbPlugin extends Plugin {
|
|||
continue;
|
||||
}
|
||||
|
||||
const metadata: any = this.getMetadataFromFileCache(file);
|
||||
const metadata = this.getMetadataFromFileCache(file);
|
||||
|
||||
const title = metadata[titleFieldName];
|
||||
if (!title) {
|
||||
erroredFiles.push({ filePath: file.path, error: `metadata field '${titleFieldName}' not found or empty` });
|
||||
if (!title || typeof title !== 'string') {
|
||||
erroredFiles.push({ filePath: file.path, error: `metadata field '${titleFieldName}' not found, empty, or not a string` });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -715,7 +705,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
|
||||
async loadSettings(): Promise<void> {
|
||||
// console.log(DEFAULT_SETTINGS);
|
||||
const diskSettings: MediaDbPluginSettings = await this.loadData();
|
||||
const diskSettings: MediaDbPluginSettings = (await this.loadData()) as MediaDbPluginSettings;
|
||||
const defaultSettings: MediaDbPluginSettings = getDefaultSettings(this);
|
||||
const loadedSettings: MediaDbPluginSettings = Object.assign({}, defaultSettings, diskSettings);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export class ConfirmOverwriteModal extends Modal {
|
|||
this.onSubmit = onSubmit;
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
onOpen(): void {
|
||||
const { contentEl } = this;
|
||||
contentEl.createEl('h2', { text: 'File already exists' });
|
||||
contentEl.createEl('p', { text: `The file "${this.fileName}" already exists. Do you want to overwrite it?` });
|
||||
|
|
@ -36,7 +36,7 @@ export class ConfirmOverwriteModal extends Modal {
|
|||
});
|
||||
}
|
||||
|
||||
onClose() {
|
||||
onClose(): void {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
this.onSubmit(this.result);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import type { ButtonComponent } from 'obsidian';
|
||||
import { Modal, Notice, Setting, TextComponent, ToggleComponent } from 'obsidian';
|
||||
import type MediaDbPlugin from '../main';
|
||||
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||
import type { AdvancedSearchModalData, AdvancedSearchModalOptions } from '../utils/ModalHelper';
|
||||
import { ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS } from '../utils/ModalHelper';
|
||||
|
||||
|
|
@ -39,7 +38,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
|||
|
||||
keyPressCallback(event: KeyboardEvent): void {
|
||||
if (event.key === 'Enter') {
|
||||
this.search();
|
||||
void this.search();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +118,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
|||
btn.setButtonText('Ok');
|
||||
btn.setCta();
|
||||
btn.onClick(() => {
|
||||
this.search();
|
||||
void this.search();
|
||||
});
|
||||
btn.buttonEl.addClass('media-db-plugin-button');
|
||||
this.searchBtn = btn;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import type { ButtonComponent } from 'obsidian';
|
||||
import { DropdownComponent, Modal, Notice, Setting, TextComponent } from 'obsidian';
|
||||
import type MediaDbPlugin from '../main';
|
||||
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||
import type { IdSearchModalData, IdSearchModalOptions } from '../utils/ModalHelper';
|
||||
import { ID_SEARCH_MODAL_DEFAULT_OPTIONS } from '../utils/ModalHelper';
|
||||
|
||||
|
|
@ -24,7 +23,7 @@ export class MediaDbIdSearchModal extends Modal {
|
|||
|
||||
this.plugin = plugin;
|
||||
this.title = idSearchModalOptions.modalTitle ?? '';
|
||||
this.selectedApi = idSearchModalOptions.preselectedAPI || plugin.apiManager.apis[0].apiName;
|
||||
this.selectedApi = idSearchModalOptions.preselectedAPI ?? plugin.apiManager.apis[0].apiName;
|
||||
this.query = '';
|
||||
this.isBusy = false;
|
||||
}
|
||||
|
|
@ -39,7 +38,7 @@ export class MediaDbIdSearchModal extends Modal {
|
|||
|
||||
keyPressCallback(event: KeyboardEvent): void {
|
||||
if (event.key === 'Enter') {
|
||||
this.search();
|
||||
void this.search();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +104,7 @@ export class MediaDbIdSearchModal extends Modal {
|
|||
btn.setButtonText('Ok');
|
||||
btn.setCta();
|
||||
btn.onClick(() => {
|
||||
this.search();
|
||||
void this.search();
|
||||
});
|
||||
btn.buttonEl.addClass('media-db-plugin-button');
|
||||
this.searchBtn = btn;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import type { ButtonComponent } from 'obsidian';
|
||||
import { Component, MarkdownRenderer, Modal, Setting } from 'obsidian';
|
||||
import type MediaDbPlugin from 'src/main';
|
||||
import type { MediaTypeModel } from 'src/models/MediaTypeModel';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import type { ButtonComponent } from 'obsidian';
|
||||
import { Modal, Notice, Setting, TextComponent, ToggleComponent } from 'obsidian';
|
||||
import type MediaDbPlugin from '../main';
|
||||
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||
import type { MediaType } from '../utils/MediaType';
|
||||
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
||||
import type { SearchModalData, SearchModalOptions } from '../utils/ModalHelper';
|
||||
|
|
@ -42,7 +41,7 @@ export class MediaDbSearchModal extends Modal {
|
|||
|
||||
keyPressCallback(event: KeyboardEvent): void {
|
||||
if (event.key === 'Enter') {
|
||||
this.search();
|
||||
void this.search();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,7 +130,7 @@ export class MediaDbSearchModal extends Modal {
|
|||
btn.setButtonText('Ok');
|
||||
btn.setCta();
|
||||
btn.onClick(() => {
|
||||
this.search();
|
||||
void this.search();
|
||||
});
|
||||
btn.buttonEl.addClass('media-db-plugin-button');
|
||||
this.searchBtn = btn;
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export abstract class SelectModal<T> extends Modal {
|
|||
this.scope.register([], 'Enter', () => this.submit());
|
||||
}
|
||||
|
||||
abstract renderElement(value: T, el: HTMLElement): any;
|
||||
abstract renderElement(value: T, el: HTMLElement): void;
|
||||
|
||||
abstract submit(): void;
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ export abstract class SelectModal<T> extends Modal {
|
|||
}
|
||||
}
|
||||
|
||||
async onOpen(): Promise<void> {
|
||||
onOpen(): void {
|
||||
const { contentEl, titleEl } = this;
|
||||
|
||||
titleEl.createEl('h2', { text: this.title });
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export class BoardGameModel extends MediaTypeModel {
|
|||
|
||||
migrateObject(this, obj, this);
|
||||
|
||||
if (!obj.hasOwnProperty('userData')) {
|
||||
if (!Object.hasOwn(obj, 'userData')) {
|
||||
migrateObject(this.userData, obj, this.userData);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export class BookModel extends MediaTypeModel {
|
|||
|
||||
migrateObject(this, obj, this);
|
||||
|
||||
if (!obj.hasOwnProperty('userData')) {
|
||||
if (!Object.hasOwn(obj, 'userData')) {
|
||||
migrateObject(this.userData, obj, this.userData);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export class ComicMangaModel extends MediaTypeModel {
|
|||
|
||||
migrateObject(this, obj, this);
|
||||
|
||||
if (!obj.hasOwnProperty('userData')) {
|
||||
if (!Object.hasOwn(obj, 'userData')) {
|
||||
migrateObject(this.userData, obj, this.userData);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export class GameModel extends MediaTypeModel {
|
|||
|
||||
migrateObject(this, obj, this);
|
||||
|
||||
if (!obj.hasOwnProperty('userData')) {
|
||||
if (!Object.hasOwn(obj, 'userData')) {
|
||||
migrateObject(this.userData, obj, this.userData);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export class MovieModel extends MediaTypeModel {
|
|||
|
||||
migrateObject(this, obj, this);
|
||||
|
||||
if (!obj.hasOwnProperty('userData')) {
|
||||
if (!Object.hasOwn(obj, 'userData')) {
|
||||
migrateObject(this.userData, obj, this.userData);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export class MusicReleaseModel extends MediaTypeModel {
|
|||
|
||||
migrateObject(this, obj, this);
|
||||
|
||||
if (!obj.hasOwnProperty('userData')) {
|
||||
if (!Object.hasOwn(obj, 'userData')) {
|
||||
migrateObject(this.userData, obj, this.userData);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export class SeriesModel extends MediaTypeModel {
|
|||
|
||||
migrateObject(this, obj, this);
|
||||
|
||||
if (!obj.hasOwnProperty('userData')) {
|
||||
if (!Object.hasOwn(obj, 'userData')) {
|
||||
migrateObject(this.userData, obj, this.userData);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export class WikiModel extends MediaTypeModel {
|
|||
|
||||
migrateObject(this, obj, this);
|
||||
|
||||
if (!obj.hasOwnProperty('userData')) {
|
||||
if (!Object.hasOwn(obj, 'userData')) {
|
||||
migrateObject(this.userData, obj, this.userData);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import type { MediaType } from 'src/utils/MediaType';
|
||||
import type MediaDbPlugin from '../main';
|
||||
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
||||
import { PropertyMappingOption } from './PropertyMapping';
|
||||
|
|
@ -16,7 +17,7 @@ export class PropertyMapper {
|
|||
* @param obj
|
||||
*/
|
||||
convertObject(obj: Record<string, unknown>): Record<string, unknown> {
|
||||
if (!obj.hasOwnProperty('type')) {
|
||||
if (!Object.hasOwn(obj, 'type')) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ export class PropertyMapper {
|
|||
* @param obj
|
||||
*/
|
||||
convertObjectBack(obj: Record<string, unknown>): Record<string, unknown> {
|
||||
if (!obj.hasOwnProperty('type')) {
|
||||
if (!Object.hasOwn(obj, 'type')) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +67,7 @@ export class PropertyMapper {
|
|||
obj.type = 'comicManga';
|
||||
console.debug(`MDB | updated metadata type`, obj.type);
|
||||
}
|
||||
if (MEDIA_TYPES.contains(obj.type as any)) {
|
||||
if (MEDIA_TYPES.contains(obj.type as MediaType)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { PropertyMappingModel, PropertyMappingOption, propertyMappingOptions } from './PropertyMapping';
|
||||
import { capitalizeFirstLetter } from '../utils/Utils';
|
||||
import Icon from './Icon.svelte';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { App } from 'obsidian';
|
||||
import { Notice, PluginSettingTab, Setting } from 'obsidian';
|
||||
import type { MediaType } from 'src/utils/MediaType';
|
||||
import { mount } from 'svelte';
|
||||
import type MediaDbPlugin from '../main';
|
||||
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||
|
|
@ -9,7 +10,6 @@ import { PropertyMapping, PropertyMappingModel, PropertyMappingOption } from './
|
|||
import PropertyMappingModelsComponent from './PropertyMappingModelsComponent.svelte';
|
||||
import { FileSuggest } from './suggesters/FileSuggest';
|
||||
import { FolderSuggest } from './suggesters/FolderSuggest';
|
||||
import type { MediaType } from 'src/utils/MediaType';
|
||||
|
||||
export interface MediaDbPluginSettings {
|
||||
OMDbKey: string;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import type { TAbstractFile } from 'obsidian';
|
||||
import { TFile } from 'obsidian';
|
||||
import { TextInputSuggest } from './Suggest';
|
||||
import { AbstractInputSuggest, TFile } from 'obsidian';
|
||||
|
||||
export class FileSuggest extends TextInputSuggest<TFile> {
|
||||
getSuggestions(inputStr: string): TFile[] {
|
||||
export class FileSuggest extends AbstractInputSuggest<TFile> {
|
||||
protected getSuggestions(query: string): TFile[] | Promise<TFile[]> {
|
||||
const abstractFiles = this.app.vault.getAllLoadedFiles();
|
||||
const files: TFile[] = [];
|
||||
const lowerCaseInputStr = inputStr.toLowerCase();
|
||||
const lowerCaseInputStr = query.toLowerCase();
|
||||
|
||||
abstractFiles.forEach((file: TAbstractFile) => {
|
||||
if (file instanceof TFile && file.name.toLowerCase().contains(lowerCaseInputStr)) {
|
||||
|
|
@ -17,13 +16,7 @@ export class FileSuggest extends TextInputSuggest<TFile> {
|
|||
return files;
|
||||
}
|
||||
|
||||
renderSuggestion(file: TFile, el: HTMLElement): void {
|
||||
el.setText(file.path);
|
||||
}
|
||||
|
||||
selectSuggestion(file: TFile): void {
|
||||
this.inputEl.value = file.path;
|
||||
this.inputEl.trigger('input');
|
||||
this.close();
|
||||
renderSuggestion(value: TFile, el: HTMLElement): void {
|
||||
el.setText(value.path);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
|
||||
|
||||
import type { TAbstractFile } from 'obsidian';
|
||||
import { TFolder } from 'obsidian';
|
||||
import { TextInputSuggest } from './Suggest';
|
||||
import { AbstractInputSuggest, TFolder } from 'obsidian';
|
||||
|
||||
export class FolderSuggest extends TextInputSuggest<TFolder> {
|
||||
getSuggestions(inputStr: string): TFolder[] {
|
||||
export class FolderSuggest extends AbstractInputSuggest<TFolder> {
|
||||
protected getSuggestions(query: string): TFolder[] | Promise<TFolder[]> {
|
||||
const abstractFiles = this.app.vault.getAllLoadedFiles();
|
||||
const folders: TFolder[] = [];
|
||||
const lowerCaseInputStr = inputStr.toLowerCase();
|
||||
const lowerCaseInputStr = query.toLowerCase();
|
||||
|
||||
abstractFiles.forEach((folder: TAbstractFile) => {
|
||||
if (folder instanceof TFolder && folder.path.toLowerCase().contains(lowerCaseInputStr)) {
|
||||
|
|
@ -19,13 +16,7 @@ export class FolderSuggest extends TextInputSuggest<TFolder> {
|
|||
return folders;
|
||||
}
|
||||
|
||||
renderSuggestion(file: TFolder, el: HTMLElement): void {
|
||||
el.setText(file.path);
|
||||
}
|
||||
|
||||
selectSuggestion(file: TFolder): void {
|
||||
this.inputEl.value = file.path;
|
||||
this.inputEl.trigger('input');
|
||||
this.close();
|
||||
renderSuggestion(value: TFolder, el: HTMLElement): void {
|
||||
el.setText(value.path);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,185 +0,0 @@
|
|||
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
|
||||
|
||||
import type { Instance as PopperInstance } from '@popperjs/core';
|
||||
import { createPopper } from '@popperjs/core';
|
||||
import type { App, ISuggestOwner } from 'obsidian';
|
||||
import { Scope } from 'obsidian';
|
||||
import { wrapAround } from 'src/utils/Utils';
|
||||
|
||||
export class Suggest<T> {
|
||||
private owner: ISuggestOwner<T>;
|
||||
private values: T[];
|
||||
private suggestions: HTMLElement[];
|
||||
private selectedItem: number;
|
||||
private containerEl: HTMLElement;
|
||||
|
||||
constructor(owner: ISuggestOwner<T>, containerEl: HTMLElement, scope: Scope) {
|
||||
this.owner = owner;
|
||||
this.containerEl = containerEl;
|
||||
this.values = [];
|
||||
this.suggestions = [];
|
||||
this.selectedItem = 0;
|
||||
|
||||
containerEl.on('click', '.suggestion-item', (e, el) => this.onSuggestionClick(e, el));
|
||||
containerEl.on('mousemove', '.suggestion-item', (e, el) => this.onSuggestionMouseover(e, el));
|
||||
|
||||
scope.register([], 'ArrowUp', event => {
|
||||
if (!event.isComposing) {
|
||||
this.setSelectedItem(this.selectedItem - 1, true);
|
||||
return false;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
scope.register([], 'ArrowDown', event => {
|
||||
if (!event.isComposing) {
|
||||
this.setSelectedItem(this.selectedItem + 1, true);
|
||||
return false;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
scope.register([], 'Enter', event => {
|
||||
if (!event.isComposing) {
|
||||
this.useSelectedItem(event);
|
||||
return false;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
onSuggestionClick(event: MouseEvent, el: HTMLElement): void {
|
||||
event.preventDefault();
|
||||
|
||||
const item = this.suggestions.indexOf(el);
|
||||
this.setSelectedItem(item, false);
|
||||
this.useSelectedItem(event);
|
||||
}
|
||||
|
||||
onSuggestionMouseover(_event: MouseEvent, el: HTMLElement): void {
|
||||
const item = this.suggestions.indexOf(el);
|
||||
this.setSelectedItem(item, false);
|
||||
}
|
||||
|
||||
setSuggestions(values: T[]): void {
|
||||
this.containerEl.empty();
|
||||
const suggestionEls: HTMLDivElement[] = [];
|
||||
|
||||
values.forEach(value => {
|
||||
const suggestionEl = this.containerEl.createDiv('suggestion-item');
|
||||
this.owner.renderSuggestion(value, suggestionEl);
|
||||
suggestionEls.push(suggestionEl);
|
||||
});
|
||||
|
||||
this.values = values;
|
||||
this.suggestions = suggestionEls;
|
||||
this.setSelectedItem(0, false);
|
||||
}
|
||||
|
||||
useSelectedItem(event: MouseEvent | KeyboardEvent): void {
|
||||
const currentValue = this.values[this.selectedItem];
|
||||
if (currentValue) {
|
||||
this.owner.selectSuggestion(currentValue, event);
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedItem(selectedIndex: number, scrollIntoView: boolean): void {
|
||||
const normalizedIndex = this.suggestions.length > 0 ? wrapAround(selectedIndex, this.suggestions.length) : 0;
|
||||
const prevSelectedSuggestion = this.suggestions[this.selectedItem];
|
||||
const selectedSuggestion = this.suggestions[normalizedIndex];
|
||||
|
||||
prevSelectedSuggestion?.removeClass('is-selected');
|
||||
selectedSuggestion?.addClass('is-selected');
|
||||
|
||||
this.selectedItem = normalizedIndex;
|
||||
|
||||
if (scrollIntoView) {
|
||||
selectedSuggestion.scrollIntoView(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class TextInputSuggest<T> implements ISuggestOwner<T> {
|
||||
protected app: App;
|
||||
protected inputEl: HTMLInputElement;
|
||||
|
||||
private popper?: PopperInstance;
|
||||
private scope: Scope;
|
||||
private suggestEl: HTMLElement;
|
||||
private suggest: Suggest<T>;
|
||||
|
||||
constructor(app: App, inputEl: HTMLInputElement) {
|
||||
this.app = app;
|
||||
this.inputEl = inputEl;
|
||||
this.scope = new Scope();
|
||||
|
||||
this.suggestEl = createDiv('suggestion-container');
|
||||
const suggestion = this.suggestEl.createDiv('suggestion');
|
||||
this.suggest = new Suggest(this, suggestion, this.scope);
|
||||
|
||||
this.scope.register([], 'Escape', this.close.bind(this));
|
||||
|
||||
this.inputEl.addEventListener('input', this.onInputChanged.bind(this));
|
||||
this.inputEl.addEventListener('focus', this.onInputChanged.bind(this));
|
||||
this.inputEl.addEventListener('blur', this.close.bind(this));
|
||||
this.suggestEl.on('mousedown', '.suggestion-container', (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
onInputChanged(): void {
|
||||
const inputStr = this.inputEl.value;
|
||||
const suggestions = this.getSuggestions(inputStr);
|
||||
|
||||
if (suggestions.length > 0) {
|
||||
this.suggest.setSuggestions(suggestions);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.open((this.app as any).dom.appContainerEl, this.inputEl);
|
||||
}
|
||||
}
|
||||
|
||||
open(container: HTMLElement, inputEl: HTMLElement): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this.app as any).keymap.pushScope(this.scope);
|
||||
|
||||
container.appendChild(this.suggestEl);
|
||||
this.popper = createPopper(inputEl, this.suggestEl, {
|
||||
placement: 'bottom-start',
|
||||
modifiers: [
|
||||
{
|
||||
name: 'sameWidth',
|
||||
enabled: true,
|
||||
fn: ({ state, instance }): void => {
|
||||
// Note: positioning needs to be calculated twice -
|
||||
// first pass - positioning it according to the width of the popper
|
||||
// second pass - position it with the width bound to the reference element
|
||||
// we need to early exit to avoid an infinite loop
|
||||
const targetWidth = `${state.rects.reference.width}px`;
|
||||
if (state.styles.popper.width === targetWidth) {
|
||||
return;
|
||||
}
|
||||
state.styles.popper.width = targetWidth;
|
||||
instance.update();
|
||||
},
|
||||
phase: 'beforeWrite',
|
||||
requires: ['computeStyles'],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
close(): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(this.app as any).keymap.popScope(this.scope);
|
||||
|
||||
this.suggest.setSuggestions([]);
|
||||
this.popper?.destroy();
|
||||
this.suggestEl.detach();
|
||||
}
|
||||
|
||||
abstract getSuggestions(inputStr: string): T[];
|
||||
|
||||
abstract renderSuggestion(item: T, el: HTMLElement): void;
|
||||
|
||||
abstract selectSuggestion(item: T): void;
|
||||
}
|
||||
|
|
@ -17,9 +17,7 @@ export class DateFormatter {
|
|||
getPreview(format?: string): string {
|
||||
const today = moment();
|
||||
|
||||
if (!format) {
|
||||
format = this.toFormat;
|
||||
}
|
||||
format ??= this.toFormat;
|
||||
|
||||
return today.locale(this.locale).format(format);
|
||||
}
|
||||
|
|
@ -35,7 +33,7 @@ export class DateFormatter {
|
|||
* from the locale of this machine.
|
||||
* @returns formatted date string or null if `dateString` is not a valid date
|
||||
*/
|
||||
format(dateString: string, dateFormat?: string, locale: string = 'en'): string | null {
|
||||
format(dateString: string | null | undefined, dateFormat?: string, locale: string = 'en'): string | null {
|
||||
if (!dateString) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { App, TAbstractFile, TFile } from 'obsidian';
|
||||
import type { App, TFile } from 'obsidian';
|
||||
import { TFolder } from 'obsidian';
|
||||
import { BoardGameModel } from '../models/BoardGameModel';
|
||||
import { BookModel } from '../models/BookModel';
|
||||
import { GameModel } from '../models/GameModel';
|
||||
import { ComicMangaModel } from '../models/ComicMangaModel';
|
||||
import { GameModel } from '../models/GameModel';
|
||||
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||
import { MovieModel } from '../models/MovieModel';
|
||||
import { MusicReleaseModel } from '../models/MusicReleaseModel';
|
||||
|
|
@ -104,9 +104,7 @@ export class MediaTypeManager {
|
|||
async getFolder(mediaTypeModel: MediaTypeModel, app: App): Promise<TFolder> {
|
||||
let folderPath = this.mediaFolderMap.get(mediaTypeModel.getMediaType());
|
||||
|
||||
if (!folderPath) {
|
||||
folderPath = `/`;
|
||||
}
|
||||
folderPath ??= `/`;
|
||||
// console.log(folderPath);
|
||||
|
||||
if (!(await app.vault.adapter.exists(folderPath))) {
|
||||
|
|
@ -115,7 +113,7 @@ export class MediaTypeManager {
|
|||
const folder = app.vault.getAbstractFileByPath(folderPath);
|
||||
|
||||
if (!(folder instanceof TFolder)) {
|
||||
throw Error(`Expected ${folder} to be instance of TFolder`);
|
||||
throw Error(`Expected ${folder?.path} to be instance of TFolder`);
|
||||
}
|
||||
|
||||
return folder;
|
||||
|
|
@ -127,7 +125,7 @@ export class MediaTypeManager {
|
|||
* @param obj
|
||||
* @param mediaType
|
||||
*/
|
||||
createMediaTypeModelFromMediaType(obj: any, mediaType: MediaType): MediaTypeModel {
|
||||
createMediaTypeModelFromMediaType(obj: object, mediaType: MediaType): MediaTypeModel {
|
||||
if (mediaType === MediaType.Movie) {
|
||||
return new MovieModel(obj);
|
||||
} else if (mediaType === MediaType.Series) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { TFile, TFolder, App } from 'obsidian';
|
||||
import { requestUrl } from 'obsidian';
|
||||
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||
|
||||
export const pluginName: string = 'obsidian-media-db-plugin';
|
||||
|
|
@ -42,7 +43,8 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel, ignoreUndefin
|
|||
return ignoreUndefined ? '' : '{{ INVALID TEMPLATE TAG - object undefined }}';
|
||||
}
|
||||
|
||||
return obj;
|
||||
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
||||
return obj?.toString() ?? 'null';
|
||||
} else if (parts.length === 2) {
|
||||
const operator = parts[0];
|
||||
|
||||
|
|
@ -58,7 +60,8 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel, ignoreUndefin
|
|||
if (!Array.isArray(obj)) {
|
||||
return '{{ INVALID TEMPLATE TAG - operator LIST is only applicable on an array }}';
|
||||
}
|
||||
return obj.map((e: any) => `- ${e}`).join('\n');
|
||||
|
||||
return obj.map((e: unknown) => `- ${e}`).join('\n');
|
||||
} else if (operator === 'ENUM') {
|
||||
if (!Array.isArray(obj)) {
|
||||
return '{{ INVALID TEMPLATE TAG - operator ENUM is only applicable on an array }}';
|
||||
|
|
@ -68,12 +71,16 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel, ignoreUndefin
|
|||
if (!Array.isArray(obj)) {
|
||||
return '{{ INVALID TEMPLATE TAG - operator FIRST is only applicable on an array }}';
|
||||
}
|
||||
return obj[0];
|
||||
|
||||
const first = obj[0] as unknown;
|
||||
return first?.toString() ?? 'null';
|
||||
} else if (operator === 'LAST') {
|
||||
if (!Array.isArray(obj)) {
|
||||
return '{{ INVALID TEMPLATE TAG - operator LAST is only applicable on an array }}';
|
||||
}
|
||||
return obj[obj.length - 1];
|
||||
|
||||
const last = obj[obj.length - 1] as unknown;
|
||||
return last?.toString() ?? 'null';
|
||||
}
|
||||
|
||||
return `{{ INVALID TEMPLATE TAG - unknown operator ${operator} }}`;
|
||||
|
|
@ -82,12 +89,12 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel, ignoreUndefin
|
|||
return '{{ INVALID TEMPLATE TAG }}';
|
||||
}
|
||||
|
||||
function traverseMetaData(path: string[], mediaTypeModel: MediaTypeModel): any {
|
||||
let o: any = mediaTypeModel;
|
||||
function traverseMetaData(path: string[], mediaTypeModel: MediaTypeModel): unknown {
|
||||
let o: unknown = mediaTypeModel;
|
||||
|
||||
for (const part of path) {
|
||||
if (o !== undefined) {
|
||||
o = o[part];
|
||||
o = (o as Record<string, unknown>)[part];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,9 +202,9 @@ export interface CreateNoteOptions {
|
|||
folder?: TFolder;
|
||||
}
|
||||
|
||||
export function migrateObject<T extends object>(object: T, oldData: any, defaultData: T): void {
|
||||
export function migrateObject<T extends object>(object: T, oldData: Record<string, unknown>, defaultData: T): void {
|
||||
for (const key in object) {
|
||||
object[key] = oldData.hasOwnProperty(key) ? oldData[key] : defaultData[key];
|
||||
object[key] = Object.hasOwn(oldData, key) && oldData[key] !== undefined && oldData[key] !== null ? (oldData[key] as T[typeof key]) : defaultData[key];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,6 +222,8 @@ export function unCamelCase(str: string): string {
|
|||
);
|
||||
}
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
export function hasTemplaterPlugin(app: App): boolean {
|
||||
const templater = (app as any).plugins.plugins['templater-obsidian'];
|
||||
|
||||
|
|
@ -224,15 +233,17 @@ export function hasTemplaterPlugin(app: App): boolean {
|
|||
// Copied from https://github.com/anpigon/obsidian-book-search-plugin
|
||||
// Licensed under the MIT license. Copyright (c) 2020 Jake Runzer
|
||||
export async function useTemplaterPluginInFile(app: App, file: TFile): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const templater = (app as any).plugins.plugins['templater-obsidian'];
|
||||
if (templater && !templater?.settings.trigger_on_file_creation) {
|
||||
await templater.templater.overwrite_file_commands(file);
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-enable */
|
||||
|
||||
export type ModelToData<T> = {
|
||||
[K in keyof T as T[K] extends Function ? never : K]?: T[K];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
[K in keyof T as T[K] extends Function ? never : K]?: T[K] | null;
|
||||
};
|
||||
|
||||
// Checks if a given URL points to an existing image (status 200), or returns false for 404/other errors.
|
||||
|
|
@ -250,3 +261,38 @@ export async function imageUrlExists(url: string): Promise<boolean> {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isTruthy<T>(value: T): value is Exclude<T, false | 0 | '' | null | undefined> {
|
||||
return Boolean(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps Obsidians `requestUrl` in a fetch like API.
|
||||
*/
|
||||
export async function obsidianFetch(input: Request): Promise<Response> {
|
||||
const obs_headers: Record<string, string> = {};
|
||||
input.headers.forEach((header, value) => {
|
||||
obs_headers[header] = value;
|
||||
});
|
||||
|
||||
const res = await requestUrl({
|
||||
url: input.url,
|
||||
method: input.method,
|
||||
headers: obs_headers,
|
||||
throw: false, // Do not throw on error, handle it manually
|
||||
});
|
||||
|
||||
const responseHeaders: Headers = new Headers();
|
||||
for (const [key, value] of Object.entries(res.headers)) {
|
||||
responseHeaders.append(key, value);
|
||||
}
|
||||
|
||||
return {
|
||||
ok: res.status >= 200 && res.status < 300,
|
||||
status: res.status,
|
||||
headers: responseHeaders,
|
||||
// eslint-disable-next-line
|
||||
json: async () => res.json,
|
||||
text: async () => res.text,
|
||||
} as Response;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue