diff --git a/src/api/apis/OpenLibraryAPI.ts b/src/api/apis/OpenLibraryAPI.ts new file mode 100644 index 0000000..f46018d --- /dev/null +++ b/src/api/apis/OpenLibraryAPI.ts @@ -0,0 +1,90 @@ +import { APIModel } from '../APIModel'; +import { MediaTypeModel } from '../../models/MediaTypeModel'; +import MediaDbPlugin from '../../main'; +import { BookModel } from 'src/models/BookModel'; +import { requestUrl } from 'obsidian'; +import { MediaType } from '../../utils/MediaType'; + +export class OpenLibraryAPI extends APIModel { + plugin: MediaDbPlugin; + + constructor(plugin: MediaDbPlugin) { + super(); + + this.plugin = plugin; + this.apiName = 'OpenLibraryAPI'; + this.apiDescription = 'A free API for books'; + this.apiUrl = 'https://openlibrary.org/'; + this.types = [MediaType.Book]; +} + +async searchByTitle(title: string): Promise { + console.log(`MDB | api "${this.apiName}" queried by Title`); + + const searchUrl = `https://openlibrary.org/search.json?title=${encodeURIComponent(title)}&limit=20}`; + + const fetchData = await fetch(searchUrl); + console.debug(fetchData); + if (fetchData.status !== 200) { + throw Error(`MDB | Received status code ${fetchData.status} from an API.`); + } + const data = await fetchData.json(); + + console.debug(data); + + const ret: MediaTypeModel[] = []; + + for (const result of data.docs) { + ret.push( + new BookModel({ + subType: '', + title: result.title, + englishTitle: result.title_english ?? result.title, + year: result.year ?? result.aired?.prop?.from?.year ?? '', + dataSource: this.apiName, + id: result.mal_id, + } as BookModel) + ); + } + + return ret; +} + +async getById(id: string): Promise { + console.log(`MDB | api "${this.apiName}" queried by ID`); + + const searchUrl = `https://openlibrary.org/isbn/${encodeURIComponent(id)}.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(); + console.debug(data); + const result = data.data; + + const model = new BookModel({ + subType: '', + title: result.title, + year: new Date(result.publish_date.date).getFullYear().toString(), + dataSource: this.apiName, + url: `https://openlibrary.org` + result.key, + id: result.isbn_10, + + author: result.authors.key ?? 'unknown', + pages: result.number_of_pages ?? 'unknown', + image: `https://covers.openlibrary.org/b/isbn/` + result.isbn_10 + `-L.jpg` ?? '', + + released: true, + + userData: { + read: false, + lastRead: '', + personalRating: 0, + }, + } as BookModel); + + return; +} +} diff --git a/src/main.ts b/src/main.ts index a62e99c..a65015d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,6 +10,7 @@ import { MusicBrainzAPI } from './api/apis/MusicBrainzAPI'; import { MEDIA_TYPES, MediaTypeManager } from './utils/MediaTypeManager'; import { SteamAPI } from './api/apis/SteamAPI'; import { BoardGameGeekAPI } from './api/apis/BoardGameGeekAPI'; +import { OpenLibraryAPI } from './api/apis/OpenLibraryAPI'; import { PropertyMapper } from './settings/PropertyMapper'; import { YAMLConverter } from './utils/YAMLConverter'; import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal'; @@ -36,6 +37,7 @@ export default class MediaDbPlugin extends Plugin { this.apiManager.registerAPI(new MusicBrainzAPI(this)); this.apiManager.registerAPI(new SteamAPI(this)); this.apiManager.registerAPI(new BoardGameGeekAPI(this)); + this.apiManager.registerAPI(new OpenLibraryAPI(this)); // this.apiManager.registerAPI(new LocGovAPI(this)); // TODO: parse data this.mediaTypeManager = new MediaTypeManager(); diff --git a/src/models/BookModel.ts b/src/models/BookModel.ts new file mode 100644 index 0000000..a36ba21 --- /dev/null +++ b/src/models/BookModel.ts @@ -0,0 +1,53 @@ +import { MediaTypeModel } from './MediaTypeModel'; +import { mediaDbTag, migrateObject } from '../utils/Utils'; +import { MediaType } from '../utils/MediaType'; + +export class BookModel extends MediaTypeModel { + author: string; + pages: string; + image: string; + + released: boolean; + + userData: { + read: boolean; + lastRead: string; + personalRating: number; + }; + + constructor(obj: any = {}) { + super(); + + this.author = undefined; + this.pages = undefined; + this.image = undefined; + + this.released = undefined; + + this.userData = { + read: undefined, + lastRead: undefined, + personalRating: undefined, + }; + + migrateObject(this, obj, this); + + if (!obj.hasOwnProperty('userData')) { + migrateObject(this.userData, obj, this.userData); + } + + this.type = this.getMediaType(); + } + + getTags(): string[] { + return [mediaDbTag, 'book']; + } + + getMediaType(): MediaType { + return MediaType.Book; + } + + getSummary(): string { + return this.englishTitle + ' (' + this.year + ')'; + } +} diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 74fb9b9..f7258e7 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -22,6 +22,7 @@ export interface MediaDbPluginSettings { wikiTemplate: string; musicReleaseTemplate: string; boardgameTemplate: string; + bookTemplate: string; movieFileNameTemplate: string; seriesFileNameTemplate: string; @@ -29,6 +30,7 @@ export interface MediaDbPluginSettings { wikiFileNameTemplate: string; musicReleaseFileNameTemplate: string; boardgameFileNameTemplate: string; + bookFileNameTemplate: string; moviePropertyConversionRules: string; seriesPropertyConversionRules: string; @@ -36,6 +38,7 @@ export interface MediaDbPluginSettings { wikiPropertyConversionRules: string; musicReleasePropertyConversionRules: string; boardgamePropertyConversionRules: string; + bookPropertyConversionRules: string; movieFolder: string; seriesFolder: string; @@ -43,6 +46,7 @@ export interface MediaDbPluginSettings { wikiFolder: string; musicReleaseFolder: string; boardgameFolder: string; + bookFolder: string; propertyMappingModels: PropertyMappingModel[]; } @@ -60,6 +64,7 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = { wikiTemplate: '', musicReleaseTemplate: '', boardgameTemplate: '', + bookTemplate: '', movieFileNameTemplate: '{{ title }} ({{ year }})', seriesFileNameTemplate: '{{ title }} ({{ year }})', @@ -67,6 +72,7 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = { wikiFileNameTemplate: '{{ title }}', musicReleaseFileNameTemplate: '{{ title }} (by {{ ENUM:artists }} - {{ year }})', boardgameFileNameTemplate: '{{ title }} ({{ year }})', + bookFileNameTemplate: '{{ title }} ({{ year }})', moviePropertyConversionRules: '', seriesPropertyConversionRules: '', @@ -74,6 +80,7 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = { wikiPropertyConversionRules: '', musicReleasePropertyConversionRules: '', boardgamePropertyConversionRules: '', + bookPropertyConversionRules: '', movieFolder: 'Media DB/movies', seriesFolder: 'Media DB/series', @@ -81,6 +88,7 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = { wikiFolder: 'Media DB/wiki', musicReleaseFolder: 'Media DB/music', boardgameFolder: 'Media DB/boardgames', + bookFolder: 'Media DB/books', propertyMappingModels: [], }; @@ -194,7 +202,7 @@ export class MediaDbSettingTab extends PluginSettingTab { // region new file location new Setting(containerEl) .setName('Movie Folder') - .setDesc('Where newly imported movies should be places.') + .setDesc('Where newly imported movies should be placed.') .addSearch(cb => { new FolderSuggest(this.app, cb.inputEl); cb.setPlaceholder(DEFAULT_SETTINGS.movieFolder) @@ -207,7 +215,7 @@ export class MediaDbSettingTab extends PluginSettingTab { new Setting(containerEl) .setName('Series Folder') - .setDesc('Where newly imported series should be places.') + .setDesc('Where newly imported series should be placed.') .addSearch(cb => { new FolderSuggest(this.app, cb.inputEl); cb.setPlaceholder(DEFAULT_SETTINGS.seriesFolder) @@ -220,7 +228,7 @@ export class MediaDbSettingTab extends PluginSettingTab { new Setting(containerEl) .setName('Game Folder') - .setDesc('Where newly imported games should be places.') + .setDesc('Where newly imported games should be placed.') .addSearch(cb => { new FolderSuggest(this.app, cb.inputEl); cb.setPlaceholder(DEFAULT_SETTINGS.gameFolder) @@ -233,7 +241,7 @@ export class MediaDbSettingTab extends PluginSettingTab { new Setting(containerEl) .setName('Wiki Folder') - .setDesc('Where newly imported wiki articles should be places.') + .setDesc('Where newly imported wiki articles should be placed.') .addSearch(cb => { new FolderSuggest(this.app, cb.inputEl); cb.setPlaceholder(DEFAULT_SETTINGS.wikiFolder) @@ -246,7 +254,7 @@ export class MediaDbSettingTab extends PluginSettingTab { new Setting(containerEl) .setName('Music Folder') - .setDesc('Where newly imported music should be places.') + .setDesc('Where newly imported music should be placed.') .addSearch(cb => { new FolderSuggest(this.app, cb.inputEl); cb.setPlaceholder(DEFAULT_SETTINGS.musicReleaseFolder) @@ -269,6 +277,18 @@ export class MediaDbSettingTab extends PluginSettingTab { this.plugin.saveSettings(); }); }); + new Setting(containerEl) + .setName('Book Folder') + .setDesc('Where newly imported books should be placed.') + .addSearch(cb => { + new FolderSuggest(this.app, cb.inputEl); + cb.setPlaceholder(DEFAULT_SETTINGS.bookFolder) + .setValue(this.plugin.settings.bookFolder) + .onChange(data => { + this.plugin.settings.bookFolder = data; + this.plugin.saveSettings(); + }); + }); // endregion containerEl.createEl('h3', { text: 'Template Settings' }); @@ -350,6 +370,19 @@ export class MediaDbSettingTab extends PluginSettingTab { this.plugin.saveSettings(); }); }); + + new Setting(containerEl) + .setName('Book template') + .setDesc('Template file to be used when creating a new note for a book.') + .addSearch(cb => { + new FileSuggest(this.app, cb.inputEl); + cb.setPlaceholder('Example: bookTemplate.md') + .setValue(this.plugin.settings.bookTemplate) + .onChange(data => { + this.plugin.settings.bookTemplate = data; + this.plugin.saveSettings(); + }); + }); // endregion containerEl.createEl('h3', { text: 'File Name Settings' }); @@ -425,6 +458,18 @@ export class MediaDbSettingTab extends PluginSettingTab { this.plugin.saveSettings(); }); }); + + new Setting(containerEl) + .setName('Book file name template') + .setDesc('Template for the file name used when creating a new note for a book.') + .addText(cb => { + cb.setPlaceholder(`Example: ${DEFAULT_SETTINGS.bookFileNameTemplate}`) + .setValue(this.plugin.settings.bookFileNameTemplate) + .onChange(data => { + this.plugin.settings.bookFileNameTemplate = data; + this.plugin.saveSettings(); + }); + }); // endregion // region Property Mappings diff --git a/src/utils/MediaType.ts b/src/utils/MediaType.ts index c36680c..cae316a 100644 --- a/src/utils/MediaType.ts +++ b/src/utils/MediaType.ts @@ -5,4 +5,5 @@ export enum MediaType { MusicRelease = 'musicRelease', Wiki = 'wiki', BoardGame = 'boardgame', + Book = 'book', } diff --git a/src/utils/MediaTypeManager.ts b/src/utils/MediaTypeManager.ts index 58a07ef..b898991 100644 --- a/src/utils/MediaTypeManager.ts +++ b/src/utils/MediaTypeManager.ts @@ -9,8 +9,9 @@ import { GameModel } from '../models/GameModel'; import { WikiModel } from '../models/WikiModel'; import { MusicReleaseModel } from '../models/MusicReleaseModel'; import { BoardGameModel } from '../models/BoardGameModel'; +import { BookModel } from '../models/BookModel'; -export const MEDIA_TYPES: MediaType[] = [MediaType.Movie, MediaType.Series, MediaType.Game, MediaType.Wiki, MediaType.MusicRelease, MediaType.BoardGame]; +export const MEDIA_TYPES: MediaType[] = [MediaType.Movie, MediaType.Series, MediaType.Game, MediaType.Wiki, MediaType.MusicRelease, MediaType.BoardGame, MediaType.Book]; export class MediaTypeManager { mediaFileNameTemplateMap: Map; @@ -27,6 +28,7 @@ export class MediaTypeManager { this.mediaFileNameTemplateMap.set(MediaType.Wiki, settings.wikiFileNameTemplate); this.mediaFileNameTemplateMap.set(MediaType.MusicRelease, settings.musicReleaseFileNameTemplate); this.mediaFileNameTemplateMap.set(MediaType.BoardGame, settings.boardgameFileNameTemplate); + this.mediaFileNameTemplateMap.set(MediaType.Book, settings.bookFileNameTemplate); this.mediaTemplateMap = new Map(); this.mediaTemplateMap.set(MediaType.Movie, settings.movieTemplate); @@ -35,6 +37,7 @@ export class MediaTypeManager { this.mediaTemplateMap.set(MediaType.Wiki, settings.wikiTemplate); this.mediaTemplateMap.set(MediaType.MusicRelease, settings.musicReleaseTemplate); this.mediaTemplateMap.set(MediaType.BoardGame, settings.boardgameTemplate); + this.mediaTemplateMap.set(MediaType.Book, settings.bookTemplate); } updateFolders(settings: MediaDbPluginSettings): void { @@ -45,6 +48,7 @@ export class MediaTypeManager { this.mediaFolderMap.set(MediaType.Wiki, settings.wikiFolder); this.mediaFolderMap.set(MediaType.MusicRelease, settings.musicReleaseFolder); this.mediaFolderMap.set(MediaType.BoardGame, settings.boardgameFolder); + this.mediaFolderMap.set(MediaType.Book, settings.bookFolder); } getFileName(mediaTypeModel: MediaTypeModel): string { @@ -111,6 +115,8 @@ export class MediaTypeManager { return new MusicReleaseModel(obj); } else if (mediaType === MediaType.BoardGame) { return new BoardGameModel(obj); + } else if (mediaType === MediaType.Book) { + return new BookModel(obj); } return undefined;