diff --git a/manifest.json b/manifest.json index ecbf5cd..17fbf0d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-media-db-plugin", "name": "Media DB Plugin", - "version": "0.3.0", + "version": "0.3.1", "minAppVersion": "0.14.0", "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", diff --git a/package.json b/package.json index b627065..6f6d6e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-media-db-plugin", - "version": "0.3.0", + "version": "0.3.1", "description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.", "main": "main.js", "scripts": { diff --git a/src/api/apis/MALAPI.ts b/src/api/apis/MALAPI.ts index 5507a10..66e1935 100644 --- a/src/api/apis/MALAPI.ts +++ b/src/api/apis/MALAPI.ts @@ -41,13 +41,20 @@ export class MALAPI extends APIModel { let ret: MediaTypeModel[] = []; for (const result of data.data) { - const type = this.typeMappings.get(result.type.toLowerCase()); + const type = this.typeMappings.get(result.type?.toLowerCase()); if (type === undefined) { - continue; + ret.push(new MovieModel({ + 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 MovieModel)); } if (type === 'movie' || type === 'special') { ret.push(new MovieModel({ - type: type, + subType: type, title: result.title, englishTitle: result.title_english ?? result.title, year: result.year ?? result.aired?.prop?.from?.year ?? '', @@ -56,7 +63,7 @@ export class MALAPI extends APIModel { } as MovieModel)); } else if (type === 'series' || type === 'ova') { ret.push(new SeriesModel({ - type: type, + subType: type, title: result.title, englishTitle: result.title_english ?? result.title, year: result.year ?? result.aired?.prop?.from?.year ?? '', @@ -83,14 +90,39 @@ export class MALAPI extends APIModel { debugLog(data); const result = data.data; - const type = this.typeMappings.get(result.type.toLowerCase()); + const type = this.typeMappings.get(result.type?.toLowerCase()); if (type === undefined) { - throw Error(`${result.type.toLowerCase()} is an unsupported type.`); + const model = new MovieModel({ + subType: '', + title: result.title, + englishTitle: result.title_english ?? result.title, + year: result.year ?? result.aired?.prop?.from?.year ?? '', + dataSource: this.apiName, + url: result.url, + id: result.mal_id, + + genres: result.genres?.map((x: any) => x.name) ?? [], + producer: result.studios?.map((x: any) => x.name).join(', ') ?? 'unknown', + duration: result.duration ?? 'unknown', + onlineRating: result.score ?? 0, + image: result.images?.jpg?.image_url ?? '', + + released: true, + premiere: (new Date(result.aired?.from)).toLocaleDateString() ?? 'unknown', + + userData: { + watched: false, + lastWatched: '', + personalRating: 0, + }, + } as MovieModel); + + return model; } if (type === 'movie' || type === 'special') { const model = new MovieModel({ - type: type, + subType: type, title: result.title, englishTitle: result.title_english ?? result.title, year: result.year ?? result.aired?.prop?.from?.year ?? '', @@ -117,7 +149,7 @@ export class MALAPI extends APIModel { return model; } else if (type === 'series' || type === 'ova') { const model = new SeriesModel({ - type: type, + subType: type, title: result.title, englishTitle: result.title_english ?? result.title, year: result.year ?? result.aired?.prop?.from?.year ?? '', diff --git a/src/main.ts b/src/main.ts index 2583bfc..c43124d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import {Notice, Plugin, TFile, TFolder} from 'obsidian'; +import {Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder} from 'obsidian'; import {DEFAULT_SETTINGS, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings'; import {APIManager} from './api/APIManager'; import {MediaTypeModel} from './models/MediaTypeModel'; @@ -109,33 +109,13 @@ export default class MediaDbPlugin extends Plugin { console.log('MDB | Creating new note...'); // console.log(mediaTypeModel); - let metadata = this.modelPropertyMapper.convertObject(mediaTypeModel.toMetaDataObject()); - if (attachFile) { - let attachFileMetadata: any = this.app.metadataCache.getFileCache(attachFile).frontmatter; - if (attachFileMetadata) { - attachFileMetadata = JSON.parse(JSON.stringify(attachFileMetadata)); // deep copy - delete attachFileMetadata.position; - } else { - attachFileMetadata = {}; - } + let fileMetadata = this.modelPropertyMapper.convertObject(mediaTypeModel.toMetaDataObject()); + let fileContent = ''; - metadata = Object.assign(attachFileMetadata, metadata); - } + ({fileMetadata, fileContent} = await this.attachFile(fileMetadata, fileContent, attachFile)); + ({fileMetadata, fileContent} = await this.attachTemplate(fileMetadata, fileContent, await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app))); - debugLog(metadata); - - let fileContent = `---\n${YAMLConverter.toYaml(metadata)}---\n`; - - if (this.settings.templates) { - fileContent += await this.mediaTypeManager.getContent(mediaTypeModel, this.app); - } - - if (attachFile) { - let attachFileContent: string = await this.app.vault.read(attachFile); - const regExp = new RegExp('^(---)\\n[\\s\\S]*\\n---'); - attachFileContent = attachFileContent.replace(regExp, ''); - fileContent += '\n\n' + attachFileContent; - } + fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(fileMetadata) : stringifyYaml(fileMetadata)}---\n` + fileContent; await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent); } catch (e) { @@ -144,6 +124,67 @@ export default class MediaDbPlugin extends Plugin { } } + async attachFile(fileMetadata: any, fileContent: string, fileToAttach?: TFile): Promise<{ fileMetadata: any, fileContent: string }> { + if (!fileToAttach) { + return {fileMetadata: fileMetadata, fileContent: fileContent}; + } + + let attachFileMetadata: any = this.app.metadataCache.getFileCache(fileToAttach).frontmatter; + if (attachFileMetadata) { + attachFileMetadata = JSON.parse(JSON.stringify(attachFileMetadata)); // deep copy + delete attachFileMetadata.position; + } else { + attachFileMetadata = {}; + } + fileMetadata = Object.assign(attachFileMetadata, fileMetadata); + + let attachFileContent: string = await this.app.vault.read(fileToAttach); + const regExp = new RegExp('^(---)\\n[\\s\\S]*\\n---'); + attachFileContent = attachFileContent.replace(regExp, ''); + fileContent += '\n' + attachFileContent; + + return {fileMetadata: fileMetadata, fileContent: fileContent}; + } + + async attachTemplate(fileMetadata: any, fileContent: string, template: string): Promise<{ fileMetadata: any, fileContent: string }> { + if (!template) { + return {fileMetadata: fileMetadata, fileContent: fileContent}; + } + + let templateMetadata: any = this.getMetaDataFromFileContent(template); + fileMetadata = Object.assign(templateMetadata, fileMetadata); + + const regExp = new RegExp('^(---)\\n[\\s\\S]*\\n---'); + const attachFileContent = template.replace(regExp, ''); + fileContent += '\n' + attachFileContent; + + return {fileMetadata: fileMetadata, fileContent: fileContent}; + } + + getMetaDataFromFileContent(fileContent: string): any { + let metadata: any; + + const regExp = new RegExp('^(---)\\n[\\s\\S]*\\n---'); + const frontMatterRegExpResult = regExp.exec(fileContent); + if (!frontMatterRegExpResult) { + return {}; + } + let frontMatter = frontMatterRegExpResult[0]; + if (!frontMatter) { + return {}; + } + frontMatter = frontMatter.substring(4); + frontMatter = frontMatter.substring(0, frontMatter.length - 3); + + metadata = parseYaml(frontMatter); + + if (!metadata) { + metadata = {}; + } + + return metadata; + } + async createNote(fileName: string, fileContent: string, openFile: boolean = false) { fileName = replaceIllegalFileNameCharactersInString(fileName); const filePath = `${this.settings.folder.replace(/\/$/, '')}/${fileName}.md`; diff --git a/src/modals/MediaDbFolderImportModal.ts b/src/modals/MediaDbFolderImportModal.ts index 39a7fdc..415e4df 100644 --- a/src/modals/MediaDbFolderImportModal.ts +++ b/src/modals/MediaDbFolderImportModal.ts @@ -46,7 +46,7 @@ export class MediaDbFolderImportModal extends Modal { const appendContentToggleElementWrapper = contentEl.createEl('div', {cls: 'media-db-plugin-list-wrapper'}); const appendContentToggleTextWrapper = appendContentToggleElementWrapper.createEl('div', {cls: 'media-db-plugin-list-text-wrapper'}); appendContentToggleTextWrapper.createEl('span', { - text: 'If this is enabled, the plugin will override meta data fields with the same name.', + text: 'If this is enabled, the plugin will override metadata fields with the same name.', cls: 'media-db-plugin-list-text', }); @@ -59,7 +59,7 @@ export class MediaDbFolderImportModal extends Modal { contentEl.createDiv({cls: 'media-db-plugin-spacer'}); - contentEl.createEl('h3', {text: 'The name of the mata data field that should be used as the title to query'}); + contentEl.createEl('h3', {text: 'The name of the metadata field that should be used as the title to query.'}); const placeholder = 'title'; const titleFieldNameComponent = new TextComponent(contentEl); diff --git a/src/modals/SelectModal.ts b/src/modals/SelectModal.ts index de2cdc3..572d568 100644 --- a/src/modals/SelectModal.ts +++ b/src/modals/SelectModal.ts @@ -72,11 +72,13 @@ export abstract class SelectModal extends Modal { contentEl.createEl('h2', {text: this.title}); contentEl.createEl('p', {text: this.description}); + contentEl.addClass('media-db-plugin-select-modal'); + const elementWrapper = contentEl.createDiv({cls: 'media-db-plugin-select-wrapper'}); let i = 0; for (const element of this.elements) { - const selectModalElement = new SelectModalElement(element, contentEl, i, this, false); + const selectModalElement = new SelectModalElement(element, elementWrapper, i, this, false); this.selectModalElements.push(selectModalElement); @@ -85,6 +87,8 @@ export abstract class SelectModal extends Modal { i += 1; } + this.selectModalElements.first()?.element.scrollIntoView(); + const bottomSetting = new Setting(contentEl); bottomSetting.addButton(btn => btn.setButtonText('Cancel').onClick(() => this.close())); if (this.skipButton) { diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index f526dbe..e849809 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -7,8 +7,11 @@ import {FileSuggest} from './suggesters/FileSuggest'; export interface MediaDbPluginSettings { folder: string, - sfwFilter: boolean, OMDbKey: string, + sfwFilter: boolean, + useCustomYamlStringifier: boolean; + templates: boolean, + movieTemplate: string, seriesTemplate: string, @@ -28,13 +31,14 @@ export interface MediaDbPluginSettings { wikiPropertyConversionRules: string, musicReleasePropertyConversionRules: string, - templates: boolean, } export const DEFAULT_SETTINGS: MediaDbPluginSettings = { folder: 'Media DB', - sfwFilter: true, OMDbKey: '', + sfwFilter: true, + useCustomYamlStringifier: true, + templates: true, movieTemplate: '', seriesTemplate: '', @@ -54,7 +58,7 @@ export const DEFAULT_SETTINGS: MediaDbPluginSettings = { wikiPropertyConversionRules: '', musicReleasePropertyConversionRules: '', - templates: true, + }; export class MediaDbSettingTab extends PluginSettingTab { @@ -108,6 +112,17 @@ export class MediaDbSettingTab extends PluginSettingTab { }); }); + new Setting(containerEl) + .setName('YAML formatter') + .setDesc('Add optional quotation marks around strings in the metadata block.') + .addToggle(cb => { + cb.setValue(this.plugin.settings.useCustomYamlStringifier) + .onChange(data => { + this.plugin.settings.useCustomYamlStringifier = data; + this.plugin.saveSettings(); + }); + }); + new Setting(containerEl) .setName('Resolve {{ tags }} in templates') .setDesc('Whether to resolve {{ tags }} in templates. The spaces inside the curly braces are important.') diff --git a/src/utils/MediaTypeManager.ts b/src/utils/MediaTypeManager.ts index bd47d84..ba0bd90 100644 --- a/src/utils/MediaTypeManager.ts +++ b/src/utils/MediaTypeManager.ts @@ -37,7 +37,7 @@ export class MediaTypeManager { return replaceTags(this.mediaFileNameTemplateMap.get(mediaTypeModel.getMediaType()), mediaTypeModel); } - async getContent(mediaTypeModel: MediaTypeModel, app: App) { + async getTemplate(mediaTypeModel: MediaTypeModel, app: App) { const templateFileName = this.mediaTemplateMap.get(mediaTypeModel.getMediaType()); if (!templateFileName) { diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index a855b23..f0cd940 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -4,7 +4,7 @@ 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.3.0'; +export const mediaDbVersion: string = '0.3.1'; export const debug: boolean = false; export function wrapAround(value: number, size: number): number { diff --git a/styles.css b/styles.css index 7befc32..5ee2836 100644 --- a/styles.css +++ b/styles.css @@ -22,8 +22,15 @@ small.media-db-plugin-list-text{ color: var(--text-muted); } +.media-db-plugin-select-modal { + display: flex; + flex-direction: column; +} + .media-db-plugin-select-wrapper { margin: 5px; + flex: 1; + overflow-y: auto; } .media-db-plugin-select-element {