Skip to content

Commit 92f3e6b

Browse files
committed
search by id
1 parent 62a4f2e commit 92f3e6b

File tree

13 files changed

+183
-29
lines changed

13 files changed

+183
-29
lines changed

src/api/APIManager.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ export class APIManager {
4242
}
4343
}
4444

45+
getApiByName(name: string): APIModel {
46+
for (const api of this.apis) {
47+
if (api.apiName === name) {
48+
return api;
49+
}
50+
}
51+
52+
return null;
53+
}
54+
4555
registerAPI(api: APIModel): void {
4656
this.apis.push(api);
4757
}

src/api/APIModel.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
import {MediaTypeModel} from '../models/MediaTypeModel';
22

33
export abstract class APIModel {
4-
apiName: string;
5-
apiUrl: string;
6-
apiDescription: string;
7-
types: string[];
4+
apiName: string;
5+
apiUrl: string;
6+
apiDescription: string;
7+
types: string[];
88

9-
/**
10-
* This function should query the api and return a list of matches. The matches should be caped at 20.
11-
*
12-
* @param title the title to query for
13-
*/
14-
abstract searchByTitle(title: string): Promise<MediaTypeModel[]>;
9+
/**
10+
* This function should query the api and return a list of matches. The matches should be caped at 20.
11+
*
12+
* @param title the title to query for
13+
*/
14+
abstract searchByTitle(title: string): Promise<MediaTypeModel[]>;
1515

16-
abstract getById(item: MediaTypeModel): Promise<MediaTypeModel>;
16+
abstract getById(item: MediaTypeModel): Promise<MediaTypeModel>;
1717

18-
hasType(type: string): boolean {
19-
return this.types.contains(type);
20-
}
18+
hasType(type: string): boolean {
19+
return this.types.contains(type);
20+
}
2121

22-
hasTypeOverlap(types: string[]): boolean {
23-
for (const type of types) {
24-
if (this.hasType(type)) {
25-
return true;
26-
}
27-
}
28-
return false;
29-
}
22+
hasTypeOverlap(types: string[]): boolean {
23+
for (const type of types) {
24+
if (this.hasType(type)) {
25+
return true;
26+
}
27+
}
28+
return false;
29+
}
3030
}

src/api/apis/MALAPI.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export class MALAPI extends APIModel {
7777
title: result.title,
7878
year: result.year ?? result.aired?.prop?.from?.year ?? '',
7979
dataSource: this.apiName,
80+
url: result.url,
8081
id: result.mal_id,
8182

8283
genres: result.genres?.map((x: any) => x.name) ?? [],
@@ -100,6 +101,7 @@ export class MALAPI extends APIModel {
100101
title: result.title,
101102
year: result.year ?? result.aired?.prop?.from?.year ?? '',
102103
dataSource: this.apiName,
104+
url: result.url,
103105
id: result.mal_id,
104106

105107
genres: result.genres?.map((x: any) => x.name) ?? [],

src/api/apis/OMDbAPI.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export class OMDbAPI extends APIModel {
3232
}
3333
const data = await fetchData.json();
3434

35+
if (data.Response === 'False') {
36+
throw Error(`Received error from ${this.apiName}: ${data.Error}`);
37+
}
38+
3539
if (!data.Search) {
3640
return [];
3741
}
@@ -76,14 +80,20 @@ export class OMDbAPI extends APIModel {
7680
}
7781

7882
const result = await fetchData.json();
83+
7984
console.log(result);
8085

86+
if (result.Response === 'False') {
87+
throw Error(`Received error from ${this.apiName}: ${result.Error}`);
88+
}
89+
8190
if (result.Type === 'movie') {
8291
const model = new MovieModel({
8392
type: 'movie',
8493
title: result.Title,
8594
year: result.Year,
8695
dataSource: this.apiName,
96+
url: `https://www.imdb.com/title/${result.imdbID}/`,
8797
id: result.imdbID,
8898

8999
genres: result.Genre?.split(', ') ?? [],
@@ -107,6 +117,7 @@ export class OMDbAPI extends APIModel {
107117
title: result.Title,
108118
year: result.Year,
109119
dataSource: this.apiName,
120+
url: `https://www.imdb.com/title/${result.imdbID}/`,
110121
id: result.imdbID,
111122

112123
genres: result.Genre?.split(', ') ?? [],

src/main.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {OMDbAPI} from './api/apis/OMDbAPI';
88
import {MediaDbAdvancedSearchModal} from './modals/MediaDbAdvancedSearchModal';
99
import {MediaDbSearchResultModal} from './modals/MediaDbSearchResultModal';
1010
import {MALAPI} from './api/apis/MALAPI';
11+
import {MediaDbIdSearchModal} from './modals/MediaDbIdSearchModal';
1112

1213
export default class MediaDbPlugin extends Plugin {
1314
settings: MediaDbPluginSettings;
@@ -18,15 +19,21 @@ export default class MediaDbPlugin extends Plugin {
1819

1920
// add icon to the left ribbon
2021
const ribbonIconEl = this.addRibbonIcon('database', 'Add new Media DB entry', (evt: MouseEvent) =>
21-
this.createMediaDbNote(),
22+
this.createMediaDbNote(this.openMediaDbSearchModal.bind(this)),
2223
);
2324
ribbonIconEl.addClass('obsidian-media-db-plugin-ribbon-class');
2425

2526
// register command to open search modal
2627
this.addCommand({
2728
id: 'open-media-db-search-modal',
2829
name: 'Add new Media DB entry',
29-
callback: () => this.createMediaDbNote(),
30+
callback: () => this.createMediaDbNote(this.openMediaDbSearchModal.bind(this)),
31+
});
32+
// register command to open id search modal
33+
this.addCommand({
34+
id: 'open-media-db-id-search-modal',
35+
name: 'Add new Media DB entry by id',
36+
callback: () => this.createMediaDbNote(this.openMediaDbIdSearchModal.bind(this)),
3037
});
3138

3239
// register the settings tab
@@ -40,9 +47,9 @@ export default class MediaDbPlugin extends Plugin {
4047
this.apiManager.registerAPI(new MALAPI(this));
4148
}
4249

43-
async createMediaDbNote(): Promise<void> {
50+
async createMediaDbNote(modal: () => Promise<MediaTypeModel>): Promise<void> {
4451
try {
45-
let data: MediaTypeModel = await this.openMediaDbSearchModal();
52+
let data: MediaTypeModel = await modal();
4653
console.log('MDB | Creating new note...');
4754

4855
data = await this.apiManager.queryDetailedInfo(data);
@@ -91,13 +98,22 @@ export default class MediaDbPlugin extends Plugin {
9198
new MediaDbAdvancedSearchModal(this.app, this.apiManager, (err, results) => {
9299
if (err) return reject(err);
93100
new MediaDbSearchResultModal(this.app, results, (err2, res) => {
94-
if (err) return reject(err2);
101+
if (err2) return reject(err2);
95102
resolve(res);
96103
}).open();
97104
}).open();
98105
}));
99106
}
100107

108+
async openMediaDbIdSearchModal(): Promise<MediaTypeModel> {
109+
return new Promise(((resolve, reject) => {
110+
new MediaDbIdSearchModal(this.app, this.apiManager, (err, res) => {
111+
if (err) return reject(err);
112+
resolve(res);
113+
}).open();
114+
}));
115+
}
116+
101117
async loadSettings() {
102118
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
103119
}

src/modals/MediaDbIdSearchModal.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import {App, ButtonComponent, DropdownComponent, Modal, Notice, Setting, TextComponent} from 'obsidian';
2+
import {MediaTypeModel} from '../models/MediaTypeModel';
3+
import {APIManager} from '../api/APIManager';
4+
5+
export class MediaDbIdSearchModal extends Modal {
6+
query: string;
7+
isBusy: boolean;
8+
apiManager: APIManager;
9+
searchBtn: ButtonComponent;
10+
selectedApi: string;
11+
onSubmit: (err: Error, result?: MediaTypeModel) => void;
12+
13+
constructor(app: App, apiManager: APIManager, onSubmit?: (err: Error, result?: MediaTypeModel) => void) {
14+
super(app);
15+
this.apiManager = apiManager;
16+
this.onSubmit = onSubmit;
17+
this.selectedApi = '';
18+
}
19+
20+
submitCallback(event: KeyboardEvent) {
21+
if (event.key === 'Enter') {
22+
this.search();
23+
}
24+
}
25+
26+
async search(): Promise<MediaTypeModel> {
27+
28+
console.log(this.selectedApi);
29+
30+
if (!this.query) {
31+
new Notice('MDB: no Id entered');
32+
return;
33+
}
34+
35+
if (!this.selectedApi) {
36+
new Notice('MDB: No API selected');
37+
return;
38+
}
39+
40+
if (!this.isBusy) {
41+
try {
42+
this.isBusy = true;
43+
this.searchBtn.setDisabled(false);
44+
this.searchBtn.setButtonText('Searching...');
45+
46+
console.log('MDB | query started with id ' + this.query);
47+
48+
const api = this.apiManager.getApiByName(this.selectedApi);
49+
if (!api) {
50+
this.onSubmit(new Error('the selected api does not exist'));
51+
}
52+
const res = await api.getById({id: this.query} as MediaTypeModel); // TODO: fix jank
53+
54+
// console.log(res)
55+
56+
this.onSubmit(null, res);
57+
} catch (e) {
58+
this.onSubmit(e);
59+
} finally {
60+
this.close();
61+
}
62+
}
63+
}
64+
65+
onOpen() {
66+
const {contentEl} = this;
67+
68+
contentEl.createEl('h2', {text: 'Search media db by id'});
69+
70+
const placeholder = 'Search by id';
71+
const searchComponent = new TextComponent(contentEl);
72+
searchComponent.inputEl.style.width = '100%';
73+
searchComponent.setPlaceholder(placeholder);
74+
searchComponent.onChange(value => (this.query = value));
75+
searchComponent.inputEl.addEventListener('keydown', this.submitCallback.bind(this));
76+
77+
contentEl.appendChild(searchComponent.inputEl);
78+
searchComponent.inputEl.focus();
79+
80+
const apiSelectorWrapper = contentEl.createEl('div', {cls: 'media-db-plugin-list-wrapper'});
81+
const apiSelectorTExtWrapper = apiSelectorWrapper.createEl('div', {cls: 'media-db-plugin-list-text-wrapper'});
82+
apiSelectorTExtWrapper.createEl('span', {text: 'API to search', cls: 'media-db-plugin-list-text'});
83+
84+
const apiSelectorComponent = new DropdownComponent(apiSelectorWrapper);
85+
apiSelectorComponent.onChange((value: string) => {
86+
this.selectedApi = value;
87+
});
88+
for (const api of this.apiManager.apis) {
89+
apiSelectorComponent.addOption(api.apiName, api.apiName);
90+
}
91+
apiSelectorWrapper.appendChild(apiSelectorComponent.selectEl);
92+
93+
new Setting(contentEl)
94+
.addButton(btn => btn.setButtonText('Cancel').onClick(() => this.close()))
95+
.addButton(btn => {
96+
return (this.searchBtn = btn
97+
.setButtonText('Ok')
98+
.setCta()
99+
.onClick(() => {
100+
this.search();
101+
}));
102+
});
103+
}
104+
105+
onClose() {
106+
const {contentEl} = this;
107+
contentEl.empty();
108+
}
109+
110+
111+
}

src/models/MediaTypeModel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export abstract class MediaTypeModel {
33
title: string;
44
year: string;
55
dataSource: string;
6+
url: string;
67
id: string;
78

89
abstract toMetaData(): string;

src/models/MovieModel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export class MovieModel extends MediaTypeModel {
77
title: string;
88
year: string;
99
dataSource: string;
10+
url: string;
1011
id: string;
1112

1213
genres: string[];

src/models/SeriesModel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export class SeriesModel extends MediaTypeModel {
77
title: string;
88
year: string;
99
dataSource: string;
10+
url: string;
1011
id: string;
1112

1213
genres: string[];

src/settings/Settings.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {App, PluginSettingTab, Setting} from 'obsidian';
22

33
import MediaDbPlugin from '../main';
4-
import {FolderSuggest} from './suggesters/FolderSuggester';
5-
import {FileSuggest} from './suggesters/FileSuggester';
4+
import {FolderSuggest} from './suggesters/FolderSuggest';
5+
import {FileSuggest} from './suggesters/FileSuggest';
66

77

88
export interface MediaDbPluginSettings {

0 commit comments

Comments
 (0)