diff --git a/src/api/apis/BoardGameGeekAPI.ts b/src/api/apis/BoardGameGeekAPI.ts new file mode 100644 index 0000000..34cdc7b --- /dev/null +++ b/src/api/apis/BoardGameGeekAPI.ts @@ -0,0 +1,104 @@ +import {APIModel} from '../APIModel'; +import {MediaTypeModel} from '../../models/MediaTypeModel'; +import MediaDbPlugin from '../../main'; +import {BoardGameModel} from 'src/models/BoardGameModel'; +import {debugLog} from '../../utils/Utils'; +import {requestUrl} from 'obsidian'; +import {MediaType} from '../../utils/MediaType'; + +export class BoardGameGeekAPI extends APIModel { + plugin: MediaDbPlugin; + + constructor(plugin: MediaDbPlugin) { + super(); + + this.plugin = plugin; + this.apiName = 'BoardGameGeekAPI'; + this.apiDescription = 'A free API for BoardGameGeek things.'; + this.apiUrl = 'https://api.geekdo.com/xmlapi'; + this.types = ['boardgames']; + } + + async searchByTitle(title: string): Promise { + console.log(`MDB | api "${this.apiName}" queried by Title`); + + const searchUrl = `${this.apiUrl}/search?search=${encodeURIComponent(title)}`; + const fetchData = await requestUrl({ + url: searchUrl, + }); + + if (fetchData.status !== 200) { + throw Error(`MDB | Received status code ${fetchData.status} from an API.`); + } + + const data = fetchData.text; + const response = new window.DOMParser().parseFromString(data, "text/xml") + + debugLog(response); + + let ret: MediaTypeModel[] = []; + + for (const boardgame of Array.from(response.querySelectorAll("boardgame"))) { + const id = boardgame.attributes.getNamedItem("objectid").value; + const title = boardgame.querySelector("name").textContent; + const year = boardgame.querySelector("yearpublished").textContent; + + ret.push(new BoardGameModel({ + dataSource: this.apiName, + id, + title, + englishTitle: title, + year, + } as BoardGameModel)); + } + + return ret; + } + + async getById(id: string): Promise { + console.log(`MDB | api "${this.apiName}" queried by ID`); + + const searchUrl = `${this.apiUrl}/boardgame/${encodeURIComponent(id)}?stats=1`; + const fetchData = await requestUrl({ + url: searchUrl, + }); + + if (fetchData.status !== 200) { + throw Error(`MDB | Received status code ${fetchData.status} from an API.`); + } + + const data = fetchData.text; + const response = new window.DOMParser().parseFromString(data, "text/xml") + debugLog(response); + + const boardgame = response.querySelector("boardgame"); + const title = boardgame.querySelector("name").textContent; + const year = boardgame.querySelector("yearpublished").textContent; + const image = boardgame.querySelector("image").textContent; + const onlineRating = Number.parseFloat(boardgame.querySelector("statistics ratings average").textContent); + const genres = Array.from(boardgame.querySelectorAll("boardgamecategory")).map(n => n.textContent); + + const model = new BoardGameModel({ + type: MediaType.BoardGame, + title, + englishTitle: title, + year, + dataSource: this.apiName, + url: `https://boardgamegeek.com/boardgame/${id}`, + id, + + genres, + onlineRating, + image, + released: true, + + userData: { + played: false, + personalRating: 0, + }, + } as BoardGameModel); + + return model; + + } +} diff --git a/src/main.ts b/src/main.ts index c43124d..2196f27 100644 --- a/src/main.ts +++ b/src/main.ts @@ -12,6 +12,7 @@ import {WikipediaAPI} from './api/apis/WikipediaAPI'; import {MusicBrainzAPI} from './api/apis/MusicBrainzAPI'; import {MediaTypeManager} from './utils/MediaTypeManager'; import {SteamAPI} from './api/apis/SteamAPI'; +import {BoardGameGeekAPI} from './api/apis/BoardGameGeekAPI'; import {ModelPropertyMapper} from './settings/ModelPropertyMapper'; import {YAMLConverter} from './utils/YAMLConverter'; import {MediaDbFolderImportModal} from './modals/MediaDbFolderImportModal'; @@ -79,6 +80,7 @@ export default class MediaDbPlugin extends Plugin { this.apiManager.registerAPI(new WikipediaAPI(this)); this.apiManager.registerAPI(new MusicBrainzAPI(this)); this.apiManager.registerAPI(new SteamAPI(this)); + this.apiManager.registerAPI(new BoardGameGeekAPI(this)); // this.apiManager.registerAPI(new LocGovAPI(this)); // TODO: parse data this.mediaTypeManager = new MediaTypeManager(this.settings); diff --git a/src/models/BoardGameModel.ts b/src/models/BoardGameModel.ts new file mode 100644 index 0000000..3f5c1e4 --- /dev/null +++ b/src/models/BoardGameModel.ts @@ -0,0 +1,43 @@ +import {MediaTypeModel} from './MediaTypeModel'; +import {mediaDbTag} from '../utils/Utils'; +import {MediaType} from '../utils/MediaType'; + + +export class BoardGameModel extends MediaTypeModel { + type: string; + title: string; + englishTitle: string; + year: string; + dataSource: string; + url: string; + id: string; + + genres: string[]; + onlineRating: number; + image: string; + + released: boolean; + + userData: { + played: boolean; + personalRating: number; + }; + + + constructor(obj: any = {}) { + super(); + + Object.assign(this, obj); + + this.type = this.getMediaType(); + } + + getTags(): string[] { + return [mediaDbTag, 'boardgame']; + } + + getMediaType(): MediaType { + return MediaType.BoardGame; + } + +} diff --git a/src/settings/ModelPropertyMapper.ts b/src/settings/ModelPropertyMapper.ts index f9dc6ad..5385ef4 100644 --- a/src/settings/ModelPropertyMapper.ts +++ b/src/settings/ModelPropertyMapper.ts @@ -16,6 +16,7 @@ export class ModelPropertyMapper { this.conversionRulesMap.set(MediaType.Game, settings.gamePropertyConversionRules); this.conversionRulesMap.set(MediaType.Wiki, settings.wikiPropertyConversionRules); this.conversionRulesMap.set(MediaType.MusicRelease, settings.musicReleasePropertyConversionRules); + this.conversionRulesMap.set(MediaType.BoardGame, settings.boardgamePropertyConversionRules); } convertObject(obj: object): object { diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index e849809..b030311 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -18,18 +18,21 @@ export interface MediaDbPluginSettings { gameTemplate: string, wikiTemplate: string, musicReleaseTemplate: string, + boardgameTemplate: string, movieFileNameTemplate: string, seriesFileNameTemplate: string, gameFileNameTemplate: string, wikiFileNameTemplate: string, musicReleaseFileNameTemplate: string, + boardgameFileNameTemplate: string, moviePropertyConversionRules: string, seriesPropertyConversionRules: string, gamePropertyConversionRules: string, wikiPropertyConversionRules: string, musicReleasePropertyConversionRules: string, + boardgamePropertyConversionRules: string, } @@ -45,19 +48,21 @@ export const DEFAULT_SETTINGS: MediaDbPluginSettings = { gameTemplate: '', wikiTemplate: '', musicReleaseTemplate: '', + boardgameTemplate: '', movieFileNameTemplate: '{{ title }} ({{ year }})', seriesFileNameTemplate: '{{ title }} ({{ year }})', gameFileNameTemplate: '{{ title }} ({{ year }})', wikiFileNameTemplate: '{{ title }}', musicReleaseFileNameTemplate: '{{ title }} (by {{ ENUM:artists }} - {{ year }})', + boardgameFileNameTemplate: '{{ title }} ({{ year }})', moviePropertyConversionRules: '', seriesPropertyConversionRules: '', gamePropertyConversionRules: '', wikiPropertyConversionRules: '', musicReleasePropertyConversionRules: '', - + boardgamePropertyConversionRules: '', }; @@ -201,6 +206,19 @@ export class MediaDbSettingTab extends PluginSettingTab { this.plugin.saveSettings(); }); }); + + new Setting(containerEl) + .setName('Board Game template') + .setDesc('Template file to be used when creating a new note for a boardgame.') + .addSearch(cb => { + new FileSuggest(this.app, cb.inputEl); + cb.setPlaceholder('Example: boardgameTemplate.md') + .setValue(this.plugin.settings.boardgameTemplate) + .onChange(data => { + this.plugin.settings.boardgameTemplate = data; + this.plugin.saveSettings(); + }); + }); // endregion containerEl.createEl('h3', {text: 'File Name Settings'}); @@ -264,6 +282,18 @@ export class MediaDbSettingTab extends PluginSettingTab { this.plugin.saveSettings(); }); }); + + new Setting(containerEl) + .setName('Board Game file name template') + .setDesc('Template for the file name used when creating a new note for a boardgame.') + .addText(cb => { + cb.setPlaceholder(`Example: ${DEFAULT_SETTINGS.boardgameFileNameTemplate}`) + .setValue(this.plugin.settings.boardgameFileNameTemplate) + .onChange(data => { + this.plugin.settings.boardgameFileNameTemplate = data; + this.plugin.saveSettings(); + }); + }); // endregion containerEl.createEl('h3', {text: 'Property Mappings'}); @@ -327,6 +357,18 @@ export class MediaDbSettingTab extends PluginSettingTab { this.plugin.saveSettings(); }); }); + + new Setting(containerEl) + .setName('Board Game model property mappings') + .setDesc('Mappings for the property names of a boardgame.') + .addTextArea(cb => { + cb.setPlaceholder(`Example: \ntitle -> name\nyear -> releaseYear`) + .setValue(this.plugin.settings.boardgamePropertyConversionRules) + .onChange(data => { + this.plugin.settings.boardgamePropertyConversionRules = data; + this.plugin.saveSettings(); + }); + }); // endregion } diff --git a/src/utils/MediaType.ts b/src/utils/MediaType.ts index c4f1076..c36680c 100644 --- a/src/utils/MediaType.ts +++ b/src/utils/MediaType.ts @@ -4,4 +4,5 @@ export enum MediaType { Game = 'game', MusicRelease = 'musicRelease', Wiki = 'wiki', + BoardGame = 'boardgame', } diff --git a/src/utils/MediaTypeManager.ts b/src/utils/MediaTypeManager.ts index ba0bd90..eeb596d 100644 --- a/src/utils/MediaTypeManager.ts +++ b/src/utils/MediaTypeManager.ts @@ -24,6 +24,7 @@ export class MediaTypeManager { this.mediaFileNameTemplateMap.set(MediaType.Game, settings.gameFileNameTemplate); this.mediaFileNameTemplateMap.set(MediaType.Wiki, settings.wikiFileNameTemplate); this.mediaFileNameTemplateMap.set(MediaType.MusicRelease, settings.musicReleaseFileNameTemplate); + this.mediaFileNameTemplateMap.set(MediaType.BoardGame, settings.boardgameFileNameTemplate); this.mediaTemplateMap = new Map(); this.mediaTemplateMap.set(MediaType.Movie, settings.movieTemplate); @@ -31,6 +32,7 @@ export class MediaTypeManager { this.mediaTemplateMap.set(MediaType.Game, settings.gameTemplate); this.mediaTemplateMap.set(MediaType.Wiki, settings.wikiTemplate); this.mediaTemplateMap.set(MediaType.MusicRelease, settings.musicReleaseTemplate); + this.mediaTemplateMap.set(MediaType.BoardGame, settings.boardgameTemplate); } getFileName(mediaTypeModel: MediaTypeModel): string {