big changes; we have a good TS config now
This commit is contained in:
parent
cbbaf54b33
commit
4497991344
54 changed files with 750 additions and 671 deletions
|
|
@ -1,2 +0,0 @@
|
||||||
npm node_modules
|
|
||||||
build
|
|
||||||
20
.eslintrc
20
.eslintrc
|
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
"root": true,
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"env": { "node": true },
|
|
||||||
"plugins": ["@typescript-eslint", "only-warn"],
|
|
||||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended"],
|
|
||||||
"parserOptions": {
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"no-unused-vars": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
|
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
|
||||||
"no-prototype-builtins": "off",
|
|
||||||
"@typescript-eslint/no-empty-function": "off",
|
|
||||||
"@typescript-eslint/no-inferrable-types": "off",
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"@typescript-eslint/explicit-function-return-type": ["warn"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -112,7 +112,7 @@ Now you select the result you want and the plugin will cast it's magic and creat
|
||||||
### Currently supported APIs:
|
### Currently supported APIs:
|
||||||
|
|
||||||
| Name | Description | Supported formats | Authentification | Rate limiting | SFW filter support |
|
| Name | Description | Supported formats | Authentification | Rate limiting | SFW filter support |
|
||||||
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
||||||
| [Jikan](https://jikan.moe/) | Jikan is an API that uses [My Anime List](https://myanimelist.net) and offers metadata for anime. | series, movies, specials, OVAs, manga, manwha, novels | No | 60 per minute and 3 per second | Yes |
|
| [Jikan](https://jikan.moe/) | Jikan is an API that uses [My Anime List](https://myanimelist.net) and offers metadata for anime. | series, movies, specials, OVAs, manga, manwha, novels | No | 60 per minute and 3 per second | Yes |
|
||||||
| [OMDb](https://www.omdbapi.com/) | OMDb is an API that offers metadata for movie, series and games. | series, movies, games | Yes, you can get a free key here [here](https://www.omdbapi.com/apikey.aspx) | 1000 per day | No |
|
| [OMDb](https://www.omdbapi.com/) | OMDb is an API that offers metadata for movie, series and games. | series, movies, games | Yes, you can get a free key here [here](https://www.omdbapi.com/apikey.aspx) | 1000 per day | No |
|
||||||
| [MusicBrainz](https://musicbrainz.org/) | MusicBrainz is an API that offers information about music releases. | music releases | No | 50 per second | No |
|
| [MusicBrainz](https://musicbrainz.org/) | MusicBrainz is an API that offers information about music releases. | music releases | No | 50 per second | No |
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import builtins from 'builtin-modules';
|
import builtins from 'builtin-modules';
|
||||||
import esbuild from 'esbuild';
|
import esbuild from 'esbuild';
|
||||||
import esbuildSvelte from 'esbuild-svelte';
|
import esbuildSvelte from 'esbuild-svelte';
|
||||||
import sveltePreprocess from 'svelte-preprocess';
|
import { sveltePreprocess } from 'svelte-preprocess';
|
||||||
import { getBuildBanner } from 'build/buildBanner';
|
import { getBuildBanner } from 'build/buildBanner';
|
||||||
|
|
||||||
const banner = getBuildBanner('Release Build', version => version);
|
const banner = getBuildBanner('Release Build', version => version);
|
||||||
|
|
@ -41,7 +41,7 @@ const build = await esbuild.build({
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
esbuildSvelte({
|
esbuildSvelte({
|
||||||
compilerOptions: { css: 'injected', dev: false, sveltePath: 'svelte' },
|
compilerOptions: { css: 'injected', dev: false },
|
||||||
preprocess: sveltePreprocess(),
|
preprocess: sveltePreprocess(),
|
||||||
filterWarnings: warning => {
|
filterWarnings: warning => {
|
||||||
// we don't want warnings from node modules that we can do nothing about
|
// we don't want warnings from node modules that we can do nothing about
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import esbuild from 'esbuild';
|
import esbuild from 'esbuild';
|
||||||
import copy from 'esbuild-plugin-copy-watch';
|
import copy from 'esbuild-plugin-copy-watch';
|
||||||
import esbuildSvelte from 'esbuild-svelte';
|
import esbuildSvelte from 'esbuild-svelte';
|
||||||
import sveltePreprocess from 'svelte-preprocess';
|
import { sveltePreprocess } from 'svelte-preprocess';
|
||||||
import manifest from '../../manifest.json' assert { type: 'json' };
|
import manifest from '../../manifest.json' assert { type: 'json' };
|
||||||
import { getBuildBanner } from 'build/buildBanner';
|
import { getBuildBanner } from 'build/buildBanner';
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ const context = await esbuild.context({
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
esbuildSvelte({
|
esbuildSvelte({
|
||||||
compilerOptions: { css: 'injected', dev: true, sveltePath: 'svelte' },
|
compilerOptions: { css: 'injected', dev: true },
|
||||||
preprocess: sveltePreprocess(),
|
preprocess: sveltePreprocess(),
|
||||||
filterWarnings: warning => {
|
filterWarnings: warning => {
|
||||||
// we don't want warnings from node modules that we can do nothing about
|
// we don't want warnings from node modules that we can do nothing about
|
||||||
|
|
|
||||||
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
54
eslint.config.mjs
Normal file
54
eslint.config.mjs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
import eslint from '@eslint/js';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import only_warn from 'eslint-plugin-only-warn';
|
||||||
|
import * as plugin_import from 'eslint-plugin-import';
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{
|
||||||
|
ignores: ['npm/', 'node_modules/', 'exampleVault/', 'automation/', 'main.js', '*.svelte'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['src/**/*.ts'],
|
||||||
|
extends: [eslint.configs.recommended, ...tseslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked, ...tseslint.configs.stylisticTypeChecked],
|
||||||
|
languageOptions: {
|
||||||
|
parser: tseslint.parser,
|
||||||
|
parserOptions: {
|
||||||
|
project: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
// @ts-ignore
|
||||||
|
'only-warn': only_warn,
|
||||||
|
import: plugin_import,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-explicit-any': ['warn'],
|
||||||
|
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{ argsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' },
|
||||||
|
],
|
||||||
|
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports', fixStyle: 'separate-type-imports' }],
|
||||||
|
|
||||||
|
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
|
||||||
|
'import/order': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
'newlines-between': 'never',
|
||||||
|
alphabetize: { order: 'asc', orderImportKind: 'asc', caseInsensitive: true },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
'@typescript-eslint/no-confusing-void-expression': ['error', { ignoreArrowShorthand: true }],
|
||||||
|
'@typescript-eslint/restrict-template-expressions': 'off',
|
||||||
|
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'@typescript-eslint/no-inferrable-types': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': ['warn'],
|
||||||
|
'@typescript-eslint/require-await': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
39
package.json
39
package.json
|
|
@ -14,8 +14,8 @@
|
||||||
"lint": "eslint --max-warnings=0 src/**",
|
"lint": "eslint --max-warnings=0 src/**",
|
||||||
"lint:fix": "eslint --max-warnings=0 --fix src/**",
|
"lint:fix": "eslint --max-warnings=0 --fix src/**",
|
||||||
"svelte-check": "svelte-check --compiler-warnings \"unused-export-let:ignore\"",
|
"svelte-check": "svelte-check --compiler-warnings \"unused-export-let:ignore\"",
|
||||||
"check": "bun run format:check && bun run tsc && bun run lint && bun run test",
|
"check": "bun run format:check && bun run tsc && bun run test",
|
||||||
"check:fix": "bun run format && bun run tsc && bun run lint:fix && bun run test",
|
"check:fix": "bun run format && bun run tsc && bun run test",
|
||||||
"release": "bun run automation/release.ts",
|
"release": "bun run automation/release.ts",
|
||||||
"stats": "bun run automation/stats.ts"
|
"stats": "bun run automation/stats.ts"
|
||||||
},
|
},
|
||||||
|
|
@ -25,27 +25,24 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@lemons_dev/parsinom": "^0.0.12",
|
"@lemons_dev/parsinom": "^0.0.12",
|
||||||
"@happy-dom/global-registrator": "^14.3.6",
|
"@happy-dom/global-registrator": "^14.12.3",
|
||||||
"@tsconfig/svelte": "^5.0.3",
|
"@types/bun": "^1.1.16",
|
||||||
"@types/bun": "^1.0.10",
|
"builtin-modules": "^4.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
"esbuild": "^0.24.2",
|
||||||
"@typescript-eslint/parser": "^7.3.1",
|
"esbuild-plugin-copy-watch": "^2.3.1",
|
||||||
"builtin-modules": "^3.3.0",
|
"esbuild-svelte": "^0.8.2",
|
||||||
"esbuild": "^0.20.2",
|
"eslint": "^9.18.0",
|
||||||
"esbuild-plugin-copy-watch": "^2.1.0",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"esbuild-svelte": "^0.8.0",
|
|
||||||
"eslint": "^8.57.0",
|
|
||||||
"eslint-plugin-import": "^2.29.1",
|
|
||||||
"eslint-plugin-isaacscript": "^3.12.2",
|
|
||||||
"eslint-plugin-only-warn": "^1.1.0",
|
"eslint-plugin-only-warn": "^1.1.0",
|
||||||
"obsidian": "latest",
|
"obsidian": "latest",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.4.2",
|
||||||
"prettier-plugin-svelte": "^3.2.2",
|
"prettier-plugin-svelte": "^3.3.3",
|
||||||
"string-argv": "^0.3.2",
|
"string-argv": "^0.3.2",
|
||||||
"svelte": "^4.2.12",
|
"svelte": "^5.17.5",
|
||||||
"svelte-check": "^3.6.8",
|
"svelte-check": "^4.1.4",
|
||||||
"svelte-preprocess": "^5.1.3",
|
"svelte-preprocess": "^6.0.3",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.3"
|
"typescript": "^5.7.3",
|
||||||
|
"typescript-eslint": "^8.20.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { APIModel } from './APIModel';
|
import { Notice } from 'obsidian';
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
|
import type { APIModel } from './APIModel';
|
||||||
|
|
||||||
export class APIManager {
|
export class APIManager {
|
||||||
apis: APIModel[];
|
apis: APIModel[];
|
||||||
|
|
@ -23,7 +24,10 @@ export class APIManager {
|
||||||
try {
|
try {
|
||||||
return await api.searchByTitle(query);
|
return await api.searchByTitle(query);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
new Notice(`Error querying ${api.apiName}: ${e}`);
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
|
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -35,7 +39,7 @@ export class APIManager {
|
||||||
*
|
*
|
||||||
* @param item
|
* @param item
|
||||||
*/
|
*/
|
||||||
async queryDetailedInfo(item: MediaTypeModel): Promise<MediaTypeModel> {
|
async queryDetailedInfo(item: MediaTypeModel): Promise<MediaTypeModel | undefined> {
|
||||||
return await this.queryDetailedInfoById(item.id, item.dataSource);
|
return await this.queryDetailedInfoById(item.id, item.dataSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,22 +49,31 @@ export class APIManager {
|
||||||
* @param id
|
* @param id
|
||||||
* @param apiName
|
* @param apiName
|
||||||
*/
|
*/
|
||||||
async queryDetailedInfoById(id: string, apiName: string): Promise<MediaTypeModel> {
|
async queryDetailedInfoById(id: string, apiName: string): Promise<MediaTypeModel | undefined> {
|
||||||
for (const api of this.apis) {
|
for (const api of this.apis) {
|
||||||
if (api.apiName === apiName) {
|
if (api.apiName === apiName) {
|
||||||
|
try {
|
||||||
return api.getById(id);
|
return api.getById(id);
|
||||||
|
} catch (e) {
|
||||||
|
new Notice(`Error querying ${api.apiName}: ${e}`);
|
||||||
|
console.warn(e);
|
||||||
|
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getApiByName(name: string): APIModel {
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiByName(name: string): APIModel | undefined {
|
||||||
for (const api of this.apis) {
|
for (const api of this.apis) {
|
||||||
if (api.apiName === name) {
|
if (api.apiName === name) {
|
||||||
return api;
|
return api;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerAPI(api: APIModel): void {
|
registerAPI(api: APIModel): void {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
import type MediaDbPlugin from '../main';
|
||||||
import { MediaType } from '../utils/MediaType';
|
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
import MediaDbPlugin from '../main';
|
import type { MediaType } from '../utils/MediaType';
|
||||||
|
|
||||||
export abstract class APIModel {
|
export abstract class APIModel {
|
||||||
apiName: string;
|
apiName!: string;
|
||||||
apiUrl: string;
|
apiUrl!: string;
|
||||||
apiDescription: string;
|
apiDescription!: string;
|
||||||
types: MediaType[];
|
types!: MediaType[];
|
||||||
plugin: MediaDbPlugin;
|
plugin!: MediaDbPlugin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function should query the api and return a list of matches. The matches should be caped at 20.
|
* This function should query the api and return a list of matches. The matches should be caped at 20.
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { APIModel } from '../APIModel';
|
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
|
||||||
import MediaDbPlugin from '../../main';
|
|
||||||
import { BoardGameModel } from 'src/models/BoardGameModel';
|
|
||||||
import { requestUrl } from 'obsidian';
|
import { requestUrl } from 'obsidian';
|
||||||
|
import { BoardGameModel } from 'src/models/BoardGameModel';
|
||||||
|
import type MediaDbPlugin from '../../main';
|
||||||
|
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import { MediaType } from '../../utils/MediaType';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
import { APIModel } from '../APIModel';
|
||||||
|
|
||||||
export class BoardGameGeekAPI extends APIModel {
|
export class BoardGameGeekAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -38,8 +38,8 @@ export class BoardGameGeekAPI extends APIModel {
|
||||||
const ret: MediaTypeModel[] = [];
|
const ret: MediaTypeModel[] = [];
|
||||||
|
|
||||||
for (const boardgame of Array.from(response.querySelectorAll('boardgame'))) {
|
for (const boardgame of Array.from(response.querySelectorAll('boardgame'))) {
|
||||||
const id = boardgame.attributes.getNamedItem('objectid')!.value;
|
const id = boardgame.attributes.getNamedItem('objectid')?.value;
|
||||||
const title = boardgame.querySelector('name[primary=true]')?.textContent ?? boardgame.querySelector('name')!.textContent!;
|
const title = boardgame.querySelector('name[primary=true]')?.textContent ?? boardgame.querySelector('name')?.textContent ?? undefined;
|
||||||
const year = boardgame.querySelector('yearpublished')?.textContent ?? '';
|
const year = boardgame.querySelector('yearpublished')?.textContent ?? '';
|
||||||
|
|
||||||
ret.push(
|
ret.push(
|
||||||
|
|
@ -49,7 +49,7 @@ export class BoardGameGeekAPI extends APIModel {
|
||||||
title,
|
title,
|
||||||
englishTitle: title,
|
englishTitle: title,
|
||||||
year,
|
year,
|
||||||
} as BoardGameModel),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,21 +72,29 @@ export class BoardGameGeekAPI extends APIModel {
|
||||||
const response = new window.DOMParser().parseFromString(data, 'text/xml');
|
const response = new window.DOMParser().parseFromString(data, 'text/xml');
|
||||||
// console.debug(response);
|
// console.debug(response);
|
||||||
|
|
||||||
const boardgame = response.querySelector('boardgame')!;
|
const boardgame = response.querySelector('boardgame');
|
||||||
const title = boardgame.querySelector('name[primary=true]')!.textContent!;
|
if (!boardgame) {
|
||||||
|
throw Error(`MDB | Received invalid data from ${this.apiName}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = boardgame.querySelector('name[primary=true]')?.textContent;
|
||||||
const year = boardgame.querySelector('yearpublished')?.textContent ?? '';
|
const year = boardgame.querySelector('yearpublished')?.textContent ?? '';
|
||||||
const image = boardgame.querySelector('image')?.textContent ?? undefined;
|
const image = boardgame.querySelector('image')?.textContent ?? undefined;
|
||||||
const onlineRating = Number.parseFloat(boardgame.querySelector('statistics ratings average')?.textContent ?? '0');
|
const onlineRating = Number.parseFloat(boardgame.querySelector('statistics ratings average')?.textContent ?? '0');
|
||||||
const genres = Array.from(boardgame.querySelectorAll('boardgamecategory')).map(n => n!.textContent!);
|
const genres = Array.from(boardgame.querySelectorAll('boardgamecategory'))
|
||||||
|
.map(n => n.textContent)
|
||||||
|
.filter(n => n !== null);
|
||||||
const complexityRating = Number.parseFloat(boardgame.querySelector('averageweight')?.textContent ?? '0');
|
const complexityRating = Number.parseFloat(boardgame.querySelector('averageweight')?.textContent ?? '0');
|
||||||
const minPlayers = Number.parseFloat(boardgame.querySelector('minplayers')?.textContent ?? '0');
|
const minPlayers = Number.parseFloat(boardgame.querySelector('minplayers')?.textContent ?? '0');
|
||||||
const maxPlayers = Number.parseFloat(boardgame.querySelector('maxplayers')?.textContent ?? '0');
|
const maxPlayers = Number.parseFloat(boardgame.querySelector('maxplayers')?.textContent ?? '0');
|
||||||
const playtime = (boardgame.querySelector('playingtime')?.textContent ?? 'unknown') + ' minutes';
|
const playtime = (boardgame.querySelector('playingtime')?.textContent ?? 'unknown') + ' minutes';
|
||||||
const publishers = Array.from(boardgame.querySelectorAll('boardgamepublisher')).map(n => n!.textContent!);
|
const publishers = Array.from(boardgame.querySelectorAll('boardgamepublisher'))
|
||||||
|
.map(n => n.textContent)
|
||||||
|
.filter(n => n !== null);
|
||||||
|
|
||||||
return new BoardGameModel({
|
return new BoardGameModel({
|
||||||
title: title,
|
title: title ?? undefined,
|
||||||
englishTitle: title,
|
englishTitle: title ?? undefined,
|
||||||
year: year === '0' ? '' : year,
|
year: year === '0' ? '' : year,
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
url: `https://boardgamegeek.com/boardgame/${id}`,
|
url: `https://boardgamegeek.com/boardgame/${id}`,
|
||||||
|
|
@ -107,6 +115,6 @@ export class BoardGameGeekAPI extends APIModel {
|
||||||
played: false,
|
played: false,
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as BoardGameModel);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { APIModel } from '../APIModel';
|
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
|
||||||
import MediaDbPlugin from '../../main';
|
|
||||||
import { GameModel } from '../../models/GameModel';
|
|
||||||
import { requestUrl } from 'obsidian';
|
import { requestUrl } from 'obsidian';
|
||||||
|
import type MediaDbPlugin from '../../main';
|
||||||
|
import { GameModel } from '../../models/GameModel';
|
||||||
|
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import { MediaType } from '../../utils/MediaType';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
import { APIModel } from '../APIModel';
|
||||||
|
|
||||||
export class GiantBombAPI extends APIModel {
|
export class GiantBombAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -54,7 +54,7 @@ export class GiantBombAPI extends APIModel {
|
||||||
year: new Date(result.original_release_date).getFullYear().toString(),
|
year: new Date(result.original_release_date).getFullYear().toString(),
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.guid,
|
id: result.guid,
|
||||||
} as GameModel),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,6 +104,6 @@ export class GiantBombAPI extends APIModel {
|
||||||
|
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as GameModel);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { APIModel } from '../APIModel';
|
import type MediaDbPlugin from '../../main';
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import { MovieModel } from '../../models/MovieModel';
|
import { MovieModel } from '../../models/MovieModel';
|
||||||
import MediaDbPlugin from '../../main';
|
|
||||||
import { SeriesModel } from '../../models/SeriesModel';
|
import { SeriesModel } from '../../models/SeriesModel';
|
||||||
import { MediaType } from '../../utils/MediaType';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
import { APIModel } from '../APIModel';
|
||||||
|
|
||||||
export class MALAPI extends APIModel {
|
export class MALAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -52,7 +52,7 @@ export class MALAPI extends APIModel {
|
||||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.mal_id,
|
id: result.mal_id,
|
||||||
} as MovieModel),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (type === 'movie' || type === 'special') {
|
if (type === 'movie' || type === 'special') {
|
||||||
|
|
@ -64,7 +64,7 @@ export class MALAPI extends APIModel {
|
||||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.mal_id,
|
id: result.mal_id,
|
||||||
} as MovieModel),
|
}),
|
||||||
);
|
);
|
||||||
} else if (type === 'series' || type === 'ova') {
|
} else if (type === 'series' || type === 'ova') {
|
||||||
ret.push(
|
ret.push(
|
||||||
|
|
@ -75,7 +75,7 @@ export class MALAPI extends APIModel {
|
||||||
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
year: result.year ?? result.aired?.prop?.from?.year ?? '',
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.mal_id,
|
id: result.mal_id,
|
||||||
} as SeriesModel),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +127,7 @@ export class MALAPI extends APIModel {
|
||||||
lastWatched: '',
|
lastWatched: '',
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as MovieModel);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'movie' || type === 'special') {
|
if (type === 'movie' || type === 'special') {
|
||||||
|
|
@ -159,7 +159,7 @@ export class MALAPI extends APIModel {
|
||||||
lastWatched: '',
|
lastWatched: '',
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as MovieModel);
|
});
|
||||||
} else if (type === 'series' || type === 'ova') {
|
} else if (type === 'series' || type === 'ova') {
|
||||||
return new SeriesModel({
|
return new SeriesModel({
|
||||||
subType: type,
|
subType: type,
|
||||||
|
|
@ -190,9 +190,9 @@ export class MALAPI extends APIModel {
|
||||||
lastWatched: '',
|
lastWatched: '',
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as SeriesModel);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
throw new Error(`MDB | Unknown media type for id ${id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { APIModel } from '../APIModel';
|
import type MediaDbPlugin from '../../main';
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
|
||||||
import MediaDbPlugin from '../../main';
|
|
||||||
import { MangaModel } from '../../models/MangaModel';
|
import { MangaModel } from '../../models/MangaModel';
|
||||||
|
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import { MediaType } from '../../utils/MediaType';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
import { APIModel } from '../APIModel';
|
||||||
|
|
||||||
export class MALAPIManga extends APIModel {
|
export class MALAPIManga extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -73,7 +73,7 @@ export class MALAPIManga extends APIModel {
|
||||||
lastWatched: '',
|
lastWatched: '',
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as MangaModel),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,6 +123,6 @@ export class MALAPIManga extends APIModel {
|
||||||
lastWatched: '',
|
lastWatched: '',
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as MangaModel);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { APIModel } from '../APIModel';
|
|
||||||
import { Notice } from 'obsidian';
|
import { Notice } from 'obsidian';
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
|
||||||
import MediaDbPlugin from '../../main';
|
|
||||||
import { GameModel } from '../../models/GameModel';
|
|
||||||
import { requestUrl } from 'obsidian';
|
import { requestUrl } from 'obsidian';
|
||||||
|
import type MediaDbPlugin from '../../main';
|
||||||
|
import { GameModel } from '../../models/GameModel';
|
||||||
|
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import { MediaType } from '../../utils/MediaType';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
import { APIModel } from '../APIModel';
|
||||||
|
|
||||||
export class MobyGamesAPI extends APIModel {
|
export class MobyGamesAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -23,9 +23,7 @@ export class MobyGamesAPI extends APIModel {
|
||||||
console.log(`MDB | api "${this.apiName}" queried by Title`);
|
console.log(`MDB | api "${this.apiName}" queried by Title`);
|
||||||
|
|
||||||
if (!this.plugin.settings.MobyGamesKey) {
|
if (!this.plugin.settings.MobyGamesKey) {
|
||||||
console.error(new Error(`MDB | API key for ${this.apiName} missing.`));
|
throw new Error(`MDB | API key for ${this.apiName} missing.`);
|
||||||
new Notice(`MediaDB | API key for ${this.apiName} missing.`);
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchUrl = `${this.apiUrl}/games?title=${encodeURIComponent(title)}&api_key=${this.plugin.settings.MobyGamesKey}`;
|
const searchUrl = `${this.apiUrl}/games?title=${encodeURIComponent(title)}&api_key=${this.plugin.settings.MobyGamesKey}`;
|
||||||
|
|
@ -68,7 +66,6 @@ export class MobyGamesAPI extends APIModel {
|
||||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||||
|
|
||||||
if (!this.plugin.settings.MobyGamesKey) {
|
if (!this.plugin.settings.MobyGamesKey) {
|
||||||
new Notice(`MediaDB | API key for ${this.apiName} missing.`);
|
|
||||||
throw Error(`MDB | API key for ${this.apiName} missing.`);
|
throw Error(`MDB | API key for ${this.apiName} missing.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +76,6 @@ export class MobyGamesAPI extends APIModel {
|
||||||
console.debug(fetchData);
|
console.debug(fetchData);
|
||||||
|
|
||||||
if (fetchData.status !== 200) {
|
if (fetchData.status !== 200) {
|
||||||
new Notice(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
|
||||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,6 +105,6 @@ export class MobyGamesAPI extends APIModel {
|
||||||
|
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as GameModel);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { APIModel } from '../APIModel';
|
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
|
||||||
import MediaDbPlugin from '../../main';
|
|
||||||
import { requestUrl } from 'obsidian';
|
import { requestUrl } from 'obsidian';
|
||||||
|
import type MediaDbPlugin from '../../main';
|
||||||
|
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import { MusicReleaseModel } from '../../models/MusicReleaseModel';
|
import { MusicReleaseModel } from '../../models/MusicReleaseModel';
|
||||||
import { contactEmail, mediaDbVersion, pluginName } from '../../utils/Utils';
|
|
||||||
import { MediaType } from '../../utils/MediaType';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
import { contactEmail, mediaDbVersion, pluginName } from '../../utils/Utils';
|
||||||
|
import { APIModel } from '../APIModel';
|
||||||
|
|
||||||
export class MusicBrainzAPI extends APIModel {
|
export class MusicBrainzAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -55,7 +55,7 @@ export class MusicBrainzAPI extends APIModel {
|
||||||
|
|
||||||
artists: result['artist-credit'].map((a: any) => a.name),
|
artists: result['artist-credit'].map((a: any) => a.name),
|
||||||
subType: result['primary-type'],
|
subType: result['primary-type'],
|
||||||
} as MusicReleaseModel),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,6 +97,6 @@ export class MusicBrainzAPI extends APIModel {
|
||||||
userData: {
|
userData: {
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as MusicReleaseModel);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { APIModel } from '../APIModel';
|
|
||||||
import { Notice } from 'obsidian';
|
import { Notice } from 'obsidian';
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
import type MediaDbPlugin from '../../main';
|
||||||
import { MovieModel } from '../../models/MovieModel';
|
|
||||||
import MediaDbPlugin from '../../main';
|
|
||||||
import { SeriesModel } from '../../models/SeriesModel';
|
|
||||||
import { GameModel } from '../../models/GameModel';
|
import { GameModel } from '../../models/GameModel';
|
||||||
|
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
|
import { MovieModel } from '../../models/MovieModel';
|
||||||
|
import { SeriesModel } from '../../models/SeriesModel';
|
||||||
import { MediaType } from '../../utils/MediaType';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
import { APIModel } from '../APIModel';
|
||||||
|
|
||||||
export class OMDbAPI extends APIModel {
|
export class OMDbAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -30,9 +30,7 @@ export class OMDbAPI extends APIModel {
|
||||||
console.log(`MDB | api "${this.apiName}" queried by Title`);
|
console.log(`MDB | api "${this.apiName}" queried by Title`);
|
||||||
|
|
||||||
if (!this.plugin.settings.OMDbKey) {
|
if (!this.plugin.settings.OMDbKey) {
|
||||||
console.error(new Error(`MDB | API key for ${this.apiName} missing.`));
|
throw new Error(`MDB | API key for ${this.apiName} missing.`);
|
||||||
new Notice(`MediaDB | API key for ${this.apiName} missing.`);
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchUrl = `https://www.omdbapi.com/?s=${encodeURIComponent(title)}&apikey=${this.plugin.settings.OMDbKey}`;
|
const searchUrl = `https://www.omdbapi.com/?s=${encodeURIComponent(title)}&apikey=${this.plugin.settings.OMDbKey}`;
|
||||||
|
|
@ -76,7 +74,7 @@ export class OMDbAPI extends APIModel {
|
||||||
year: result.Year,
|
year: result.Year,
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.imdbID,
|
id: result.imdbID,
|
||||||
} as MovieModel),
|
}),
|
||||||
);
|
);
|
||||||
} else if (type === 'series') {
|
} else if (type === 'series') {
|
||||||
ret.push(
|
ret.push(
|
||||||
|
|
@ -87,7 +85,7 @@ export class OMDbAPI extends APIModel {
|
||||||
year: result.Year,
|
year: result.Year,
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.imdbID,
|
id: result.imdbID,
|
||||||
} as SeriesModel),
|
}),
|
||||||
);
|
);
|
||||||
} else if (type === 'game') {
|
} else if (type === 'game') {
|
||||||
ret.push(
|
ret.push(
|
||||||
|
|
@ -98,7 +96,7 @@ export class OMDbAPI extends APIModel {
|
||||||
year: result.Year,
|
year: result.Year,
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.imdbID,
|
id: result.imdbID,
|
||||||
} as GameModel),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +108,6 @@ export class OMDbAPI extends APIModel {
|
||||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||||
|
|
||||||
if (!this.plugin.settings.OMDbKey) {
|
if (!this.plugin.settings.OMDbKey) {
|
||||||
new Notice(`MediaDB | API key for ${this.apiName} missing.`);
|
|
||||||
throw Error(`MDB | API key for ${this.apiName} missing.`);
|
throw Error(`MDB | API key for ${this.apiName} missing.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,11 +115,9 @@ export class OMDbAPI extends APIModel {
|
||||||
const fetchData = await fetch(searchUrl);
|
const fetchData = await fetch(searchUrl);
|
||||||
|
|
||||||
if (fetchData.status === 401) {
|
if (fetchData.status === 401) {
|
||||||
new Notice(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
|
|
||||||
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
|
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
|
||||||
}
|
}
|
||||||
if (fetchData.status !== 200) {
|
if (fetchData.status !== 200) {
|
||||||
new Notice(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
|
||||||
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,7 +125,6 @@ export class OMDbAPI extends APIModel {
|
||||||
// console.debug(result);
|
// console.debug(result);
|
||||||
|
|
||||||
if (result.Response === 'False') {
|
if (result.Response === 'False') {
|
||||||
new Notice(`MDB | Received error from ${this.apiName}: ${result.Error}`);
|
|
||||||
throw Error(`MDB | Received error from ${this.apiName}: ${result.Error}`);
|
throw Error(`MDB | Received error from ${this.apiName}: ${result.Error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,7 +162,7 @@ export class OMDbAPI extends APIModel {
|
||||||
lastWatched: '',
|
lastWatched: '',
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as MovieModel);
|
});
|
||||||
} else if (type === 'series') {
|
} else if (type === 'series') {
|
||||||
return new SeriesModel({
|
return new SeriesModel({
|
||||||
type: type,
|
type: type,
|
||||||
|
|
@ -200,7 +194,7 @@ export class OMDbAPI extends APIModel {
|
||||||
lastWatched: '',
|
lastWatched: '',
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as SeriesModel);
|
});
|
||||||
} else if (type === 'game') {
|
} else if (type === 'game') {
|
||||||
return new GameModel({
|
return new GameModel({
|
||||||
type: type,
|
type: type,
|
||||||
|
|
@ -224,9 +218,9 @@ export class OMDbAPI extends APIModel {
|
||||||
played: false,
|
played: false,
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as GameModel);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
throw new Error(`MDB | Unknown media type for id ${id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { APIModel } from '../APIModel';
|
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
|
||||||
import MediaDbPlugin from '../../main';
|
|
||||||
import { BookModel } from 'src/models/BookModel';
|
import { BookModel } from 'src/models/BookModel';
|
||||||
|
import type MediaDbPlugin from '../../main';
|
||||||
|
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import { MediaType } from '../../utils/MediaType';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
import { APIModel } from '../APIModel';
|
||||||
|
|
||||||
export class OpenLibraryAPI extends APIModel {
|
export class OpenLibraryAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -42,7 +42,7 @@ export class OpenLibraryAPI extends APIModel {
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.key,
|
id: result.key,
|
||||||
author: result.author_name ?? 'unknown',
|
author: result.author_name ?? 'unknown',
|
||||||
} as BookModel),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,6 +87,6 @@ export class OpenLibraryAPI extends APIModel {
|
||||||
lastRead: '',
|
lastRead: '',
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as BookModel);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { APIModel } from '../APIModel';
|
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
|
||||||
import MediaDbPlugin from '../../main';
|
|
||||||
import { GameModel } from '../../models/GameModel';
|
|
||||||
import { requestUrl } from 'obsidian';
|
import { requestUrl } from 'obsidian';
|
||||||
|
import type MediaDbPlugin from '../../main';
|
||||||
|
import { GameModel } from '../../models/GameModel';
|
||||||
|
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import { MediaType } from '../../utils/MediaType';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
import { APIModel } from '../APIModel';
|
||||||
|
|
||||||
export class SteamAPI extends APIModel {
|
export class SteamAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -49,7 +49,7 @@ export class SteamAPI extends APIModel {
|
||||||
year: '',
|
year: '',
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.appid,
|
id: result.appid,
|
||||||
} as GameModel),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,8 +94,8 @@ export class SteamAPI extends APIModel {
|
||||||
url: `https://store.steampowered.com/app/${result.steam_appid}`,
|
url: `https://store.steampowered.com/app/${result.steam_appid}`,
|
||||||
id: result.steam_appid,
|
id: result.steam_appid,
|
||||||
|
|
||||||
developers: result['developers'],
|
developers: result.developers,
|
||||||
publishers: result['publishers'],
|
publishers: result.publishers,
|
||||||
genres: result.genres?.map((x: any) => x.description) ?? [],
|
genres: result.genres?.map((x: any) => x.description) ?? [],
|
||||||
onlineRating: Number.parseFloat(result.metacritic?.score ?? 0),
|
onlineRating: Number.parseFloat(result.metacritic?.score ?? 0),
|
||||||
image: result.header_image ?? '',
|
image: result.header_image ?? '',
|
||||||
|
|
@ -107,6 +107,6 @@ export class SteamAPI extends APIModel {
|
||||||
played: false,
|
played: false,
|
||||||
personalRating: 0,
|
personalRating: 0,
|
||||||
},
|
},
|
||||||
} as GameModel);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { APIModel } from '../APIModel';
|
import type MediaDbPlugin from '../../main';
|
||||||
import { MediaTypeModel } from '../../models/MediaTypeModel';
|
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||||
import MediaDbPlugin from '../../main';
|
|
||||||
import { WikiModel } from '../../models/WikiModel';
|
import { WikiModel } from '../../models/WikiModel';
|
||||||
import { MediaType } from '../../utils/MediaType';
|
import { MediaType } from '../../utils/MediaType';
|
||||||
|
import { APIModel } from '../APIModel';
|
||||||
|
|
||||||
export class WikipediaAPI extends APIModel {
|
export class WikipediaAPI extends APIModel {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -42,7 +42,7 @@ export class WikipediaAPI extends APIModel {
|
||||||
year: '',
|
year: '',
|
||||||
dataSource: this.apiName,
|
dataSource: this.apiName,
|
||||||
id: result.pageid,
|
id: result.pageid,
|
||||||
} as WikiModel),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,10 +73,10 @@ export class WikipediaAPI extends APIModel {
|
||||||
id: result.pageid,
|
id: result.pageid,
|
||||||
|
|
||||||
wikiUrl: result.fullurl,
|
wikiUrl: result.fullurl,
|
||||||
lastUpdated: this.plugin.dateFormatter.format(result.touched, this.apiDateFormat),
|
lastUpdated: this.plugin.dateFormatter.format(result.touched, this.apiDateFormat) ?? undefined,
|
||||||
length: result.length,
|
length: result.length,
|
||||||
|
|
||||||
userData: {},
|
userData: {},
|
||||||
} as WikiModel);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
108
src/main.ts
108
src/main.ts
|
|
@ -1,33 +1,28 @@
|
||||||
import { MarkdownView, Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder } from 'obsidian';
|
import { MarkdownView, Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder } from 'obsidian';
|
||||||
import { getDefaultSettings, MediaDbPluginSettings, MediaDbSettingTab } from './settings/Settings';
|
import type { MediaType } from 'src/utils/MediaType';
|
||||||
import { APIManager } from './api/APIManager';
|
import { APIManager } from './api/APIManager';
|
||||||
import { MediaTypeModel } from './models/MediaTypeModel';
|
import { BoardGameGeekAPI } from './api/apis/BoardGameGeekAPI';
|
||||||
import {
|
import { GiantBombAPI } from './api/apis/GiantBombAPI';
|
||||||
CreateNoteOptions,
|
|
||||||
dateTimeToString,
|
|
||||||
markdownTable,
|
|
||||||
replaceIllegalFileNameCharactersInString,
|
|
||||||
unCamelCase,
|
|
||||||
hasTemplaterPlugin,
|
|
||||||
useTemplaterPluginInFile,
|
|
||||||
} from './utils/Utils';
|
|
||||||
import { OMDbAPI } from './api/apis/OMDbAPI';
|
|
||||||
import { MALAPI } from './api/apis/MALAPI';
|
import { MALAPI } from './api/apis/MALAPI';
|
||||||
import { MALAPIManga } from './api/apis/MALAPIManga';
|
import { MALAPIManga } from './api/apis/MALAPIManga';
|
||||||
import { WikipediaAPI } from './api/apis/WikipediaAPI';
|
|
||||||
import { MusicBrainzAPI } from './api/apis/MusicBrainzAPI';
|
import { MusicBrainzAPI } from './api/apis/MusicBrainzAPI';
|
||||||
|
import { OMDbAPI } from './api/apis/OMDbAPI';
|
||||||
|
import type { MediaTypeModel } from './models/MediaTypeModel';
|
||||||
|
import type { MediaDbPluginSettings } from './settings/Settings';
|
||||||
|
import { getDefaultSettings, MediaDbSettingTab } from './settings/Settings';
|
||||||
import { MEDIA_TYPES, MediaTypeManager } from './utils/MediaTypeManager';
|
import { MEDIA_TYPES, MediaTypeManager } from './utils/MediaTypeManager';
|
||||||
|
import type { SearchModalOptions } from './utils/ModalHelper';
|
||||||
|
import type { CreateNoteOptions } from './utils/Utils';
|
||||||
|
import { dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString, unCamelCase, hasTemplaterPlugin, useTemplaterPluginInFile } from './utils/Utils';
|
||||||
|
import { WikipediaAPI } from './api/apis/WikipediaAPI';
|
||||||
import { SteamAPI } from './api/apis/SteamAPI';
|
import { SteamAPI } from './api/apis/SteamAPI';
|
||||||
import { BoardGameGeekAPI } from './api/apis/BoardGameGeekAPI';
|
|
||||||
import { OpenLibraryAPI } from './api/apis/OpenLibraryAPI';
|
import { OpenLibraryAPI } from './api/apis/OpenLibraryAPI';
|
||||||
import { MobyGamesAPI } from './api/apis/MobyGamesAPI';
|
import { MobyGamesAPI } from './api/apis/MobyGamesAPI';
|
||||||
import { GiantBombAPI } from './api/apis/GiantBombAPI';
|
|
||||||
import { PropertyMapper } from './settings/PropertyMapper';
|
import { PropertyMapper } from './settings/PropertyMapper';
|
||||||
import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal';
|
import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal';
|
||||||
import { PropertyMapping, PropertyMappingModel } from './settings/PropertyMapping';
|
import { PropertyMapping, PropertyMappingModel } from './settings/PropertyMapping';
|
||||||
import { ModalHelper, ModalResultCode, SearchModalOptions } from './utils/ModalHelper';
|
import { ModalHelper, ModalResultCode } from './utils/ModalHelper';
|
||||||
import { DateFormatter } from './utils/DateFormatter';
|
import { DateFormatter } from './utils/DateFormatter';
|
||||||
import { MediaType } from 'src/utils/MediaType';
|
|
||||||
|
|
||||||
export type Metadata = Record<string, unknown>;
|
export type Metadata = Record<string, unknown>;
|
||||||
|
|
||||||
|
|
@ -38,12 +33,12 @@ export interface MediaTypeModelObj {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MediaDbPlugin extends Plugin {
|
export default class MediaDbPlugin extends Plugin {
|
||||||
settings: MediaDbPluginSettings;
|
settings!: MediaDbPluginSettings;
|
||||||
apiManager: APIManager;
|
apiManager!: APIManager;
|
||||||
mediaTypeManager: MediaTypeManager;
|
mediaTypeManager!: MediaTypeManager;
|
||||||
modelPropertyMapper: PropertyMapper;
|
modelPropertyMapper!: PropertyMapper;
|
||||||
modalHelper: ModalHelper;
|
modalHelper!: ModalHelper;
|
||||||
dateFormatter: DateFormatter;
|
dateFormatter!: DateFormatter;
|
||||||
|
|
||||||
frontMatterRexExpPattern: string = '^(---)\\n[\\s\\S]*?\\n---';
|
frontMatterRexExpPattern: string = '^(---)\\n[\\s\\S]*?\\n---';
|
||||||
|
|
||||||
|
|
@ -165,7 +160,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
* - maybe custom link syntax
|
* - maybe custom link syntax
|
||||||
*/
|
*/
|
||||||
async createLinkWithSearchModal(): Promise<void> {
|
async createLinkWithSearchModal(): Promise<void> {
|
||||||
const apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal({}, async advancedSearchModalData => {
|
const apiSearchResults = await this.modalHelper.openAdvancedSearchModal({}, async advancedSearchModalData => {
|
||||||
return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis);
|
return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -173,7 +168,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectResults: MediaTypeModel[] = await this.modalHelper.openSelectModal({ elements: apiSearchResults, multiSelect: false }, async selectModalData => {
|
const selectResults = await this.modalHelper.openSelectModal({ elements: apiSearchResults, multiSelect: false }, async selectModalData => {
|
||||||
return await this.queryDetails(selectModalData.selected);
|
return await this.queryDetails(selectModalData.selected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -193,7 +188,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
|
|
||||||
async createEntryWithSearchModal(searchModalOptions?: SearchModalOptions): Promise<void> {
|
async createEntryWithSearchModal(searchModalOptions?: SearchModalOptions): Promise<void> {
|
||||||
let types: string[] = [];
|
let types: string[] = [];
|
||||||
let apiSearchResults: MediaTypeModel[] = await this.modalHelper.openSearchModal(searchModalOptions ?? {}, async searchModalData => {
|
let apiSearchResults = await this.modalHelper.openSearchModal(searchModalOptions ?? {}, async searchModalData => {
|
||||||
types = searchModalData.types;
|
types = searchModalData.types;
|
||||||
const apis = this.apiManager.apis.filter(x => x.hasTypeOverlap(searchModalData.types)).map(x => x.apiName);
|
const apis = this.apiManager.apis.filter(x => x.hasTypeOverlap(searchModalData.types)).map(x => x.apiName);
|
||||||
try {
|
try {
|
||||||
|
|
@ -214,12 +209,13 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
apiSearchResults = apiSearchResults.filter(x => types.contains(x.type));
|
apiSearchResults = apiSearchResults.filter(x => types.contains(x.type));
|
||||||
|
|
||||||
let selectResults: MediaTypeModel[];
|
let selectResults: MediaTypeModel[];
|
||||||
let proceed: boolean;
|
let proceed: boolean = false;
|
||||||
|
|
||||||
while (!proceed) {
|
while (!proceed) {
|
||||||
selectResults = await this.modalHelper.openSelectModal({ elements: apiSearchResults }, async selectModalData => {
|
selectResults =
|
||||||
|
(await this.modalHelper.openSelectModal({ elements: apiSearchResults }, async selectModalData => {
|
||||||
return await this.queryDetails(selectModalData.selected);
|
return await this.queryDetails(selectModalData.selected);
|
||||||
});
|
})) ?? [];
|
||||||
if (!selectResults) {
|
if (!selectResults) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -229,11 +225,11 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.createMediaDbNotes(selectResults);
|
await this.createMediaDbNotes(selectResults!);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createEntryWithAdvancedSearchModal(): Promise<void> {
|
async createEntryWithAdvancedSearchModal(): Promise<void> {
|
||||||
const apiSearchResults: MediaTypeModel[] = await this.modalHelper.openAdvancedSearchModal({}, async advancedSearchModalData => {
|
const apiSearchResults = await this.modalHelper.openAdvancedSearchModal({}, async advancedSearchModalData => {
|
||||||
return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis);
|
return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -243,12 +239,13 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectResults: MediaTypeModel[];
|
let selectResults: MediaTypeModel[];
|
||||||
let proceed: boolean;
|
let proceed: boolean = false;
|
||||||
|
|
||||||
while (!proceed) {
|
while (!proceed) {
|
||||||
selectResults = await this.modalHelper.openSelectModal({ elements: apiSearchResults }, async selectModalData => {
|
selectResults =
|
||||||
|
(await this.modalHelper.openSelectModal({ elements: apiSearchResults }, async selectModalData => {
|
||||||
return await this.queryDetails(selectModalData.selected);
|
return await this.queryDetails(selectModalData.selected);
|
||||||
});
|
})) ?? [];
|
||||||
if (!selectResults) {
|
if (!selectResults) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -258,12 +255,12 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.createMediaDbNotes(selectResults);
|
await this.createMediaDbNotes(selectResults!);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createEntryWithIdSearchModal(): Promise<void> {
|
async createEntryWithIdSearchModal(): Promise<void> {
|
||||||
let idSearchResult: MediaTypeModel;
|
let idSearchResult: MediaTypeModel | undefined = undefined;
|
||||||
let proceed: boolean;
|
let proceed: boolean = false;
|
||||||
|
|
||||||
while (!proceed) {
|
while (!proceed) {
|
||||||
idSearchResult = await this.modalHelper.openIdSearchModal({}, async idSearchModalData => {
|
idSearchResult = await this.modalHelper.openIdSearchModal({}, async idSearchModalData => {
|
||||||
|
|
@ -278,6 +275,9 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!idSearchResult) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
await this.createMediaDbNoteFromModel(idSearchResult, { attachTemplate: true, openNote: true });
|
await this.createMediaDbNoteFromModel(idSearchResult, { attachTemplate: true, openNote: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,11 +290,9 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
async queryDetails(models: MediaTypeModel[]): Promise<MediaTypeModel[]> {
|
async queryDetails(models: MediaTypeModel[]): Promise<MediaTypeModel[]> {
|
||||||
const detailModels: MediaTypeModel[] = [];
|
const detailModels: MediaTypeModel[] = [];
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
try {
|
const res = await this.apiManager.queryDetailedInfo(model);
|
||||||
detailModels.push(await this.apiManager.queryDetailedInfo(model));
|
if (res) {
|
||||||
} catch (e) {
|
detailModels.push(res);
|
||||||
console.warn(e);
|
|
||||||
new Notice(e.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return detailModels;
|
return detailModels;
|
||||||
|
|
@ -319,7 +317,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
new Notice(e.toString());
|
new Notice(`${e}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,7 +381,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
|
|
||||||
// Updating a previous file
|
// Updating a previous file
|
||||||
if (options.attachFile) {
|
if (options.attachFile) {
|
||||||
const previousMetadata = this.app.metadataCache.getFileCache(options.attachFile).frontmatter;
|
const previousMetadata = this.app.metadataCache.getFileCache(options.attachFile)?.frontmatter ?? {};
|
||||||
|
|
||||||
// Use contents (below front matter) from previous file
|
// Use contents (below front matter) from previous file
|
||||||
fileContent = await this.app.vault.read(options.attachFile);
|
fileContent = await this.app.vault.read(options.attachFile);
|
||||||
|
|
@ -443,7 +441,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
||||||
}
|
}
|
||||||
|
|
||||||
async attachTemplate(fileMetadata: Metadata, fileContent: string, template: string): Promise<{ fileMetadata: Metadata; fileContent: string }> {
|
async attachTemplate(fileMetadata: Metadata, fileContent: string, template: string | undefined): Promise<{ fileMetadata: Metadata; fileContent: string }> {
|
||||||
if (!template) {
|
if (!template) {
|
||||||
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
return { fileMetadata: fileMetadata, fileContent: fileContent };
|
||||||
}
|
}
|
||||||
|
|
@ -486,7 +484,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
getMetadataFromFileCache(file: TFile): Metadata {
|
getMetadataFromFileCache(file: TFile): Metadata {
|
||||||
const metadata: Metadata | undefined = this.app.metadataCache.getFileCache(file).frontmatter;
|
const metadata: Metadata | undefined = this.app.metadataCache.getFileCache(file)?.frontmatter;
|
||||||
return structuredClone(metadata ?? {});
|
return structuredClone(metadata ?? {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -501,6 +499,10 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
// find and possibly create the folder set in settings or passed in folder
|
// find and possibly create the folder set in settings or passed in folder
|
||||||
const folder = options.folder ?? this.app.vault.getAbstractFileByPath('/');
|
const folder = options.folder ?? this.app.vault.getAbstractFileByPath('/');
|
||||||
|
|
||||||
|
if (!folder || !(folder instanceof TFolder)) {
|
||||||
|
throw new Error('MDB | invalid folder');
|
||||||
|
}
|
||||||
|
|
||||||
fileName = replaceIllegalFileNameCharactersInString(fileName);
|
fileName = replaceIllegalFileNameCharactersInString(fileName);
|
||||||
const filePath = `${folder.path}/${fileName}.md`;
|
const filePath = `${folder.path}/${fileName}.md`;
|
||||||
|
|
||||||
|
|
@ -519,7 +521,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
const activeLeaf = this.app.workspace.getUnpinnedLeaf();
|
const activeLeaf = this.app.workspace.getUnpinnedLeaf();
|
||||||
if (!activeLeaf) {
|
if (!activeLeaf) {
|
||||||
console.warn('MDB | no active leaf, not opening newly created note');
|
console.warn('MDB | no active leaf, not opening newly created note');
|
||||||
return;
|
return targetFile;
|
||||||
}
|
}
|
||||||
await activeLeaf.openFile(targetFile, { state: { mode: 'source' } });
|
await activeLeaf.openFile(targetFile, { state: { mode: 'source' } });
|
||||||
}
|
}
|
||||||
|
|
@ -532,7 +534,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
* Tries to read the type, id and dataSource of the active note. If successful it will query the api, delete the old note and create a new one.
|
* Tries to read the type, id and dataSource of the active note. If successful it will query the api, delete the old note and create a new one.
|
||||||
*/
|
*/
|
||||||
async updateActiveNote(onlyMetadata: boolean = false): Promise<void> {
|
async updateActiveNote(onlyMetadata: boolean = false): Promise<void> {
|
||||||
const activeFile: TFile = this.app.workspace.getActiveFile();
|
const activeFile = this.app.workspace.getActiveFile() ?? undefined;
|
||||||
if (!activeFile) {
|
if (!activeFile) {
|
||||||
throw new Error('MDB | there is no active note');
|
throw new Error('MDB | there is no active note');
|
||||||
}
|
}
|
||||||
|
|
@ -560,9 +562,9 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
// console.debug(newMediaTypeModel);
|
// console.debug(newMediaTypeModel);
|
||||||
|
|
||||||
if (onlyMetadata) {
|
if (onlyMetadata) {
|
||||||
await this.createMediaDbNoteFromModel(newMediaTypeModel, { attachFile: activeFile, folder: activeFile.parent, openNote: true });
|
await this.createMediaDbNoteFromModel(newMediaTypeModel, { attachFile: activeFile, folder: activeFile.parent ?? undefined, openNote: true });
|
||||||
} else {
|
} else {
|
||||||
await this.createMediaDbNoteFromModel(newMediaTypeModel, { attachTemplate: true, folder: activeFile.parent, openNote: true });
|
await this.createMediaDbNoteFromModel(newMediaTypeModel, { attachTemplate: true, folder: activeFile.parent ?? undefined, openNote: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -596,7 +598,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
try {
|
try {
|
||||||
results = await this.apiManager.query(title, [selectedAPI]);
|
results = await this.apiManager.query(title, [selectedAPI]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
erroredFiles.push({ filePath: file.path, error: e.toString() });
|
erroredFiles.push({ filePath: file.path, error: `${e}` });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!results || results.length === 0) {
|
if (!results || results.length === 0) {
|
||||||
|
|
@ -631,7 +633,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
const detailedResults = await this.queryDetails(selectModalResult.data.selected);
|
const detailedResults = await this.queryDetails(selectModalResult.data.selected);
|
||||||
await this.createMediaDbNotes(detailedResults, appendContent ? file : null);
|
await this.createMediaDbNotes(detailedResults, appendContent ? file : undefined);
|
||||||
|
|
||||||
selectModal.close();
|
selectModal.close();
|
||||||
}
|
}
|
||||||
|
|
@ -661,7 +663,7 @@ export default class MediaDbPlugin extends Plugin {
|
||||||
// migrate the settings loaded from the disk to match the structure of the default settings
|
// migrate the settings loaded from the disk to match the structure of the default settings
|
||||||
const newPropertyMappings: PropertyMappingModel[] = [];
|
const newPropertyMappings: PropertyMappingModel[] = [];
|
||||||
for (const defaultPropertyMappingModel of defaultSettings.propertyMappingModels) {
|
for (const defaultPropertyMappingModel of defaultSettings.propertyMappingModels) {
|
||||||
const newPropertyMappingModel: PropertyMappingModel = loadedSettings.propertyMappingModels.find(x => x.type === defaultPropertyMappingModel.type);
|
const newPropertyMappingModel = loadedSettings.propertyMappingModels.find(x => x.type === defaultPropertyMappingModel.type);
|
||||||
if (newPropertyMappingModel === undefined) {
|
if (newPropertyMappingModel === undefined) {
|
||||||
// if the propertyMappingModel exists in the default settings but not the loaded settings, add it
|
// if the propertyMappingModel exists in the default settings but not the loaded settings, add it
|
||||||
newPropertyMappings.push(defaultPropertyMappingModel);
|
newPropertyMappings.push(defaultPropertyMappingModel);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { ButtonComponent, Modal, Notice, Setting, TextComponent, ToggleComponent } from 'obsidian';
|
import type { ButtonComponent } from 'obsidian';
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
import { Modal, Notice, Setting, TextComponent, ToggleComponent } from 'obsidian';
|
||||||
import MediaDbPlugin from '../main';
|
import type MediaDbPlugin from '../main';
|
||||||
import { ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS, AdvancedSearchModalData, AdvancedSearchModalOptions } from '../utils/ModalHelper';
|
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
|
import type { AdvancedSearchModalData, AdvancedSearchModalOptions } from '../utils/ModalHelper';
|
||||||
|
import { ADVANCED_SEARCH_MODAL_DEFAULT_OPTIONS } from '../utils/ModalHelper';
|
||||||
|
|
||||||
export class MediaDbAdvancedSearchModal extends Modal {
|
export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -9,9 +11,9 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
query: string;
|
query: string;
|
||||||
isBusy: boolean;
|
isBusy: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
selectedApis: { name: string; selected: boolean }[];
|
selectedApis: string[];
|
||||||
|
|
||||||
searchBtn: ButtonComponent;
|
searchBtn?: ButtonComponent;
|
||||||
|
|
||||||
submitCallback?: (res: AdvancedSearchModalData) => void;
|
submitCallback?: (res: AdvancedSearchModalData) => void;
|
||||||
closeCallback?: (err?: Error) => void;
|
closeCallback?: (err?: Error) => void;
|
||||||
|
|
@ -22,12 +24,9 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.selectedApis = [];
|
this.selectedApis = [];
|
||||||
this.title = advancedSearchModalOptions.modalTitle;
|
this.title = advancedSearchModalOptions.modalTitle ?? '';
|
||||||
this.query = advancedSearchModalOptions.prefilledSearchString;
|
this.query = advancedSearchModalOptions.prefilledSearchString ?? '';
|
||||||
|
this.isBusy = false;
|
||||||
for (const api of this.plugin.apiManager.apis) {
|
|
||||||
this.selectedApis.push({ name: api.apiName, selected: advancedSearchModalOptions.preselectedAPIs.contains(api.apiName) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setSubmitCallback(submitCallback: (res: AdvancedSearchModalData) => void): void {
|
setSubmitCallback(submitCallback: (res: AdvancedSearchModalData) => void): void {
|
||||||
|
|
@ -44,13 +43,13 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async search(): Promise<MediaTypeModel[]> {
|
async search(): Promise<void> {
|
||||||
if (!this.query || this.query.length < 3) {
|
if (!this.query || this.query.length < 3) {
|
||||||
new Notice('MDB | Query too short');
|
new Notice('MDB | Query too short');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const apis: string[] = this.selectedApis.filter(x => x.selected).map(x => x.name);
|
const apis: string[] = this.selectedApis;
|
||||||
|
|
||||||
if (apis.length === 0) {
|
if (apis.length === 0) {
|
||||||
new Notice('MDB | No API selected');
|
new Notice('MDB | No API selected');
|
||||||
|
|
@ -59,10 +58,10 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
|
|
||||||
if (!this.isBusy) {
|
if (!this.isBusy) {
|
||||||
this.isBusy = true;
|
this.isBusy = true;
|
||||||
this.searchBtn.setDisabled(false);
|
this.searchBtn?.setDisabled(false);
|
||||||
this.searchBtn.setButtonText('Searching...');
|
this.searchBtn?.setButtonText('Searching...');
|
||||||
|
|
||||||
this.submitCallback({ query: this.query, apis: apis });
|
this.submitCallback?.({ query: this.query, apis: apis });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,9 +96,13 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
|
|
||||||
const apiToggleComponent = new ToggleComponent(apiToggleComponentWrapper);
|
const apiToggleComponent = new ToggleComponent(apiToggleComponentWrapper);
|
||||||
apiToggleComponent.setTooltip(api.apiName);
|
apiToggleComponent.setTooltip(api.apiName);
|
||||||
apiToggleComponent.setValue(this.selectedApis.find(x => x.name === api.apiName).selected);
|
apiToggleComponent.setValue(this.selectedApis.some(x => x === api.apiName));
|
||||||
apiToggleComponent.onChange(value => {
|
apiToggleComponent.onChange(value => {
|
||||||
this.selectedApis.find(x => x.name === api.apiName).selected = value;
|
if (value) {
|
||||||
|
this.selectedApis.push(api.apiName);
|
||||||
|
} else {
|
||||||
|
this.selectedApis = this.selectedApis.filter(x => x !== api.apiName);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
apiToggleComponentWrapper.appendChild(apiToggleComponent.toggleEl);
|
apiToggleComponentWrapper.appendChild(apiToggleComponent.toggleEl);
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +127,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose(): void {
|
onClose(): void {
|
||||||
this.closeCallback();
|
this.closeCallback?.();
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
contentEl.empty();
|
contentEl.empty();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import { App, ButtonComponent, DropdownComponent, Modal, Setting, TextComponent, ToggleComponent } from 'obsidian';
|
import type { App, ButtonComponent } from 'obsidian';
|
||||||
import MediaDbPlugin from '../main';
|
import { DropdownComponent, Modal, Setting, TextComponent, ToggleComponent } from 'obsidian';
|
||||||
|
import type MediaDbPlugin from '../main';
|
||||||
|
|
||||||
export class MediaDbFolderImportModal extends Modal {
|
export class MediaDbFolderImportModal extends Modal {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
onSubmit: (selectedAPI: string, titleFieldName: string, appendContent: boolean) => void;
|
onSubmit: (selectedAPI: string, titleFieldName: string, appendContent: boolean) => void;
|
||||||
selectedApi: string;
|
selectedApi: string;
|
||||||
searchBtn: ButtonComponent;
|
searchBtn?: ButtonComponent;
|
||||||
titleFieldName: string;
|
titleFieldName: string;
|
||||||
appendContent: boolean;
|
appendContent: boolean;
|
||||||
|
|
||||||
|
|
@ -14,6 +15,8 @@ export class MediaDbFolderImportModal extends Modal {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.onSubmit = onSubmit;
|
this.onSubmit = onSubmit;
|
||||||
this.selectedApi = plugin.apiManager.apis[0].apiName;
|
this.selectedApi = plugin.apiManager.apis[0].apiName;
|
||||||
|
this.titleFieldName = '';
|
||||||
|
this.appendContent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
submit(): void {
|
submit(): void {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { ButtonComponent, DropdownComponent, Modal, Notice, Setting, TextComponent } from 'obsidian';
|
import type { ButtonComponent } from 'obsidian';
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
import { DropdownComponent, Modal, Notice, Setting, TextComponent } from 'obsidian';
|
||||||
import MediaDbPlugin from '../main';
|
import type MediaDbPlugin from '../main';
|
||||||
import { ID_SEARCH_MODAL_DEFAULT_OPTIONS, IdSearchModalData, IdSearchModalOptions } from '../utils/ModalHelper';
|
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
|
import type { IdSearchModalData, IdSearchModalOptions } from '../utils/ModalHelper';
|
||||||
|
import { ID_SEARCH_MODAL_DEFAULT_OPTIONS } from '../utils/ModalHelper';
|
||||||
|
|
||||||
export class MediaDbIdSearchModal extends Modal {
|
export class MediaDbIdSearchModal extends Modal {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -11,7 +13,7 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
title: string;
|
title: string;
|
||||||
selectedApi: string;
|
selectedApi: string;
|
||||||
|
|
||||||
searchBtn: ButtonComponent;
|
searchBtn?: ButtonComponent;
|
||||||
|
|
||||||
submitCallback?: (res: IdSearchModalData, err?: Error) => void;
|
submitCallback?: (res: IdSearchModalData, err?: Error) => void;
|
||||||
closeCallback?: (err?: Error) => void;
|
closeCallback?: (err?: Error) => void;
|
||||||
|
|
@ -21,8 +23,10 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
super(plugin.app);
|
super(plugin.app);
|
||||||
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.title = idSearchModalOptions.modalTitle;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSubmitCallback(submitCallback: (res: IdSearchModalData, err?: Error) => void): void {
|
setSubmitCallback(submitCallback: (res: IdSearchModalData, err?: Error) => void): void {
|
||||||
|
|
@ -39,7 +43,7 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async search(): Promise<MediaTypeModel> {
|
async search(): Promise<void> {
|
||||||
if (!this.query) {
|
if (!this.query) {
|
||||||
new Notice('MDB | no Id entered');
|
new Notice('MDB | no Id entered');
|
||||||
return;
|
return;
|
||||||
|
|
@ -52,10 +56,10 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
|
|
||||||
if (!this.isBusy) {
|
if (!this.isBusy) {
|
||||||
this.isBusy = true;
|
this.isBusy = true;
|
||||||
this.searchBtn.setDisabled(false);
|
this.searchBtn?.setDisabled(false);
|
||||||
this.searchBtn.setButtonText('Searching...');
|
this.searchBtn?.setButtonText('Searching...');
|
||||||
|
|
||||||
this.submitCallback({ query: this.query, api: this.selectedApi });
|
this.submitCallback?.({ query: this.query, api: this.selectedApi });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,7 +113,7 @@ export class MediaDbIdSearchModal extends Modal {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose(): void {
|
onClose(): void {
|
||||||
this.closeCallback();
|
this.closeCallback?.();
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
contentEl.empty();
|
contentEl.empty();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,19 @@
|
||||||
import { ButtonComponent, Component, MarkdownRenderer, Modal, Setting } from 'obsidian';
|
import type { ButtonComponent } from 'obsidian';
|
||||||
import MediaDbPlugin from 'src/main';
|
import { Component, MarkdownRenderer, Modal, Setting } from 'obsidian';
|
||||||
import { MediaTypeModel } from 'src/models/MediaTypeModel';
|
import type MediaDbPlugin from 'src/main';
|
||||||
import { PREVIEW_MODAL_DEFAULT_OPTIONS, PreviewModalData, PreviewModalOptions } from '../utils/ModalHelper';
|
import type { MediaTypeModel } from 'src/models/MediaTypeModel';
|
||||||
import { CreateNoteOptions } from '../utils/Utils';
|
import type { PreviewModalData, PreviewModalOptions } from '../utils/ModalHelper';
|
||||||
|
import { PREVIEW_MODAL_DEFAULT_OPTIONS } from '../utils/ModalHelper';
|
||||||
|
|
||||||
export class MediaDbPreviewModal extends Modal {
|
export class MediaDbPreviewModal extends Modal {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
||||||
createNoteOptions: CreateNoteOptions;
|
|
||||||
elements: MediaTypeModel[];
|
elements: MediaTypeModel[];
|
||||||
isBusy: boolean;
|
|
||||||
title: string;
|
title: string;
|
||||||
cancelButton: ButtonComponent;
|
|
||||||
submitButton: ButtonComponent;
|
|
||||||
markdownComponent: Component;
|
markdownComponent: Component;
|
||||||
|
|
||||||
submitCallback: (previewModalData: PreviewModalData) => void;
|
submitCallback?: (previewModalData: PreviewModalData) => void;
|
||||||
closeCallback: (err?: Error) => void;
|
closeCallback?: (err?: Error) => void;
|
||||||
|
|
||||||
constructor(plugin: MediaDbPlugin, previewModalOptions: PreviewModalOptions) {
|
constructor(plugin: MediaDbPlugin, previewModalOptions: PreviewModalOptions) {
|
||||||
previewModalOptions = Object.assign({}, PREVIEW_MODAL_DEFAULT_OPTIONS, previewModalOptions);
|
previewModalOptions = Object.assign({}, PREVIEW_MODAL_DEFAULT_OPTIONS, previewModalOptions);
|
||||||
|
|
@ -24,8 +21,8 @@ export class MediaDbPreviewModal extends Modal {
|
||||||
super(plugin.app);
|
super(plugin.app);
|
||||||
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.title = previewModalOptions.modalTitle;
|
this.title = previewModalOptions.modalTitle ?? '';
|
||||||
this.elements = previewModalOptions.elements;
|
this.elements = previewModalOptions.elements ?? [];
|
||||||
|
|
||||||
this.markdownComponent = new Component();
|
this.markdownComponent = new Component();
|
||||||
}
|
}
|
||||||
|
|
@ -70,14 +67,12 @@ export class MediaDbPreviewModal extends Modal {
|
||||||
btn.setButtonText('Cancel');
|
btn.setButtonText('Cancel');
|
||||||
btn.onClick(() => this.close());
|
btn.onClick(() => this.close());
|
||||||
btn.buttonEl.addClass('media-db-plugin-button');
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
this.cancelButton = btn;
|
|
||||||
});
|
});
|
||||||
bottomSettingRow.addButton(btn => {
|
bottomSettingRow.addButton(btn => {
|
||||||
btn.setButtonText('Ok');
|
btn.setButtonText('Ok');
|
||||||
btn.setCta();
|
btn.setCta();
|
||||||
btn.onClick(() => this.submitCallback({ confirmed: true }));
|
btn.onClick(() => this.submitCallback?.({ confirmed: true }));
|
||||||
btn.buttonEl.addClass('media-db-plugin-button');
|
btn.buttonEl.addClass('media-db-plugin-button');
|
||||||
this.submitButton = btn;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,6 +82,6 @@ export class MediaDbPreviewModal extends Modal {
|
||||||
|
|
||||||
onClose(): void {
|
onClose(): void {
|
||||||
this.markdownComponent.unload();
|
this.markdownComponent.unload();
|
||||||
this.closeCallback();
|
this.closeCallback?.();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import { ButtonComponent, Modal, Notice, Setting, TextComponent, ToggleComponent } from 'obsidian';
|
import type { ButtonComponent } from 'obsidian';
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
import { Modal, Notice, Setting, TextComponent, ToggleComponent } from 'obsidian';
|
||||||
import MediaDbPlugin from '../main';
|
import type MediaDbPlugin from '../main';
|
||||||
import { SEARCH_MODAL_DEFAULT_OPTIONS, SearchModalData, SearchModalOptions } from '../utils/ModalHelper';
|
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
|
import type { MediaType } from '../utils/MediaType';
|
||||||
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
||||||
|
import type { SearchModalData, SearchModalOptions } from '../utils/ModalHelper';
|
||||||
|
import { SEARCH_MODAL_DEFAULT_OPTIONS } from '../utils/ModalHelper';
|
||||||
import { unCamelCase } from '../utils/Utils';
|
import { unCamelCase } from '../utils/Utils';
|
||||||
import { MediaType } from '../utils/MediaType';
|
|
||||||
|
|
||||||
export class MediaDbSearchModal extends Modal {
|
export class MediaDbSearchModal extends Modal {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -12,9 +14,9 @@ export class MediaDbSearchModal extends Modal {
|
||||||
query: string;
|
query: string;
|
||||||
isBusy: boolean;
|
isBusy: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
selectedTypes: { name: MediaType; selected: boolean }[];
|
selectedTypes: MediaType[];
|
||||||
|
|
||||||
searchBtn: ButtonComponent;
|
searchBtn?: ButtonComponent;
|
||||||
|
|
||||||
submitCallback?: (res: SearchModalData) => void;
|
submitCallback?: (res: SearchModalData) => void;
|
||||||
closeCallback?: (err?: Error) => void;
|
closeCallback?: (err?: Error) => void;
|
||||||
|
|
@ -24,13 +26,10 @@ export class MediaDbSearchModal extends Modal {
|
||||||
super(plugin.app);
|
super(plugin.app);
|
||||||
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.selectedTypes = [];
|
this.selectedTypes = [...(searchModalOptions.preselectedTypes ?? [])];
|
||||||
this.title = searchModalOptions.modalTitle;
|
this.title = searchModalOptions.modalTitle ?? '';
|
||||||
this.query = searchModalOptions.prefilledSearchString;
|
this.query = searchModalOptions.prefilledSearchString ?? '';
|
||||||
|
this.isBusy = false;
|
||||||
for (const mediaType of MEDIA_TYPES) {
|
|
||||||
this.selectedTypes.push({ name: mediaType, selected: searchModalOptions.preselectedTypes.contains(mediaType) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setSubmitCallback(submitCallback: (res: SearchModalData) => void): void {
|
setSubmitCallback(submitCallback: (res: SearchModalData) => void): void {
|
||||||
|
|
@ -47,13 +46,13 @@ export class MediaDbSearchModal extends Modal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async search(): Promise<MediaTypeModel[]> {
|
async search(): Promise<void> {
|
||||||
if (!this.query || this.query.length < 3) {
|
if (!this.query || this.query.length < 3) {
|
||||||
new Notice('MDB | Query too short');
|
new Notice('MDB | Query too short');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const types: MediaType[] = this.selectedTypes.filter(x => x.selected).map(x => x.name);
|
const types: MediaType[] = this.selectedTypes;
|
||||||
|
|
||||||
if (types.length === 0) {
|
if (types.length === 0) {
|
||||||
new Notice('MDB | No Type selected');
|
new Notice('MDB | No Type selected');
|
||||||
|
|
@ -62,10 +61,10 @@ export class MediaDbSearchModal extends Modal {
|
||||||
|
|
||||||
if (!this.isBusy) {
|
if (!this.isBusy) {
|
||||||
this.isBusy = true;
|
this.isBusy = true;
|
||||||
this.searchBtn.setDisabled(false);
|
this.searchBtn?.setDisabled(false);
|
||||||
this.searchBtn.setButtonText('Searching...');
|
this.searchBtn?.setButtonText('Searching...');
|
||||||
|
|
||||||
this.submitCallback({ query: this.query, types: types });
|
this.submitCallback?.({ query: this.query, types: types });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,7 +75,7 @@ export class MediaDbSearchModal extends Modal {
|
||||||
|
|
||||||
const placeholder = 'Search by title';
|
const placeholder = 'Search by title';
|
||||||
const searchComponent = new TextComponent(contentEl);
|
const searchComponent = new TextComponent(contentEl);
|
||||||
let currentToggle: ToggleComponent = null;
|
let currentToggle: ToggleComponent | undefined = undefined;
|
||||||
|
|
||||||
searchComponent.inputEl.style.width = '100%';
|
searchComponent.inputEl.style.width = '100%';
|
||||||
searchComponent.setPlaceholder(placeholder);
|
searchComponent.setPlaceholder(placeholder);
|
||||||
|
|
@ -100,7 +99,7 @@ export class MediaDbSearchModal extends Modal {
|
||||||
|
|
||||||
const apiToggleComponent = new ToggleComponent(apiToggleComponentWrapper);
|
const apiToggleComponent = new ToggleComponent(apiToggleComponentWrapper);
|
||||||
apiToggleComponent.setTooltip(unCamelCase(mediaType));
|
apiToggleComponent.setTooltip(unCamelCase(mediaType));
|
||||||
apiToggleComponent.setValue(this.selectedTypes.find(x => x.name === mediaType).selected);
|
apiToggleComponent.setValue(this.selectedTypes.contains(mediaType));
|
||||||
if (apiToggleComponent.getValue()) {
|
if (apiToggleComponent.getValue()) {
|
||||||
currentToggle = apiToggleComponent;
|
currentToggle = apiToggleComponent;
|
||||||
}
|
}
|
||||||
|
|
@ -108,13 +107,13 @@ export class MediaDbSearchModal extends Modal {
|
||||||
if (value) {
|
if (value) {
|
||||||
if (currentToggle && currentToggle !== apiToggleComponent) {
|
if (currentToggle && currentToggle !== apiToggleComponent) {
|
||||||
currentToggle.setValue(false);
|
currentToggle.setValue(false);
|
||||||
this.selectedTypes.find(x => x.name === mediaType).selected = false;
|
this.selectedTypes = this.selectedTypes.filter(x => x !== mediaType);
|
||||||
}
|
}
|
||||||
currentToggle = apiToggleComponent;
|
currentToggle = apiToggleComponent;
|
||||||
this.selectedTypes.find(x => x.name === mediaType).selected = true;
|
this.selectedTypes.push(mediaType);
|
||||||
} else {
|
} else {
|
||||||
currentToggle = null;
|
currentToggle = undefined;
|
||||||
this.selectedTypes.find(x => x.name === mediaType).selected = false;
|
this.selectedTypes = this.selectedTypes.filter(x => x !== mediaType);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
apiToggleComponentWrapper.appendChild(apiToggleComponent.toggleEl);
|
apiToggleComponentWrapper.appendChild(apiToggleComponent.toggleEl);
|
||||||
|
|
@ -140,7 +139,7 @@ export class MediaDbSearchModal extends Modal {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose(): void {
|
onClose(): void {
|
||||||
this.closeCallback();
|
this.closeCallback?.();
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
contentEl.empty();
|
contentEl.empty();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
import type MediaDbPlugin from '../main';
|
||||||
import MediaDbPlugin from '../main';
|
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
|
import type { SelectModalData, SelectModalOptions } from '../utils/ModalHelper';
|
||||||
|
import { SELECT_MODAL_OPTIONS_DEFAULT } from '../utils/ModalHelper';
|
||||||
import { SelectModal } from './SelectModal';
|
import { SelectModal } from './SelectModal';
|
||||||
import { SELECT_MODAL_OPTIONS_DEFAULT, SelectModalData, SelectModalOptions } from '../utils/ModalHelper';
|
|
||||||
|
|
||||||
export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -9,18 +10,18 @@ export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
||||||
busy: boolean;
|
busy: boolean;
|
||||||
sendCallback: boolean;
|
sendCallback: boolean;
|
||||||
|
|
||||||
submitCallback: (res: SelectModalData) => void;
|
submitCallback?: (res: SelectModalData) => void;
|
||||||
closeCallback: (err?: Error) => void;
|
closeCallback?: (err?: Error) => void;
|
||||||
skipCallback: () => void;
|
skipCallback?: () => void;
|
||||||
|
|
||||||
constructor(plugin: MediaDbPlugin, selectModalOptions: SelectModalOptions) {
|
constructor(plugin: MediaDbPlugin, selectModalOptions: SelectModalOptions) {
|
||||||
selectModalOptions = Object.assign({}, SELECT_MODAL_OPTIONS_DEFAULT, selectModalOptions);
|
selectModalOptions = Object.assign({}, SELECT_MODAL_OPTIONS_DEFAULT, selectModalOptions);
|
||||||
super(plugin.app, selectModalOptions.elements, selectModalOptions.multiSelect);
|
super(plugin.app, selectModalOptions.elements ?? [], selectModalOptions.multiSelect);
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
||||||
this.title = selectModalOptions.modalTitle;
|
this.title = selectModalOptions.modalTitle ?? '';
|
||||||
this.description = 'Select one or multiple search results.';
|
this.description = 'Select one or multiple search results.';
|
||||||
this.addSkipButton = selectModalOptions.skipButton;
|
this.addSkipButton = selectModalOptions.skipButton ?? false;
|
||||||
|
|
||||||
this.busy = false;
|
this.busy = false;
|
||||||
|
|
||||||
|
|
@ -50,17 +51,17 @@ export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
||||||
submit(): void {
|
submit(): void {
|
||||||
if (!this.busy) {
|
if (!this.busy) {
|
||||||
this.busy = true;
|
this.busy = true;
|
||||||
this.submitButton.setButtonText('Creating entry...');
|
this.submitButton?.setButtonText('Creating entry...');
|
||||||
this.submitCallback({ selected: this.selectModalElements.filter(x => x.isActive()).map(x => x.value) });
|
this.submitCallback?.({ selected: this.selectModalElements.filter(x => x.isActive()).map(x => x.value) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
skip(): void {
|
skip(): void {
|
||||||
this.skipButton.setButtonText('Skipping...');
|
this.skipButton?.setButtonText('Skipping...');
|
||||||
this.skipCallback();
|
this.skipCallback?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose(): void {
|
onClose(): void {
|
||||||
this.closeCallback();
|
this.closeCallback?.();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { App, ButtonComponent, Modal, Setting } from 'obsidian';
|
import type { App, ButtonComponent } from 'obsidian';
|
||||||
import { SelectModalElement } from './SelectModalElement';
|
import { Modal, Setting } from 'obsidian';
|
||||||
import { mod } from '../utils/Utils';
|
import { mod } from '../utils/Utils';
|
||||||
|
import { SelectModalElement } from './SelectModalElement';
|
||||||
|
|
||||||
export abstract class SelectModal<T> extends Modal {
|
export abstract class SelectModal<T> extends Modal {
|
||||||
allowMultiSelect: boolean;
|
allowMultiSelect: boolean;
|
||||||
|
|
@ -142,7 +143,7 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
}
|
}
|
||||||
|
|
||||||
// nothing is highlighted
|
// nothing is highlighted
|
||||||
this.selectModalElements.last().setHighlighted(true);
|
this.selectModalElements.last()?.setHighlighted(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
highlightDown(): void {
|
highlightDown(): void {
|
||||||
|
|
@ -154,20 +155,20 @@ export abstract class SelectModal<T> extends Modal {
|
||||||
}
|
}
|
||||||
|
|
||||||
// nothing is highlighted
|
// nothing is highlighted
|
||||||
this.selectModalElements.first().setHighlighted(true);
|
this.selectModalElements.first()?.setHighlighted(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getNextSelectModalElement(selectModalElement: SelectModalElement<T>): SelectModalElement<T> {
|
private getNextSelectModalElement(selectModalElement: SelectModalElement<T>): SelectModalElement<T> {
|
||||||
let nextId = selectModalElement.id + 1;
|
let nextId = selectModalElement.id + 1;
|
||||||
nextId = mod(nextId, this.selectModalElements.length);
|
nextId = mod(nextId, this.selectModalElements.length);
|
||||||
|
|
||||||
return this.selectModalElements.filter(x => x.id === nextId).first();
|
return this.selectModalElements.find(x => x.id === nextId)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPreviousSelectModalElement(selectModalElement: SelectModalElement<T>): SelectModalElement<T> {
|
private getPreviousSelectModalElement(selectModalElement: SelectModalElement<T>): SelectModalElement<T> {
|
||||||
let nextId = selectModalElement.id - 1;
|
let nextId = selectModalElement.id - 1;
|
||||||
nextId = mod(nextId, this.selectModalElements.length);
|
nextId = mod(nextId, this.selectModalElements.length);
|
||||||
|
|
||||||
return this.selectModalElements.filter(x => x.id === nextId).first();
|
return this.selectModalElements.find(x => x.id === nextId)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { SelectModal } from './SelectModal';
|
import type { SelectModal } from './SelectModal';
|
||||||
|
|
||||||
export class SelectModalElement<T> {
|
export class SelectModalElement<T> {
|
||||||
selectModal: SelectModal<T>;
|
selectModal: SelectModal<T>;
|
||||||
|
|
@ -35,6 +35,8 @@ export class SelectModalElement<T> {
|
||||||
this.element.on('mouseleave', '#' + this.getHTMLId(), () => {
|
this.element.on('mouseleave', '#' + this.getHTMLId(), () => {
|
||||||
this.setHighlighted(false);
|
this.setHighlighted(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.highlighted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getHTMLId(): string {
|
getHTMLId(): string {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { MediaTypeModel } from './MediaTypeModel';
|
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
import { mediaDbTag, migrateObject, type ModelToData } from '../utils/Utils';
|
||||||
|
import { MediaTypeModel } from './MediaTypeModel';
|
||||||
|
|
||||||
|
export type BoardGameData = ModelToData<BoardGameModel>;
|
||||||
|
|
||||||
export class BoardGameModel extends MediaTypeModel {
|
export class BoardGameModel extends MediaTypeModel {
|
||||||
genres: string[];
|
genres: string[];
|
||||||
|
|
@ -19,23 +21,23 @@ export class BoardGameModel extends MediaTypeModel {
|
||||||
personalRating: number;
|
personalRating: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(obj: any = {}) {
|
constructor(obj: BoardGameData) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.genres = undefined;
|
this.genres = [];
|
||||||
this.onlineRating = undefined;
|
this.onlineRating = 0;
|
||||||
this.minPlayers = undefined;
|
this.complexityRating = 0;
|
||||||
this.maxPlayers = undefined;
|
this.minPlayers = 0;
|
||||||
this.playtime = undefined;
|
this.maxPlayers = 0;
|
||||||
this.publishers = undefined;
|
this.playtime = '';
|
||||||
this.complexityRating = undefined;
|
this.publishers = [];
|
||||||
this.image = undefined;
|
this.image = '';
|
||||||
|
|
||||||
this.released = undefined;
|
this.released = false;
|
||||||
|
|
||||||
this.userData = {
|
this.userData = {
|
||||||
played: undefined,
|
played: false,
|
||||||
personalRating: undefined,
|
personalRating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
migrateObject(this, obj, this);
|
migrateObject(this, obj, this);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { MediaTypeModel } from './MediaTypeModel';
|
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
import { mediaDbTag, migrateObject, type ModelToData } from '../utils/Utils';
|
||||||
|
import { MediaTypeModel } from './MediaTypeModel';
|
||||||
|
|
||||||
|
export type BookData = ModelToData<BookModel>;
|
||||||
|
|
||||||
export class BookModel extends MediaTypeModel {
|
export class BookModel extends MediaTypeModel {
|
||||||
author: string;
|
author: string;
|
||||||
|
|
@ -8,7 +10,6 @@ export class BookModel extends MediaTypeModel {
|
||||||
pages: number;
|
pages: number;
|
||||||
image: string;
|
image: string;
|
||||||
onlineRating: number;
|
onlineRating: number;
|
||||||
english_title: string;
|
|
||||||
isbn: number;
|
isbn: number;
|
||||||
isbn13: number;
|
isbn13: number;
|
||||||
|
|
||||||
|
|
@ -20,22 +21,23 @@ export class BookModel extends MediaTypeModel {
|
||||||
personalRating: number;
|
personalRating: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(obj: any = {}) {
|
constructor(obj: BookData) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.author = undefined;
|
this.author = '';
|
||||||
this.pages = undefined;
|
this.plot = '';
|
||||||
this.image = undefined;
|
this.pages = 0;
|
||||||
this.onlineRating = undefined;
|
this.image = '';
|
||||||
this.isbn = undefined;
|
this.onlineRating = 0;
|
||||||
this.isbn13 = undefined;
|
this.isbn = 0;
|
||||||
|
this.isbn13 = 0;
|
||||||
|
|
||||||
this.released = undefined;
|
this.released = false;
|
||||||
|
|
||||||
this.userData = {
|
this.userData = {
|
||||||
read: undefined,
|
read: false,
|
||||||
lastRead: undefined,
|
lastRead: '',
|
||||||
personalRating: undefined,
|
personalRating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
migrateObject(this, obj, this);
|
migrateObject(this, obj, this);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { MediaTypeModel } from './MediaTypeModel';
|
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
import { mediaDbTag, migrateObject, type ModelToData } from '../utils/Utils';
|
||||||
|
import { MediaTypeModel } from './MediaTypeModel';
|
||||||
|
|
||||||
|
export type GameData = ModelToData<GameModel>;
|
||||||
|
|
||||||
export class GameModel extends MediaTypeModel {
|
export class GameModel extends MediaTypeModel {
|
||||||
developers: string[];
|
developers: string[];
|
||||||
|
|
@ -17,19 +19,21 @@ export class GameModel extends MediaTypeModel {
|
||||||
personalRating: number;
|
personalRating: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(obj: any = {}) {
|
constructor(obj: GameData) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.developers = undefined;
|
this.developers = [];
|
||||||
this.publishers = undefined;
|
this.publishers = [];
|
||||||
this.genres = undefined;
|
this.genres = [];
|
||||||
this.onlineRating = undefined;
|
this.onlineRating = 0;
|
||||||
this.image = undefined;
|
this.image = '';
|
||||||
this.released = undefined;
|
|
||||||
this.releaseDate = undefined;
|
this.released = false;
|
||||||
|
this.releaseDate = '';
|
||||||
|
|
||||||
this.userData = {
|
this.userData = {
|
||||||
played: undefined,
|
played: false,
|
||||||
personalRating: undefined,
|
personalRating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
migrateObject(this, obj, this);
|
migrateObject(this, obj, this);
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,12 @@
|
||||||
import { MediaTypeModel } from './MediaTypeModel';
|
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
import { mediaDbTag, migrateObject, type ModelToData } from '../utils/Utils';
|
||||||
|
import { MediaTypeModel } from './MediaTypeModel';
|
||||||
|
|
||||||
|
export type MangaData = ModelToData<MangaModel>;
|
||||||
|
|
||||||
export class MangaModel extends MediaTypeModel {
|
export class MangaModel extends MediaTypeModel {
|
||||||
type: string;
|
|
||||||
subType: string;
|
|
||||||
title: string;
|
|
||||||
plot: string;
|
plot: string;
|
||||||
englishTitle: string;
|
|
||||||
alternateTitles: string[];
|
alternateTitles: string[];
|
||||||
year: string;
|
|
||||||
dataSource: string;
|
|
||||||
url: string;
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
genres: string[];
|
genres: string[];
|
||||||
authors: string[];
|
authors: string[];
|
||||||
chapters: number;
|
chapters: number;
|
||||||
|
|
@ -32,27 +25,27 @@ export class MangaModel extends MediaTypeModel {
|
||||||
personalRating: number;
|
personalRating: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(obj: any = {}) {
|
constructor(obj: MangaData) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.plot = undefined;
|
this.plot = '';
|
||||||
this.genres = undefined;
|
this.alternateTitles = [];
|
||||||
this.authors = undefined;
|
this.genres = [];
|
||||||
this.alternateTitles = undefined;
|
this.authors = [];
|
||||||
this.chapters = undefined;
|
this.chapters = 0;
|
||||||
this.volumes = undefined;
|
this.volumes = 0;
|
||||||
this.onlineRating = undefined;
|
this.onlineRating = 0;
|
||||||
this.image = undefined;
|
this.image = '';
|
||||||
|
|
||||||
this.released = undefined;
|
this.released = false;
|
||||||
this.status = undefined;
|
this.status = '';
|
||||||
this.publishedFrom = undefined;
|
this.publishedFrom = '';
|
||||||
this.publishedTo = undefined;
|
this.publishedTo = '';
|
||||||
|
|
||||||
this.userData = {
|
this.userData = {
|
||||||
watched: undefined,
|
watched: false,
|
||||||
lastWatched: undefined,
|
lastWatched: '',
|
||||||
personalRating: undefined,
|
personalRating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
migrateObject(this, obj, this);
|
migrateObject(this, obj, this);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { MediaType } from '../utils/MediaType';
|
import type { MediaType } from '../utils/MediaType';
|
||||||
|
|
||||||
export abstract class MediaTypeModel {
|
export abstract class MediaTypeModel {
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -13,14 +13,14 @@ export abstract class MediaTypeModel {
|
||||||
userData: object;
|
userData: object;
|
||||||
|
|
||||||
protected constructor() {
|
protected constructor() {
|
||||||
this.type = undefined;
|
this.type = '';
|
||||||
this.subType = undefined;
|
this.subType = '';
|
||||||
this.title = undefined;
|
this.title = '';
|
||||||
this.englishTitle = undefined;
|
this.englishTitle = '';
|
||||||
this.year = undefined;
|
this.year = '';
|
||||||
this.dataSource = undefined;
|
this.dataSource = '';
|
||||||
this.url = undefined;
|
this.url = '';
|
||||||
this.id = undefined;
|
this.id = '';
|
||||||
this.userData = {};
|
this.userData = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { MediaTypeModel } from './MediaTypeModel';
|
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
import { mediaDbTag, migrateObject, type ModelToData } from '../utils/Utils';
|
||||||
|
import { MediaTypeModel } from './MediaTypeModel';
|
||||||
|
|
||||||
|
export type MovieData = ModelToData<MovieModel>;
|
||||||
|
|
||||||
export class MovieModel extends MediaTypeModel {
|
export class MovieModel extends MediaTypeModel {
|
||||||
plot: string;
|
plot: string;
|
||||||
|
|
@ -23,27 +25,27 @@ export class MovieModel extends MediaTypeModel {
|
||||||
personalRating: number;
|
personalRating: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(obj: any = {}) {
|
constructor(obj: MovieData) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.plot = undefined;
|
this.plot = '';
|
||||||
this.genres = undefined;
|
this.genres = [];
|
||||||
this.director = undefined;
|
this.director = [];
|
||||||
this.writer = undefined;
|
this.writer = [];
|
||||||
this.studio = undefined;
|
this.studio = [];
|
||||||
this.duration = undefined;
|
this.duration = '';
|
||||||
this.onlineRating = undefined;
|
this.onlineRating = 0;
|
||||||
this.actors = undefined;
|
this.actors = [];
|
||||||
this.image = undefined;
|
this.image = '';
|
||||||
|
|
||||||
this.released = undefined;
|
this.released = false;
|
||||||
this.streamingServices = undefined;
|
this.streamingServices = [];
|
||||||
this.premiere = undefined;
|
this.premiere = '';
|
||||||
|
|
||||||
this.userData = {
|
this.userData = {
|
||||||
watched: undefined,
|
watched: false,
|
||||||
lastWatched: undefined,
|
lastWatched: '',
|
||||||
personalRating: undefined,
|
personalRating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
migrateObject(this, obj, this);
|
migrateObject(this, obj, this);
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,28 @@
|
||||||
import { MediaTypeModel } from './MediaTypeModel';
|
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
import { mediaDbTag, migrateObject, type ModelToData } from '../utils/Utils';
|
||||||
|
import { MediaTypeModel } from './MediaTypeModel';
|
||||||
|
|
||||||
|
export type MusicReleaseData = ModelToData<MusicReleaseModel>;
|
||||||
|
|
||||||
export class MusicReleaseModel extends MediaTypeModel {
|
export class MusicReleaseModel extends MediaTypeModel {
|
||||||
type: string;
|
|
||||||
subType: string;
|
|
||||||
title: string;
|
|
||||||
englishTitle: string;
|
|
||||||
year: string;
|
|
||||||
dataSource: string;
|
|
||||||
url: string;
|
|
||||||
id: string;
|
|
||||||
image: string;
|
|
||||||
|
|
||||||
genres: string[];
|
genres: string[];
|
||||||
artists: string[];
|
artists: string[];
|
||||||
|
image: string;
|
||||||
rating: number;
|
rating: number;
|
||||||
|
|
||||||
userData: {
|
userData: {
|
||||||
personalRating: number;
|
personalRating: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(obj: any = {}) {
|
constructor(obj: MusicReleaseData) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.genres = undefined;
|
this.genres = [];
|
||||||
this.artists = undefined;
|
this.artists = [];
|
||||||
this.image = undefined;
|
this.image = '';
|
||||||
this.rating = undefined;
|
this.rating = 0;
|
||||||
this.userData = {
|
this.userData = {
|
||||||
personalRating: undefined,
|
personalRating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
migrateObject(this, obj, this);
|
migrateObject(this, obj, this);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,10 @@
|
||||||
import { MediaTypeModel } from './MediaTypeModel';
|
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
import { mediaDbTag, migrateObject, type ModelToData } from '../utils/Utils';
|
||||||
|
import { MediaTypeModel } from './MediaTypeModel';
|
||||||
|
|
||||||
|
export type SeriesData = ModelToData<SeriesModel>;
|
||||||
|
|
||||||
export class SeriesModel extends MediaTypeModel {
|
export class SeriesModel extends MediaTypeModel {
|
||||||
type: string;
|
|
||||||
subType: string;
|
|
||||||
title: string;
|
|
||||||
englishTitle: string;
|
|
||||||
year: string;
|
|
||||||
dataSource: string;
|
|
||||||
url: string;
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
plot: string;
|
plot: string;
|
||||||
genres: string[];
|
genres: string[];
|
||||||
writer: string[];
|
writer: string[];
|
||||||
|
|
@ -34,29 +27,29 @@ export class SeriesModel extends MediaTypeModel {
|
||||||
personalRating: number;
|
personalRating: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(obj: any = {}) {
|
constructor(obj: SeriesData) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.plot = undefined;
|
this.plot = '';
|
||||||
this.genres = undefined;
|
this.genres = [];
|
||||||
this.writer = undefined;
|
this.writer = [];
|
||||||
this.studio = undefined;
|
this.studio = [];
|
||||||
this.episodes = undefined;
|
this.episodes = 0;
|
||||||
this.duration = undefined;
|
this.duration = '';
|
||||||
this.onlineRating = undefined;
|
this.onlineRating = 0;
|
||||||
this.actors = undefined;
|
this.actors = [];
|
||||||
this.image = undefined;
|
this.image = '';
|
||||||
|
|
||||||
this.released = undefined;
|
this.released = false;
|
||||||
this.streamingServices = undefined;
|
this.streamingServices = [];
|
||||||
this.airing = undefined;
|
this.airing = false;
|
||||||
this.airedFrom = undefined;
|
this.airedFrom = '';
|
||||||
this.airedTo = undefined;
|
this.airedTo = '';
|
||||||
|
|
||||||
this.userData = {
|
this.userData = {
|
||||||
watched: undefined,
|
watched: false,
|
||||||
lastWatched: undefined,
|
lastWatched: '',
|
||||||
personalRating: undefined,
|
personalRating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
migrateObject(this, obj, this);
|
migrateObject(this, obj, this);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,10 @@
|
||||||
import { MediaTypeModel } from './MediaTypeModel';
|
|
||||||
import { mediaDbTag, migrateObject } from '../utils/Utils';
|
|
||||||
import { MediaType } from '../utils/MediaType';
|
import { MediaType } from '../utils/MediaType';
|
||||||
|
import { mediaDbTag, migrateObject, type ModelToData } from '../utils/Utils';
|
||||||
|
import { MediaTypeModel } from './MediaTypeModel';
|
||||||
|
|
||||||
|
export type WikiData = ModelToData<WikiModel>;
|
||||||
|
|
||||||
export class WikiModel extends MediaTypeModel {
|
export class WikiModel extends MediaTypeModel {
|
||||||
type: string;
|
|
||||||
subType: string;
|
|
||||||
title: string;
|
|
||||||
englishTitle: string;
|
|
||||||
year: string;
|
|
||||||
dataSource: string;
|
|
||||||
url: string;
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
wikiUrl: string;
|
wikiUrl: string;
|
||||||
lastUpdated: string;
|
lastUpdated: string;
|
||||||
length: number;
|
length: number;
|
||||||
|
|
@ -19,13 +12,13 @@ export class WikiModel extends MediaTypeModel {
|
||||||
|
|
||||||
userData: Record<string, unknown>;
|
userData: Record<string, unknown>;
|
||||||
|
|
||||||
constructor(obj: any = {}) {
|
constructor(obj: WikiData) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.wikiUrl = undefined;
|
this.wikiUrl = '';
|
||||||
this.lastUpdated = undefined;
|
this.lastUpdated = '';
|
||||||
this.length = undefined;
|
this.length = 0;
|
||||||
this.article = undefined;
|
this.article = '';
|
||||||
this.userData = {};
|
this.userData = {};
|
||||||
|
|
||||||
migrateObject(this, obj, this);
|
migrateObject(this, obj, this);
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,16 @@
|
||||||
import { setIcon } from 'obsidian';
|
import { setIcon } from 'obsidian';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
export let iconName: string = '';
|
interface Props {
|
||||||
export let iconSize: number = 20;
|
iconName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
let iconEl: HTMLElement;
|
let { iconName = '' }: Props = $props();
|
||||||
|
|
||||||
|
let iconEl: HTMLElement | undefined = $state();
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
setIcon(iconEl, iconName, iconSize);
|
setIcon(iconEl!, iconName);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { PropertyMappingOption } from './PropertyMapping';
|
import type MediaDbPlugin from '../main';
|
||||||
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
||||||
import MediaDbPlugin from '../main';
|
import { PropertyMappingOption } from './PropertyMapping';
|
||||||
|
|
||||||
export class PropertyMapper {
|
export class PropertyMapper {
|
||||||
plugin: MediaDbPlugin;
|
plugin: MediaDbPlugin;
|
||||||
|
|
@ -66,7 +66,7 @@ export class PropertyMapper {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
const propertyMappings = this.plugin.settings.propertyMappingModels.find(x => x.type === obj.type).properties;
|
const propertyMappings = this.plugin.settings.propertyMappingModels.find(x => x.type === obj.type)?.properties ?? [];
|
||||||
|
|
||||||
const originalObj: Record<string, unknown> = {};
|
const originalObj: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
import type { MediaType } from '../utils/MediaType';
|
||||||
import { containsOnlyLettersAndUnderscores, PropertyMappingNameConflictError, PropertyMappingValidationError } from '../utils/Utils';
|
import { containsOnlyLettersAndUnderscores, PropertyMappingNameConflictError, PropertyMappingValidationError } from '../utils/Utils';
|
||||||
import { MediaType } from '../utils/MediaType';
|
|
||||||
|
|
||||||
export enum PropertyMappingOption {
|
export enum PropertyMappingOption {
|
||||||
Default = 'default',
|
Default = 'default',
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,22 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { run } from 'svelte/legacy';
|
||||||
|
|
||||||
import { PropertyMappingModel, PropertyMappingOption, propertyMappingOptions } from './PropertyMapping';
|
import { PropertyMappingModel, PropertyMappingOption, propertyMappingOptions } from './PropertyMapping';
|
||||||
import { capitalizeFirstLetter } from '../utils/Utils';
|
import { capitalizeFirstLetter } from '../utils/Utils';
|
||||||
import Icon from './Icon.svelte';
|
import Icon from './Icon.svelte';
|
||||||
|
|
||||||
export let model: PropertyMappingModel;
|
interface Props {
|
||||||
export let save: (model: PropertyMappingModel) => void;
|
model: PropertyMappingModel;
|
||||||
|
save: (model: PropertyMappingModel) => void;
|
||||||
let validationResult: { res: boolean; err?: Error };
|
|
||||||
|
|
||||||
$: modelChanged(model);
|
|
||||||
|
|
||||||
function modelChanged(model: PropertyMappingModel) {
|
|
||||||
validationResult = model.validate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let { model, save }: Props = $props();
|
||||||
|
|
||||||
|
let validationResult: { res: boolean; err?: Error } | undefined = $state();
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
validationResult = model.validate();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="media-db-plugin-property-mappings-model-container">
|
<div class="media-db-plugin-property-mappings-model-container">
|
||||||
|
|
@ -51,7 +55,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
<button
|
<button
|
||||||
class="media-db-plugin-property-mappings-save-button {validationResult?.res ? 'mod-cta' : 'mod-muted'}"
|
class="media-db-plugin-property-mappings-save-button {validationResult?.res ? 'mod-cta' : 'mod-muted'}"
|
||||||
on:click={() => {
|
onclick={() => {
|
||||||
if (model.validate().res) save(model);
|
if (model.validate().res) save(model);
|
||||||
}}
|
}}
|
||||||
>Save
|
>Save
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,12 @@
|
||||||
import { PropertyMappingModel } from './PropertyMapping';
|
import { PropertyMappingModel } from './PropertyMapping';
|
||||||
import PropertyMappingModelComponent from './PropertyMappingModelComponent.svelte';
|
import PropertyMappingModelComponent from './PropertyMappingModelComponent.svelte';
|
||||||
|
|
||||||
export let models: PropertyMappingModel[] = [];
|
interface Props {
|
||||||
export let save: (model: PropertyMappingModel) => void;
|
models?: PropertyMappingModel[];
|
||||||
|
save: (model: PropertyMappingModel) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { models = [], save }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="setting-item" style="display: flex; gap: 10px; flex-direction: column; align-items: stretch;">
|
<div class="setting-item" style="display: flex; gap: 10px; flex-direction: column; align-items: stretch;">
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
import { App, Notice, PluginSettingTab, Setting } from 'obsidian';
|
import type { App } from 'obsidian';
|
||||||
|
import { Notice, PluginSettingTab, Setting } from 'obsidian';
|
||||||
import MediaDbPlugin from '../main';
|
import { mount } from 'svelte';
|
||||||
import { FolderSuggest } from './suggesters/FolderSuggest';
|
import type MediaDbPlugin from '../main';
|
||||||
import { FileSuggest } from './suggesters/FileSuggest';
|
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
import PropertyMappingModelsComponent from './PropertyMappingModelsComponent.svelte';
|
|
||||||
import { PropertyMapping, PropertyMappingModel, PropertyMappingOption } from './PropertyMapping';
|
|
||||||
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
import { MEDIA_TYPES } from '../utils/MediaTypeManager';
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
|
||||||
import { fragWithHTML } from '../utils/Utils';
|
import { fragWithHTML } from '../utils/Utils';
|
||||||
|
import { PropertyMapping, PropertyMappingModel, PropertyMappingOption } from './PropertyMapping';
|
||||||
|
import PropertyMappingModelsComponent from './PropertyMappingModelsComponent.svelte';
|
||||||
|
import { FileSuggest } from './suggesters/FileSuggest';
|
||||||
|
import { FolderSuggest } from './suggesters/FolderSuggest';
|
||||||
|
|
||||||
export interface MediaDbPluginSettings {
|
export interface MediaDbPluginSettings {
|
||||||
OMDbKey: string;
|
OMDbKey: string;
|
||||||
|
|
@ -254,7 +255,10 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
||||||
.onChange(data => {
|
.onChange(data => {
|
||||||
const newDateFormat = data ? data : DEFAULT_SETTINGS.customDateFormat;
|
const newDateFormat = data ? data : DEFAULT_SETTINGS.customDateFormat;
|
||||||
this.plugin.settings.customDateFormat = newDateFormat;
|
this.plugin.settings.customDateFormat = newDateFormat;
|
||||||
document.getElementById('media-db-dateformat-preview').textContent = this.plugin.dateFormatter.getPreview(newDateFormat); // update preview
|
const previewEl = document.getElementById('media-db-dateformat-preview');
|
||||||
|
if (previewEl) {
|
||||||
|
previewEl.textContent = this.plugin.dateFormatter.getPreview(newDateFormat); // update preview
|
||||||
|
}
|
||||||
void this.plugin.saveSettings();
|
void this.plugin.saveSettings();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -689,7 +693,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
||||||
Don't forget to save your changes using the save button for each individual category.
|
Don't forget to save your changes using the save button for each individual category.
|
||||||
</p>`;
|
</p>`;
|
||||||
|
|
||||||
new PropertyMappingModelsComponent({
|
mount(PropertyMappingModelsComponent, {
|
||||||
target: this.containerEl,
|
target: this.containerEl,
|
||||||
props: {
|
props: {
|
||||||
models: this.plugin.settings.propertyMappingModels.map(x => x.copy()),
|
models: this.plugin.settings.propertyMappingModels.map(x => x.copy()),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
import type { TAbstractFile } from 'obsidian';
|
||||||
|
import { TFile } from 'obsidian';
|
||||||
import { TextInputSuggest } from './Suggest';
|
import { TextInputSuggest } from './Suggest';
|
||||||
import { TAbstractFile, TFile } from 'obsidian';
|
|
||||||
|
|
||||||
export class FileSuggest extends TextInputSuggest<TFile> {
|
export class FileSuggest extends TextInputSuggest<TFile> {
|
||||||
getSuggestions(inputStr: string): TFile[] {
|
getSuggestions(inputStr: string): TFile[] {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
|
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
|
||||||
|
|
||||||
import { TAbstractFile, TFolder } from 'obsidian';
|
import type { TAbstractFile } from 'obsidian';
|
||||||
|
import { TFolder } from 'obsidian';
|
||||||
import { TextInputSuggest } from './Suggest';
|
import { TextInputSuggest } from './Suggest';
|
||||||
|
|
||||||
export class FolderSuggest extends TextInputSuggest<TFolder> {
|
export class FolderSuggest extends TextInputSuggest<TFolder> {
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,34 @@
|
||||||
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
|
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
|
||||||
|
|
||||||
import { App, ISuggestOwner, Scope } from 'obsidian';
|
import type { Instance as PopperInstance } from '@popperjs/core';
|
||||||
import { createPopper, 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';
|
import { wrapAround } from 'src/utils/Utils';
|
||||||
|
|
||||||
export class Suggest<T> {
|
export class Suggest<T> {
|
||||||
private owner: ISuggestOwner<T>;
|
private owner: ISuggestOwner<T>;
|
||||||
private values: T[];
|
private values: T[];
|
||||||
private suggestions: HTMLDivElement[];
|
private suggestions: HTMLElement[];
|
||||||
private selectedItem: number;
|
private selectedItem: number;
|
||||||
private containerEl: HTMLElement;
|
private containerEl: HTMLElement;
|
||||||
|
|
||||||
constructor(owner: ISuggestOwner<T>, containerEl: HTMLElement, scope: Scope) {
|
constructor(owner: ISuggestOwner<T>, containerEl: HTMLElement, scope: Scope) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.containerEl = containerEl;
|
this.containerEl = containerEl;
|
||||||
|
this.values = [];
|
||||||
|
this.suggestions = [];
|
||||||
|
this.selectedItem = 0;
|
||||||
|
|
||||||
containerEl.on('click', '.suggestion-item', this.onSuggestionClick.bind(this));
|
containerEl.on('click', '.suggestion-item', (e, el) => this.onSuggestionClick(e, el));
|
||||||
containerEl.on('mousemove', '.suggestion-item', this.onSuggestionMouseover.bind(this));
|
containerEl.on('mousemove', '.suggestion-item', (e, el) => this.onSuggestionMouseover(e, el));
|
||||||
|
|
||||||
scope.register([], 'ArrowUp', event => {
|
scope.register([], 'ArrowUp', event => {
|
||||||
if (!event.isComposing) {
|
if (!event.isComposing) {
|
||||||
this.setSelectedItem(this.selectedItem - 1, true);
|
this.setSelectedItem(this.selectedItem - 1, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.register([], 'ArrowDown', event => {
|
scope.register([], 'ArrowDown', event => {
|
||||||
|
|
@ -30,6 +36,7 @@ export class Suggest<T> {
|
||||||
this.setSelectedItem(this.selectedItem + 1, true);
|
this.setSelectedItem(this.selectedItem + 1, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.register([], 'Enter', event => {
|
scope.register([], 'Enter', event => {
|
||||||
|
|
@ -37,10 +44,11 @@ export class Suggest<T> {
|
||||||
this.useSelectedItem(event);
|
this.useSelectedItem(event);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuggestionClick(event: MouseEvent, el: HTMLDivElement): void {
|
onSuggestionClick(event: MouseEvent, el: HTMLElement): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const item = this.suggestions.indexOf(el);
|
const item = this.suggestions.indexOf(el);
|
||||||
|
|
@ -48,7 +56,7 @@ export class Suggest<T> {
|
||||||
this.useSelectedItem(event);
|
this.useSelectedItem(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuggestionMouseover(_event: MouseEvent, el: HTMLDivElement): void {
|
onSuggestionMouseover(_event: MouseEvent, el: HTMLElement): void {
|
||||||
const item = this.suggestions.indexOf(el);
|
const item = this.suggestions.indexOf(el);
|
||||||
this.setSelectedItem(item, false);
|
this.setSelectedItem(item, false);
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +103,7 @@ export abstract class TextInputSuggest<T> implements ISuggestOwner<T> {
|
||||||
protected app: App;
|
protected app: App;
|
||||||
protected inputEl: HTMLInputElement;
|
protected inputEl: HTMLInputElement;
|
||||||
|
|
||||||
private popper: PopperInstance;
|
private popper?: PopperInstance;
|
||||||
private scope: Scope;
|
private scope: Scope;
|
||||||
private suggestEl: HTMLElement;
|
private suggestEl: HTMLElement;
|
||||||
private suggest: Suggest<T>;
|
private suggest: Suggest<T>;
|
||||||
|
|
@ -126,13 +134,13 @@ export abstract class TextInputSuggest<T> implements ISuggestOwner<T> {
|
||||||
if (suggestions.length > 0) {
|
if (suggestions.length > 0) {
|
||||||
this.suggest.setSuggestions(suggestions);
|
this.suggest.setSuggestions(suggestions);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
this.open((<any>this.app).dom.appContainerEl, this.inputEl);
|
this.open((this.app as any).dom.appContainerEl, this.inputEl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open(container: HTMLElement, inputEl: HTMLElement): void {
|
open(container: HTMLElement, inputEl: HTMLElement): void {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(<any>this.app).keymap.pushScope(this.scope);
|
(this.app as any).keymap.pushScope(this.scope);
|
||||||
|
|
||||||
container.appendChild(this.suggestEl);
|
container.appendChild(this.suggestEl);
|
||||||
this.popper = createPopper(inputEl, this.suggestEl, {
|
this.popper = createPopper(inputEl, this.suggestEl, {
|
||||||
|
|
@ -162,7 +170,7 @@ export abstract class TextInputSuggest<T> implements ISuggestOwner<T> {
|
||||||
|
|
||||||
close(): void {
|
close(): void {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(<any>this.app).keymap.popScope(this.scope);
|
(this.app as any).keymap.popScope(this.scope);
|
||||||
|
|
||||||
this.suggest.setSuggestions([]);
|
this.suggest.setSuggestions([]);
|
||||||
this.popper?.destroy();
|
this.popper?.destroy();
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
import { MediaDbPluginSettings } from '../settings/Settings';
|
import type { App, TAbstractFile, TFile } from 'obsidian';
|
||||||
import { MediaType } from './MediaType';
|
import { TFolder } from 'obsidian';
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
|
||||||
import { replaceTags } from './Utils';
|
|
||||||
import { App, TAbstractFile, TFile, TFolder } from 'obsidian';
|
|
||||||
import { MovieModel } from '../models/MovieModel';
|
|
||||||
import { SeriesModel } from '../models/SeriesModel';
|
|
||||||
import { MangaModel } from '../models/MangaModel';
|
|
||||||
import { GameModel } from '../models/GameModel';
|
|
||||||
import { WikiModel } from '../models/WikiModel';
|
|
||||||
import { MusicReleaseModel } from '../models/MusicReleaseModel';
|
|
||||||
import { BoardGameModel } from '../models/BoardGameModel';
|
import { BoardGameModel } from '../models/BoardGameModel';
|
||||||
import { BookModel } from '../models/BookModel';
|
import { BookModel } from '../models/BookModel';
|
||||||
|
import { GameModel } from '../models/GameModel';
|
||||||
|
import { MangaModel } from '../models/MangaModel';
|
||||||
|
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
|
import { MovieModel } from '../models/MovieModel';
|
||||||
|
import { MusicReleaseModel } from '../models/MusicReleaseModel';
|
||||||
|
import { SeriesModel } from '../models/SeriesModel';
|
||||||
|
import type { MediaDbPluginSettings } from '../settings/Settings';
|
||||||
|
import { MediaType } from './MediaType';
|
||||||
|
import { replaceTags } from './Utils';
|
||||||
|
import { WikiModel } from '../models/WikiModel';
|
||||||
|
|
||||||
export const MEDIA_TYPES: MediaType[] = [
|
export const MEDIA_TYPES: MediaType[] = [
|
||||||
MediaType.Movie,
|
MediaType.Movie,
|
||||||
|
|
@ -28,7 +29,11 @@ export class MediaTypeManager {
|
||||||
mediaTemplateMap: Map<MediaType, string>;
|
mediaTemplateMap: Map<MediaType, string>;
|
||||||
mediaFolderMap: Map<MediaType, string>;
|
mediaFolderMap: Map<MediaType, string>;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {
|
||||||
|
this.mediaFileNameTemplateMap = new Map<MediaType, string>();
|
||||||
|
this.mediaTemplateMap = new Map<MediaType, string>();
|
||||||
|
this.mediaFolderMap = new Map<MediaType, string>();
|
||||||
|
}
|
||||||
|
|
||||||
updateTemplates(settings: MediaDbPluginSettings): void {
|
updateTemplates(settings: MediaDbPluginSettings): void {
|
||||||
this.mediaFileNameTemplateMap = new Map<MediaType, string>();
|
this.mediaFileNameTemplateMap = new Map<MediaType, string>();
|
||||||
|
|
@ -66,7 +71,7 @@ export class MediaTypeManager {
|
||||||
|
|
||||||
getFileName(mediaTypeModel: MediaTypeModel): string {
|
getFileName(mediaTypeModel: MediaTypeModel): string {
|
||||||
// Ignore undefined tags since some search APIs do not return all properties in the model and produce clean file names even if errors occur
|
// Ignore undefined tags since some search APIs do not return all properties in the model and produce clean file names even if errors occur
|
||||||
return replaceTags(this.mediaFileNameTemplateMap.get(mediaTypeModel.getMediaType()), mediaTypeModel, true);
|
return replaceTags(this.mediaFileNameTemplateMap.get(mediaTypeModel.getMediaType())!, mediaTypeModel, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTemplate(mediaTypeModel: MediaTypeModel, app: App): Promise<string> {
|
async getTemplate(mediaTypeModel: MediaTypeModel, app: App): Promise<string> {
|
||||||
|
|
@ -76,7 +81,7 @@ export class MediaTypeManager {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
let templateFile = app.vault.getAbstractFileByPath(templateFilePath);
|
let templateFile = app.vault.getAbstractFileByPath(templateFilePath) ?? undefined;
|
||||||
|
|
||||||
// WARNING: This was previously selected by filename, but that could lead to collisions and unwanted effects.
|
// WARNING: This was previously selected by filename, but that could lead to collisions and unwanted effects.
|
||||||
// This now falls back to the previous method if no file is found
|
// This now falls back to the previous method if no file is found
|
||||||
|
|
@ -107,7 +112,7 @@ export class MediaTypeManager {
|
||||||
if (!(await app.vault.adapter.exists(folderPath))) {
|
if (!(await app.vault.adapter.exists(folderPath))) {
|
||||||
await app.vault.createFolder(folderPath);
|
await app.vault.createFolder(folderPath);
|
||||||
}
|
}
|
||||||
const folder: TAbstractFile = app.vault.getAbstractFileByPath(folderPath);
|
const folder = app.vault.getAbstractFileByPath(folderPath);
|
||||||
|
|
||||||
if (!(folder instanceof TFolder)) {
|
if (!(folder instanceof TFolder)) {
|
||||||
throw Error(`Expected ${folder} to be instance of TFolder`);
|
throw Error(`Expected ${folder} to be instance of TFolder`);
|
||||||
|
|
@ -141,6 +146,6 @@ export class MediaTypeManager {
|
||||||
return new BookModel(obj);
|
return new BookModel(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
throw new Error(`Unknown media type: ${mediaType}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
|
import { Notice } from 'obsidian';
|
||||||
|
import { MediaDbPreviewModal } from 'src/modals/MediaDbPreviewModal';
|
||||||
|
import type MediaDbPlugin from '../main';
|
||||||
import { MediaDbAdvancedSearchModal } from '../modals/MediaDbAdvancedSearchModal';
|
import { MediaDbAdvancedSearchModal } from '../modals/MediaDbAdvancedSearchModal';
|
||||||
import { MediaDbIdSearchModal } from '../modals/MediaDbIdSearchModal';
|
import { MediaDbIdSearchModal } from '../modals/MediaDbIdSearchModal';
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
|
||||||
import { MediaDbSearchResultModal } from '../modals/MediaDbSearchResultModal';
|
|
||||||
import { Notice } from 'obsidian';
|
|
||||||
import MediaDbPlugin from '../main';
|
|
||||||
import { MediaDbPreviewModal } from 'src/modals/MediaDbPreviewModal';
|
|
||||||
import { MediaDbSearchModal } from '../modals/MediaDbSearchModal';
|
import { MediaDbSearchModal } from '../modals/MediaDbSearchModal';
|
||||||
import { MediaType } from './MediaType';
|
import { MediaDbSearchResultModal } from '../modals/MediaDbSearchResultModal';
|
||||||
|
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
|
import type { MediaType } from './MediaType';
|
||||||
|
|
||||||
export enum ModalResultCode {
|
export enum ModalResultCode {
|
||||||
SUCCESS = 'SUCCESS',
|
SUCCESS = 'SUCCESS',
|
||||||
|
|
@ -15,60 +15,59 @@ export enum ModalResultCode {
|
||||||
ERROR = 'ERROR',
|
ERROR = 'ERROR',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ModalResult<T> =
|
||||||
|
| {
|
||||||
|
code: ModalResultCode.CLOSE;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
code: ModalResultCode.ERROR;
|
||||||
|
error: Error;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
code: ModalResultCode.SUCCESS;
|
||||||
|
data: T;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SkippableModalResult<T> =
|
||||||
|
| ModalResult<T>
|
||||||
|
| {
|
||||||
|
code: ModalResultCode.SKIP;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object containing the data {@link ModalHelper.createSearchModal} returns.
|
* Object containing the data {@link ModalHelper.createSearchModal} returns.
|
||||||
* On {@link ModalResultCode.SUCCESS} this contains {@link SearchModalData}.
|
* On {@link ModalResultCode.SUCCESS} this contains {@link SearchModalData}.
|
||||||
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
*/
|
*/
|
||||||
export interface SearchModalResult {
|
export type SearchModalResult = ModalResult<SearchModalData>;
|
||||||
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR;
|
|
||||||
data?: SearchModalData;
|
|
||||||
error?: Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object containing the data {@link ModalHelper.createAdvancedSearchModal} returns.
|
* Object containing the data {@link ModalHelper.createAdvancedSearchModal} returns.
|
||||||
* On {@link ModalResultCode.SUCCESS} this contains {@link AdvancedSearchModalData}.
|
* On {@link ModalResultCode.SUCCESS} this contains {@link AdvancedSearchModalData}.
|
||||||
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
*/
|
*/
|
||||||
export interface AdvancedSearchModalResult {
|
export type AdvancedSearchModalResult = ModalResult<AdvancedSearchModalData>;
|
||||||
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR;
|
|
||||||
data?: AdvancedSearchModalData;
|
|
||||||
error?: Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object containing the data {@link ModalHelper.createIdSearchModal} returns.
|
* Object containing the data {@link ModalHelper.createIdSearchModal} returns.
|
||||||
* On {@link ModalResultCode.SUCCESS} this contains {@link IdSearchModalData}.
|
* On {@link ModalResultCode.SUCCESS} this contains {@link IdSearchModalData}.
|
||||||
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
*/
|
*/
|
||||||
export interface IdSearchModalResult {
|
export type IdSearchModalResult = ModalResult<IdSearchModalData>;
|
||||||
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR;
|
|
||||||
data?: IdSearchModalData;
|
|
||||||
error?: Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object containing the data {@link ModalHelper.createSelectModal} returns.
|
* Object containing the data {@link ModalHelper.createSelectModal} returns.
|
||||||
* On {@link ModalResultCode.SUCCESS} this contains {@link SelectModalData}.
|
* On {@link ModalResultCode.SUCCESS} this contains {@link SelectModalData}.
|
||||||
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
*/
|
*/
|
||||||
export interface SelectModalResult {
|
export type SelectModalResult = SkippableModalResult<SelectModalData>;
|
||||||
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.SKIP | ModalResultCode.ERROR;
|
|
||||||
data?: SelectModalData;
|
|
||||||
error?: Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object containing the data {@link ModalHelper.createPreviewModal} returns.
|
* Object containing the data {@link ModalHelper.createPreviewModal} returns.
|
||||||
* On {@link ModalResultCode.SUCCESS} this contains {@link PreviewModalData}.
|
* On {@link ModalResultCode.SUCCESS} this contains {@link PreviewModalData}.
|
||||||
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
* On {@link ModalResultCode.ERROR} this contains a reference to that error.
|
||||||
*/
|
*/
|
||||||
export interface PreviewModalResult {
|
export type PreviewModalResult = ModalResult<PreviewModalData>;
|
||||||
code: ModalResultCode.SUCCESS | ModalResultCode.CLOSE | ModalResultCode.ERROR;
|
|
||||||
data?: PreviewModalData;
|
|
||||||
error?: Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data the search modal returns.
|
* The data the search modal returns.
|
||||||
|
|
@ -248,7 +247,10 @@ export class ModalHelper {
|
||||||
* @param submitCallback the callback that gets executed after the modal has been submitted, but after it has been closed
|
* @param submitCallback the callback that gets executed after the modal has been submitted, but after it has been closed
|
||||||
* @returns the user input or nothing and a reference to the modal.
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
*/
|
*/
|
||||||
async openSearchModal(searchModalOptions: SearchModalOptions, submitCallback: (searchModalData: SearchModalData) => Promise<MediaTypeModel[]>): Promise<MediaTypeModel[]> {
|
async openSearchModal(
|
||||||
|
searchModalOptions: SearchModalOptions,
|
||||||
|
submitCallback: (searchModalData: SearchModalData) => Promise<MediaTypeModel[]>,
|
||||||
|
): Promise<MediaTypeModel[] | undefined> {
|
||||||
const { searchModalResult, searchModal } = await this.createSearchModal(searchModalOptions);
|
const { searchModalResult, searchModal } = await this.createSearchModal(searchModalOptions);
|
||||||
console.debug(`MDB | searchModal closed with code ${searchModalResult.code}`);
|
console.debug(`MDB | searchModal closed with code ${searchModalResult.code}`);
|
||||||
|
|
||||||
|
|
@ -271,7 +273,7 @@ export class ModalHelper {
|
||||||
return callbackRes;
|
return callbackRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
new Notice(e.toString());
|
new Notice(`${e}`);
|
||||||
searchModal.close();
|
searchModal.close();
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
@ -314,7 +316,7 @@ export class ModalHelper {
|
||||||
async openAdvancedSearchModal(
|
async openAdvancedSearchModal(
|
||||||
advancedSearchModalOptions: AdvancedSearchModalOptions,
|
advancedSearchModalOptions: AdvancedSearchModalOptions,
|
||||||
submitCallback: (advancedSearchModalData: AdvancedSearchModalData) => Promise<MediaTypeModel[]>,
|
submitCallback: (advancedSearchModalData: AdvancedSearchModalData) => Promise<MediaTypeModel[]>,
|
||||||
): Promise<MediaTypeModel[]> {
|
): Promise<MediaTypeModel[] | undefined> {
|
||||||
const { advancedSearchModalResult, advancedSearchModal } = await this.createAdvancedSearchModal(advancedSearchModalOptions);
|
const { advancedSearchModalResult, advancedSearchModal } = await this.createAdvancedSearchModal(advancedSearchModalOptions);
|
||||||
console.debug(`MDB | advencedSearchModal closed with code ${advancedSearchModalResult.code}`);
|
console.debug(`MDB | advencedSearchModal closed with code ${advancedSearchModalResult.code}`);
|
||||||
|
|
||||||
|
|
@ -337,7 +339,7 @@ export class ModalHelper {
|
||||||
return callbackRes;
|
return callbackRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
new Notice(e.toString());
|
new Notice(`${e}`);
|
||||||
advancedSearchModal.close();
|
advancedSearchModal.close();
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
@ -377,8 +379,8 @@ export class ModalHelper {
|
||||||
*/
|
*/
|
||||||
async openIdSearchModal(
|
async openIdSearchModal(
|
||||||
idSearchModalOptions: IdSearchModalOptions,
|
idSearchModalOptions: IdSearchModalOptions,
|
||||||
submitCallback: (idSearchModalData: IdSearchModalData) => Promise<MediaTypeModel>,
|
submitCallback: (idSearchModalData: IdSearchModalData) => Promise<MediaTypeModel | undefined>,
|
||||||
): Promise<MediaTypeModel> {
|
): Promise<MediaTypeModel | undefined> {
|
||||||
const { idSearchModalResult, idSearchModal } = await this.createIdSearchModal(idSearchModalOptions);
|
const { idSearchModalResult, idSearchModal } = await this.createIdSearchModal(idSearchModalOptions);
|
||||||
console.debug(`MDB | idSearchModal closed with code ${idSearchModalResult.code}`);
|
console.debug(`MDB | idSearchModal closed with code ${idSearchModalResult.code}`);
|
||||||
|
|
||||||
|
|
@ -396,12 +398,12 @@ export class ModalHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const callbackRes: MediaTypeModel = await submitCallback(idSearchModalResult.data);
|
const callbackRes = await submitCallback(idSearchModalResult.data);
|
||||||
idSearchModal.close();
|
idSearchModal.close();
|
||||||
return callbackRes;
|
return callbackRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
new Notice(e.toString());
|
new Notice(`${e}`);
|
||||||
idSearchModal.close();
|
idSearchModal.close();
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
@ -440,7 +442,10 @@ export class ModalHelper {
|
||||||
* @param submitCallback the callback that gets executed after the modal has been submitted, but before it has been closed
|
* @param submitCallback the callback that gets executed after the modal has been submitted, but before it has been closed
|
||||||
* @returns the user input or nothing and a reference to the modal.
|
* @returns the user input or nothing and a reference to the modal.
|
||||||
*/
|
*/
|
||||||
async openSelectModal(selectModalOptions: SelectModalOptions, submitCallback: (selectModalData: SelectModalData) => Promise<MediaTypeModel[]>): Promise<MediaTypeModel[]> {
|
async openSelectModal(
|
||||||
|
selectModalOptions: SelectModalOptions,
|
||||||
|
submitCallback: (selectModalData: SelectModalData) => Promise<MediaTypeModel[]>,
|
||||||
|
): Promise<MediaTypeModel[] | undefined> {
|
||||||
const { selectModalResult, selectModal } = await this.createSelectModal(selectModalOptions);
|
const { selectModalResult, selectModal } = await this.createSelectModal(selectModalOptions);
|
||||||
console.debug(`MDB | selectModal closed with code ${selectModalResult.code}`);
|
console.debug(`MDB | selectModal closed with code ${selectModalResult.code}`);
|
||||||
|
|
||||||
|
|
@ -468,7 +473,7 @@ export class ModalHelper {
|
||||||
return callbackRes;
|
return callbackRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
new Notice(e.toString());
|
new Notice(`${e}`);
|
||||||
selectModal.close();
|
selectModal.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -500,12 +505,12 @@ export class ModalHelper {
|
||||||
console.warn(previewModalResult.error);
|
console.warn(previewModalResult.error);
|
||||||
new Notice(previewModalResult.error.toString());
|
new Notice(previewModalResult.error.toString());
|
||||||
previewModal.close();
|
previewModal.close();
|
||||||
return undefined;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previewModalResult.code === ModalResultCode.CLOSE) {
|
if (previewModalResult.code === ModalResultCode.CLOSE) {
|
||||||
// modal is already being closed
|
// modal is already being closed
|
||||||
return undefined;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -514,9 +519,9 @@ export class ModalHelper {
|
||||||
return callbackRes;
|
return callbackRes;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
new Notice(e.toString());
|
new Notice(`${e}`);
|
||||||
previewModal.close();
|
previewModal.close();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { MediaTypeModel } from '../models/MediaTypeModel';
|
import type { TFile, TFolder, App } from 'obsidian';
|
||||||
import { TFile, TFolder, App } from 'obsidian';
|
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||||
|
|
||||||
export const pluginName: string = 'obsidian-media-db-plugin';
|
export const pluginName: string = 'obsidian-media-db-plugin';
|
||||||
export const contactEmail: string = 'm.projects.code@gmail.com';
|
export const contactEmail: string = 'm.projects.code@gmail.com';
|
||||||
|
|
@ -82,7 +82,7 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel, ignoreUndefin
|
||||||
return '{{ INVALID TEMPLATE TAG }}';
|
return '{{ INVALID TEMPLATE TAG }}';
|
||||||
}
|
}
|
||||||
|
|
||||||
function traverseMetaData(path: Array<string>, mediaTypeModel: MediaTypeModel): any {
|
function traverseMetaData(path: string[], mediaTypeModel: MediaTypeModel): any {
|
||||||
let o: any = mediaTypeModel;
|
let o: any = mediaTypeModel;
|
||||||
|
|
||||||
for (const part of path) {
|
for (const part of path) {
|
||||||
|
|
@ -226,7 +226,11 @@ export function hasTemplaterPlugin(app: App): boolean {
|
||||||
export async function useTemplaterPluginInFile(app: App, file: TFile): Promise<void> {
|
export async function useTemplaterPluginInFile(app: App, file: TFile): Promise<void> {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const templater = (app as any).plugins.plugins['templater-obsidian'];
|
const templater = (app as any).plugins.plugins['templater-obsidian'];
|
||||||
if (templater && !templater?.settings['trigger_on_file_creation']) {
|
if (templater && !templater?.settings.trigger_on_file_creation) {
|
||||||
await templater.templater.overwrite_file_commands(file);
|
await templater.templater.overwrite_file_commands(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ModelToData<T> = {
|
||||||
|
[K in keyof T as T[K] extends Function ? never : K]?: T[K];
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,22 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"target": "ES6",
|
"target": "ESNext",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
|
"strict": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"sourceMap": true,
|
||||||
"lib": ["DOM", "ESNext"],
|
"lib": ["DOM", "ESNext"],
|
||||||
"types": ["svelte"],
|
|
||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "tests/**/*.ts"]
|
"include": ["src/**/*.ts", "tests/**/*.ts"]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue