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 ## 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 ### Features
#### Search by Title #### Search by Title
@ -34,45 +34,36 @@ I also published my own templates [here](https://github.com/mProjectsCode/obsidi
- movies (including specials) - movies (including specials)
- series (including OVAs) - series (including OVAs)
- games - games
- music releases
- wiki articles
### Currently supported APIs: ### Currently supported APIs:
- [Jikan](https://jikan.moe/), an API that uses [My Anime List](https://myanimelist.net) | Name | Description | Supported formats | Authentification | Rate limiting | SFW filter support |
- supported formats | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------- | ------------------------------ | ------------------ |
- series | [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 |
- movies | [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 |
- specials | [MusicBrainz](https://musicbrainz.org/) | MusicBrainz is an API that offers information about music releases. | music releases | No | 50 per second | No |
- OVAs | [Wikipedia](https://en.wikipedia.org/wiki/Main_Page) | The Wikipedia API allows acces to all Wikipedia articles. | wiki articles | No | None | No | | | | | | |
- authentication
- no authentication or API key needed #### Notes
- SFW filter support - [Jikan](https://jikan.moe/)
- 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 - 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 - 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" - e.g. instead of "Demon Slayer" you have to search "Kimetsu no Yaiba"
- the API is rate limited to
- 60 requests per minute #### Search by ID
- 3 requests per second - [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/) - [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) - 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 - 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` - e.g. for "Rogue One" the URL looks like this `https://www.imdb.com/title/tt3748528/` so the ID is `tt3748528`
- notes - [MusicBrainz](https://musicbrainz.org/)
- the api is rate limited to 1000 requests a day - 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 ### Contributions
Thank you for wanting to contribute to this project. Thank you for wanting to contribute to this project.

View file

@ -1,9 +1,9 @@
{ {
"id": "obsidian-media-db-plugin", "id": "obsidian-media-db-plugin",
"name": "Media DB Plugin", "name": "Media DB Plugin",
"version": "0.1.7", "version": "0.1.8",
"minAppVersion": "0.14.0", "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", "author": "Moritz Jung",
"authorUrl": "https://mprojectscode.github.io/", "authorUrl": "https://mprojectscode.github.io/",
"isDesktopOnly": false "isDesktopOnly": false

View file

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

View file

@ -19,6 +19,7 @@ export abstract class APIModel {
return this.types.contains(type); return this.types.contains(type);
} }
// for future use (https://github.com/mProjectsCode/obsidian-media-db-plugin/issues/5)
hasTypeOverlap(types: string[]): boolean { hasTypeOverlap(types: string[]): boolean {
for (const type of types) { for (const type of types) {
if (this.hasType(type)) { 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 {MovieModel} from '../../models/MovieModel';
import MediaDbPlugin from '../../main'; import MediaDbPlugin from '../../main';
import {SeriesModel} from '../../models/SeriesModel'; import {SeriesModel} from '../../models/SeriesModel';
import {debugLog} from '../../utils/Utils';
export class MALAPI extends APIModel { export class MALAPI extends APIModel {
plugin: MediaDbPlugin; plugin: MediaDbPlugin;
@ -24,18 +25,18 @@ export class MALAPI extends APIModel {
} }
async searchByTitle(title: string): Promise<MediaTypeModel[]> { 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); const fetchData = await fetch(searchUrl);
console.log(fetchData); debugLog(fetchData);
if (fetchData.status !== 200) { 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(); const data = await fetchData.json();
console.log(data); debugLog(data);
let ret: MediaTypeModel[] = []; let ret: MediaTypeModel[] = [];
@ -69,16 +70,17 @@ export class MALAPI extends APIModel {
} }
async getById(item: MediaTypeModel): Promise<MediaTypeModel> { 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 searchUrl = `https://api.jikan.moe/v4/anime/${item.id}`;
const fetchData = await fetch(searchUrl); const fetchData = await fetch(searchUrl);
if (fetchData.status !== 200) { 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(); const data = await fetchData.json();
console.log(data); debugLog(data);
const result = data.data; const result = data.data;
const type = this.typeMappings.get(result.type.toLowerCase()); 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 MediaDbPlugin from '../../main';
import {SeriesModel} from '../../models/SeriesModel'; import {SeriesModel} from '../../models/SeriesModel';
import {GameModel} from '../../models/GameModel'; import {GameModel} from '../../models/GameModel';
import {debugLog} from '../../utils/Utils';
export class OMDbAPI extends APIModel { export class OMDbAPI extends APIModel {
plugin: MediaDbPlugin; plugin: MediaDbPlugin;
@ -24,29 +25,28 @@ export class OMDbAPI extends APIModel {
} }
async searchByTitle(title: string): Promise<MediaTypeModel[]> { async searchByTitle(title: string): Promise<MediaTypeModel[]> {
console.log(`MDB | api "${this.apiName}" queried`); console.log(`MDB | api "${this.apiName}" queried by Title`);
const searchUrl = `http://www.omdbapi.com/?s=${title}&apikey=${this.plugin.settings.OMDbKey}`;
const searchUrl = `http://www.omdbapi.com/?s=${encodeURIComponent(title)}&apikey=${this.plugin.settings.OMDbKey}`;
const fetchData = await fetch(searchUrl); const fetchData = await fetch(searchUrl);
if (fetchData.status === 401) { 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) { 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(); const data = await fetchData.json();
if (data.Response === 'False') { 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) { if (!data.Search) {
return []; return [];
} }
console.log(data.Search); debugLog(data.Search);
let ret: MediaTypeModel[] = []; let ret: MediaTypeModel[] = [];
@ -89,23 +89,23 @@ export class OMDbAPI extends APIModel {
} }
async getById(item: MediaTypeModel): Promise<MediaTypeModel> { 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 searchUrl = `http://www.omdbapi.com/?i=${item.id}&apikey=${this.plugin.settings.OMDbKey}`;
const fetchData = await fetch(searchUrl); const fetchData = await fetch(searchUrl);
if (fetchData.status === 401) { 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) { 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(); const result = await fetchData.json();
debugLog(result);
console.log(result);
if (result.Response === 'False') { 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()); 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 {Notice, Plugin, TFile} from 'obsidian';
import {DEFAULT_SETTINGS, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings'; import {DEFAULT_SETTINGS, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings';
import {APIManager} from './api/APIManager'; import {APIManager} from './api/APIManager';
import {TestAPI} from './api/apis/TestAPI';
import {MediaTypeModel} from './models/MediaTypeModel'; import {MediaTypeModel} from './models/MediaTypeModel';
import {replaceIllegalFileNameCharactersInString, replaceTags} from './utils/Utils'; import {replaceIllegalFileNameCharactersInString, replaceTags} from './utils/Utils';
import {OMDbAPI} from './api/apis/OMDbAPI'; import {OMDbAPI} from './api/apis/OMDbAPI';
@ -9,6 +8,8 @@ import {MediaDbAdvancedSearchModal} from './modals/MediaDbAdvancedSearchModal';
import {MediaDbSearchResultModal} from './modals/MediaDbSearchResultModal'; import {MediaDbSearchResultModal} from './modals/MediaDbSearchResultModal';
import {MALAPI} from './api/apis/MALAPI'; import {MALAPI} from './api/apis/MALAPI';
import {MediaDbIdSearchModal} from './modals/MediaDbIdSearchModal'; import {MediaDbIdSearchModal} from './modals/MediaDbIdSearchModal';
import {WikipediaAPI} from './api/apis/WikipediaAPI';
import {MusicBrainzAPI} from './api/apis/MusicBrainzAPI';
export default class MediaDbPlugin extends Plugin { export default class MediaDbPlugin extends Plugin {
settings: MediaDbPluginSettings; settings: MediaDbPluginSettings;
@ -42,9 +43,11 @@ export default class MediaDbPlugin extends Plugin {
this.apiManager = new APIManager(); this.apiManager = new APIManager();
// register APIs // register APIs
this.apiManager.registerAPI(new TestAPI());
this.apiManager.registerAPI(new OMDbAPI(this)); this.apiManager.registerAPI(new OMDbAPI(this));
this.apiManager.registerAPI(new MALAPI(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> { async createMediaDbNote(modal: () => Promise<MediaTypeModel>): Promise<void> {
@ -54,9 +57,7 @@ export default class MediaDbPlugin extends Plugin {
data = await this.apiManager.queryDetailedInfo(data); data = await this.apiManager.queryDetailedInfo(data);
console.log(data); // console.log(data);
data.toMetaData();
let fileContent = `---\n${data.toMetaData()}---\n`; 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(); templateFile = this.app.vault.getFiles().filter((f: TFile) => f.name === this.settings.seriesTemplate).first();
} else if (data.type === 'game' && this.settings.gameTemplate) { } else if (data.type === 'game' && this.settings.gameTemplate) {
templateFile = this.app.vault.getFiles().filter((f: TFile) => f.name === this.settings.gameTemplate).first(); 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) { if (templateFile) {
let template = await this.app.vault.read(templateFile); let template = await this.app.vault.cachedRead(templateFile);
// console.log(template); // console.log(template);
if (this.settings.templates) { if (this.settings.templates) {
template = replaceTags(template, data); template = replaceTags(template, data);
@ -84,9 +89,9 @@ export default class MediaDbPlugin extends Plugin {
const targetFile = await this.app.vault.create(filePath, fileContent); const targetFile = await this.app.vault.create(filePath, fileContent);
// open file // open file
const activeLeaf = this.app.workspace.getLeaf(); const activeLeaf = this.app.workspace.getUnpinnedLeaf();
if (!activeLeaf) { if (!activeLeaf) {
console.warn('No active leaf'); console.warn('MDB | no active leaf, not opening media db note');
return; return;
} }
await activeLeaf.openFile(targetFile, {state: {mode: 'source'}}); 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 {App, ButtonComponent, Component, Modal, Notice, Setting, TextComponent, ToggleComponent} from 'obsidian';
import {MediaTypeModel} from '../models/MediaTypeModel'; import {MediaTypeModel} from '../models/MediaTypeModel';
import {APIManager} from '../api/APIManager'; import {APIManager} from '../api/APIManager';
import {debugLog} from '../utils/Utils';
export class MediaDbAdvancedSearchModal extends Modal { export class MediaDbAdvancedSearchModal extends Modal {
query: string; query: string;
@ -28,10 +29,10 @@ export class MediaDbAdvancedSearchModal extends Modal {
async search(): Promise<MediaTypeModel[]> { async search(): Promise<MediaTypeModel[]> {
console.log(this.selectedApis); debugLog(this.selectedApis);
if (!this.query || this.query.length < 5) { if (!this.query || this.query.length < 3) {
new Notice('MDB: Query to short'); new Notice('MDB | Query to short');
return; return;
} }
@ -43,7 +44,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
} }
if (selectedAPICount === 0) { if (selectedAPICount === 0) {
new Notice('MDB: No API selected'); new Notice('MDB | No API selected');
return; return;
} }
@ -53,12 +54,9 @@ export class MediaDbAdvancedSearchModal extends Modal {
this.searchBtn.setDisabled(false); this.searchBtn.setDisabled(false);
this.searchBtn.setButtonText('Searching...'); 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); const res = await this.apiManager.query(this.query, this.selectedApis);
// console.log(res)
this.onSubmit(null, res); this.onSubmit(null, res);
} catch (e) { } catch (e) {
this.onSubmit(e); this.onSubmit(e);

View file

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

View file

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

View file

@ -1,5 +1,6 @@
import {MediaTypeModel} from './MediaTypeModel'; import {MediaTypeModel} from './MediaTypeModel';
import {stringifyYaml} from 'obsidian'; import {stringifyYaml} from 'obsidian';
import {mediaDbTag} from '../utils/Utils';
export class MovieModel extends MediaTypeModel { export class MovieModel extends MediaTypeModel {
@ -32,11 +33,15 @@ export class MovieModel extends MediaTypeModel {
} }
toMetaData(): string { toMetaData(): string {
return stringifyYaml(this); return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
} }
getFileName(): string { getFileName(): string {
return this.title + (this.year ? ` (${this.year})` : ''); 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 {MediaTypeModel} from './MediaTypeModel';
import {stringifyYaml} from 'obsidian'; import {stringifyYaml} from 'obsidian';
import {mediaDbTag} from '../utils/Utils';
export class SeriesModel extends MediaTypeModel { export class SeriesModel extends MediaTypeModel {
@ -35,11 +36,15 @@ export class SeriesModel extends MediaTypeModel {
} }
toMetaData(): string { toMetaData(): string {
return stringifyYaml(this); return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
} }
getFileName(): string { 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, movieTemplate: string,
seriesTemplate: string, seriesTemplate: string,
gameTemplate: string, gameTemplate: string,
wikiTemplate: string,
musicReleaseTemplate: string,
templates: boolean, templates: boolean,
} }
@ -22,6 +24,8 @@ export const DEFAULT_SETTINGS: MediaDbPluginSettings = {
movieTemplate: '', movieTemplate: '',
seriesTemplate: '', seriesTemplate: '',
gameTemplate: '', gameTemplate: '',
wikiTemplate: '',
musicReleaseTemplate: '',
templates: true, 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) new Setting(containerEl)
.setName('OMDb API key') .setName('OMDb API key')
.setDesc('API key for "www.omdbapi.com".') .setDesc('API key for "www.omdbapi.com".')

View file

@ -1,18 +1,24 @@
import {MediaTypeModel} from '../models/MediaTypeModel'; 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 { export function wrapAround(value: number, size: number): number {
return ((value % size) + size) % size; return ((value % size) + size) % size;
} }
export function sleep(ms: number) { export function debugLog(o: any): void {
return new Promise(resolve => setTimeout(resolve, ms)); if (debug) {
console.log(o);
}
} }
export function getFileName(item: MediaTypeModel) { export function replaceIllegalFileNameCharactersInString(string: string): string {
return replaceIllegalFileNameCharactersInString(item.year ? `${item.title} (${item.year})` : `${item.title}`);
}
export function replaceIllegalFileNameCharactersInString(string: string) {
return string.replace(/[\\,#%&{}/*<>$"@.?]*/g, '').replace(/:+/g, ' -'); return string.replace(/[\\,#%&{}/*<>$"@.?]*/g, '').replace(/:+/g, ' -');
} }
@ -35,7 +41,7 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel): string {
let obj = traverseMetaData(path, mediaTypeModel); let obj = traverseMetaData(path, mediaTypeModel);
if (obj === undefined) { if (obj === undefined) {
return '{{ INVALID TEMPLATE TAG }}'; return '{{ INVALID TEMPLATE TAG - object undefined }}';
} }
return obj; return obj;
@ -47,7 +53,7 @@ function replaceTag(match: string, mediaTypeModel: MediaTypeModel): string {
let obj = traverseMetaData(path, mediaTypeModel); let obj = traverseMetaData(path, mediaTypeModel);
if (obj === undefined) { if (obj === undefined) {
return '{{ INVALID TEMPLATE TAG }}'; return '{{ INVALID TEMPLATE TAG - object undefined }}';
} }
if (operator === 'LIST') { if (operator === 'LIST') {