Merge pull request #6 from mProjectsCode/master

merge master into release for 0.1.8
This commit is contained in:
Moritz Jung 2022-05-19 10:39:59 +02:00 committed by GitHub
commit abab2cde65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 479 additions and 149 deletions

View file

@ -1,6 +1,6 @@
## Obsidian Media DB Plugin
A plugin that can query multiple APIs for movies, series, anime and games, and import them into your vault.
A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.
### Features
#### Search by Title
@ -10,23 +10,23 @@ Search a movie, series, anime or game by its name across multiple APIs.
Allows you to search by an ID that varies from API to API. Concrete info can be found in the description of the individual APIs.
#### Templates
The plugin allows you to set a template note that gets added to the end of any note created by this plugin.
The plugin also offers simple "template tgs". E.g. if the template includes `{{ title }}`, it will be replaced by the title of the movie, show or game.
The plugin allows you to set a template note that gets added to the end of any note created by this plugin.
The plugin also offers simple "template tgs". E.g. if the template includes `{{ title }}`, it will be replaced by the title of the movie, show or game.
Note that "template tags" are surrounded with two curly braces and that the spaces inside the curly braces are important.
For arrays there are two special ways of displaying them.
- using `{{ LIST:variable_name }}` will result in
```
- element 1
- element 2
- element 3
- ...
```
```
- element 1
- element 2
- element 3
- ...
```
- using `{{ ENUM:variable_name }}` will result in
```
element 1, element 2, element 3, ...
```
```
element 1, element 2, element 3, ...
```
I also published my own templates [here](https://github.com/mProjectsCode/obsidian-media-db-templates).
@ -34,48 +34,39 @@ I also published my own templates [here](https://github.com/mProjectsCode/obsidi
- movies (including specials)
- series (including OVAs)
- games
- music releases
- wiki articles
### Currently supported APIs:
- [Jikan](https://jikan.moe/), an API that uses [My Anime List](https://myanimelist.net)
- supported formats
- series
- movies
- specials
- OVAs
- authentication
- no authentication or API key needed
- SFW filter support
- yes
- Search by ID
- the ID you need is the ID of the anime on [My Anime List](https://myanimelist.net)
- you can find this ID in the URL
- e.g. for "Beyond the Boundary" the URL looks like this `https://myanimelist.net/anime/18153/Kyoukai_no_Kanata` so the ID is `18153`
- notes
- sometimes the api is very slow, this is normal
- you need to use the title the anime has on [My Anime List](https://myanimelist.net), which is in most cases the japanese title
- e.g. instead of "Demon Slayer" you have to search "Kimetsu no Yaiba"
- the API is rate limited to
- 60 requests per minute
- 3 requests per second
| 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 | No | 60 per minute and 3 per second | Yes |
| [OMDb](https://www.omdbapi.com/) | OMDb is an API that offers metadata for movie, series and games. | series, movies, games | Yes, you can get a free key here [here](https://www.omdbapi.com/apikey.aspx) | 1000 per day | No |
| [MusicBrainz](https://musicbrainz.org/) | MusicBrainz is an API that offers information about music releases. | music releases | No | 50 per second | No |
| [Wikipedia](https://en.wikipedia.org/wiki/Main_Page) | The Wikipedia API allows acces to all Wikipedia articles. | wiki articles | No | None | No | | | | | | |
#### Notes
- [Jikan](https://jikan.moe/)
- sometimes the api is very slow, this is normal
- you need to use the title the anime has on [My Anime List](https://myanimelist.net), which is in most cases the japanese title
- e.g. instead of "Demon Slayer" you have to search "Kimetsu no Yaiba"
#### Search by ID
- [Jikan](https://jikan.moe/)
- the ID you need is the ID of the anime on [My Anime List](https://myanimelist.net)
- you can find this ID in the URL
- e.g. for "Beyond the Boundary" the URL looks like this `https://myanimelist.net/anime/18153/Kyoukai_no_Kanata` so the ID is `18153`
- [OMDb](https://www.omdbapi.com/)
- supported formats
- series
- movies
- games
- authentication
- an API key is needed
- you can get one [here](https://www.omdbapi.com/apikey.aspx) for free
- SFW filter support
- no, but I haven't encountered any NSFW content while developing this plugin
- Search by ID
- the ID you need is the ID of the movie or show on [IMDb](https://www.imdb.com)
- you can find this ID in the URL
- e.g. for "Rogue One" the URL looks like this `https://www.imdb.com/title/tt3748528/` so the ID is `tt3748528`
- notes
- the api is rate limited to 1000 requests a day
- the ID you need is the ID of the movie or show on [IMDb](https://www.imdb.com)
- you can find this ID in the URL
- e.g. for "Rogue One" the URL looks like this `https://www.imdb.com/title/tt3748528/` so the ID is `tt3748528`
- [MusicBrainz](https://musicbrainz.org/)
- the id of a release is not easyly accessibe, you are better of just searching by title
- [Wikipedia](https://en.wikipedia.org/wiki/Main_Page)
- [here](https://en.wikipedia.org/wiki/Wikipedia:Finding_a_Wikidata_ID) is a guide to finding the Wikipedia ID for an article
### Contributions
Thank you for wanting to contribute to this project.
Thank you for wanting to contribute to this project.
Contributions are always welcome. If you have an idea, feel free to open a feature request under the issue tab or even create a pull request.

View file

@ -1,9 +1,9 @@
{
"id": "obsidian-media-db-plugin",
"name": "Media DB Plugin",
"version": "0.1.7",
"version": "0.1.8",
"minAppVersion": "0.14.0",
"description": "A plugin that can query multiple APIs for movies, series, anime and games, and import them into your vault.",
"description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault. ",
"author": "Moritz Jung",
"authorUrl": "https://mprojectscode.github.io/",
"isDesktopOnly": false

View file

@ -1,5 +1,6 @@
import {APIModel} from './APIModel';
import {MediaTypeModel} from '../models/MediaTypeModel';
import {debugLog} from '../utils/Utils';
export class APIManager {
apis: APIModel[];
@ -9,28 +10,17 @@ export class APIManager {
}
async query(query: string, apisToQuery: any): Promise<MediaTypeModel[]> {
console.log('MDB | api manager queried');
debugLog(`MDB | api manager queried with "${query}"`);
let res: MediaTypeModel[] = [];
for (const api of this.apis) {
if (Object.keys(apisToQuery).contains(api.apiName) && apisToQuery[api.apiName]) {
const apiRes = await api.searchByTitle(query);
// console.log(apiRes);
res = res.concat(apiRes);
}
}
/*
for (const api of this.apis) {
if (types.length === 0 || api.hasTypeOverlap(types)) {
const apiRes = await api.searchByTitle(query);
// console.log(apiRes);
res = res.concat(apiRes);
}
}
*/
return res;
}

View file

@ -19,6 +19,7 @@ export abstract class APIModel {
return this.types.contains(type);
}
// for future use (https://github.com/mProjectsCode/obsidian-media-db-plugin/issues/5)
hasTypeOverlap(types: string[]): boolean {
for (const type of types) {
if (this.hasType(type)) {

65
src/api/apis/LocGovAPI.ts Normal file
View file

@ -0,0 +1,65 @@
import {APIModel} from '../APIModel';
import {MediaTypeModel} from '../../models/MediaTypeModel';
import MediaDbPlugin from '../../main';
import {debugLog} from '../../utils/Utils';
// WIP
export class LocGovAPI extends APIModel {
plugin: MediaDbPlugin;
typeMappings: Map<string, string>;
constructor(plugin: MediaDbPlugin) {
super();
this.plugin = plugin;
this.apiName = 'loc.gov API';
this.apiDescription = 'A free API for the Library of Congress collections.';
this.apiUrl = 'https://libraryofcongress.github.io/data-exploration/index.html';
this.types = [];
this.typeMappings = new Map<string, string>();
// this.typeMappings.set('movie', 'movie');
}
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
console.log(`MDB | api "${this.apiName}" queried by Title`);
const searchUrl = `https://www.loc.gov/search/?q=${encodeURIComponent(title)}&fo=json&c=20`;
const fetchData = await fetch(searchUrl);
debugLog(fetchData);
if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
}
const data = await fetchData.json();
debugLog(data);
let ret: MediaTypeModel[] = [];
throw new Error('MDB | Under construction, API implementation not finished');
// return ret;
}
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
console.log(`MDB | api "${this.apiName}" queried by ID`);
const searchUrl = `https://www.loc.gov/item/${item.id}/?fo=json`;
const fetchData = await fetch(searchUrl);
if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
}
const data = await fetchData.json();
debugLog(data);
const result = data.data;
const type = this.typeMappings.get(result.type.toLowerCase());
if (type === undefined) {
throw Error(`${result.type.toLowerCase()} is an unsupported type.`);
}
throw new Error('MDB | Under construction, API implementation not finished');
// return;
}
}

View file

@ -3,6 +3,7 @@ import {MediaTypeModel} from '../../models/MediaTypeModel';
import {MovieModel} from '../../models/MovieModel';
import MediaDbPlugin from '../../main';
import {SeriesModel} from '../../models/SeriesModel';
import {debugLog} from '../../utils/Utils';
export class MALAPI extends APIModel {
plugin: MediaDbPlugin;
@ -24,18 +25,18 @@ export class MALAPI extends APIModel {
}
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
console.log(`MDB | api "${this.apiName}" queried`);
console.log(`MDB | api "${this.apiName}" queried by Title`);
const searchUrl = `https://api.jikan.moe/v4/anime?q=${title}&limit=20${this.plugin.settings.sfwFilter ? '&sfw' : ''}`;
const searchUrl = `https://api.jikan.moe/v4/anime?q=${encodeURIComponent(title)}&limit=20${this.plugin.settings.sfwFilter ? '&sfw' : ''}`;
const fetchData = await fetch(searchUrl);
console.log(fetchData);
debugLog(fetchData);
if (fetchData.status !== 200) {
throw Error(`Received status code ${fetchData.status} from an API.`);
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
}
const data = await fetchData.json();
console.log(data);
debugLog(data);
let ret: MediaTypeModel[] = [];
@ -69,16 +70,17 @@ export class MALAPI extends APIModel {
}
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
console.log(`MDB | api "${this.apiName}" queried by ID`);
const searchUrl = `https://api.jikan.moe/v4/anime/${item.id}`;
const fetchData = await fetch(searchUrl);
if (fetchData.status !== 200) {
throw Error(`Received status code ${fetchData.status} from an API.`);
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
}
const data = await fetchData.json();
console.log(data);
debugLog(data);
const result = data.data;
const type = this.typeMappings.get(result.type.toLowerCase());

View file

@ -0,0 +1,97 @@
import {APIModel} from '../APIModel';
import {MediaTypeModel} from '../../models/MediaTypeModel';
import MediaDbPlugin from '../../main';
import {requestUrl} from 'obsidian';
import {MusicReleaseModel} from '../../models/MusicReleaseModel';
import {contactEmail, debugLog, mediaDbVersion, pluginName} from '../../utils/Utils';
export class MusicBrainzAPI extends APIModel {
plugin: MediaDbPlugin;
constructor(plugin: MediaDbPlugin) {
super();
this.plugin = plugin;
this.apiName = 'MusicBrainz API';
this.apiDescription = 'Free API for music albums.';
this.apiUrl = 'https://musicbrainz.org/';
this.types = ['music'];
}
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
console.log(`MDB | api "${this.apiName}" queried by Title`);
const searchUrl = `https://musicbrainz.org/ws/2/release-group?query=${encodeURIComponent(title)}&limit=20&fmt=json`;
const fetchData = await requestUrl({
url: searchUrl,
headers: {
'User-Agent': `${pluginName}/${mediaDbVersion} (${contactEmail})`,
},
});
debugLog(fetchData);
if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
}
const data = await fetchData.json;
debugLog(data);
let ret: MediaTypeModel[] = [];
for (const result of data['release-groups']) {
ret.push(new MusicReleaseModel({
type: 'musicRelease',
title: result.title,
englishTitle: result.title,
year: (new Date(result['first-release-date'])).getFullYear().toString(),
dataSource: this.apiName,
url: '',
id: result.id,
artists: result['artist-credit'].map((a: any) => a.name),
subType: result['primary-type'],
} as MusicReleaseModel));
}
return ret;
}
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
console.log(`MDB | api "${this.apiName}" queried by ID`);
const searchUrl = `https://musicbrainz.org/ws/2/release-group/${encodeURIComponent(item.id)}?inc=releases+artists+tags+ratings+genres&fmt=json`;
const fetchData = await requestUrl({
url: searchUrl,
headers: {
'User-Agent': `${pluginName}/0.1.7 (${contactEmail})`,
},
});
if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
}
const data = await fetchData.json;
debugLog(data);
const result = data;
const model = new MusicReleaseModel({
type: 'musicRelease',
title: result.title,
englishTitle: result.title,
year: (new Date(result['first-release-date'])).getFullYear().toString(),
dataSource: this.apiName,
url: '',
id: result.id,
artists: result['artist-credit'].map((a: any) => a.name),
genres: result.genres.map((g: any) => g.name),
subType: result['primary-type'],
rating: result.rating.value * 2,
} as MusicReleaseModel);
return model;
}
}

View file

@ -4,6 +4,7 @@ import {MovieModel} from '../../models/MovieModel';
import MediaDbPlugin from '../../main';
import {SeriesModel} from '../../models/SeriesModel';
import {GameModel} from '../../models/GameModel';
import {debugLog} from '../../utils/Utils';
export class OMDbAPI extends APIModel {
plugin: MediaDbPlugin;
@ -24,29 +25,28 @@ export class OMDbAPI extends APIModel {
}
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
console.log(`MDB | api "${this.apiName}" queried`);
const searchUrl = `http://www.omdbapi.com/?s=${title}&apikey=${this.plugin.settings.OMDbKey}`;
console.log(`MDB | api "${this.apiName}" queried by Title`);
const searchUrl = `http://www.omdbapi.com/?s=${encodeURIComponent(title)}&apikey=${this.plugin.settings.OMDbKey}`;
const fetchData = await fetch(searchUrl);
if (fetchData.status === 401) {
throw Error(`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) {
throw Error(`Received status code ${fetchData.status} from an API.`);
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
}
const data = await fetchData.json();
if (data.Response === 'False') {
throw Error(`Received error from ${this.apiName}: ${data.Error}`);
throw Error(`MDB | Received error from ${this.apiName}: ${data.Error}`);
}
if (!data.Search) {
return [];
}
console.log(data.Search);
debugLog(data.Search);
let ret: MediaTypeModel[] = [];
@ -89,23 +89,23 @@ export class OMDbAPI extends APIModel {
}
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
console.log(`MDB | api "${this.apiName}" queried by ID`);
const searchUrl = `http://www.omdbapi.com/?i=${item.id}&apikey=${this.plugin.settings.OMDbKey}`;
const fetchData = await fetch(searchUrl);
if (fetchData.status === 401) {
throw Error(`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) {
throw Error(`Received status code ${fetchData.status} from an API.`);
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
}
const result = await fetchData.json();
console.log(result);
debugLog(result);
if (result.Response === 'False') {
throw Error(`Received error from ${this.apiName}: ${result.Error}`);
throw Error(`MDB | Received error from ${this.apiName}: ${result.Error}`);
}
const type = this.typeMappings.get(result.Type.toLowerCase());

View file

@ -1,26 +0,0 @@
import {APIModel} from '../APIModel';
import {MediaTypeModel} from '../../models/MediaTypeModel';
import {MovieModel} from '../../models/MovieModel';
export class TestAPI extends APIModel {
constructor() {
super();
this.apiName = 'testAPI';
this.apiDescription = 'The test API used during development.';
this.apiUrl = 'www.test.api';
this.types = ['test'];
}
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
console.log(`MDB | api "${this.apiName}" queried`);
return [
new MovieModel({title: 'test_1', type: this.types[0], dataSource: this.apiName}),
new MovieModel({title: 'test_2', type: this.types[0], dataSource: this.apiName, producer: 'Max Musterman'}),
];
}
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
return item;
}
}

View file

@ -0,0 +1,78 @@
import {APIModel} from '../APIModel';
import {MediaTypeModel} from '../../models/MediaTypeModel';
import MediaDbPlugin from '../../main';
import {WikiModel} from '../../models/WikiModel';
import {debugLog} from '../../utils/Utils';
export class WikipediaAPI extends APIModel {
plugin: MediaDbPlugin;
constructor(plugin: MediaDbPlugin) {
super();
this.plugin = plugin;
this.apiName = 'Wikipedia API';
this.apiDescription = 'The API behind Wikipedia';
this.apiUrl = 'https://www.wikipedia.com';
this.types = ['wiki'];
}
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
console.log(`MDB | api "${this.apiName}" queried by Title`);
const searchUrl = `https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=${encodeURIComponent(title)}&srlimit=20&utf8=&format=json&origin=*`;
const fetchData = await fetch(searchUrl);
debugLog(fetchData);
if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
}
const data = await fetchData.json();
debugLog(data);
let ret: MediaTypeModel[] = [];
for (const result of data.query.search) {
ret.push(new WikiModel({
type: 'wiki',
title: result.title,
englishTitle: result.title,
year: '',
dataSource: this.apiName,
id: result.pageid,
} as WikiModel));
}
return ret;
}
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
console.log(`MDB | api "${this.apiName}" queried by ID`);
const searchUrl = `https://en.wikipedia.org/w/api.php?action=query&prop=info&pageids=${item.id}&inprop=url&format=json&origin=*`;
const fetchData = await fetch(searchUrl);
if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
}
const data = await fetchData.json();
debugLog(data);
const result = Object.entries(data?.query?.pages)[0][1];
const model = new WikiModel({
type: 'wiki',
title: result.title,
englishTitle: result.title,
year: '',
dataSource: this.apiName,
id: result.pageid,
wikiUrl: result.fullurl,
lastUpdated: (new Date(result.touched)).toLocaleDateString() ?? 'unknown',
length: result.length,
} as WikiModel);
return model;
}
}

View file

@ -1,7 +1,6 @@
import {Notice, Plugin, TFile} from 'obsidian';
import {DEFAULT_SETTINGS, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings';
import {APIManager} from './api/APIManager';
import {TestAPI} from './api/apis/TestAPI';
import {MediaTypeModel} from './models/MediaTypeModel';
import {replaceIllegalFileNameCharactersInString, replaceTags} from './utils/Utils';
import {OMDbAPI} from './api/apis/OMDbAPI';
@ -9,6 +8,8 @@ import {MediaDbAdvancedSearchModal} from './modals/MediaDbAdvancedSearchModal';
import {MediaDbSearchResultModal} from './modals/MediaDbSearchResultModal';
import {MALAPI} from './api/apis/MALAPI';
import {MediaDbIdSearchModal} from './modals/MediaDbIdSearchModal';
import {WikipediaAPI} from './api/apis/WikipediaAPI';
import {MusicBrainzAPI} from './api/apis/MusicBrainzAPI';
export default class MediaDbPlugin extends Plugin {
settings: MediaDbPluginSettings;
@ -42,9 +43,11 @@ export default class MediaDbPlugin extends Plugin {
this.apiManager = new APIManager();
// register APIs
this.apiManager.registerAPI(new TestAPI());
this.apiManager.registerAPI(new OMDbAPI(this));
this.apiManager.registerAPI(new MALAPI(this));
this.apiManager.registerAPI(new WikipediaAPI(this));
this.apiManager.registerAPI(new MusicBrainzAPI(this));
// this.apiManager.registerAPI(new LocGovAPI(this)); // TODO: parse data
}
async createMediaDbNote(modal: () => Promise<MediaTypeModel>): Promise<void> {
@ -54,9 +57,7 @@ export default class MediaDbPlugin extends Plugin {
data = await this.apiManager.queryDetailedInfo(data);
console.log(data);
data.toMetaData();
// console.log(data);
let fileContent = `---\n${data.toMetaData()}---\n`;
@ -68,10 +69,14 @@ export default class MediaDbPlugin extends Plugin {
templateFile = this.app.vault.getFiles().filter((f: TFile) => f.name === this.settings.seriesTemplate).first();
} else if (data.type === 'game' && this.settings.gameTemplate) {
templateFile = this.app.vault.getFiles().filter((f: TFile) => f.name === this.settings.gameTemplate).first();
} else if (data.type === 'wiki' && this.settings.wikiTemplate) {
templateFile = this.app.vault.getFiles().filter((f: TFile) => f.name === this.settings.wikiTemplate).first();
} else if (data.type === 'musicRelease' && this.settings.musicReleaseTemplate) {
templateFile = this.app.vault.getFiles().filter((f: TFile) => f.name === this.settings.musicReleaseTemplate).first();
}
if (templateFile) {
let template = await this.app.vault.read(templateFile);
let template = await this.app.vault.cachedRead(templateFile);
// console.log(template);
if (this.settings.templates) {
template = replaceTags(template, data);
@ -84,9 +89,9 @@ export default class MediaDbPlugin extends Plugin {
const targetFile = await this.app.vault.create(filePath, fileContent);
// open file
const activeLeaf = this.app.workspace.getLeaf();
const activeLeaf = this.app.workspace.getUnpinnedLeaf();
if (!activeLeaf) {
console.warn('No active leaf');
console.warn('MDB | no active leaf, not opening media db note');
return;
}
await activeLeaf.openFile(targetFile, {state: {mode: 'source'}});

View file

@ -1,6 +1,7 @@
import {App, ButtonComponent, Component, Modal, Notice, Setting, TextComponent, ToggleComponent} from 'obsidian';
import {MediaTypeModel} from '../models/MediaTypeModel';
import {APIManager} from '../api/APIManager';
import {debugLog} from '../utils/Utils';
export class MediaDbAdvancedSearchModal extends Modal {
query: string;
@ -28,10 +29,10 @@ export class MediaDbAdvancedSearchModal extends Modal {
async search(): Promise<MediaTypeModel[]> {
console.log(this.selectedApis);
debugLog(this.selectedApis);
if (!this.query || this.query.length < 5) {
new Notice('MDB: Query to short');
if (!this.query || this.query.length < 3) {
new Notice('MDB | Query to short');
return;
}
@ -43,7 +44,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
}
if (selectedAPICount === 0) {
new Notice('MDB: No API selected');
new Notice('MDB | No API selected');
return;
}
@ -53,12 +54,9 @@ export class MediaDbAdvancedSearchModal extends Modal {
this.searchBtn.setDisabled(false);
this.searchBtn.setButtonText('Searching...');
console.log('MDB | query started with ' + this.query);
console.log(`MDB | query started with title ${this.query}`);
const res = await this.apiManager.query(this.query, this.selectedApis);
// console.log(res)
this.onSubmit(null, res);
} catch (e) {
this.onSubmit(e);

View file

@ -1,6 +1,7 @@
import {App, ButtonComponent, DropdownComponent, Modal, Notice, Setting, TextComponent} from 'obsidian';
import {MediaTypeModel} from '../models/MediaTypeModel';
import {APIManager} from '../api/APIManager';
import {debugLog} from '../utils/Utils';
export class MediaDbIdSearchModal extends Modal {
query: string;
@ -25,15 +26,15 @@ export class MediaDbIdSearchModal extends Modal {
async search(): Promise<MediaTypeModel> {
console.log(this.selectedApi);
debugLog(this.selectedApi);
if (!this.query) {
new Notice('MDB: no Id entered');
new Notice('MDB | no Id entered');
return;
}
if (!this.selectedApi) {
new Notice('MDB: No API selected');
new Notice('MDB | No API selected');
return;
}
@ -43,16 +44,13 @@ export class MediaDbIdSearchModal extends Modal {
this.searchBtn.setDisabled(false);
this.searchBtn.setButtonText('Searching...');
console.log('MDB | query started with id ' + this.query);
console.log(`MDB | query started with id ${this.query}`);
const api = this.apiManager.getApiByName(this.selectedApi);
if (!api) {
this.onSubmit(new Error('the selected api does not exist'));
}
const res = await api.getById({id: this.query} as MediaTypeModel); // TODO: fix jank
// console.log(res)
this.onSubmit(null, res);
} catch (e) {
this.onSubmit(e);

View file

@ -1,5 +1,6 @@
import {MediaTypeModel} from './MediaTypeModel';
import {stringifyYaml} from 'obsidian';
import {mediaDbTag} from '../utils/Utils';
export class GameModel extends MediaTypeModel {
@ -29,11 +30,15 @@ export class GameModel extends MediaTypeModel {
}
toMetaData(): string {
return stringifyYaml(this);
return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
}
getFileName(): string {
return this.title + (this.year ? ` (${this.year})` : '');
}
getTags(): string[] {
return [mediaDbTag, 'game'];
}
}

View file

@ -10,4 +10,6 @@ export abstract class MediaTypeModel {
abstract toMetaData(): string;
abstract getFileName(): string;
abstract getTags(): string[];
}

View file

@ -1,5 +1,6 @@
import {MediaTypeModel} from './MediaTypeModel';
import {stringifyYaml} from 'obsidian';
import {mediaDbTag} from '../utils/Utils';
export class MovieModel extends MediaTypeModel {
@ -32,11 +33,15 @@ export class MovieModel extends MediaTypeModel {
}
toMetaData(): string {
return stringifyYaml(this);
return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
}
getFileName(): string {
return this.title + (this.year ? ` (${this.year})` : '');
}
getTags(): string[] {
return [mediaDbTag, 'tv', 'movie'];
}
}

View file

@ -0,0 +1,40 @@
import {MediaTypeModel} from './MediaTypeModel';
import {stringifyYaml} from 'obsidian';
import {mediaDbTag} from '../utils/Utils';
export class MusicReleaseModel extends MediaTypeModel {
type: string;
title: string;
englishTitle: string;
year: string;
dataSource: string;
url: string;
id: string;
genres: string[];
artists: string[];
subType: string;
rating: number;
personalRating: number;
constructor(obj: any = {}) {
super();
Object.assign(this, obj);
}
toMetaData(): string {
return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
}
getFileName(): string {
return this.title + ' (' + this.artists.join(', ') + ' - ' + this.year + ' - ' + this.subType + ')';
}
getTags(): string[] {
return [mediaDbTag, 'music', 'album'];
}
}

View file

@ -1,5 +1,6 @@
import {MediaTypeModel} from './MediaTypeModel';
import {stringifyYaml} from 'obsidian';
import {mediaDbTag} from '../utils/Utils';
export class SeriesModel extends MediaTypeModel {
@ -35,11 +36,15 @@ export class SeriesModel extends MediaTypeModel {
}
toMetaData(): string {
return stringifyYaml(this);
return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
}
getFileName(): string {
return this.title;
return this.title + ' (' + this.year + ')';
}
getTags(): string[] {
return [mediaDbTag, 'tv', 'series'];
}
}

38
src/models/WikiModel.ts Normal file
View file

@ -0,0 +1,38 @@
import {MediaTypeModel} from './MediaTypeModel';
import {stringifyYaml} from 'obsidian';
import {mediaDbTag} from '../utils/Utils';
export class WikiModel extends MediaTypeModel {
type: string;
title: string;
englishTitle: string;
year: string;
dataSource: string;
url: string;
id: string;
wikiUrl: string;
lastUpdated: string;
length: number;
constructor(obj: any = {}) {
super();
Object.assign(this, obj);
}
toMetaData(): string {
return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
}
getFileName(): string {
return this.title;
}
getTags(): string[] {
return [mediaDbTag, 'wiki'];
}
}

View file

@ -12,6 +12,8 @@ export interface MediaDbPluginSettings {
movieTemplate: string,
seriesTemplate: string,
gameTemplate: string,
wikiTemplate: string,
musicReleaseTemplate: string,
templates: boolean,
}
@ -22,6 +24,8 @@ export const DEFAULT_SETTINGS: MediaDbPluginSettings = {
movieTemplate: '',
seriesTemplate: '',
gameTemplate: '',
wikiTemplate: '',
musicReleaseTemplate: '',
templates: true,
};
@ -103,6 +107,32 @@ export class MediaDbSettingTab extends PluginSettingTab {
});
});
new Setting(containerEl)
.setName('Wiki template')
.setDesc('Template file to be used when creating a new note for a wiki entry.')
.addSearch(cb => {
new FileSuggest(this.app, cb.inputEl);
cb.setPlaceholder('Example: wikiTemplate.md')
.setValue(this.plugin.settings.wikiTemplate)
.onChange(data => {
this.plugin.settings.wikiTemplate = data;
this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Music Release template')
.setDesc('Template file to be used when creating a new note for a music release.')
.addSearch(cb => {
new FileSuggest(this.app, cb.inputEl);
cb.setPlaceholder('Example: musicReleaseTemplate.md')
.setValue(this.plugin.settings.musicReleaseTemplate)
.onChange(data => {
this.plugin.settings.musicReleaseTemplate = data;
this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('OMDb API key')
.setDesc('API key for "www.omdbapi.com".')

View file

@ -1,18 +1,24 @@
import {MediaTypeModel} from '../models/MediaTypeModel';
export const pluginName: string = 'obsidian-media-db-plugin';
export const contactEmail: string = 'm.projects.code@gmail.com';
export const mediaDbTag: string = 'mediaDB';
export const mediaDbVersion: string = '0.1.8';
export const debug: boolean = false;
export function wrapAround(value: number, size: number): number {
return ((value % size) + size) % size;
}
export function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
export function debugLog(o: any): void {
if (debug) {
console.log(o);
}
}
export function getFileName(item: MediaTypeModel) {
return replaceIllegalFileNameCharactersInString(item.year ? `${item.title} (${item.year})` : `${item.title}`);
}
export function replaceIllegalFileNameCharactersInString(string: string) {
export function replaceIllegalFileNameCharactersInString(string: string): string {
return string.replace(/[\\,#%&{}/*<>$"@.?]*/g, '').replace(/:+/g, ' -');
}
@ -35,7 +41,7 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel): string {
let obj = traverseMetaData(path, mediaTypeModel);
if (obj === undefined) {
return '{{ INVALID TEMPLATE TAG }}';
return '{{ INVALID TEMPLATE TAG - object undefined }}';
}
return obj;
@ -47,7 +53,7 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel): string {
let obj = traverseMetaData(path, mediaTypeModel);
if (obj === undefined) {
return '{{ INVALID TEMPLATE TAG }}';
return '{{ INVALID TEMPLATE TAG - object undefined }}';
}
if (operator === 'LIST') {