diff --git a/README.md b/README.md index 033cbd8..0d0fd4b 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/manifest.json b/manifest.json index d860fef..86d0d3f 100644 --- a/manifest.json +++ b/manifest.json @@ -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 diff --git a/src/api/APIManager.ts b/src/api/APIManager.ts index 665e8a2..c20c677 100644 --- a/src/api/APIManager.ts +++ b/src/api/APIManager.ts @@ -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 { - 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; } diff --git a/src/api/APIModel.ts b/src/api/APIModel.ts index 4d042c8..0cbf5fa 100644 --- a/src/api/APIModel.ts +++ b/src/api/APIModel.ts @@ -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)) { diff --git a/src/api/apis/LocGovAPI.ts b/src/api/apis/LocGovAPI.ts new file mode 100644 index 0000000..fd0b2d5 --- /dev/null +++ b/src/api/apis/LocGovAPI.ts @@ -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; + + 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(); + // this.typeMappings.set('movie', 'movie'); + } + + async searchByTitle(title: string): Promise { + 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 { + 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; + } +} diff --git a/src/api/apis/MALAPI.ts b/src/api/apis/MALAPI.ts index 8057ab7..67c5790 100644 --- a/src/api/apis/MALAPI.ts +++ b/src/api/apis/MALAPI.ts @@ -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 { - 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 { + 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()); diff --git a/src/api/apis/MusicBrainzAPI.ts b/src/api/apis/MusicBrainzAPI.ts new file mode 100644 index 0000000..87ea855 --- /dev/null +++ b/src/api/apis/MusicBrainzAPI.ts @@ -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 { + 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 { + 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; + } +} diff --git a/src/api/apis/OMDbAPI.ts b/src/api/apis/OMDbAPI.ts index e3531db..4ae38f4 100644 --- a/src/api/apis/OMDbAPI.ts +++ b/src/api/apis/OMDbAPI.ts @@ -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 { - 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 { + 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()); diff --git a/src/api/apis/TestAPI.ts b/src/api/apis/TestAPI.ts deleted file mode 100644 index cdba595..0000000 --- a/src/api/apis/TestAPI.ts +++ /dev/null @@ -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 { - 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 { - return item; - } -} diff --git a/src/api/apis/WikipediaAPI.ts b/src/api/apis/WikipediaAPI.ts new file mode 100644 index 0000000..1387b65 --- /dev/null +++ b/src/api/apis/WikipediaAPI.ts @@ -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 { + 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 { + 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; + } +} diff --git a/src/main.ts b/src/main.ts index 5cad2c0..69c713b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -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): Promise { @@ -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'}}); diff --git a/src/modals/MediaDbAdvancedSearchModal.ts b/src/modals/MediaDbAdvancedSearchModal.ts index 08da542..1472f7d 100644 --- a/src/modals/MediaDbAdvancedSearchModal.ts +++ b/src/modals/MediaDbAdvancedSearchModal.ts @@ -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 { - 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); diff --git a/src/modals/MediaDbIdSearchModal.ts b/src/modals/MediaDbIdSearchModal.ts index bf7b5ce..0216746 100644 --- a/src/modals/MediaDbIdSearchModal.ts +++ b/src/modals/MediaDbIdSearchModal.ts @@ -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 { - 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); diff --git a/src/models/GameModel.ts b/src/models/GameModel.ts index 7740bae..8a807ba 100644 --- a/src/models/GameModel.ts +++ b/src/models/GameModel.ts @@ -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']; + } + } diff --git a/src/models/MediaTypeModel.ts b/src/models/MediaTypeModel.ts index 9f6c9f1..4963fb5 100644 --- a/src/models/MediaTypeModel.ts +++ b/src/models/MediaTypeModel.ts @@ -10,4 +10,6 @@ export abstract class MediaTypeModel { abstract toMetaData(): string; abstract getFileName(): string; + + abstract getTags(): string[]; } diff --git a/src/models/MovieModel.ts b/src/models/MovieModel.ts index 6b54613..e0ff845 100644 --- a/src/models/MovieModel.ts +++ b/src/models/MovieModel.ts @@ -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']; + } + } diff --git a/src/models/MusicReleaseModel.ts b/src/models/MusicReleaseModel.ts new file mode 100644 index 0000000..2561cf0 --- /dev/null +++ b/src/models/MusicReleaseModel.ts @@ -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']; + } + +} diff --git a/src/models/SeriesModel.ts b/src/models/SeriesModel.ts index d6ab109..205d51a 100644 --- a/src/models/SeriesModel.ts +++ b/src/models/SeriesModel.ts @@ -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']; } } diff --git a/src/models/WikiModel.ts b/src/models/WikiModel.ts new file mode 100644 index 0000000..cf5dea5 --- /dev/null +++ b/src/models/WikiModel.ts @@ -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']; + } + +} diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 8b85f32..cc8a967 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -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".') diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index aa385df..12927db 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -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 getFileName(item: MediaTypeModel) { - return replaceIllegalFileNameCharactersInString(item.year ? `${item.title} (${item.year})` : `${item.title}`); +export function debugLog(o: any): void { + if (debug) { + console.log(o); + } } -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') {