cleanup and reduced number of settings validations

This commit is contained in:
mProjectsCode 2022-09-27 15:04:56 +02:00
parent 68ec5135cc
commit 3db3496e0d
12 changed files with 171 additions and 297 deletions

View file

@ -1,6 +1,5 @@
import {APIModel} from './APIModel';
import {MediaTypeModel} from '../models/MediaTypeModel';
import {debugLog} from '../utils/Utils';
export class APIManager {
apis: APIModel[];
@ -10,7 +9,7 @@ export class APIManager {
}
async query(query: string, apisToQuery: string[]): Promise<MediaTypeModel[]> {
debugLog(`MDB | api manager queried with "${query}"`);
console.debug(`MDB | api manager queried with "${query}"`);
let res: MediaTypeModel[] = [];

View file

@ -2,7 +2,7 @@ import {Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder} from 'obsidian
import {getDefaultSettings, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings';
import {APIManager} from './api/APIManager';
import {MediaTypeModel} from './models/MediaTypeModel';
import {dateTimeToString, debugLog, markdownTable, replaceIllegalFileNameCharactersInString, UserCancelError, UserSkipError} from './utils/Utils';
import {dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString, UserCancelError, UserSkipError} from './utils/Utils';
import {OMDbAPI} from './api/apis/OMDbAPI';
import {MediaDbAdvancedSearchModal} from './modals/MediaDbAdvancedSearchModal';
import {MediaDbSearchResultModal} from './modals/MediaDbSearchResultModal';
@ -44,12 +44,8 @@ export default class MediaDbPlugin extends Plugin {
// register the settings tab
this.addSettingTab(new MediaDbSettingTab(this.app, this));
// TESTING
// this.settings.propertyMappingModels = getDefaultSettings(this).propertyMappingModels;
this.mediaTypeManager.updateTemplates(this.settings);
// add icon to the left ribbon
const ribbonIconEl = this.addRibbonIcon('database', 'Add new Media DB entry', (evt: MouseEvent) =>
this.createEntryWithAdvancedSearchModal(),
@ -136,7 +132,6 @@ export default class MediaDbPlugin extends Plugin {
selectModal.close();
debugLog(results);
if (results) {
await this.createMediaDbNotes(results);
}
@ -162,7 +157,6 @@ export default class MediaDbPlugin extends Plugin {
idSearchModal.close();
debugLog(result);
if (result) {
await this.createMediaDbNoteFromModel(result);
}
@ -189,7 +183,7 @@ export default class MediaDbPlugin extends Plugin {
async createMediaDbNoteFromModel(mediaTypeModel: MediaTypeModel, attachFile?: TFile): Promise<void> {
try {
console.log('MDB | Creating new note...');
console.debug('MDB | creating new note');
let fileContent = await this.generateMediaDbNoteContents(mediaTypeModel, attachFile);
@ -216,13 +210,7 @@ export default class MediaDbPlugin extends Plugin {
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 = {};
}
let attachFileMetadata: any = this.getMetadataFromFileCache(fileToAttach);
fileMetadata = Object.assign(attachFileMetadata, fileMetadata);
let attachFileContent: string = await this.app.vault.read(fileToAttach);
@ -241,7 +229,7 @@ export default class MediaDbPlugin extends Plugin {
let templateMetadata: any = this.getMetaDataFromFileContent(template);
fileMetadata = Object.assign(templateMetadata, fileMetadata);
const regExp = new RegExp('^(---)\\n[\\s\\S]*\\n---');
const regExp = new RegExp(this.frontMatterRexExpPattern);
const attachFileContent = template.replace(regExp, '');
fileContent += attachFileContent;
@ -251,7 +239,7 @@ export default class MediaDbPlugin extends Plugin {
getMetaDataFromFileContent(fileContent: string): any {
let metadata: any;
const regExp = new RegExp('^(---)\\n[\\s\\S]*\\n---');
const regExp = new RegExp(this.frontMatterRexExpPattern);
const frontMatterRegExpResult = regExp.exec(fileContent);
if (!frontMatterRegExpResult) {
return {};
@ -269,6 +257,19 @@ export default class MediaDbPlugin extends Plugin {
metadata = {};
}
console.debug(`MDB | metadata read from file content`, metadata);
return metadata;
}
getMetadataFromFileCache(file: TFile) {
let metadata: any = this.app.metadataCache.getFileCache(file).frontmatter;
if (metadata) {
metadata = Object.assign({}, metadata); // copy
delete metadata.position;
} else {
metadata = {};
}
return metadata;
}
@ -297,12 +298,13 @@ export default class MediaDbPlugin extends Plugin {
// create the file
const targetFile = await this.app.vault.create(filePath, fileContent);
console.debug(`MDB | created new file at ${filePath}`);
// open newly crated file
if (openFile) {
const activeLeaf = this.app.workspace.getUnpinnedLeaf();
if (!activeLeaf) {
console.warn('MDB | no active leaf, not opening media db note');
console.warn('MDB | no active leaf, not opening newly created note');
return;
}
await activeLeaf.openFile(targetFile, {state: {mode: 'source'}});
@ -319,12 +321,10 @@ export default class MediaDbPlugin extends Plugin {
throw new Error('MDB | there is no active note');
}
let metadata: any = this.app.metadataCache.getFileCache(activeFile).frontmatter;
metadata = JSON.parse(JSON.stringify(metadata)); // deep copy
delete metadata.position; // remove unnecessary data from the FrontMatterCache
let metadata: any = this.getMetadataFromFileCache(activeFile);
metadata = this.modelPropertyMapper.convertObjectBack(metadata);
debugLog(metadata);
console.debug(`MDB | read metadata`, metadata);
if (!metadata?.type || !metadata?.dataSource || !metadata?.id) {
throw new Error('MDB | active note is not a Media DB entry or is missing metadata');
@ -339,7 +339,8 @@ export default class MediaDbPlugin extends Plugin {
newMediaTypeModel = Object.assign(oldMediaTypeModel, newMediaTypeModel.getWithOutUserData());
console.log('MDB | deleting old entry');
// deletion not happening anymore why is this log statement still here
console.debug('MDB | deleting old entry');
await this.createMediaDbNoteFromModel(newMediaTypeModel, activeFile);
}
@ -361,7 +362,7 @@ export default class MediaDbPlugin extends Plugin {
continue;
}
let metadata: any = this.app.metadataCache.getFileCache(file).frontmatter;
let metadata: any = this.getMetadataFromFileCache(file);
let title = metadata[titleFieldName];
if (!title) {
@ -525,7 +526,6 @@ export default class MediaDbPlugin extends Plugin {
async saveSettings() {
this.mediaTypeManager.updateTemplates(this.settings);
//this.modelPropertyMapper.updateConversionRules(this.settings);
await this.saveData(this.settings);
}

View file

@ -1,6 +1,5 @@
import {ButtonComponent, Component, Modal, Notice, Setting, TextComponent, ToggleComponent} from 'obsidian';
import {ButtonComponent, Modal, Notice, Setting, TextComponent, ToggleComponent} from 'obsidian';
import {MediaTypeModel} from '../models/MediaTypeModel';
import {debugLog} from '../utils/Utils';
import MediaDbPlugin from '../main';
export class MediaDbAdvancedSearchModal extends Modal {
@ -36,9 +35,6 @@ export class MediaDbAdvancedSearchModal extends Modal {
}
async search(): Promise<MediaTypeModel[]> {
debugLog(this.selectedApis);
if (!this.query || this.query.length < 3) {
new Notice('MDB | Query to short');
return;
@ -78,7 +74,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
contentEl.createDiv({cls: 'media-db-plugin-spacer'});
contentEl.createEl('h3', {text: 'APIs to search'});
const apiToggleComponents: Component[] = [];
// const apiToggleComponents: Component[] = [];
for (const api of this.plugin.apiManager.apis) {
const apiToggleListElementWrapper = contentEl.createEl('div', {cls: 'media-db-plugin-list-wrapper'});

View file

@ -1,6 +1,5 @@
import {ButtonComponent, DropdownComponent, Modal, Notice, Setting, TextComponent} from 'obsidian';
import {MediaTypeModel} from '../models/MediaTypeModel';
import {debugLog} from '../utils/Utils';
import MediaDbPlugin from '../main';
export class MediaDbIdSearchModal extends Modal {
@ -33,9 +32,6 @@ export class MediaDbIdSearchModal extends Modal {
}
async search(): Promise<MediaTypeModel> {
debugLog(this.selectedApi);
if (!this.query) {
new Notice('MDB | no Id entered');
return;

View file

@ -13,7 +13,7 @@ export abstract class MediaTypeModel {
userData: object;
constructor() {
protected constructor() {
this.type = undefined;
this.subType = undefined;
this.title = undefined;

View file

@ -45,7 +45,7 @@ export class MusicReleaseModel extends MediaTypeModel {
}
getSummary(): string {
var summary = this.title + ' (' + this.year + ')';
let summary = this.title + ' (' + this.year + ')';
if (this.artists.length > 0)
summary += ' - ' + this.artists.join(', ');
return summary;

View file

@ -19,6 +19,8 @@ export class PropertyMappingModel {
}
validate(): { res: boolean, err?: Error } {
console.debug(`MDB | validated property mappings for ${this.type}`);
// check properties
for (const property of this.properties) {
const propertyValidation = property.validate();
@ -89,28 +91,6 @@ export class PropertyMapping {
this.newProperty = newProperty;
this.mapping = mapping;
this.locked = locked ?? false;
/*
const conversionRuleParts = conversionRule.split('->');
if (conversionRuleParts.length !== 2) {
throw Error(`Conversion rule "${conversionRule}" may only have exactly one "->"`);
}
let property = conversionRuleParts[0].trim();
let newProperty = conversionRuleParts[1].trim();
if (!property || !containsOnlyLettersAndUnderscores(property)) {
throw Error(`Error in conversion rule "${conversionRule}": property may not be empty and only contain letters and underscores.`);
}
if (!newProperty || !containsOnlyLettersAndUnderscores(newProperty)) {
throw Error(`Error in conversion rule "${conversionRule}": new property may not be empty and only contain letters and underscores.`);
}
this.property = property;
this.newProperty = newProperty;
*/
}
validate(): { res: boolean, err?: Error } {

View file

@ -0,0 +1,63 @@
<script lang="ts">
import {PropertyMappingModel, PropertyMappingOption, propertyMappingOptions} from './PropertyMapping';
import {capitalizeFirstLetter} from '../utils/Utils';
import Icon from './Icon.svelte';
export let model: PropertyMappingModel;
export let save: (model: PropertyMappingModel) => void;
let validationResult: { res: boolean, err?: Error };
$: modelChanged(model);
function modelChanged(model: PropertyMappingModel) {
validationResult = model.validate();
}
</script>
<style>
</style>
<div class="media-db-plugin-property-mappings-model-container">
<div class="setting-item-name">{capitalizeFirstLetter(model.type)}</div>
<div class="media-db-plugin-property-mappings-container">
{ #each model.properties as property }
<div class="media-db-plugin-property-mapping-element">
<div class="media-db-plugin-property-mapping-element-property-name-wrapper">
<pre
class="media-db-plugin-property-mapping-element-property-name"><code>{property.property}</code></pre>
</div>
{ #if property.locked }
<div class="media-db-plugin-property-binding-text">
property can not be remapped
</div>
{ :else }
<select class="dropdown" bind:value={property.mapping}>
{#each propertyMappingOptions as remappingOption}
<option value={remappingOption}>
{remappingOption}
</option>
{/each}
</select>
{ #if property.mapping === PropertyMappingOption.Map }
<Icon iconName="arrow-right"/>
<div class="media-db-plugin-property-mapping-to">
<input type="text" spellcheck="false" bind:value="{property.newProperty}">
</div>
{ /if }
{ /if }
</div>
{ /each }
</div>
{ #if !validationResult?.res }
<div class="media-db-plugin-property-mapping-validation">
{validationResult?.err?.message}
</div>
{ /if }
<button
class="media-db-plugin-property-mappings-save-button {validationResult?.res ? 'mod-cta' : 'mod-muted'}"
on:click={() => { if(model.validate().res) save(model) }}>Save
</button>
</div>

View file

@ -1,7 +1,6 @@
<script lang="ts">
import {capitalizeFirstLetter} from '../utils/Utils';
import {PropertyMappingModel, PropertyMappingOption, propertyMappingOptions} from './PropertyMapping';
import Icon from './Icon.svelte';
import {PropertyMappingModel} from './PropertyMapping';
import PropertyMappingModelComponent from './PropertyMappingModelComponent.svelte';
export let models: PropertyMappingModel[] = [];
@ -11,108 +10,21 @@
</script>
<style>
.media-db-plugin-property-mappings-model-container {
border: 1px solid var(--background-modifier-border);
border-radius: 5px;
padding: 10px;
width: 100%;
}
.media-db-plugin-property-mappings-container {
margin: 10px 0;
display: flex;
flex-direction: column;
gap: 5px;
}
.media-db-plugin-property-mapping-element {
display: flex;
flex-direction: row;
gap: 10px;
}
.media-db-plugin-property-mapping-element-property-name-wrapper {
min-width: 160px;
background: var(--background-modifier-form-field);
padding: 2px 5px;
border-radius: 5px;
display: flex;
align-items: center;
}
.media-db-plugin-property-mapping-element-property-name {
margin: 0;
}
.media-db-plugin-property-mappings-save-button {
margin: 0;
}
.media-db-plugin-property-mapping-to {
display: flex;
align-items: center;
}
.media-db-plugin-property-mapping-validation {
color: var(--text-error);
margin-bottom: 5px;
}
</style>
<div class="setting-item" style="display: flex; gap: 10px; flex-direction: column; align-items: stretch;">
{ #each models as model }
<div class="media-db-plugin-property-mappings-model-container">
<div class="setting-item-name">{capitalizeFirstLetter(model.type)}</div>
<div class="media-db-plugin-property-mappings-container">
{ #each model.properties as property }
<div class="media-db-plugin-property-mapping-element">
<div class="media-db-plugin-property-mapping-element-property-name-wrapper">
<pre
class="media-db-plugin-property-mapping-element-property-name"><code>{property.property}</code></pre>
</div>
{#if property.locked}
<div class="media-db-plugin-property-binding-text">
property can not be remapped
</div>
{:else}
<select class="dropdown" bind:value={property.mapping}>
{#each propertyMappingOptions as remappingOption}
<option value={remappingOption}>
{remappingOption}
</option>
{/each}
</select>
{#if property.mapping === PropertyMappingOption.Map}
<Icon iconName="arrow-right"/>
<div class="media-db-plugin-property-mapping-to">
<input type="text" spellcheck="false" bind:value="{property.newProperty}">
</div>
{/if}
{/if}
</div>
{ /each }
</div>
{ #if !model.validate().res }
<div class="media-db-plugin-property-mapping-validation">
{model.validate().err?.message}
</div>
{/if}
<button
class="media-db-plugin-property-mappings-save-button {model.validate().res ? 'mod-cta' : 'mod-muted'}"
on:click={() => { if(model.validate().res) save(model) }}>Save
</button>
</div>
{ /each }
<pre>{JSON.stringify(models, null, 4)}</pre>
<PropertyMappingModelComponent model={model} save={save}></PropertyMappingModelComponent>
{ /each }
<!--
{ #each ICON_LIST as icon }
<p>
{icon} <Icon iconName="{icon}"/>
</p>
{/each}
<pre>{JSON.stringify(models, null, 4)}</pre>
{ #each ICON_LIST as icon }
<p>
{icon} <Icon iconName="{icon}"/>
</p>
{/each}
-->
</div>

View file

@ -42,7 +42,7 @@ export interface MediaDbPluginSettings {
}
export const DEFAULT_SETTINGS: MediaDbPluginSettings = {
const DEFAULT_SETTINGS: MediaDbPluginSettings = {
folder: 'Media DB',
OMDbKey: '',
sfwFilter: true,
@ -70,63 +70,7 @@ export const DEFAULT_SETTINGS: MediaDbPluginSettings = {
musicReleasePropertyConversionRules: '',
boardgamePropertyConversionRules: '',
propertyMappingModels: [
/*
{
type: MediaType.Movie,
properties: [
new PropertyMapping('type', '', PropertyMappingOption.Default, true),
new PropertyMapping('subType', '', PropertyMappingOption.Default),
new PropertyMapping('title', '', PropertyMappingOption.Default),
new PropertyMapping('englishTitle', '', PropertyMappingOption.Default),
new PropertyMapping('year', '', PropertyMappingOption.Default),
new PropertyMapping('dataSource', '', PropertyMappingOption.Default, true),
new PropertyMapping('url', '', PropertyMappingOption.Default),
new PropertyMapping('id', '', PropertyMappingOption.Default, true),
new PropertyMapping('genres', '', PropertyMappingOption.Default),
new PropertyMapping('producer', '', PropertyMappingOption.Default),
new PropertyMapping('duration', '', PropertyMappingOption.Default),
new PropertyMapping('onlineRating', '', PropertyMappingOption.Default),
new PropertyMapping('image', '', PropertyMappingOption.Default),
new PropertyMapping('released', '', PropertyMappingOption.Default),
new PropertyMapping('premiere', '', PropertyMappingOption.Default),
new PropertyMapping('watched', '', PropertyMappingOption.Default),
new PropertyMapping('lastWatched', '', PropertyMappingOption.Default),
new PropertyMapping('personalRating', '', PropertyMappingOption.Default),
],
},
{
type: MediaType.Series,
properties: [
new PropertyMapping('type', '', PropertyMappingOption.Default, true),
new PropertyMapping('subType', '', PropertyMappingOption.Default),
new PropertyMapping('title', '', PropertyMappingOption.Default),
new PropertyMapping('englishTitle', '', PropertyMappingOption.Default),
new PropertyMapping('year', '', PropertyMappingOption.Default),
new PropertyMapping('dataSource', '', PropertyMappingOption.Default, true),
new PropertyMapping('url', '', PropertyMappingOption.Default),
new PropertyMapping('id', '', PropertyMappingOption.Default, true),
new PropertyMapping('genres', '', PropertyMappingOption.Default),
new PropertyMapping('studios', '', PropertyMappingOption.Default),
new PropertyMapping('episodes', '', PropertyMappingOption.Default),
new PropertyMapping('duration', '', PropertyMappingOption.Default),
new PropertyMapping('onlineRating', '', PropertyMappingOption.Default),
new PropertyMapping('image', '', PropertyMappingOption.Default),
new PropertyMapping('released', '', PropertyMappingOption.Default),
new PropertyMapping('airing', '', PropertyMappingOption.Default),
new PropertyMapping('airedFrom', '', PropertyMappingOption.Default),
new PropertyMapping('airedTo', '', PropertyMappingOption.Default),
new PropertyMapping('watched', '', PropertyMappingOption.Default),
new PropertyMapping('lastWatched', '', PropertyMappingOption.Default),
new PropertyMapping('personalRating', '', PropertyMappingOption.Default),
],
},
*/
],
propertyMappingModels: [],
};
export const lockedPropertyMappings: string[] = ['type', 'id', 'dataSource'];
@ -387,89 +331,13 @@ export class MediaDbSettingTab extends PluginSettingTab {
});
// endregion
containerEl.createEl('h3', {text: 'Property Mappings'});
// region Property Mappings
/*
new Setting(containerEl)
.setName('Movie model property mappings')
.setDesc('Mappings for the property names of a movie.')
.addTextArea(cb => {
cb.setPlaceholder(`Example: \ntitle -> name\nyear -> releaseYear`)
.setValue(this.plugin.settings.moviePropertyConversionRules)
.onChange(data => {
this.plugin.settings.moviePropertyConversionRules = data;
this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Series model property mappings')
.setDesc('Mappings for the property names of a series.')
.addTextArea(cb => {
cb.setPlaceholder(`Example: \ntitle -> name\nyear -> releaseYear`)
.setValue(this.plugin.settings.seriesPropertyConversionRules)
.onChange(data => {
this.plugin.settings.seriesPropertyConversionRules = data;
this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Game model property mappings')
.setDesc('Mappings for the property names of a game.')
.addTextArea(cb => {
cb.setPlaceholder(`Example: \ntitle -> name\nyear -> releaseYear`)
.setValue(this.plugin.settings.gamePropertyConversionRules)
.onChange(data => {
this.plugin.settings.gamePropertyConversionRules = data;
this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Wiki model property mappings')
.setDesc('Mappings for the property names of a wiki entry.')
.addTextArea(cb => {
cb.setPlaceholder(`Example: \ntitle -> name\nyear -> releaseYear`)
.setValue(this.plugin.settings.wikiPropertyConversionRules)
.onChange(data => {
this.plugin.settings.wikiPropertyConversionRules = data;
this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Music Release model property mappings')
.setDesc('Mappings for the property names of a music release.')
.addTextArea(cb => {
cb.setPlaceholder(`Example: \ntitle -> name\nyear -> releaseYear`)
.setValue(this.plugin.settings.musicReleasePropertyConversionRules)
.onChange(data => {
this.plugin.settings.musicReleasePropertyConversionRules = data;
this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Board Game model property mappings')
.setDesc('Mappings for the property names of a boardgame.')
.addTextArea(cb => {
cb.setPlaceholder(`Example: \ntitle -> name\nyear -> releaseYear`)
.setValue(this.plugin.settings.boardgamePropertyConversionRules)
.onChange(data => {
this.plugin.settings.boardgamePropertyConversionRules = data;
this.plugin.saveSettings();
});
});
*/
// endregion
console.log(this.plugin.settings.propertyMappingModels);
// console.log(getDefaultSettings(this.plugin));
containerEl.createEl('h3', {text: 'Property Mappings'});
let propertyMappingExplanation = containerEl.createEl('div');
propertyMappingExplanation.innerHTML = `<p>Allow you to remap the metadata fields of newly created media db entries.</p>
propertyMappingExplanation.innerHTML = `
<p>Allow you to remap the metadata fields of newly created media db entries.</p>
<p>
The different options are:
<lu>
@ -477,6 +345,9 @@ export class MediaDbSettingTab extends PluginSettingTab {
<li>"remap": renames the metadata field to what ever you specify</li>
<li>"remove": removes the metadata field entirely</li>
</lu>
</p>
<p>
Don't forget to save your changes using the save button for each individual category.
</p>`;
@ -502,6 +373,8 @@ export class MediaDbSettingTab extends PluginSettingTab {
},
});
// endregion
}
}

View file

@ -14,6 +14,10 @@ export function wrapAround(value: number, size: number): number {
return mod(value, size);
}
/**
* @deprecated use console.debug instead
* @param o
*/
export function debugLog(o: any): void {
if (debug) {
console.log(o);

View file

@ -55,3 +55,54 @@ small.media-db-plugin-list-text{
.media-db-plugin-spacer {
margin-bottom: 10px;
}
/* region property mappings */
.media-db-plugin-property-mappings-model-container {
border: 1px solid var(--background-modifier-border);
border-radius: 5px;
padding: 10px;
width: 100%;
}
.media-db-plugin-property-mappings-container {
margin: 10px 0;
display: flex;
flex-direction: column;
gap: 5px;
}
.media-db-plugin-property-mapping-element {
display: flex;
flex-direction: row;
gap: 10px;
}
.media-db-plugin-property-mapping-element-property-name-wrapper {
min-width: 160px;
background: var(--background-modifier-form-field);
padding: 2px 5px;
border-radius: 5px;
display: flex;
align-items: center;
}
.media-db-plugin-property-mapping-element-property-name {
margin: 0;
}
.media-db-plugin-property-mappings-save-button {
margin: 0;
}
.media-db-plugin-property-mapping-to {
display: flex;
align-items: center;
}
.media-db-plugin-property-mapping-validation {
color: var(--text-error);
margin-bottom: 5px;
}
/* endregion */