Skip to content

Commit 9941f14

Browse files
Merge pull request #207 from Spydarlee/batch-import-by-id
Allow batch / folder import by id as well as title
2 parents 535987e + 29269aa commit 9941f14

File tree

3 files changed

+132
-69
lines changed

3 files changed

+132
-69
lines changed

src/main.ts

Lines changed: 67 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import type { SearchModalOptions } from './utils/ModalHelper';
2626
import { ModalHelper, ModalResultCode } from './utils/ModalHelper';
2727
import type { CreateNoteOptions } from './utils/Utils';
2828
import { dateTimeToString, markdownTable, replaceIllegalFileNameCharactersInString, unCamelCase, hasTemplaterPlugin, useTemplaterPluginInFile } from './utils/Utils';
29+
import { BulkImportLookupMethod } from 'src/utils/BulkImportLookupMethod';
2930

3031
export type Metadata = Record<string, unknown>;
3132

@@ -561,9 +562,14 @@ export default class MediaDbPlugin extends Plugin {
561562
const erroredFiles: { filePath: string; error: string }[] = [];
562563
let canceled: boolean = false;
563564

564-
const { selectedAPI, titleFieldName, appendContent } = await new Promise<{ selectedAPI: string; titleFieldName: string; appendContent: boolean }>(resolve => {
565-
new MediaDbFolderImportModal(this.app, this, (selectedAPI: string, titleFieldName: string, appendContent: boolean) => {
566-
resolve({ selectedAPI, titleFieldName, appendContent });
565+
const { selectedAPI, lookupMethod, fieldName, appendContent } = await new Promise<{
566+
selectedAPI: string;
567+
lookupMethod: string;
568+
fieldName: string;
569+
appendContent: boolean;
570+
}>(resolve => {
571+
new MediaDbFolderImportModal(this.app, this, (selectedAPI: string, lookupMethod: string, fieldName: string, appendContent: boolean) => {
572+
resolve({ selectedAPI, lookupMethod, fieldName, appendContent });
567573
}).open();
568574
});
569575

@@ -576,55 +582,74 @@ export default class MediaDbPlugin extends Plugin {
576582
}
577583

578584
const metadata = this.getMetadataFromFileCache(file);
585+
const lookupValue = metadata[fieldName];
579586

580-
const title = metadata[titleFieldName];
581-
if (!title || typeof title !== 'string') {
582-
erroredFiles.push({ filePath: file.path, error: `metadata field '${titleFieldName}' not found, empty, or not a string` });
587+
if (!lookupValue || typeof lookupValue !== 'string') {
588+
erroredFiles.push({ filePath: file.path, error: `metadata field '${fieldName}' not found, empty, or not a string` });
583589
continue;
584-
}
590+
} else if (lookupMethod === BulkImportLookupMethod.ID) {
591+
try {
592+
const model = await this.apiManager.queryDetailedInfoById(lookupValue, selectedAPI);
593+
if (model) {
594+
await this.createMediaDbNotes([model], appendContent ? file : undefined);
595+
} else {
596+
erroredFiles.push({ filePath: file.path, error: `Failed to query API with id: ${lookupValue}` });
597+
}
598+
} catch (e) {
599+
erroredFiles.push({ filePath: file.path, error: `${e}` });
600+
continue;
601+
}
602+
} else if (lookupMethod === BulkImportLookupMethod.TITLE) {
603+
let results: MediaTypeModel[] = [];
604+
try {
605+
results = await this.apiManager.query(lookupValue, [selectedAPI]);
606+
} catch (e) {
607+
erroredFiles.push({ filePath: file.path, error: `${e}` });
608+
continue;
609+
}
610+
if (!results || results.length === 0) {
611+
erroredFiles.push({ filePath: file.path, error: `no search results` });
612+
continue;
613+
}
585614

586-
let results: MediaTypeModel[] = [];
587-
try {
588-
results = await this.apiManager.query(title, [selectedAPI]);
589-
} catch (e) {
590-
erroredFiles.push({ filePath: file.path, error: `${e}` });
591-
continue;
592-
}
593-
if (!results || results.length === 0) {
594-
erroredFiles.push({ filePath: file.path, error: `no search results` });
595-
continue;
596-
}
615+
const { selectModalResult, selectModal } = await this.modalHelper.createSelectModal({
616+
elements: results,
617+
skipButton: true,
618+
modalTitle: `Results for '${lookupValue}'`,
619+
});
597620

598-
const { selectModalResult, selectModal } = await this.modalHelper.createSelectModal({ elements: results, skipButton: true, modalTitle: `Results for '${title}'` });
621+
if (selectModalResult.code === ModalResultCode.ERROR) {
622+
erroredFiles.push({ filePath: file.path, error: selectModalResult.error.message });
623+
selectModal.close();
624+
continue;
625+
}
599626

600-
if (selectModalResult.code === ModalResultCode.ERROR) {
601-
erroredFiles.push({ filePath: file.path, error: selectModalResult.error.message });
602-
selectModal.close();
603-
continue;
604-
}
627+
if (selectModalResult.code === ModalResultCode.CLOSE) {
628+
erroredFiles.push({ filePath: file.path, error: 'user canceled' });
629+
selectModal.close();
630+
canceled = true;
631+
continue;
632+
}
605633

606-
if (selectModalResult.code === ModalResultCode.CLOSE) {
607-
erroredFiles.push({ filePath: file.path, error: 'user canceled' });
608-
selectModal.close();
609-
canceled = true;
610-
continue;
611-
}
634+
if (selectModalResult.code === ModalResultCode.SKIP) {
635+
erroredFiles.push({ filePath: file.path, error: 'user skipped' });
636+
selectModal.close();
637+
continue;
638+
}
612639

613-
if (selectModalResult.code === ModalResultCode.SKIP) {
614-
erroredFiles.push({ filePath: file.path, error: 'user skipped' });
615-
selectModal.close();
616-
continue;
617-
}
640+
if (selectModalResult.data.selected.length === 0) {
641+
erroredFiles.push({ filePath: file.path, error: `no search results selected` });
642+
continue;
643+
}
644+
645+
const detailedResults = await this.queryDetails(selectModalResult.data.selected);
646+
await this.createMediaDbNotes(detailedResults, appendContent ? file : undefined);
618647

619-
if (selectModalResult.data.selected.length === 0) {
620-
erroredFiles.push({ filePath: file.path, error: `no search results selected` });
648+
selectModal.close();
649+
} else {
650+
erroredFiles.push({ filePath: file.path, error: `invalid lookup type` });
621651
continue;
622652
}
623-
624-
const detailedResults = await this.queryDetails(selectModalResult.data.selected);
625-
await this.createMediaDbNotes(detailedResults, appendContent ? file : undefined);
626-
627-
selectModal.close();
628653
}
629654
}
630655

src/modals/MediaDbFolderImportModal.ts

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
import type { App, ButtonComponent } from 'obsidian';
22
import { DropdownComponent, Modal, Setting, TextComponent, ToggleComponent } from 'obsidian';
33
import type MediaDbPlugin from '../main';
4+
import type { APIModel } from 'src/api/APIModel';
5+
import { BulkImportLookupMethod } from 'src/utils/BulkImportLookupMethod';
46

57
export class MediaDbFolderImportModal extends Modal {
68
plugin: MediaDbPlugin;
7-
onSubmit: (selectedAPI: string, titleFieldName: string, appendContent: boolean) => void;
9+
onSubmit: (selectedAPI: string, lookupMethod: string, fieldName: string, appendContent: boolean) => void;
810
selectedApi: string;
911
searchBtn?: ButtonComponent;
10-
titleFieldName: string;
12+
lookupMethod: string;
13+
fieldName: string;
1114
appendContent: boolean;
1215

13-
constructor(app: App, plugin: MediaDbPlugin, onSubmit: (selectedAPI: string, titleFieldName: string, appendContent: boolean) => void) {
16+
constructor(app: App, plugin: MediaDbPlugin, onSubmit: (selectedAPI: string, lookupMethod: string, fieldName: string, appendContent: boolean) => void) {
1417
super(app);
1518
this.plugin = plugin;
1619
this.onSubmit = onSubmit;
1720
this.selectedApi = plugin.apiManager.apis[0].apiName;
18-
this.titleFieldName = '';
21+
this.lookupMethod = BulkImportLookupMethod.TITLE;
22+
this.fieldName = '';
1923
this.appendContent = false;
2024
}
2125

2226
submit(): void {
23-
this.onSubmit(this.selectedApi, this.titleFieldName, this.appendContent);
27+
this.onSubmit(this.selectedApi, this.lookupMethod, this.fieldName, this.appendContent);
2428
this.close();
2529
}
2630

@@ -29,21 +33,19 @@ export class MediaDbFolderImportModal extends Modal {
2933

3034
contentEl.createEl('h2', { text: 'Import folder as Media DB entries' });
3135

32-
const apiSelectorWrapper = contentEl.createEl('div', { cls: 'media-db-plugin-list-wrapper' });
33-
const apiSelectorTextWrapper = apiSelectorWrapper.createEl('div', { cls: 'media-db-plugin-list-text-wrapper' });
34-
apiSelectorTextWrapper.createEl('span', { text: 'API to search', cls: 'media-db-plugin-list-text' });
35-
36-
const apiSelectorComponent = new DropdownComponent(apiSelectorWrapper);
37-
apiSelectorComponent.onChange((value: string) => {
38-
this.selectedApi = value;
39-
});
40-
for (const api of this.plugin.apiManager.apis) {
41-
apiSelectorComponent.addOption(api.apiName, api.apiName);
42-
}
43-
apiSelectorWrapper.appendChild(apiSelectorComponent.selectEl);
36+
this.createDropdownEl(
37+
contentEl,
38+
'API to search',
39+
(value: string) => {
40+
this.selectedApi = value;
41+
},
42+
this.plugin.apiManager.apis.map((api: APIModel) => {
43+
return { value: api.apiName, display: api.apiName };
44+
}),
45+
);
4446

4547
contentEl.createDiv({ cls: 'media-db-plugin-spacer' });
46-
contentEl.createEl('h3', { text: 'Append note content to Media DB entry.' });
48+
contentEl.createEl('h3', { text: 'Append note content to Media DB entry?' });
4749

4850
const appendContentToggleElementWrapper = contentEl.createEl('div', { cls: 'media-db-plugin-list-wrapper' });
4951
const appendContentToggleTextWrapper = appendContentToggleElementWrapper.createEl('div', { cls: 'media-db-plugin-list-text-wrapper' });
@@ -60,19 +62,38 @@ export class MediaDbFolderImportModal extends Modal {
6062
appendContentToggleComponentWrapper.appendChild(appendContentToggle.toggleEl);
6163

6264
contentEl.createDiv({ cls: 'media-db-plugin-spacer' });
63-
contentEl.createEl('h3', { text: 'The name of the metadata field that should be used as the title to query.' });
64-
65-
const placeholder = 'title';
66-
const titleFieldNameComponent = new TextComponent(contentEl);
67-
titleFieldNameComponent.inputEl.style.width = '100%';
68-
titleFieldNameComponent.setPlaceholder(placeholder);
69-
titleFieldNameComponent.onChange(value => (this.titleFieldName = value));
70-
titleFieldNameComponent.inputEl.addEventListener('keydown', ke => {
65+
contentEl.createEl('h3', { text: 'Media lookup method' });
66+
contentEl.createEl('p', {
67+
text: 'Choose whether to search the API by title (can return multiple results) or lookup directly using an ID (returns at most one result), and specify the name of the frontmatter property which contains the title or ID of the media.',
68+
});
69+
70+
this.createDropdownEl(
71+
contentEl,
72+
'Lookup media by',
73+
(value: string) => {
74+
this.lookupMethod = value;
75+
},
76+
[
77+
{ value: BulkImportLookupMethod.TITLE, display: 'Title' },
78+
{ value: BulkImportLookupMethod.ID, display: 'ID' },
79+
],
80+
);
81+
82+
contentEl.createDiv({ cls: 'media-db-plugin-spacer' });
83+
84+
const fieldNameWrapperEl = contentEl.createEl('div', { cls: 'media-db-plugin-list-wrapper' });
85+
const fieldNameLabelWrapperEl = fieldNameWrapperEl.createEl('div', { cls: 'media-db-plugin-list-text-wrapper' });
86+
fieldNameLabelWrapperEl.createEl('span', { text: 'Using the property named', cls: 'media-db-plugin-list-text' });
87+
88+
const fieldNameComponent = new TextComponent(fieldNameWrapperEl);
89+
fieldNameComponent.setPlaceholder('title / id');
90+
fieldNameComponent.onChange(value => (this.fieldName = value));
91+
fieldNameComponent.inputEl.addEventListener('keydown', ke => {
7192
if (ke.key === 'Enter') {
7293
this.submit();
7394
}
7495
});
75-
contentEl.appendChild(titleFieldNameComponent.inputEl);
96+
contentEl.appendChild(fieldNameWrapperEl);
7697

7798
contentEl.createDiv({ cls: 'media-db-plugin-spacer' });
7899

@@ -93,6 +114,19 @@ export class MediaDbFolderImportModal extends Modal {
93114
});
94115
}
95116

117+
createDropdownEl(parentEl: HTMLElement, label: string, onChange: (value: string) => void, options: { value: string; display: string }[]): void {
118+
const wrapperEl = parentEl.createEl('div', { cls: 'media-db-plugin-list-wrapper' });
119+
const labelWrapperEl = wrapperEl.createEl('div', { cls: 'media-db-plugin-list-text-wrapper' });
120+
labelWrapperEl.createEl('span', { text: label, cls: 'media-db-plugin-list-text' });
121+
122+
const dropDownComponent = new DropdownComponent(wrapperEl);
123+
dropDownComponent.onChange(onChange);
124+
for (const option of options) {
125+
dropDownComponent.addOption(option.value, option.display);
126+
}
127+
wrapperEl.appendChild(dropDownComponent.selectEl);
128+
}
129+
96130
onClose(): void {
97131
const { contentEl } = this;
98132
contentEl.empty();
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export enum BulkImportLookupMethod {
2+
ID = 'id',
3+
TITLE = 'title',
4+
}

0 commit comments

Comments
 (0)