Start of #48
This commit is contained in:
parent
72b7eb8982
commit
e00c5ce2c2
13 changed files with 764 additions and 196 deletions
|
|
@ -1,27 +0,0 @@
|
|||
import {containsOnlyLettersAndUnderscores} from '../utils/Utils';
|
||||
|
||||
export class ModelPropertyConversionRule {
|
||||
property: string;
|
||||
newProperty: string;
|
||||
|
||||
constructor(conversionRule: string) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
import {MediaType} from '../utils/MediaType';
|
||||
import {MediaDbPluginSettings} from './Settings';
|
||||
import {ModelPropertyConversionRule} from './ModelPropertyConversionRule';
|
||||
|
||||
export class ModelPropertyMapper {
|
||||
conversionRulesMap: Map<MediaType, string>;
|
||||
|
||||
constructor(settings: MediaDbPluginSettings) {
|
||||
this.updateConversionRules(settings);
|
||||
}
|
||||
|
||||
updateConversionRules(settings: MediaDbPluginSettings) {
|
||||
this.conversionRulesMap = new Map<MediaType, string>();
|
||||
this.conversionRulesMap.set(MediaType.Movie, settings.moviePropertyConversionRules);
|
||||
this.conversionRulesMap.set(MediaType.Series, settings.seriesPropertyConversionRules);
|
||||
this.conversionRulesMap.set(MediaType.Game, settings.gamePropertyConversionRules);
|
||||
this.conversionRulesMap.set(MediaType.Wiki, settings.wikiPropertyConversionRules);
|
||||
this.conversionRulesMap.set(MediaType.MusicRelease, settings.musicReleasePropertyConversionRules);
|
||||
this.conversionRulesMap.set(MediaType.BoardGame, settings.boardgamePropertyConversionRules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object using the conversion rules for its type.
|
||||
* Returns an unaltered object if object.type is null or undefined or if there are no conversion rules for the type.
|
||||
*
|
||||
* @param obj
|
||||
*/
|
||||
convertObject(obj: object): object {
|
||||
if (!obj.hasOwnProperty('type')) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
// get conversion rules from settings corresponding to the object type
|
||||
const conversionRulesString: string = this.conversionRulesMap.get(obj['type']);
|
||||
if (!conversionRulesString) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// parse the conversion rules
|
||||
const conversionRules: ModelPropertyConversionRule[] = [];
|
||||
for (const conversionRuleString of conversionRulesString.split('\n')) {
|
||||
if (conversionRuleString) {
|
||||
conversionRules.push(new ModelPropertyConversionRule(conversionRuleString));
|
||||
}
|
||||
}
|
||||
|
||||
const newObj: object = {};
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
// property 'type' can not be remapped
|
||||
if (key === 'type') {
|
||||
// @ts-ignore
|
||||
newObj[key] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
let hasConversionRule = false;
|
||||
for (const conversionRule of conversionRules) {
|
||||
if (conversionRule.property === key) {
|
||||
hasConversionRule = true;
|
||||
// if the conversion rule maps to 'x', then that means it should be ignored
|
||||
if (conversionRule.newProperty.toLowerCase() !== 'x') {
|
||||
// @ts-ignore
|
||||
newObj[conversionRule.newProperty] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasConversionRule) {
|
||||
// @ts-ignore
|
||||
newObj[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object back using the conversion rules for its type.
|
||||
* Returns an unaltered object if object.type is null or undefined or if there are no conversion rules for the type.
|
||||
*
|
||||
* @param obj
|
||||
*/
|
||||
convertObjectBack(obj: object): object {
|
||||
if (!obj.hasOwnProperty('type')) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
// get conversion rules from settings corresponding to the object type
|
||||
const conversionRulesString: string = this.conversionRulesMap.get(obj['type']);
|
||||
if (!conversionRulesString) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
const conversionRules: ModelPropertyConversionRule[] = [];
|
||||
// parse the conversion rules
|
||||
for (const conversionRuleString of conversionRulesString.split('\n')) {
|
||||
if (conversionRuleString) {
|
||||
conversionRules.push(new ModelPropertyConversionRule(conversionRuleString));
|
||||
}
|
||||
}
|
||||
|
||||
const originalObj: object = {};
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
// property 'type' can not be remapped
|
||||
if (key === 'type') {
|
||||
// @ts-ignore
|
||||
originalObj[key] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
let hasConversionRule = false;
|
||||
for (const conversionRule of conversionRules) {
|
||||
if (conversionRule.newProperty === key) {
|
||||
hasConversionRule = true;
|
||||
// @ts-ignore
|
||||
originalObj[conversionRule.property] = value;
|
||||
}
|
||||
}
|
||||
if (!hasConversionRule) {
|
||||
// @ts-ignore
|
||||
originalObj[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return originalObj;
|
||||
}
|
||||
}
|
||||
74
src/settings/PropertyBindingsComponent.svelte
Normal file
74
src/settings/PropertyBindingsComponent.svelte
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<script lang="ts">
|
||||
import {capitalizeFirstLetter} from '../utils/Utils';
|
||||
import {PropertyMappingModel, PropertyMappingOption, propertyMappingOptions} from './PropertyMapping';
|
||||
|
||||
export let models: PropertyMappingModel[] = [];
|
||||
|
||||
export let save: (models: PropertyMappingModel[]) => void;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.media-db-plugin-property-mappings-container {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-element-property-name {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="setting-item" style="display: block;">
|
||||
{ #each models as model }
|
||||
<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 bind:value={property.mapping}>
|
||||
{#each propertyMappingOptions as remappingOption}
|
||||
<option value={remappingOption}>
|
||||
{remappingOption}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
|
||||
{#if property.mapping === PropertyMappingOption.Map}
|
||||
<div class="media-db-plugin-property-mapping-text">
|
||||
->
|
||||
</div>
|
||||
<div class="media-db-plugin-property-binding-to">
|
||||
<input type="text" spellcheck="false" bind:value="{property.newProperty}">
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{ /each }
|
||||
</div>
|
||||
{ /each }
|
||||
|
||||
<pre>{JSON.stringify(models, null, 4)}</pre>
|
||||
</div>
|
||||
98
src/settings/PropertyMapper.ts
Normal file
98
src/settings/PropertyMapper.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import {PropertyMappingOption} from './PropertyMapping';
|
||||
import {MEDIA_TYPES} from '../utils/MediaTypeManager';
|
||||
import MediaDbPlugin from '../main';
|
||||
|
||||
export class PropertyMapper {
|
||||
plugin: MediaDbPlugin;
|
||||
|
||||
constructor(plugin: MediaDbPlugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object using the conversion rules for its type.
|
||||
* Returns an unaltered object if object.type is null or undefined or if there are no conversion rules for the type.
|
||||
*
|
||||
* @param obj
|
||||
*/
|
||||
convertObject(obj: object): object {
|
||||
if (!obj.hasOwnProperty('type')) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
if (MEDIA_TYPES.contains(obj.type)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const propertyMappings = this.plugin.settings.propertyMappings.find(x => x.type === obj.type).properties;
|
||||
|
||||
const newObj: object = {};
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
for (const propertyMapping of propertyMappings) {
|
||||
if (propertyMapping.property === key) {
|
||||
if (propertyMapping.mapping === PropertyMappingOption.Map) {
|
||||
// @ts-ignore
|
||||
newObj[propertyMapping.newProperty] = value;
|
||||
} else if (propertyMapping.mapping === PropertyMappingOption.Remove) {
|
||||
|
||||
} else if (propertyMapping.mapping === PropertyMappingOption.None) {
|
||||
// @ts-ignore
|
||||
newObj[key] = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object back using the conversion rules for its type.
|
||||
* Returns an unaltered object if object.type is null or undefined or if there are no conversion rules for the type.
|
||||
*
|
||||
* @param obj
|
||||
*/
|
||||
convertObjectBack(obj: object): object {
|
||||
if (!obj.hasOwnProperty('type')) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
if (MEDIA_TYPES.contains(obj.type)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const propertyMappings = this.plugin.settings.propertyMappings.find(x => x.type === obj.type).properties;
|
||||
|
||||
const originalObj: object = {};
|
||||
|
||||
objLoop: for (const [key, value] of Object.entries(obj)) {
|
||||
// first try if it is a normal property
|
||||
for (const propertyMapping of propertyMappings) {
|
||||
if (propertyMapping.property === key) {
|
||||
// @ts-ignore
|
||||
originalObj[key] = value;
|
||||
|
||||
continue objLoop;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise see if it is a mapped property
|
||||
for (const propertyMapping of propertyMappings) {
|
||||
if (propertyMapping.newProperty === key) {
|
||||
// @ts-ignore
|
||||
originalObj[propertyMapping.property] = value;
|
||||
|
||||
continue objLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return originalObj;
|
||||
}
|
||||
}
|
||||
75
src/settings/PropertyMapping.ts
Normal file
75
src/settings/PropertyMapping.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import {containsOnlyLettersAndUnderscores} from '../utils/Utils';
|
||||
import {MediaType} from '../utils/MediaType';
|
||||
|
||||
export enum PropertyMappingOption {
|
||||
None = 'none',
|
||||
Map = 'remap',
|
||||
Remove = 'remove',
|
||||
}
|
||||
|
||||
export const propertyMappingOptions = [PropertyMappingOption.None, PropertyMappingOption.Map, PropertyMappingOption.Remove];
|
||||
|
||||
export interface PropertyMappingModel {
|
||||
type: MediaType,
|
||||
properties: PropertyMapping[],
|
||||
}
|
||||
|
||||
export class PropertyMapping {
|
||||
property: string;
|
||||
newProperty: string;
|
||||
locked: boolean;
|
||||
mapping: PropertyMappingOption;
|
||||
|
||||
constructor(property: string, newProperty: string, mapping: PropertyMappingOption, locked?: boolean) {
|
||||
this.property = property;
|
||||
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(): string {
|
||||
if (!this.property || !containsOnlyLettersAndUnderscores(this.property)) {
|
||||
return `Error in conversion rule "${this.toString()}": property may not be empty and only contain letters and underscores.`;
|
||||
}
|
||||
|
||||
if (!this.newProperty || !containsOnlyLettersAndUnderscores(this.newProperty)) {
|
||||
return `Error in conversion rule "${this.toString()}": new property may not be empty and only contain letters and underscores.`;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
if (this.mapping === PropertyMappingOption.None) {
|
||||
return this.property;
|
||||
} else if (this.mapping === PropertyMappingOption.Map) {
|
||||
return `${this.property} -> ${this.newProperty}`;
|
||||
} else if (this.mapping === PropertyMappingOption.Remove) {
|
||||
return `remove ${this.property}`;
|
||||
}
|
||||
|
||||
return this.property;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,9 @@ import {App, PluginSettingTab, Setting} from 'obsidian';
|
|||
import MediaDbPlugin from '../main';
|
||||
import {FolderSuggest} from './suggesters/FolderSuggest';
|
||||
import {FileSuggest} from './suggesters/FileSuggest';
|
||||
import PropertyBindingsComponent from './PropertyBindingsComponent.svelte';
|
||||
import {PropertyMapping, PropertyMappingModel, PropertyMappingOption} from './PropertyMapping';
|
||||
import {MediaType} from '../utils/MediaType';
|
||||
|
||||
|
||||
export interface MediaDbPluginSettings {
|
||||
|
|
@ -34,6 +37,8 @@ export interface MediaDbPluginSettings {
|
|||
musicReleasePropertyConversionRules: string,
|
||||
boardgamePropertyConversionRules: string,
|
||||
|
||||
propertyMappings: PropertyMappingModel[],
|
||||
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: MediaDbPluginSettings = {
|
||||
|
|
@ -64,6 +69,60 @@ export const DEFAULT_SETTINGS: MediaDbPluginSettings = {
|
|||
musicReleasePropertyConversionRules: '',
|
||||
boardgamePropertyConversionRules: '',
|
||||
|
||||
propertyMappings: [
|
||||
{
|
||||
type: MediaType.Movie,
|
||||
properties: [
|
||||
new PropertyMapping('type', '', PropertyMappingOption.None, true),
|
||||
new PropertyMapping('subType', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('title', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('englishTitle', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('year', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('dataSource', '', PropertyMappingOption.None, true),
|
||||
new PropertyMapping('url', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('id', '', PropertyMappingOption.None, true),
|
||||
|
||||
new PropertyMapping('genres', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('producer', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('duration', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('onlineRating', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('image', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('released', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('premiere', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('watched', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('lastWatched', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('personalRating', '', PropertyMappingOption.None),
|
||||
],
|
||||
},
|
||||
{
|
||||
type: MediaType.Series,
|
||||
properties: [
|
||||
new PropertyMapping('type', '', PropertyMappingOption.None, true),
|
||||
new PropertyMapping('subType', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('title', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('englishTitle', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('year', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('dataSource', '', PropertyMappingOption.None, true),
|
||||
new PropertyMapping('url', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('id', '', PropertyMappingOption.None, true),
|
||||
|
||||
new PropertyMapping('genres', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('studios', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('episodes', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('duration', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('onlineRating', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('image', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('released', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('airing', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('airedFrom', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('airedTo', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('watched', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('lastWatched', '', PropertyMappingOption.None),
|
||||
new PropertyMapping('personalRating', '', PropertyMappingOption.None),
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
};
|
||||
|
||||
export class MediaDbSettingTab extends PluginSettingTab {
|
||||
|
|
@ -298,6 +357,7 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
|
||||
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.')
|
||||
|
|
@ -369,8 +429,23 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
*/
|
||||
// endregion
|
||||
|
||||
console.log(this.plugin.settings.propertyMappings);
|
||||
|
||||
new PropertyBindingsComponent({
|
||||
target: this.containerEl,
|
||||
props: {
|
||||
models: this.plugin.settings.propertyMappings,
|
||||
save: (models: PropertyMappingModel[]) => {
|
||||
this.plugin.settings.propertyMappings = models;
|
||||
this.plugin.saveSettings();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue