Skip to content

Commit 3cebee4

Browse files
Merge pull request #119 from kelszo/master
Support disabling default front matter and add support for Templater
2 parents 841fbec + 8a9448b commit 3cebee4

File tree

5 files changed

+179
-40
lines changed

5 files changed

+179
-40
lines changed

src/main.ts

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@ import { MarkdownView, Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder
22
import { getDefaultSettings, MediaDbPluginSettings, MediaDbSettingTab } from './settings/Settings';
33
import { APIManager } from './api/APIManager';
44
import { MediaTypeModel } from './models/MediaTypeModel';
5-
import { CreateNoteOptions, dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString, unCamelCase } from './utils/Utils';
5+
import {
6+
CreateNoteOptions,
7+
dateTimeToString,
8+
markdownTable,
9+
replaceIllegalFileNameCharactersInString,
10+
unCamelCase,
11+
hasTemplaterPlugin,
12+
useTemplaterPluginInFile,
13+
} from './utils/Utils';
614
import { OMDbAPI } from './api/apis/OMDbAPI';
715
import { MALAPI } from './api/apis/MALAPI';
816
import { MALAPIManga } from './api/apis/MALAPIManga';
@@ -63,7 +71,8 @@ export default class MediaDbPlugin extends Plugin {
6371
this.app.workspace.on('file-menu', (menu, file) => {
6472
if (file instanceof TFolder) {
6573
menu.addItem(item => {
66-
item.setTitle('Import folder as Media DB entries')
74+
item
75+
.setTitle('Import folder as Media DB entries')
6776
.setIcon('database')
6877
.onClick(() => this.createEntriesFromFolder(file));
6978
});
@@ -286,7 +295,11 @@ export default class MediaDbPlugin extends Plugin {
286295
options.folder = await this.mediaTypeManager.getFolder(mediaTypeModel, this.app);
287296
}
288297

289-
await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent, options);
298+
const targetFile = await this.createNote(this.mediaTypeManager.getFileName(mediaTypeModel), fileContent, options);
299+
300+
if (this.settings.enableTemplaterIntegration) {
301+
await useTemplaterPluginInFile(this.app, targetFile);
302+
}
290303
} catch (e) {
291304
console.warn(e);
292305
new Notice(e.toString());
@@ -299,14 +312,87 @@ export default class MediaDbPlugin extends Plugin {
299312
}
300313

301314
async generateMediaDbNoteContents(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions): Promise<string> {
315+
let template = await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app);
316+
317+
if (this.settings.useDefaultFrontMatter || !template) {
318+
return this.generateContentWithDefaultFrontMatter(mediaTypeModel, options, template);
319+
} else {
320+
return this.generateContentWithCustomFrontMatter(mediaTypeModel, options, template);
321+
}
322+
}
323+
324+
async generateContentWithDefaultFrontMatter(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions, template?: string): Promise<string> {
302325
let fileMetadata = this.modelPropertyMapper.convertObject(mediaTypeModel.toMetaDataObject());
303326
let fileContent = '';
304-
const template = options.attachTemplate ? await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app) : '';
327+
template = options.attachTemplate ? template : '';
305328

306329
({ fileMetadata, fileContent } = await this.attachFile(fileMetadata, fileContent, options.attachFile));
307330
({ fileMetadata, fileContent } = await this.attachTemplate(fileMetadata, fileContent, template));
308331

309-
fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(fileMetadata) : stringifyYaml(fileMetadata)}---\n` + fileContent;
332+
if (this.settings.enableTemplaterIntegration && hasTemplaterPlugin(this.app)) {
333+
// Only support stringifyYaml for templater plugin
334+
// Include the media variable in all templater commands by using a top level JavaScript execution command.
335+
fileContent = `---\n<%* const media = ${JSON.stringify(mediaTypeModel)} %>\n${stringifyYaml(fileMetadata)}---\n${fileContent}`;
336+
} else {
337+
fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(fileMetadata) : stringifyYaml(fileMetadata)}---\n` + fileContent;
338+
}
339+
340+
return fileContent;
341+
}
342+
343+
async generateContentWithCustomFrontMatter(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions, template: string): Promise<string> {
344+
const frontMatterRegex = /^---*\n([\s\S]*?)\n---\h*/;
345+
346+
const match = template.match(frontMatterRegex);
347+
348+
if (!match || match.length !== 2) {
349+
throw new Error('Cannot find YAML front matter for template.');
350+
}
351+
352+
let frontMatter = parseYaml(match[1]);
353+
let fileContent: string = template.replace(frontMatterRegex, '');
354+
355+
// Updating a previous file
356+
if (options.attachFile) {
357+
const previousMetadata = this.app.metadataCache.getFileCache(options.attachFile).frontmatter;
358+
359+
// Use contents (below front matter) from previous file
360+
fileContent = await this.app.vault.read(options.attachFile);
361+
const regExp = new RegExp(this.frontMatterRexExpPattern);
362+
fileContent = fileContent.replace(regExp, '');
363+
fileContent = fileContent.startsWith('\n') ? fileContent.substring(1) : fileContent;
364+
365+
// Update updated front matter with entries from the old front matter, if it isn't defined in the new front matter
366+
Object.keys(previousMetadata).forEach(key => {
367+
const value = previousMetadata[key];
368+
369+
if (!frontMatter[key] && value) {
370+
frontMatter[key] = value;
371+
}
372+
});
373+
}
374+
375+
// Ensure that id, type, and dataSource are defined
376+
if (!frontMatter.id) {
377+
frontMatter.id = mediaTypeModel.id;
378+
}
379+
380+
if (!frontMatter.type) {
381+
frontMatter.type = mediaTypeModel.type;
382+
}
383+
384+
if (!frontMatter.dataSource) {
385+
frontMatter.dataSource = mediaTypeModel.dataSource;
386+
}
387+
388+
if (this.settings.enableTemplaterIntegration && hasTemplaterPlugin(this.app)) {
389+
// Only support stringifyYaml for templater plugin
390+
// Include the media variable in all templater commands by using a top level JavaScript execution command.
391+
fileContent = `---\n<%* const media = ${JSON.stringify(mediaTypeModel)} %>\n${stringifyYaml(frontMatter)}---\n${fileContent}`;
392+
} else {
393+
fileContent = `---\n${this.settings.useCustomYamlStringifier ? YAMLConverter.toYaml(frontMatter) : stringifyYaml(frontMatter)}---\n` + fileContent;
394+
}
395+
310396
return fileContent;
311397
}
312398

@@ -386,7 +472,7 @@ export default class MediaDbPlugin extends Plugin {
386472
* @param fileContent
387473
* @param options
388474
*/
389-
async createNote(fileName: string, fileContent: string, options: CreateNoteOptions): Promise<void> {
475+
async createNote(fileName: string, fileContent: string, options: CreateNoteOptions): Promise<TFile> {
390476
// find and possibly create the folder set in settings or passed in folder
391477
const folder = options.folder ?? this.app.vault.getAbstractFileByPath('/');
392478

@@ -412,6 +498,8 @@ export default class MediaDbPlugin extends Plugin {
412498
}
413499
await activeLeaf.openFile(targetFile, { state: { mode: 'source' } });
414500
}
501+
502+
return targetFile;
415503
}
416504

417505
/**

src/settings/Settings.ts

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export interface MediaDbPluginSettings {
1616
templates: boolean;
1717
customDateFormat: string;
1818
openNoteInNewTab: boolean;
19+
useDefaultFrontMatter: boolean;
20+
enableTemplaterIntegration: boolean;
1921

2022
movieTemplate: string;
2123
seriesTemplate: string;
@@ -63,6 +65,8 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = {
6365
templates: true,
6466
customDateFormat: 'L',
6567
openNoteInNewTab: true,
68+
useDefaultFrontMatter: true,
69+
enableTemplaterIntegration: false,
6670

6771
movieTemplate: '',
6872
seriesTemplate: '',
@@ -218,6 +222,30 @@ export class MediaDbSettingTab extends PluginSettingTab {
218222
});
219223
});
220224

225+
new Setting(containerEl)
226+
.setName('Use default front matter')
227+
.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.')
228+
.addToggle(cb => {
229+
cb.setValue(this.plugin.settings.useDefaultFrontMatter).onChange(data => {
230+
this.plugin.settings.useDefaultFrontMatter = data;
231+
this.plugin.saveSettings();
232+
// Redraw settings to display/remove the property mappings
233+
this.display();
234+
});
235+
});
236+
237+
new Setting(containerEl)
238+
.setName('Enable Templater integration')
239+
.setDesc(
240+
'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.',
241+
)
242+
.addToggle(cb => {
243+
cb.setValue(this.plugin.settings.enableTemplaterIntegration).onChange(data => {
244+
this.plugin.settings.enableTemplaterIntegration = data;
245+
this.plugin.saveSettings();
246+
});
247+
});
248+
221249
containerEl.createEl('h3', { text: 'New File Location' });
222250
// region new file location
223251
new Setting(containerEl)
@@ -531,11 +559,11 @@ export class MediaDbSettingTab extends PluginSettingTab {
531559
// endregion
532560

533561
// region Property Mappings
562+
if (this.plugin.settings.useDefaultFrontMatter) {
563+
containerEl.createEl('h3', { text: 'Property Mappings' });
534564

535-
containerEl.createEl('h3', { text: 'Property Mappings' });
536-
537-
const propertyMappingExplanation = containerEl.createEl('div');
538-
propertyMappingExplanation.innerHTML = `
565+
const propertyMappingExplanation = containerEl.createEl('div');
566+
propertyMappingExplanation.innerHTML = `
539567
<p>Allow you to remap the metadata fields of newly created media db entries.</p>
540568
<p>
541569
The different options are:
@@ -549,27 +577,28 @@ export class MediaDbSettingTab extends PluginSettingTab {
549577
Don't forget to save your changes using the save button for each individual category.
550578
</p>`;
551579

552-
new PropertyMappingModelsComponent({
553-
target: this.containerEl,
554-
props: {
555-
models: this.plugin.settings.propertyMappingModels.map(x => x.copy()),
556-
save: (model: PropertyMappingModel): void => {
557-
const propertyMappingModels: PropertyMappingModel[] = [];
558-
559-
for (const model2 of this.plugin.settings.propertyMappingModels) {
560-
if (model2.type === model.type) {
561-
propertyMappingModels.push(model);
562-
} else {
563-
propertyMappingModels.push(model2);
580+
new PropertyMappingModelsComponent({
581+
target: this.containerEl,
582+
props: {
583+
models: this.plugin.settings.propertyMappingModels.map(x => x.copy()),
584+
save: (model: PropertyMappingModel): void => {
585+
const propertyMappingModels: PropertyMappingModel[] = [];
586+
587+
for (const model2 of this.plugin.settings.propertyMappingModels) {
588+
if (model2.type === model.type) {
589+
propertyMappingModels.push(model);
590+
} else {
591+
propertyMappingModels.push(model2);
592+
}
564593
}
565-
}
566594

567-
this.plugin.settings.propertyMappingModels = propertyMappingModels;
568-
new Notice(`MDB: Property Mappings for ${model.type} saved successfully.`);
569-
this.plugin.saveSettings();
595+
this.plugin.settings.propertyMappingModels = propertyMappingModels;
596+
new Notice(`MDB: Property Mappings for ${model.type} saved successfully.`);
597+
this.plugin.saveSettings();
598+
},
570599
},
571-
},
572-
});
600+
});
601+
}
573602

574603
// endregion
575604
}

src/settings/suggesters/FileSuggest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ export class FileSuggest extends TextInputSuggest<TFile> {
1717
}
1818

1919
renderSuggestion(file: TFile, el: HTMLElement): void {
20-
el.setText(file.name);
20+
el.setText(file.path);
2121
}
2222

2323
selectSuggestion(file: TFile): void {
24-
this.inputEl.value = file.name;
24+
this.inputEl.value = file.path;
2525
this.inputEl.trigger('input');
2626
this.close();
2727
}

src/utils/MediaTypeManager.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,22 +69,28 @@ export class MediaTypeManager {
6969
}
7070

7171
async getTemplate(mediaTypeModel: MediaTypeModel, app: App): Promise<string> {
72-
const templateFileName = this.mediaTemplateMap.get(mediaTypeModel.getMediaType());
72+
const templateFilePath = this.mediaTemplateMap.get(mediaTypeModel.getMediaType());
7373

74-
if (!templateFileName) {
74+
if (!templateFilePath) {
7575
return '';
7676
}
7777

78-
const templateFile: TFile = app.vault
79-
.getFiles()
80-
.filter((f: TFile) => f.name === templateFileName)
81-
.first();
78+
let templateFile = app.vault.getAbstractFileByPath(templateFilePath);
8279

83-
if (!templateFile) {
84-
return '';
80+
// WARNING: This was previously selected by filename, but that could lead to collisions and unwanted effects.
81+
// This now falls back to the previous method if no file is found
82+
if (!templateFile || templateFile instanceof TFolder) {
83+
templateFile = app.vault
84+
.getFiles()
85+
.filter((f: TFile) => f.name === templateFilePath)
86+
.first();
87+
88+
if (!templateFile) {
89+
return '';
90+
}
8591
}
8692

87-
const template = await app.vault.cachedRead(templateFile);
93+
const template = await app.vault.cachedRead(templateFile as TFile);
8894
// console.log(template);
8995
return replaceTags(template, mediaTypeModel);
9096
}

src/utils/Utils.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { MediaTypeModel } from '../models/MediaTypeModel';
2-
import { TFile, TFolder } from 'obsidian';
2+
import { TFile, TFolder, App } from 'obsidian';
33

44
export const pluginName: string = 'obsidian-media-db-plugin';
55
export const contactEmail: string = '[email protected]';
@@ -202,3 +202,19 @@ export function unCamelCase(str: string): string {
202202
})
203203
);
204204
}
205+
206+
export function hasTemplaterPlugin(app: App) {
207+
const templater = (app as any).plugins.plugins['templater-obsidian'];
208+
209+
return !!templater;
210+
}
211+
212+
// Copied from https://github.com/anpigon/obsidian-book-search-plugin
213+
// Licensed under the MIT license. Copyright (c) 2020 Jake Runzer
214+
export async function useTemplaterPluginInFile(app: App, file: TFile) {
215+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
216+
const templater = (app as any).plugins.plugins['templater-obsidian'];
217+
if (templater && !templater?.settings['trigger_on_file_creation']) {
218+
await templater.templater.overwrite_file_commands(file);
219+
}
220+
}

0 commit comments

Comments
 (0)