From 38d28e10a008491e0670dabb2053fe2e5383cda7 Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Fri, 22 Sep 2023 20:31:02 +0200 Subject: [PATCH 01/10] Testing openlibrary support --- src/api/apis/OpenLibraryAPI.ts | 90 ++++++++++++++++++++++++++++++++++ src/main.ts | 2 + src/models/BookModel.ts | 53 ++++++++++++++++++++ src/settings/Settings.ts | 55 +++++++++++++++++++-- src/utils/MediaType.ts | 1 + src/utils/MediaTypeManager.ts | 8 ++- 6 files changed, 203 insertions(+), 6 deletions(-) create mode 100644 src/api/apis/OpenLibraryAPI.ts create mode 100644 src/models/BookModel.ts 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; From 0bb8ef049178b46d014519910b31fcec3c217600 Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Fri, 22 Sep 2023 20:32:52 +0200 Subject: [PATCH 02/10] Test version without release --- .github/workflows/release.yml | 56 ----------------------------------- 1 file changed, 56 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc02e9a..ca8e533 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,59 +29,3 @@ jobs: zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }} ls echo "tag_name=$(git tag --sort version:refname | tail -n 1)" >> $GITHUB_OUTPUT - - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VERSION: ${{ github.ref }} - with: - tag_name: ${{ github.ref }} - release_name: ${{ github.ref }} - draft: false - prerelease: false - - - name: Upload zip file - id: upload-zip - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./${{ env.PLUGIN_NAME }}.zip - asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip - asset_content_type: application/zip - - - name: Upload main.js - id: upload-main - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./main.js - asset_name: main.js - asset_content_type: text/javascript - - - name: Upload manifest.json - id: upload-manifest - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./manifest.json - asset_name: manifest.json - asset_content_type: application/json - - - name: Upload styles.css - id: upload-css - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./styles.css - asset_name: styles.css - asset_content_type: text/css From 503b387168cd9358b7c968c57e0d9c02e7b588ae Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Fri, 22 Sep 2023 20:36:29 +0200 Subject: [PATCH 03/10] Revert "Test version without release" This reverts commit 0bb8ef049178b46d014519910b31fcec3c217600. --- .github/workflows/release.yml | 56 +++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ca8e533..bc02e9a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,3 +29,59 @@ jobs: zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }} ls echo "tag_name=$(git tag --sort version:refname | tail -n 1)" >> $GITHUB_OUTPUT + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ github.ref }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + draft: false + prerelease: false + + - name: Upload zip file + id: upload-zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./${{ env.PLUGIN_NAME }}.zip + asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip + asset_content_type: application/zip + + - name: Upload main.js + id: upload-main + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./main.js + asset_name: main.js + asset_content_type: text/javascript + + - name: Upload manifest.json + id: upload-manifest + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./manifest.json + asset_name: manifest.json + asset_content_type: application/json + + - name: Upload styles.css + id: upload-css + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./styles.css + asset_name: styles.css + asset_content_type: text/css From cf36d19e11b1e129c0ba9e887bdce6229a9e3e72 Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Fri, 22 Sep 2023 23:10:52 +0200 Subject: [PATCH 04/10] Fixed bugs Added a header so it doesn't limit the rate of requests as fast Fixed year missing from title Replaced ID from isbn to OpenLibrary ID Added ISBN10 as separate field --- src/api/apis/OpenLibraryAPI.ts | 34 ++++++++++++++++++++-------------- src/models/BookModel.ts | 2 ++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/api/apis/OpenLibraryAPI.ts b/src/api/apis/OpenLibraryAPI.ts index f46018d..e00c121 100644 --- a/src/api/apis/OpenLibraryAPI.ts +++ b/src/api/apis/OpenLibraryAPI.ts @@ -3,6 +3,7 @@ import { MediaTypeModel } from '../../models/MediaTypeModel'; import MediaDbPlugin from '../../main'; import { BookModel } from 'src/models/BookModel'; import { requestUrl } from 'obsidian'; +import { contactEmail, mediaDbVersion, pluginName } from '../../utils/Utils'; import { MediaType } from '../../utils/MediaType'; export class OpenLibraryAPI extends APIModel { @@ -21,7 +22,7 @@ export class OpenLibraryAPI extends APIModel { 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 searchUrl = `https://openlibrary.org/search.json?title=${encodeURIComponent(title)}`; const fetchData = await fetch(searchUrl); console.debug(fetchData); @@ -37,12 +38,11 @@ async searchByTitle(title: string): Promise { 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 ?? '', + year: result.first_publish_year, dataSource: this.apiName, - id: result.mal_id, + id: result.cover_edition_key, } as BookModel) ); } @@ -53,28 +53,34 @@ async searchByTitle(title: string): Promise { 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); + const searchUrl = `https://openlibrary.org/books/${encodeURIComponent(id)}.json`; + const fetchData = await requestUrl({ + url: searchUrl, + headers: { + 'User-Agent': `${pluginName}/${mediaDbVersion} (${contactEmail})`, + }, + }); + + 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 result = data.data; + const result = await fetchData.json; const model = new BookModel({ - subType: '', title: result.title, - year: new Date(result.publish_date.date).getFullYear().toString(), + year: new Date(result.publish_date).getFullYear().toString(), dataSource: this.apiName, url: `https://openlibrary.org` + result.key, - id: result.isbn_10, + id: result.key.slice(7), + isbn10: result.isbn_10, + englishTitle: result.title_english ?? result.title, author: result.authors.key ?? 'unknown', pages: result.number_of_pages ?? 'unknown', - image: `https://covers.openlibrary.org/b/isbn/` + result.isbn_10 + `-L.jpg` ?? '', + image: `https://covers.openlibrary.org/b/OLID/` + result.key.slice(7) + `-L.jpg` ?? '', released: true, @@ -85,6 +91,6 @@ async getById(id: string): Promise { }, } as BookModel); - return; + return model; } } diff --git a/src/models/BookModel.ts b/src/models/BookModel.ts index a36ba21..d0f0235 100644 --- a/src/models/BookModel.ts +++ b/src/models/BookModel.ts @@ -5,7 +5,9 @@ import { MediaType } from '../utils/MediaType'; export class BookModel extends MediaTypeModel { author: string; pages: string; + isbn10: string; image: string; + english_title: string; released: boolean; From e89bf8e455992fbe46fd00999b2b9ccf814b7f4d Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Sat, 23 Sep 2023 00:29:24 +0200 Subject: [PATCH 05/10] Added fallback if there is no default cover --- src/api/apis/OpenLibraryAPI.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/apis/OpenLibraryAPI.ts b/src/api/apis/OpenLibraryAPI.ts index e00c121..c97adf5 100644 --- a/src/api/apis/OpenLibraryAPI.ts +++ b/src/api/apis/OpenLibraryAPI.ts @@ -42,7 +42,7 @@ async searchByTitle(title: string): Promise { englishTitle: result.title_english ?? result.title, year: result.first_publish_year, dataSource: this.apiName, - id: result.cover_edition_key, + id: result.cover_edition_key ?? result.edition_key, } as BookModel) ); } From a15af42d853a753fd4e4a72e40214c0ab40fb89b Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Sat, 23 Sep 2023 02:51:50 +0200 Subject: [PATCH 06/10] Removed isbn10 field Since I'm now using the general work rather than a specific edition --- src/models/BookModel.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/BookModel.ts b/src/models/BookModel.ts index d0f0235..fcb6686 100644 --- a/src/models/BookModel.ts +++ b/src/models/BookModel.ts @@ -5,7 +5,6 @@ import { MediaType } from '../utils/MediaType'; export class BookModel extends MediaTypeModel { author: string; pages: string; - isbn10: string; image: string; english_title: string; From 03d1c1dc7dab6fc15f17358e5098294fc68711a2 Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Sat, 23 Sep 2023 02:52:20 +0200 Subject: [PATCH 07/10] Changed api search to use works rather than individual editions --- src/api/apis/OpenLibraryAPI.ts | 121 ++++++++++++++++----------------- 1 file changed, 58 insertions(+), 63 deletions(-) diff --git a/src/api/apis/OpenLibraryAPI.ts b/src/api/apis/OpenLibraryAPI.ts index c97adf5..99ade6d 100644 --- a/src/api/apis/OpenLibraryAPI.ts +++ b/src/api/apis/OpenLibraryAPI.ts @@ -19,78 +19,73 @@ export class OpenLibraryAPI extends APIModel { this.types = [MediaType.Book]; } -async searchByTitle(title: string): Promise { - console.log(`MDB | api "${this.apiName}" queried by Title`); + async searchByTitle(title: string): Promise { + console.log(`MDB | api "${this.apiName}" queried by Title`); - const searchUrl = `https://openlibrary.org/search.json?title=${encodeURIComponent(title)}`; + const searchUrl = `https://openlibrary.org/search.json?title=${encodeURIComponent(title)}`; - 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(); + 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); + console.debug(data); - const ret: MediaTypeModel[] = []; + const ret: MediaTypeModel[] = []; - for (const result of data.docs) { - ret.push( - new BookModel({ - title: result.title, - englishTitle: result.title_english ?? result.title, - year: result.first_publish_year, - dataSource: this.apiName, - id: result.cover_edition_key ?? result.edition_key, - } as BookModel) - ); + for (const result of data.docs) { + ret.push( + new BookModel({ + title: result.title, + englishTitle: result.title_english ?? result.title, + year: result.first_publish_year, + dataSource: this.apiName, + id: result.key, + } as BookModel) + ); + } + + return ret; } - return ret; -} + async getById(id: string): Promise { + console.log(`MDB | api "${this.apiName}" queried by ID`); -async getById(id: string): Promise { - console.log(`MDB | api "${this.apiName}" queried by ID`); + const searchUrl = `https://openlibrary.org/search.json?q=key:${encodeURIComponent(id)}`; + const fetchData = await fetch(searchUrl); + console.debug(fetchData); - const searchUrl = `https://openlibrary.org/books/${encodeURIComponent(id)}.json`; - const fetchData = await requestUrl({ - url: searchUrl, - headers: { - 'User-Agent': `${pluginName}/${mediaDbVersion} (${contactEmail})`, - }, - }); + if (fetchData.status !== 200) { + throw Error(`MDB | Received status code ${fetchData.status} from an API.`); + } - console.debug(fetchData); + const data = await fetchData.json(); + console.debug(data); + const result = data.docs[0]; - if (fetchData.status !== 200) { - throw Error(`MDB | Received status code ${fetchData.status} from an API.`); + const model = new BookModel({ + title: result.title, + year: result.first_publish_year, + dataSource: this.apiName, + url: `https://openlibrary.org` + result.key, + id: result.key, + englishTitle: result.title_english ?? result.title, + + author: result.author_name ?? 'unknown', + pages: result.number_of_pages_median ?? 'unknown', + image: `https://covers.openlibrary.org/b/OLID/` + result.cover_edition_key + `-L.jpg` ?? '', + + released: true, + + userData: { + read: false, + lastRead: '', + personalRating: 0, + }, + } as BookModel); + + return model; } - - const result = await fetchData.json; - - const model = new BookModel({ - title: result.title, - year: new Date(result.publish_date).getFullYear().toString(), - dataSource: this.apiName, - url: `https://openlibrary.org` + result.key, - id: result.key.slice(7), - isbn10: result.isbn_10, - englishTitle: result.title_english ?? result.title, - - author: result.authors.key ?? 'unknown', - pages: result.number_of_pages ?? 'unknown', - image: `https://covers.openlibrary.org/b/OLID/` + result.key.slice(7) + `-L.jpg` ?? '', - - released: true, - - userData: { - read: false, - lastRead: '', - personalRating: 0, - }, - } as BookModel); - - return model; -} -} +} \ No newline at end of file From 4968c491dca6f7d750a0d07a589c7e51e8ea1d70 Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Sat, 23 Sep 2023 03:21:03 +0200 Subject: [PATCH 08/10] Added online rating --- src/api/apis/OpenLibraryAPI.ts | 3 +-- src/models/BookModel.ts | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/api/apis/OpenLibraryAPI.ts b/src/api/apis/OpenLibraryAPI.ts index 99ade6d..225f159 100644 --- a/src/api/apis/OpenLibraryAPI.ts +++ b/src/api/apis/OpenLibraryAPI.ts @@ -2,8 +2,6 @@ import { APIModel } from '../APIModel'; import { MediaTypeModel } from '../../models/MediaTypeModel'; import MediaDbPlugin from '../../main'; import { BookModel } from 'src/models/BookModel'; -import { requestUrl } from 'obsidian'; -import { contactEmail, mediaDbVersion, pluginName } from '../../utils/Utils'; import { MediaType } from '../../utils/MediaType'; export class OpenLibraryAPI extends APIModel { @@ -75,6 +73,7 @@ export class OpenLibraryAPI extends APIModel { author: result.author_name ?? 'unknown', pages: result.number_of_pages_median ?? 'unknown', + onlineRating: Number.parseFloat(result.ratings_average.toFixed(2)) ?? 0, image: `https://covers.openlibrary.org/b/OLID/` + result.cover_edition_key + `-L.jpg` ?? '', released: true, diff --git a/src/models/BookModel.ts b/src/models/BookModel.ts index fcb6686..c0c9c4e 100644 --- a/src/models/BookModel.ts +++ b/src/models/BookModel.ts @@ -4,8 +4,9 @@ import { MediaType } from '../utils/MediaType'; export class BookModel extends MediaTypeModel { author: string; - pages: string; + pages: number; image: string; + onlineRating: number; english_title: string; released: boolean; @@ -22,6 +23,7 @@ export class BookModel extends MediaTypeModel { this.author = undefined; this.pages = undefined; this.image = undefined; + this.onlineRating = undefined; this.released = undefined; From 34f635363028bd617b21f56dcb568b5f48282fbe Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Sat, 23 Sep 2023 03:51:31 +0200 Subject: [PATCH 09/10] Fixed breaking error when there was no rating --- src/api/apis/OpenLibraryAPI.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/apis/OpenLibraryAPI.ts b/src/api/apis/OpenLibraryAPI.ts index 225f159..60e6852 100644 --- a/src/api/apis/OpenLibraryAPI.ts +++ b/src/api/apis/OpenLibraryAPI.ts @@ -73,7 +73,7 @@ export class OpenLibraryAPI extends APIModel { author: result.author_name ?? 'unknown', pages: result.number_of_pages_median ?? 'unknown', - onlineRating: Number.parseFloat(result.ratings_average.toFixed(2)) ?? 0, + onlineRating: Number.parseFloat(Number(result.ratings_average ?? 0).toFixed(2)), image: `https://covers.openlibrary.org/b/OLID/` + result.cover_edition_key + `-L.jpg` ?? '', released: true, From 184d66668a47b692ab7209f0462cc1327cf48843 Mon Sep 17 00:00:00 2001 From: ltctceplrm <14954927+ltctceplrm@users.noreply.github.com> Date: Sat, 23 Sep 2023 13:01:18 +0200 Subject: [PATCH 10/10] Added Open Library to README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index bf1042e..7eadce2 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,7 @@ Now you select the result you want and the plugin will cast it's magic and creat - games - music releases - wiki articles +- books ### Currently supported APIs: @@ -117,6 +118,8 @@ Now you select the result you want and the plugin will cast it's magic and creat | [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 access to all Wikipedia articles. | wiki articles | No | None | No | | [Steam](https://store.steampowered.com/) | The Steam API offers information on all steam games. | games | No | 10000 per day | No | +| [Open Library](https://openlibrary.org) | The OpenLibrary API offers metadata for books | books | No | Cover access is rate-limited when not using CoverID or OLID by max 100 requests/IP every 5 minutes. This plugin uses OLID so there shouldn't be a rate limit. | No | + #### Notes @@ -142,6 +145,11 @@ Now you select the result you want and the plugin will cast it's magic and creat - [Steam](https://store.steampowered.com/) - you can find this ID in the URL - e.g. for "Factorio" the URL looks like this `https://store.steampowered.com/app/427520/Factorio/` so the ID is `427520` +- [Open Library](https://openlibrary.org) + - The ID you need is the "work" ID and not the "book" ID, it needs to start with `/works/`. You can find this ID in the URL + - e.g. for "Fantastic Mr. Fox" the URL looks like this `https://openlibrary.org/works/OL45804W` so the ID is `/works/OL45804W` + - This URL is located near the top of the page above the title, see `An edition of Fantastic Mr Fox (1970) ` + ### Problems, unexpected behavior or improvement suggestions?