Merge pull request #119 from kelszo/master

Support disabling default front matter and add support for Templater
This commit is contained in:
Moritz Jung 2024-01-25 00:31:47 +01:00 committed by GitHub
commit 3cebee452e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 178 additions and 39 deletions

View file

@ -2,7 +2,15 @@ import { MarkdownView, Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder
import { getDefaultSettings, MediaDbPluginSettings, MediaDbSettingTab } from './settings/Settings';
import { APIManager } from './api/APIManager';
import { MediaTypeModel } from './models/MediaTypeModel';
import { CreateNoteOptions, dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString, unCamelCase } from './utils/Utils';
import {
CreateNoteOptions,
dateTimeToString,
markdownTable,
replaceIllegalFileNameCharactersInString,
unCamelCase,
hasTemplaterPlugin,
useTemplaterPluginInFile,
} from './utils/Utils';
import { OMDbAPI } from './api/apis/OMDbAPI';
import { MALAPI } from './api/apis/MALAPI';
import { MALAPIManga } from './api/apis/MALAPIManga';
@ -63,7 +71,8 @@ export default class MediaDbPlugin extends Plugin {
this.app.workspace.on('file-menu', (menu, file) => {
if (file instanceof TFolder) {
menu.addItem(item => {
item.setTitle('Import folder as Media DB entries')
item
.setTitle('Import folder as Media DB entries')
.setIcon('database')
.onClick(() => this.createEntriesFromFolder(file));
});
@ -286,7 +295,11 @@ export default class MediaDbPlugin extends Plugin {
options.folder = await this.mediaTypeManager.getFolder(mediaTypeModel, this.app);
}
await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent, options);
const targetFile = await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent, options);
if (this.settings.enableTemplaterIntegration) {
await useTemplaterPluginInFile(this.app, targetFile);
}
} catch (e) {
console.warn(e);
new Notice(e.toString());
@ -299,14 +312,87 @@ export default class MediaDbPlugin extends Plugin {
}
async generateMediaDbNoteContents(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions): Promise<string> {
let template = await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app);
if (this.settings.useDefaultFrontMatter || !template) {
return this.generateContentWithDefaultFrontMatter(mediaTypeModel, options, template);
} else {
return this.generateContentWithCustomFrontMatter(mediaTypeModel, options, template);
}
}
async generateContentWithDefaultFrontMatter(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions, template?: string): Promise<string> {
let fileMetadata = this.modelPropertyMapper.convertObject(mediaTypeModel.toMetaDataObject());
let fileContent = '';
const template = options.attachTemplate ? await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app) : '';
template = options.attachTemplate ? template : '';
({ fileMetadata, fileContent } = await this.attachFile(fileMetadata, fileContent, options.attachFile));
({ fileMetadata, fileContent } = await this.attachTemplate(fileMetadata, fileContent, template));
fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(fileMetadata) : stringifyYaml(fileMetadata)}---\n` + fileContent;
if (this.settings.enableTemplaterIntegration && hasTemplaterPlugin(this.app)) {
// Only support stringifyYaml for templater plugin
// Include the media variable in all templater commands by using a top level JavaScript execution command.
fileContent = `---\n<%* const media = ${JSON.stringify(mediaTypeModel)} %>\n${stringifyYaml(fileMetadata)}---\n${fileContent}`;
} else {
fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(fileMetadata) : stringifyYaml(fileMetadata)}---\n` + fileContent;
}
return fileContent;
}
async generateContentWithCustomFrontMatter(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions, template: string): Promise<string> {
const frontMatterRegex = /^---*\n([\s\S]*?)\n---\h*/;
const match = template.match(frontMatterRegex);
if (!match || match.length !== 2) {
throw new Error('Cannot find YAML front matter for template.');
}
let frontMatter = parseYaml(match[1]);
let fileContent: string = template.replace(frontMatterRegex, '');
// Updating a previous file
if (options.attachFile) {
const previousMetadata = this.app.metadataCache.getFileCache(options.attachFile).frontmatter;
// Use contents (below front matter) from previous file
fileContent = await this.app.vault.read(options.attachFile);
const regExp = new RegExp(this.frontMatterRexExpPattern);
fileContent = fileContent.replace(regExp, '');
fileContent = fileContent.startsWith('\n') ? fileContent.substring(1) : fileContent;
// Update updated front matter with entries from the old front matter, if it isn't defined in the new front matter
Object.keys(previousMetadata).forEach(key => {
const value = previousMetadata[key];
if (!frontMatter[key] && value) {
frontMatter[key] = value;
}
});
}
// Ensure that id, type, and dataSource are defined
if (!frontMatter.id) {
frontMatter.id = mediaTypeModel.id;
}
if (!frontMatter.type) {
frontMatter.type = mediaTypeModel.type;
}
if (!frontMatter.dataSource) {
frontMatter.dataSource = mediaTypeModel.dataSource;
}
if (this.settings.enableTemplaterIntegration && hasTemplaterPlugin(this.app)) {
// Only support stringifyYaml for templater plugin
// Include the media variable in all templater commands by using a top level JavaScript execution command.
fileContent = `---\n<%* const media = ${JSON.stringify(mediaTypeModel)} %>\n${stringifyYaml(frontMatter)}---\n${fileContent}`;
} else {
fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(frontMatter) : stringifyYaml(frontMatter)}---\n` + fileContent;
}
return fileContent;
}
@ -386,7 +472,7 @@ export default class MediaDbPlugin extends Plugin {
* @param fileContent
* @param options
*/
async createNote(fileName: string, fileContent: string, options: CreateNoteOptions): Promise<void> {
async createNote(fileName: string, fileContent: string, options: CreateNoteOptions): Promise<TFile> {
// find and possibly create the folder set in settings or passed in folder
const folder = options.folder ?? this.app.vault.getAbstractFileByPath('/');
@ -412,6 +498,8 @@ export default class MediaDbPlugin extends Plugin {
}
await activeLeaf.openFile(targetFile, { state: { mode: 'source' } });
}
return targetFile;
}
/**

View file

@ -16,6 +16,8 @@ export interface MediaDbPluginSettings {
templates: boolean;
customDateFormat: string;
openNoteInNewTab: boolean;
useDefaultFrontMatter: boolean;
enableTemplaterIntegration: boolean;
movieTemplate: string;
seriesTemplate: string;
@ -63,6 +65,8 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = {
templates: true,
customDateFormat: 'L',
openNoteInNewTab: true,
useDefaultFrontMatter: true,
enableTemplaterIntegration: false,
movieTemplate: '',
seriesTemplate: '',
@ -218,6 +222,30 @@ export class MediaDbSettingTab extends PluginSettingTab {
});
});
new Setting(containerEl)
.setName('Use default front matter')
.setDesc('Wheter to use the default front matter. If disabled, the front matter from the template will be used. Same as mapping everything to remove.')
.addToggle(cb => {
cb.setValue(this.plugin.settings.useDefaultFrontMatter).onChange(data => {
this.plugin.settings.useDefaultFrontMatter = data;
this.plugin.saveSettings();
// Redraw settings to display/remove the property mappings
this.display();
});
});
new Setting(containerEl)
.setName('Enable Templater integration')
.setDesc(
'Enable integration with the templater plugin, this also needs templater to be installed. Warning: Templater allows you to execute arbitrary JavaScript code and system commands.',
)
.addToggle(cb => {
cb.setValue(this.plugin.settings.enableTemplaterIntegration).onChange(data => {
this.plugin.settings.enableTemplaterIntegration = data;
this.plugin.saveSettings();
});
});
containerEl.createEl('h3', { text: 'New File Location' });
// region new file location
new Setting(containerEl)
@ -531,11 +559,11 @@ export class MediaDbSettingTab extends PluginSettingTab {
// endregion
// region Property Mappings
if (this.plugin.settings.useDefaultFrontMatter) {
containerEl.createEl('h3', { text: 'Property Mappings' });
containerEl.createEl('h3', { text: 'Property Mappings' });
const propertyMappingExplanation = containerEl.createEl('div');
propertyMappingExplanation.innerHTML = `
const propertyMappingExplanation = containerEl.createEl('div');
propertyMappingExplanation.innerHTML = `
<p>Allow you to remap the metadata fields of newly created media db entries.</p>
<p>
The different options are:
@ -549,27 +577,28 @@ export class MediaDbSettingTab extends PluginSettingTab {
Don't forget to save your changes using the save button for each individual category.
</p>`;
new PropertyMappingModelsComponent({
target: this.containerEl,
props: {
models: this.plugin.settings.propertyMappingModels.map(x => x.copy()),
save: (model: PropertyMappingModel): void => {
const propertyMappingModels: PropertyMappingModel[] = [];
new PropertyMappingModelsComponent({
target: this.containerEl,
props: {
models: this.plugin.settings.propertyMappingModels.map(x => x.copy()),
save: (model: PropertyMappingModel): void => {
const propertyMappingModels: PropertyMappingModel[] = [];
for (const model2 of this.plugin.settings.propertyMappingModels) {
if (model2.type === model.type) {
propertyMappingModels.push(model);
} else {
propertyMappingModels.push(model2);
for (const model2 of this.plugin.settings.propertyMappingModels) {
if (model2.type === model.type) {
propertyMappingModels.push(model);
} else {
propertyMappingModels.push(model2);
}
}
}
this.plugin.settings.propertyMappingModels = propertyMappingModels;
new Notice(`MDB: Property Mappings for ${model.type} saved successfully.`);
this.plugin.saveSettings();
this.plugin.settings.propertyMappingModels = propertyMappingModels;
new Notice(`MDB: Property Mappings for ${model.type} saved successfully.`);
this.plugin.saveSettings();
},
},
},
});
});
}
// endregion
}

View file

@ -17,11 +17,11 @@ export class FileSuggest extends TextInputSuggest<TFile> {
}
renderSuggestion(file: TFile, el: HTMLElement): void {
el.setText(file.name);
el.setText(file.path);
}
selectSuggestion(file: TFile): void {
this.inputEl.value = file.name;
this.inputEl.value = file.path;
this.inputEl.trigger('input');
this.close();
}

View file

@ -69,22 +69,28 @@ export class MediaTypeManager {
}
async getTemplate(mediaTypeModel: MediaTypeModel, app: App): Promise<string> {
const templateFileName = this.mediaTemplateMap.get(mediaTypeModel.getMediaType());
const templateFilePath = this.mediaTemplateMap.get(mediaTypeModel.getMediaType());
if (!templateFileName) {
if (!templateFilePath) {
return '';
}
const templateFile: TFile = app.vault
.getFiles()
.filter((f: TFile) => f.name === templateFileName)
.first();
let templateFile = app.vault.getAbstractFileByPath(templateFilePath);
if (!templateFile) {
return '';
// WARNING: This was previously selected by filename, but that could lead to collisions and unwanted effects.
// This now falls back to the previous method if no file is found
if (!templateFile || templateFile instanceof TFolder) {
templateFile = app.vault
.getFiles()
.filter((f: TFile) => f.name === templateFilePath)
.first();
if (!templateFile) {
return '';
}
}
const template = await app.vault.cachedRead(templateFile);
const template = await app.vault.cachedRead(templateFile as TFile);
// console.log(template);
return replaceTags(template, mediaTypeModel);
}

View file

@ -1,5 +1,5 @@
import { MediaTypeModel } from '../models/MediaTypeModel';
import { TFile, TFolder } from 'obsidian';
import { TFile, TFolder, App } from 'obsidian';
export const pluginName: string = 'obsidian-media-db-plugin';
export const contactEmail: string = 'm.projects.code@gmail.com';
@ -202,3 +202,19 @@ export function unCamelCase(str: string): string {
})
);
}
export function hasTemplaterPlugin(app: App) {
const templater = (app as any).plugins.plugins['templater-obsidian'];
return !!templater;
}
// Copied from https://github.com/anpigon/obsidian-book-search-plugin
// Licensed under the MIT license. Copyright (c) 2020 Jake Runzer
export async function useTemplaterPluginInFile(app: App, file: TFile) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const templater = (app as any).plugins.plugins['templater-obsidian'];
if (templater && !templater?.settings['trigger_on_file_creation']) {
await templater.templater.overwrite_file_commands(file);
}
}