commit
30208ea71a
25 changed files with 627 additions and 161 deletions
67
README.md
67
README.md
|
|
@ -4,32 +4,60 @@ A plugin that can query multiple APIs for movies, series, anime, games, music an
|
|||
|
||||
### Features
|
||||
#### Search by Title
|
||||
Search a movie, series, anime or game by its name across multiple APIs.
|
||||
Search a movie, series, anime, game, music release or wiki article by its name across multiple APIs.
|
||||
|
||||
#### Search by ID
|
||||
Allows you to search by an ID that varies from API to API. Concrete info can be found in the description of the individual APIs.
|
||||
Allows you to search by an ID that varies from API to API. Concrete information on this feature can be found in the description of the individual APIs.
|
||||
|
||||
#### Templates
|
||||
The plugin allows you to set a template note that gets added to the end of any note created by this plugin.
|
||||
The plugin also offers simple "template tgs". E.g. if the template includes `{{ title }}`, it will be replaced by the title of the movie, show or game.
|
||||
The plugin allows you to set a template note that gets added to the end of any note created by this plugin.
|
||||
The plugin also offers simple "template tgs". E.g. if the template includes `{{ title }}`, it will be replaced by the title of the movie, show or game.
|
||||
Note that "template tags" are surrounded with two curly braces and that the spaces inside the curly braces are important.
|
||||
|
||||
For arrays there are two special ways of displaying them.
|
||||
- using `{{ LIST:variable_name }}` will result in
|
||||
```
|
||||
- element 1
|
||||
- element 2
|
||||
- element 3
|
||||
- ...
|
||||
```
|
||||
```
|
||||
- element 1
|
||||
- element 2
|
||||
- element 3
|
||||
- ...
|
||||
```
|
||||
- using `{{ ENUM:variable_name }}` will result in
|
||||
```
|
||||
element 1, element 2, element 3, ...
|
||||
```
|
||||
```
|
||||
element 1, element 2, element 3, ...
|
||||
```
|
||||
|
||||
Available variables that can be used in template tags are the same variables from the metadata of the note.
|
||||
|
||||
I also published my own templates [here](https://github.com/mProjectsCode/obsidian-media-db-templates).
|
||||
|
||||
### How to install
|
||||
Currently, you have to manually download the zip archive from the latest release here on GitHub.
|
||||
After downloading, extract the archive into the `.obsidian/plugins` folder in your vault.
|
||||
|
||||
The folder structure should look like this:
|
||||
```
|
||||
[path to your vault]
|
||||
|_ .obsidian
|
||||
|_ plugins
|
||||
|_ obsidian-media-db-plugin
|
||||
|_ main.js
|
||||
|_ manifest.json
|
||||
|_ styles.css
|
||||
```
|
||||
|
||||
Once the plugin submission goes through, the plugin will also be installable directly through obsidian's plugin installer.
|
||||
|
||||
### How to use
|
||||
(pictures are coming)
|
||||
|
||||
Once you have installed this plugin, you will find a database icon in the left ribbon.
|
||||
When using this or the `Add new Media DB entry` command, a popup will open.
|
||||
Here you can enter the title of what you want to search for and then select in which APIs to search.
|
||||
|
||||
After clicking search, a new popup will open prompting you to select from the search results.
|
||||
Now you select the result you want and the plugin will cast it's magic and create a new note in your vault, that contains the metadata of the selected search result.
|
||||
|
||||
### Currently supported media types
|
||||
- movies (including specials)
|
||||
- series (including OVAs)
|
||||
|
|
@ -39,11 +67,12 @@ I also published my own templates [here](https://github.com/mProjectsCode/obsidi
|
|||
|
||||
### Currently supported APIs:
|
||||
| Name | Description | Supported formats | Authentification | Rate limiting | SFW filter support |
|
||||
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------- | ------------------------------ | ------------------ |
|
||||
|------------------------------------------------------|---------------------------------------------------------------------------------------------------|--------------------------------|------------------------------------------------------------------------------|--------------------------------|--------------------|
|
||||
| [Jikan](https://jikan.moe/) | Jikan is an API that uses [My Anime List](https://myanimelist.net) and offers metadata for anime. | series, movies, specials, OVAs | No | 60 per minute and 3 per second | Yes |
|
||||
| [OMDb](https://www.omdbapi.com/) | OMDb is an API that offers metadata for movie, series and games. | series, movies, games | Yes, you can get a free key here [here](https://www.omdbapi.com/apikey.aspx) | 1000 per day | No |
|
||||
| [MusicBrainz](https://musicbrainz.org/) | MusicBrainz is an API that offers information about music releases. | music releases | No | 50 per second | No |
|
||||
| [Wikipedia](https://en.wikipedia.org/wiki/Main_Page) | The Wikipedia API allows acces to all Wikipedia articles. | wiki articles | No | None | No | | | | | | |
|
||||
| [Wikipedia](https://en.wikipedia.org/wiki/Main_Page) | The Wikipedia API allows access to all Wikipedia articles. | wiki articles | No | None | No |
|
||||
| [Steam](https://store.steampowered.com/) | The Steam API offers information on all steam games. | games | No | 10000 per day | No |
|
||||
|
||||
#### Notes
|
||||
- [Jikan](https://jikan.moe/)
|
||||
|
|
@ -61,9 +90,15 @@ I also published my own templates [here](https://github.com/mProjectsCode/obsidi
|
|||
- you can find this ID in the URL
|
||||
- e.g. for "Rogue One" the URL looks like this `https://www.imdb.com/title/tt3748528/` so the ID is `tt3748528`
|
||||
- [MusicBrainz](https://musicbrainz.org/)
|
||||
- the id of a release is not easily accessibe, you are better of just searching by title
|
||||
- the id of a release is not easily accessible, you are better off just searching by title
|
||||
- [Wikipedia](https://en.wikipedia.org/wiki/Main_Page)
|
||||
- [here](https://en.wikipedia.org/wiki/Wikipedia:Finding_a_Wikidata_ID) is a guide to finding the Wikipedia ID for an article
|
||||
- [Steam](https://store.steampowered.com/)
|
||||
- you can find this ID in the URL
|
||||
- e.g. for "Factorio" the URL looks like this `https://store.steampowered.com/app/427520/Factorio/` so the ID is `427520`
|
||||
|
||||
### Problems, unexpected behavior or improvement suggestions?
|
||||
You are more than welcome to open an issue on [GitHub](https://github.com/mProjectsCode/obsidian-media-db-plugin/issues).
|
||||
|
||||
### Contributions
|
||||
Thank you for wanting to contribute to this project.
|
||||
|
|
|
|||
|
|
@ -25,9 +25,13 @@ export class APIManager {
|
|||
}
|
||||
|
||||
async queryDetailedInfo(item: MediaTypeModel): Promise<MediaTypeModel> {
|
||||
return await this.queryDetailedInfoById(item.id, item.dataSource);
|
||||
}
|
||||
|
||||
async queryDetailedInfoById(id: string, dataSource: string): Promise<MediaTypeModel> {
|
||||
for (const api of this.apis) {
|
||||
if (api.apiName === item.dataSource) {
|
||||
return api.getById(item);
|
||||
if (api.apiName === dataSource) {
|
||||
return api.getById(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export abstract class APIModel {
|
|||
*/
|
||||
abstract searchByTitle(title: string): Promise<MediaTypeModel[]>;
|
||||
|
||||
abstract getById(item: MediaTypeModel): Promise<MediaTypeModel>;
|
||||
abstract getById(id: string): Promise<MediaTypeModel>;
|
||||
|
||||
hasType(type: string): boolean {
|
||||
return this.types.contains(type);
|
||||
|
|
|
|||
|
|
@ -40,10 +40,10 @@ export class LocGovAPI extends APIModel {
|
|||
// return ret;
|
||||
}
|
||||
|
||||
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
|
||||
async getById(id: string): Promise<MediaTypeModel> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||
|
||||
const searchUrl = `https://www.loc.gov/item/${item.id}/?fo=json`;
|
||||
const searchUrl = `https://www.loc.gov/item/${encodeURIComponent(id)}/?fo=json`;
|
||||
const fetchData = await fetch(searchUrl);
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
||||
|
|
|
|||
|
|
@ -69,10 +69,10 @@ export class MALAPI extends APIModel {
|
|||
return ret;
|
||||
}
|
||||
|
||||
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
|
||||
async getById(id: string): Promise<MediaTypeModel> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||
|
||||
const searchUrl = `https://api.jikan.moe/v4/anime/${item.id}`;
|
||||
const searchUrl = `https://api.jikan.moe/v4/anime/${encodeURIComponent(id)}`;
|
||||
const fetchData = await fetch(searchUrl);
|
||||
|
||||
if (fetchData.status !== 200) {
|
||||
|
|
@ -107,9 +107,11 @@ export class MALAPI extends APIModel {
|
|||
released: true,
|
||||
premiere: (new Date(result.aired?.from)).toLocaleDateString() ?? 'unknown',
|
||||
|
||||
watched: false,
|
||||
lastWatched: '',
|
||||
personalRating: 0,
|
||||
userData: {
|
||||
watched: false,
|
||||
lastWatched: '',
|
||||
personalRating: 0,
|
||||
},
|
||||
} as MovieModel);
|
||||
|
||||
return model;
|
||||
|
|
@ -135,9 +137,11 @@ export class MALAPI extends APIModel {
|
|||
airedTo: (new Date(result.aired?.to)).toLocaleDateString() ?? 'unknown',
|
||||
airing: result.airing,
|
||||
|
||||
watched: false,
|
||||
lastWatched: '',
|
||||
personalRating: 0,
|
||||
userData: {
|
||||
watched: false,
|
||||
lastWatched: '',
|
||||
personalRating: 0,
|
||||
},
|
||||
} as SeriesModel);
|
||||
|
||||
return model;
|
||||
|
|
|
|||
|
|
@ -58,14 +58,14 @@ export class MusicBrainzAPI extends APIModel {
|
|||
return ret;
|
||||
}
|
||||
|
||||
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
|
||||
async getById(id: string): Promise<MediaTypeModel> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||
|
||||
const searchUrl = `https://musicbrainz.org/ws/2/release-group/${encodeURIComponent(item.id)}?inc=releases+artists+tags+ratings+genres&fmt=json`;
|
||||
const searchUrl = `https://musicbrainz.org/ws/2/release-group/${encodeURIComponent(id)}?inc=releases+artists+tags+ratings+genres&fmt=json`;
|
||||
const fetchData = await requestUrl({
|
||||
url: searchUrl,
|
||||
headers: {
|
||||
'User-Agent': `${pluginName}/0.1.7 (${contactEmail})`,
|
||||
'User-Agent': `${pluginName}/${mediaDbVersion} (${contactEmail})`,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -90,6 +90,10 @@ export class MusicBrainzAPI extends APIModel {
|
|||
genres: result.genres.map((g: any) => g.name),
|
||||
subType: result['primary-type'],
|
||||
rating: result.rating.value * 2,
|
||||
|
||||
userData: {
|
||||
personalRating: 0,
|
||||
},
|
||||
} as MusicReleaseModel);
|
||||
|
||||
return model;
|
||||
|
|
|
|||
|
|
@ -88,10 +88,10 @@ export class OMDbAPI extends APIModel {
|
|||
return ret;
|
||||
}
|
||||
|
||||
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
|
||||
async getById(id: string): Promise<MediaTypeModel> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||
|
||||
const searchUrl = `http://www.omdbapi.com/?i=${item.id}&apikey=${this.plugin.settings.OMDbKey}`;
|
||||
const searchUrl = `http://www.omdbapi.com/?i=${encodeURIComponent(id)}&apikey=${this.plugin.settings.OMDbKey}`;
|
||||
const fetchData = await fetch(searchUrl);
|
||||
|
||||
if (fetchData.status === 401) {
|
||||
|
|
@ -132,9 +132,11 @@ export class OMDbAPI extends APIModel {
|
|||
released: true,
|
||||
premiere: (new Date(result.Released)).toLocaleDateString() ?? 'unknown',
|
||||
|
||||
watched: false,
|
||||
lastWatched: '',
|
||||
personalRating: 0,
|
||||
userData: {
|
||||
watched: false,
|
||||
lastWatched: '',
|
||||
personalRating: 0,
|
||||
},
|
||||
} as MovieModel);
|
||||
|
||||
return model;
|
||||
|
|
@ -160,9 +162,11 @@ export class OMDbAPI extends APIModel {
|
|||
airedFrom: (new Date(result.Released)).toLocaleDateString() ?? 'unknown',
|
||||
airedTo: 'unknown',
|
||||
|
||||
watched: false,
|
||||
lastWatched: '',
|
||||
personalRating: 0,
|
||||
userData: {
|
||||
watched: false,
|
||||
lastWatched: '',
|
||||
personalRating: 0,
|
||||
},
|
||||
} as SeriesModel);
|
||||
|
||||
return model;
|
||||
|
|
@ -183,8 +187,10 @@ export class OMDbAPI extends APIModel {
|
|||
released: true,
|
||||
releaseDate: (new Date(result.Released)).toLocaleDateString() ?? 'unknown',
|
||||
|
||||
played: false,
|
||||
personalRating: 0,
|
||||
userData: {
|
||||
played: false,
|
||||
personalRating: 0,
|
||||
},
|
||||
} as GameModel);
|
||||
|
||||
return model;
|
||||
|
|
|
|||
119
src/api/apis/SteamAPI.ts
Normal file
119
src/api/apis/SteamAPI.ts
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
import {APIModel} from '../APIModel';
|
||||
import {MediaTypeModel} from '../../models/MediaTypeModel';
|
||||
import MediaDbPlugin from '../../main';
|
||||
import {GameModel} from '../../models/GameModel';
|
||||
import {debugLog} from '../../utils/Utils';
|
||||
import {requestUrl} from 'obsidian';
|
||||
import {MediaType} from '../../utils/MediaType';
|
||||
|
||||
export class SteamAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
typeMappings: Map<string, string>;
|
||||
|
||||
constructor(plugin: MediaDbPlugin) {
|
||||
super();
|
||||
|
||||
this.plugin = plugin;
|
||||
this.apiName = 'SteamAPI';
|
||||
this.apiDescription = 'A free API for all Steam games.';
|
||||
this.apiUrl = 'http://www.steampowered.com/';
|
||||
this.types = ['games'];
|
||||
this.typeMappings = new Map<string, string>();
|
||||
this.typeMappings.set('game', 'game');
|
||||
}
|
||||
|
||||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by Title`);
|
||||
|
||||
const searchUrl = `http://api.steampowered.com/ISteamApps/GetAppList/v0002/?format=json`;
|
||||
const fetchData = await requestUrl({
|
||||
url: searchUrl,
|
||||
});
|
||||
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
||||
}
|
||||
|
||||
const data = await fetchData.json;
|
||||
|
||||
debugLog(data);
|
||||
|
||||
let filteredData = [];
|
||||
|
||||
for (const app of data.applist.apps) {
|
||||
if (app.name.toLowerCase().includes(title.toLowerCase())) {
|
||||
filteredData.push(app);
|
||||
}
|
||||
if (filteredData.length > 20) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let ret: MediaTypeModel[] = [];
|
||||
|
||||
for (const result of filteredData) {
|
||||
ret.push(new GameModel({
|
||||
type: MediaType.Game,
|
||||
title: result.name,
|
||||
englishTitle: result.name,
|
||||
year: '',
|
||||
dataSource: this.apiName,
|
||||
id: result.appid,
|
||||
} as GameModel));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
async getById(id: string): Promise<MediaTypeModel> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||
|
||||
const searchUrl = `http://store.steampowered.com/api/appdetails?appids=${encodeURIComponent(id)}`;
|
||||
const fetchData = await requestUrl({
|
||||
url: searchUrl,
|
||||
});
|
||||
|
||||
if (fetchData.status !== 200) {
|
||||
throw Error(`MDB | Received status code ${fetchData.status} from an API.`);
|
||||
}
|
||||
|
||||
debugLog(await fetchData.json);
|
||||
|
||||
let result;
|
||||
for (const [key, value] of Object.entries(await fetchData.json)) {
|
||||
if (key == id) {
|
||||
result = value.data;
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
throw Error(`MDB | API returned invalid data.`);
|
||||
}
|
||||
|
||||
debugLog(result);
|
||||
|
||||
const model = new GameModel({
|
||||
type: MediaType.Game,
|
||||
title: result.name,
|
||||
englishTitle: result.name,
|
||||
year: (new Date(result.release_date.date)).getFullYear().toString(),
|
||||
dataSource: this.apiName,
|
||||
url: `https://store.steampowered.com/app/${result.id}`,
|
||||
id: result.steam_appid,
|
||||
|
||||
genres: result.genres?.map((x: any) => x.description) ?? [],
|
||||
onlineRating: Number.parseFloat(result.metacritic?.score ?? 0),
|
||||
image: result.header_image ?? '',
|
||||
|
||||
released: !result.release_date?.comming_soon,
|
||||
releaseDate: (new Date(result.release_date?.date)).toLocaleDateString() ?? 'unknown',
|
||||
|
||||
userData: {
|
||||
played: false,
|
||||
personalRating: 0,
|
||||
},
|
||||
} as GameModel);
|
||||
|
||||
return model;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -46,10 +46,10 @@ export class WikipediaAPI extends APIModel {
|
|||
return ret;
|
||||
}
|
||||
|
||||
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
|
||||
async getById(id: string): Promise<MediaTypeModel> {
|
||||
console.log(`MDB | api "${this.apiName}" queried by ID`);
|
||||
|
||||
const searchUrl = `https://en.wikipedia.org/w/api.php?action=query&prop=info&pageids=${item.id}&inprop=url&format=json&origin=*`;
|
||||
const searchUrl = `https://en.wikipedia.org/w/api.php?action=query&prop=info&pageids=${encodeURIComponent(id)}&inprop=url&format=json&origin=*`;
|
||||
const fetchData = await fetch(searchUrl);
|
||||
|
||||
if (fetchData.status !== 200) {
|
||||
|
|
@ -71,6 +71,8 @@ export class WikipediaAPI extends APIModel {
|
|||
wikiUrl: result.fullurl,
|
||||
lastUpdated: (new Date(result.touched)).toLocaleDateString() ?? 'unknown',
|
||||
length: result.length,
|
||||
|
||||
userData: {},
|
||||
} as WikiModel);
|
||||
|
||||
return model;
|
||||
|
|
|
|||
110
src/main.ts
110
src/main.ts
|
|
@ -1,8 +1,8 @@
|
|||
import {Notice, Plugin, TFile} from 'obsidian';
|
||||
import {FrontMatterCache, Notice, Plugin, TFile} from 'obsidian';
|
||||
import {DEFAULT_SETTINGS, MediaDbPluginSettings, MediaDbSettingTab} from './settings/Settings';
|
||||
import {APIManager} from './api/APIManager';
|
||||
import {MediaTypeModel} from './models/MediaTypeModel';
|
||||
import {replaceIllegalFileNameCharactersInString, replaceTags} from './utils/Utils';
|
||||
import {replaceIllegalFileNameCharactersInString} from './utils/Utils';
|
||||
import {OMDbAPI} from './api/apis/OMDbAPI';
|
||||
import {MediaDbAdvancedSearchModal} from './modals/MediaDbAdvancedSearchModal';
|
||||
import {MediaDbSearchResultModal} from './modals/MediaDbSearchResultModal';
|
||||
|
|
@ -10,17 +10,20 @@ import {MALAPI} from './api/apis/MALAPI';
|
|||
import {MediaDbIdSearchModal} from './modals/MediaDbIdSearchModal';
|
||||
import {WikipediaAPI} from './api/apis/WikipediaAPI';
|
||||
import {MusicBrainzAPI} from './api/apis/MusicBrainzAPI';
|
||||
import {MediaTypeManager} from './utils/MediaTypeManager';
|
||||
import {SteamAPI} from './api/apis/SteamAPI';
|
||||
|
||||
export default class MediaDbPlugin extends Plugin {
|
||||
settings: MediaDbPluginSettings;
|
||||
apiManager: APIManager;
|
||||
mediaTypeManager: MediaTypeManager;
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
// add icon to the left ribbon
|
||||
const ribbonIconEl = this.addRibbonIcon('database', 'Add new Media DB entry', (evt: MouseEvent) =>
|
||||
this.createMediaDbNote(this.openMediaDbSearchModal.bind(this)),
|
||||
this.createMediaDbNote(this.openMediaDbAdvancedSearchModal.bind(this)),
|
||||
);
|
||||
ribbonIconEl.addClass('obsidian-media-db-plugin-ribbon-class');
|
||||
|
||||
|
|
@ -28,7 +31,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
this.addCommand({
|
||||
id: 'open-media-db-search-modal',
|
||||
name: 'Add new Media DB entry',
|
||||
callback: () => this.createMediaDbNote(this.openMediaDbSearchModal.bind(this)),
|
||||
callback: () => this.createMediaDbNote(this.openMediaDbAdvancedSearchModal.bind(this)),
|
||||
});
|
||||
// register command to open id search modal
|
||||
this.addCommand({
|
||||
|
|
@ -36,6 +39,20 @@ export default class MediaDbPlugin extends Plugin {
|
|||
name: 'Add new Media DB entry by id',
|
||||
callback: () => this.createMediaDbNote(this.openMediaDbIdSearchModal.bind(this)),
|
||||
});
|
||||
// register command to update the open note
|
||||
this.addCommand({
|
||||
id: 'update-media-db-note',
|
||||
name: 'Update the open note, if it is a Media DB entry.',
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (!this.app.workspace.getActiveFile()) {
|
||||
return false;
|
||||
}
|
||||
if (!checking) {
|
||||
this.updateActiveNote()
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
// register the settings tab
|
||||
this.addSettingTab(new MediaDbSettingTab(this.app, this));
|
||||
|
|
@ -47,45 +64,43 @@ export default class MediaDbPlugin extends Plugin {
|
|||
this.apiManager.registerAPI(new MALAPI(this));
|
||||
this.apiManager.registerAPI(new WikipediaAPI(this));
|
||||
this.apiManager.registerAPI(new MusicBrainzAPI(this));
|
||||
this.apiManager.registerAPI(new SteamAPI(this));
|
||||
// this.apiManager.registerAPI(new LocGovAPI(this)); // TODO: parse data
|
||||
|
||||
this.mediaTypeManager = new MediaTypeManager(this.settings);
|
||||
}
|
||||
|
||||
async createMediaDbNote(modal: () => Promise<MediaTypeModel>): Promise<void> {
|
||||
try {
|
||||
let data: MediaTypeModel = await modal();
|
||||
console.log('MDB | Creating new note...');
|
||||
|
||||
data = await this.apiManager.queryDetailedInfo(data);
|
||||
|
||||
// console.log(data);
|
||||
await this.createMediaDbNoteFromModel(data);
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
new Notice(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
let fileContent = `---\n${data.toMetaData()}---\n`;
|
||||
async createMediaDbNoteFromModel(mediaTypeModel: MediaTypeModel): Promise<void> {
|
||||
try {
|
||||
console.log('MDB | Creating new note...');
|
||||
// console.log(mediaTypeModel);
|
||||
|
||||
let templateFile: TFile = null;
|
||||
let fileContent = `---\n${mediaTypeModel.toMetaData()}---\n`;
|
||||
|
||||
if (data.type === 'movie' && this.settings.movieTemplate) {
|
||||
templateFile = this.app.vault.getFiles().filter((f: TFile) => f.name === this.settings.movieTemplate).first();
|
||||
} else if (data.type === 'series' && this.settings.seriesTemplate) {
|
||||
templateFile = this.app.vault.getFiles().filter((f: TFile) => f.name === this.settings.seriesTemplate).first();
|
||||
} else if (data.type === 'game' && this.settings.gameTemplate) {
|
||||
templateFile = this.app.vault.getFiles().filter((f: TFile) => f.name === this.settings.gameTemplate).first();
|
||||
} else if (data.type === 'wiki' && this.settings.wikiTemplate) {
|
||||
templateFile = this.app.vault.getFiles().filter((f: TFile) => f.name === this.settings.wikiTemplate).first();
|
||||
} else if (data.type === 'musicRelease' && this.settings.musicReleaseTemplate) {
|
||||
templateFile = this.app.vault.getFiles().filter((f: TFile) => f.name === this.settings.musicReleaseTemplate).first();
|
||||
if (this.settings.templates) {
|
||||
fileContent += await this.mediaTypeManager.getContent(mediaTypeModel, this.app);
|
||||
}
|
||||
|
||||
if (templateFile) {
|
||||
let template = await this.app.vault.cachedRead(templateFile);
|
||||
// console.log(template);
|
||||
if (this.settings.templates) {
|
||||
template = replaceTags(template, data);
|
||||
}
|
||||
fileContent += template;
|
||||
}
|
||||
|
||||
const fileName = replaceIllegalFileNameCharactersInString(data.getFileName());
|
||||
const fileName = replaceIllegalFileNameCharactersInString(this.mediaTypeManager.getFileName(mediaTypeModel));
|
||||
const filePath = `${this.settings.folder.replace(/\/$/, '')}/${fileName}.md`;
|
||||
|
||||
const file = this.app.vault.getAbstractFileByPath(filePath);
|
||||
if (file) {
|
||||
await this.app.vault.delete(file);
|
||||
}
|
||||
|
||||
const targetFile = await this.app.vault.create(filePath, fileContent);
|
||||
|
||||
// open file
|
||||
|
|
@ -95,17 +110,18 @@ export default class MediaDbPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
await activeLeaf.openFile(targetFile, {state: {mode: 'source'}});
|
||||
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
new Notice(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
async openMediaDbSearchModal(): Promise<MediaTypeModel> {
|
||||
async openMediaDbAdvancedSearchModal(): Promise<MediaTypeModel> {
|
||||
return new Promise(((resolve, reject) => {
|
||||
new MediaDbAdvancedSearchModal(this.app, this.apiManager, (err, results) => {
|
||||
new MediaDbAdvancedSearchModal(this.app, this, (err, results) => {
|
||||
if (err) return reject(err);
|
||||
new MediaDbSearchResultModal(this.app, results, (err2, res) => {
|
||||
new MediaDbSearchResultModal(this.app, this, results, (err2, res) => {
|
||||
if (err2) return reject(err2);
|
||||
resolve(res);
|
||||
}).open();
|
||||
|
|
@ -115,18 +131,46 @@ export default class MediaDbPlugin extends Plugin {
|
|||
|
||||
async openMediaDbIdSearchModal(): Promise<MediaTypeModel> {
|
||||
return new Promise(((resolve, reject) => {
|
||||
new MediaDbIdSearchModal(this.app, this.apiManager, (err, res) => {
|
||||
new MediaDbIdSearchModal(this.app, this, (err, res) => {
|
||||
if (err) return reject(err);
|
||||
resolve(res);
|
||||
}).open();
|
||||
}));
|
||||
}
|
||||
|
||||
async updateActiveNote() {
|
||||
const activeFile: TFile = this.app.workspace.getActiveFile();
|
||||
if (!activeFile) {
|
||||
throw new Error('MDB | there is no active note');
|
||||
}
|
||||
|
||||
let metadata: FrontMatterCache = this.app.metadataCache.getFileCache(activeFile).frontmatter;
|
||||
|
||||
if (!metadata?.type || !metadata?.dataSource || !metadata?.id) {
|
||||
throw new Error('MDB | active note is not a Media DB entry or is missing metadata');
|
||||
}
|
||||
|
||||
delete metadata.position; // remove unnecessary data from the FrontMatterCache
|
||||
let oldMediaTypeModel = this.mediaTypeManager.createMediaTypeModelFromMediaType(metadata, metadata.type);
|
||||
|
||||
let newMediaTypeModel = await this.apiManager.queryDetailedInfoById(metadata.id, metadata.dataSource);
|
||||
if (!newMediaTypeModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
newMediaTypeModel = Object.assign(oldMediaTypeModel, newMediaTypeModel.getWithOutUserData());
|
||||
|
||||
console.log('MDB | deleting old entry');
|
||||
await this.app.vault.delete(activeFile);
|
||||
await this.createMediaDbNoteFromModel(newMediaTypeModel);
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
this.mediaTypeManager.updateTemplates(this.settings);
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
import {App, ButtonComponent, Component, Modal, Notice, Setting, TextComponent, ToggleComponent} from 'obsidian';
|
||||
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||
import {APIManager} from '../api/APIManager';
|
||||
import {debugLog} from '../utils/Utils';
|
||||
import MediaDbPlugin from '../main';
|
||||
|
||||
export class MediaDbAdvancedSearchModal extends Modal {
|
||||
query: string;
|
||||
isBusy: boolean;
|
||||
apiManager: APIManager;
|
||||
plugin: MediaDbPlugin;
|
||||
searchBtn: ButtonComponent;
|
||||
selectedApis: any;
|
||||
onSubmit: (err: Error, result?: MediaTypeModel[]) => void;
|
||||
|
||||
constructor(app: App, apiManager: APIManager, onSubmit?: (err: Error, result?: MediaTypeModel[]) => void) {
|
||||
constructor(app: App, plugin: MediaDbPlugin, onSubmit?: (err: Error, result?: MediaTypeModel[]) => void) {
|
||||
super(app);
|
||||
this.apiManager = apiManager;
|
||||
this.plugin = plugin;
|
||||
this.onSubmit = onSubmit;
|
||||
this.selectedApis = [];
|
||||
for (const api of this.apiManager.apis) {
|
||||
for (const api of this.plugin.apiManager.apis) {
|
||||
this.selectedApis[api.apiName] = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
|||
|
||||
console.log(`MDB | query started with title ${this.query}`);
|
||||
|
||||
const res = await this.apiManager.query(this.query, this.selectedApis);
|
||||
const res = await this.plugin.apiManager.query(this.query, this.selectedApis);
|
||||
this.onSubmit(null, res);
|
||||
} catch (e) {
|
||||
this.onSubmit(e);
|
||||
|
|
@ -84,7 +84,7 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
|||
contentEl.createEl('h3', {text: 'APIs to search'});
|
||||
|
||||
const apiToggleComponents: Component[] = [];
|
||||
for (const api of this.apiManager.apis) {
|
||||
for (const api of this.plugin.apiManager.apis) {
|
||||
const apiToggleListElementWrapper = contentEl.createEl('div', {cls: 'media-db-plugin-list-wrapper'});
|
||||
|
||||
const apiToggleTextWrapper = apiToggleListElementWrapper.createEl('div', {cls: 'media-db-plugin-list-text-wrapper'});
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
import {App, ButtonComponent, DropdownComponent, Modal, Notice, Setting, TextComponent} from 'obsidian';
|
||||
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||
import {APIManager} from '../api/APIManager';
|
||||
import {debugLog} from '../utils/Utils';
|
||||
import MediaDbPlugin from '../main';
|
||||
|
||||
export class MediaDbIdSearchModal extends Modal {
|
||||
query: string;
|
||||
isBusy: boolean;
|
||||
apiManager: APIManager;
|
||||
plugin: MediaDbPlugin;
|
||||
searchBtn: ButtonComponent;
|
||||
selectedApi: string;
|
||||
onSubmit: (err: Error, result?: MediaTypeModel) => void;
|
||||
|
||||
constructor(app: App, apiManager: APIManager, onSubmit?: (err: Error, result?: MediaTypeModel) => void) {
|
||||
constructor(app: App, plugin: MediaDbPlugin, onSubmit?: (err: Error, result?: MediaTypeModel) => void) {
|
||||
super(app);
|
||||
this.apiManager = apiManager;
|
||||
this.plugin = plugin;
|
||||
this.onSubmit = onSubmit;
|
||||
this.selectedApi = '';
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ export class MediaDbIdSearchModal extends Modal {
|
|||
|
||||
console.log(`MDB | query started with id ${this.query}`);
|
||||
|
||||
const api = this.apiManager.getApiByName(this.selectedApi);
|
||||
const api = this.plugin.apiManager.getApiByName(this.selectedApi);
|
||||
if (!api) {
|
||||
this.onSubmit(new Error('the selected api does not exist'));
|
||||
}
|
||||
|
|
@ -83,7 +83,7 @@ export class MediaDbIdSearchModal extends Modal {
|
|||
apiSelectorComponent.onChange((value: string) => {
|
||||
this.selectedApi = value;
|
||||
});
|
||||
for (const api of this.apiManager.apis) {
|
||||
for (const api of this.plugin.apiManager.apis) {
|
||||
apiSelectorComponent.addOption(api.apiName, api.apiName);
|
||||
}
|
||||
apiSelectorWrapper.appendChild(apiSelectorComponent.selectEl);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
import {App, SuggestModal} from 'obsidian';
|
||||
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||
import MediaDbPlugin from '../main';
|
||||
|
||||
export class MediaDbSearchResultModal extends SuggestModal<MediaTypeModel> {
|
||||
suggestion: MediaTypeModel[];
|
||||
plugin: MediaDbPlugin;
|
||||
onChoose: (error: Error, result?: MediaTypeModel) => void;
|
||||
|
||||
constructor(app: App, suggestion: MediaTypeModel[], onChoose: (error: Error, result?: MediaTypeModel) => void) {
|
||||
constructor(app: App, plugin: MediaDbPlugin, suggestion: MediaTypeModel[], onChoose: (error: Error, result?: MediaTypeModel) => void) {
|
||||
super(app);
|
||||
this.plugin = plugin;
|
||||
this.suggestion = suggestion;
|
||||
this.onChoose = onChoose;
|
||||
}
|
||||
|
|
@ -20,9 +23,9 @@ export class MediaDbSearchResultModal extends SuggestModal<MediaTypeModel> {
|
|||
|
||||
// Renders each suggestion item.
|
||||
renderSuggestion(item: MediaTypeModel, el: HTMLElement) {
|
||||
el.createEl('div', {text: item.getFileName()});
|
||||
el.createEl('div', {text: this.plugin.mediaTypeManager.getFileName(item)});
|
||||
el.createEl('small', {text: `${item.englishTitle}\n`});
|
||||
el.createEl('small', {text: `${item.type.toUpperCase()} from ${item.dataSource}`});
|
||||
el.createEl('small', {text: `${item.type.toUpperCase() + (item.subType ? ` (${item.subType})` : '')} from ${item.dataSource}`});
|
||||
}
|
||||
|
||||
// Perform action on the selected suggestion.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import {MediaTypeModel} from './MediaTypeModel';
|
||||
import {stringifyYaml} from 'obsidian';
|
||||
import {mediaDbTag} from '../utils/Utils';
|
||||
import {MediaType} from '../utils/MediaType';
|
||||
|
||||
|
||||
export class GameModel extends MediaTypeModel {
|
||||
type: string;
|
||||
subType: string;
|
||||
title: string;
|
||||
englishTitle: string;
|
||||
year: string;
|
||||
|
|
@ -19,26 +20,26 @@ export class GameModel extends MediaTypeModel {
|
|||
released: boolean;
|
||||
releaseDate: string;
|
||||
|
||||
played: boolean;
|
||||
personalRating: number;
|
||||
userData: {
|
||||
played: boolean;
|
||||
personalRating: number;
|
||||
};
|
||||
|
||||
|
||||
constructor(obj: any = {}) {
|
||||
super();
|
||||
|
||||
Object.assign(this, obj);
|
||||
}
|
||||
|
||||
toMetaData(): string {
|
||||
return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
|
||||
}
|
||||
|
||||
getFileName(): string {
|
||||
return this.title + (this.year ? ` (${this.year})` : '');
|
||||
this.type = this.getMediaType();
|
||||
}
|
||||
|
||||
getTags(): string[] {
|
||||
return [mediaDbTag, 'game'];
|
||||
}
|
||||
|
||||
getMediaType(): MediaType {
|
||||
return MediaType.Game;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import {MediaType} from '../utils/MediaType';
|
||||
import {YAMLConverter} from '../utils/YAMLConverter';
|
||||
|
||||
export abstract class MediaTypeModel {
|
||||
type: string;
|
||||
subType: string;
|
||||
title: string;
|
||||
englishTitle: string;
|
||||
year: string;
|
||||
|
|
@ -7,9 +11,20 @@ export abstract class MediaTypeModel {
|
|||
url: string;
|
||||
id: string;
|
||||
|
||||
abstract toMetaData(): string;
|
||||
userData: object;
|
||||
|
||||
abstract getFileName(): string;
|
||||
abstract getMediaType(): MediaType;
|
||||
|
||||
abstract getTags(): string[];
|
||||
|
||||
toMetaData(): string {
|
||||
return YAMLConverter.toYaml({...this.getWithOutUserData(), ...this.userData, tags: '#' + this.getTags().join('/')});
|
||||
}
|
||||
|
||||
getWithOutUserData(): object {
|
||||
const copy = JSON.parse(JSON.stringify(this));
|
||||
delete copy.userData;
|
||||
return copy;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import {MediaTypeModel} from './MediaTypeModel';
|
||||
import {stringifyYaml} from 'obsidian';
|
||||
import {mediaDbTag} from '../utils/Utils';
|
||||
import {MediaType} from '../utils/MediaType';
|
||||
|
||||
|
||||
export class MovieModel extends MediaTypeModel {
|
||||
type: string;
|
||||
subType: string;
|
||||
title: string;
|
||||
englishTitle: string;
|
||||
year: string;
|
||||
|
|
@ -21,27 +22,26 @@ export class MovieModel extends MediaTypeModel {
|
|||
released: boolean;
|
||||
premiere: string;
|
||||
|
||||
watched: boolean;
|
||||
lastWatched: string;
|
||||
personalRating: number;
|
||||
|
||||
userData: {
|
||||
watched: boolean;
|
||||
lastWatched: string;
|
||||
personalRating: number;
|
||||
};
|
||||
|
||||
constructor(obj: any = {}) {
|
||||
super();
|
||||
|
||||
Object.assign(this, obj);
|
||||
}
|
||||
|
||||
toMetaData(): string {
|
||||
return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
|
||||
}
|
||||
|
||||
getFileName(): string {
|
||||
return this.title + (this.year ? ` (${this.year})` : '');
|
||||
this.type = this.getMediaType();
|
||||
}
|
||||
|
||||
getTags(): string[] {
|
||||
return [mediaDbTag, 'tv', 'movie'];
|
||||
}
|
||||
|
||||
getMediaType(): MediaType {
|
||||
return MediaType.Movie;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import {MediaTypeModel} from './MediaTypeModel';
|
||||
import {stringifyYaml} from 'obsidian';
|
||||
import {mediaDbTag} from '../utils/Utils';
|
||||
import {MediaType} from '../utils/MediaType';
|
||||
|
||||
|
||||
export class MusicReleaseModel extends MediaTypeModel {
|
||||
type: string;
|
||||
subType: string;
|
||||
title: string;
|
||||
englishTitle: string;
|
||||
year: string;
|
||||
|
|
@ -14,27 +15,26 @@ export class MusicReleaseModel extends MediaTypeModel {
|
|||
|
||||
genres: string[];
|
||||
artists: string[];
|
||||
subType: string;
|
||||
rating: number;
|
||||
|
||||
personalRating: number;
|
||||
userData: {
|
||||
personalRating: number;
|
||||
};
|
||||
|
||||
constructor(obj: any = {}) {
|
||||
super();
|
||||
|
||||
Object.assign(this, obj);
|
||||
}
|
||||
|
||||
toMetaData(): string {
|
||||
return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
|
||||
}
|
||||
|
||||
getFileName(): string {
|
||||
return this.title + ' (' + this.artists.join(', ') + ' - ' + this.year + ' - ' + this.subType + ')';
|
||||
this.type = this.getMediaType();
|
||||
}
|
||||
|
||||
getTags(): string[] {
|
||||
return [mediaDbTag, 'music', 'album'];
|
||||
return [mediaDbTag, 'music', this.subType];
|
||||
}
|
||||
|
||||
getMediaType(): MediaType {
|
||||
return MediaType.MusicRelease;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import {MediaTypeModel} from './MediaTypeModel';
|
||||
import {stringifyYaml} from 'obsidian';
|
||||
import {mediaDbTag} from '../utils/Utils';
|
||||
import {MediaType} from '../utils/MediaType';
|
||||
|
||||
|
||||
export class SeriesModel extends MediaTypeModel {
|
||||
type: string;
|
||||
subType: string;
|
||||
title: string;
|
||||
englishTitle: string;
|
||||
year: string;
|
||||
|
|
@ -24,27 +25,26 @@ export class SeriesModel extends MediaTypeModel {
|
|||
airedFrom: string;
|
||||
airedTo: string;
|
||||
|
||||
watched: boolean;
|
||||
lastWatched: string;
|
||||
personalRating: number;
|
||||
|
||||
userData: {
|
||||
watched: boolean;
|
||||
lastWatched: string;
|
||||
personalRating: number;
|
||||
};
|
||||
|
||||
constructor(obj: any = {}) {
|
||||
super();
|
||||
|
||||
Object.assign(this, obj);
|
||||
}
|
||||
|
||||
toMetaData(): string {
|
||||
return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
|
||||
}
|
||||
|
||||
getFileName(): string {
|
||||
return this.title + ' (' + this.year + ')';
|
||||
this.type = this.getMediaType();
|
||||
}
|
||||
|
||||
getTags(): string[] {
|
||||
return [mediaDbTag, 'tv', 'series'];
|
||||
}
|
||||
|
||||
getMediaType(): MediaType {
|
||||
return MediaType.Series;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import {MediaTypeModel} from './MediaTypeModel';
|
||||
import {stringifyYaml} from 'obsidian';
|
||||
import {mediaDbTag} from '../utils/Utils';
|
||||
import {MediaType} from '../utils/MediaType';
|
||||
|
||||
|
||||
export class WikiModel extends MediaTypeModel {
|
||||
type: string;
|
||||
subType: string;
|
||||
title: string;
|
||||
englishTitle: string;
|
||||
year: string;
|
||||
|
|
@ -16,23 +17,22 @@ export class WikiModel extends MediaTypeModel {
|
|||
lastUpdated: string;
|
||||
length: number;
|
||||
|
||||
userData: {};
|
||||
|
||||
constructor(obj: any = {}) {
|
||||
super();
|
||||
|
||||
Object.assign(this, obj);
|
||||
}
|
||||
|
||||
toMetaData(): string {
|
||||
return stringifyYaml({...this, tags: '#' + this.getTags().join('/')});
|
||||
}
|
||||
|
||||
getFileName(): string {
|
||||
return this.title;
|
||||
this.type = this.getMediaType();
|
||||
}
|
||||
|
||||
getTags(): string[] {
|
||||
return [mediaDbTag, 'wiki'];
|
||||
}
|
||||
|
||||
getMediaType(): MediaType {
|
||||
return MediaType.Wiki;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,23 +9,39 @@ export interface MediaDbPluginSettings {
|
|||
folder: string,
|
||||
sfwFilter: boolean,
|
||||
OMDbKey: string,
|
||||
|
||||
movieTemplate: string,
|
||||
seriesTemplate: string,
|
||||
gameTemplate: string,
|
||||
wikiTemplate: string,
|
||||
musicReleaseTemplate: string,
|
||||
|
||||
movieFileNameTemplate: string,
|
||||
seriesFileNameTemplate: string,
|
||||
gameFileNameTemplate: string,
|
||||
wikiFileNameTemplate: string,
|
||||
musicReleaseFileNameTemplate: string,
|
||||
|
||||
templates: boolean,
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: MediaDbPluginSettings = {
|
||||
folder: '',
|
||||
folder: 'Media DB',
|
||||
sfwFilter: true,
|
||||
OMDbKey: '',
|
||||
|
||||
movieTemplate: '',
|
||||
seriesTemplate: '',
|
||||
gameTemplate: '',
|
||||
wikiTemplate: '',
|
||||
musicReleaseTemplate: '',
|
||||
|
||||
movieFileNameTemplate: '{{ title }} ({{ year }})',
|
||||
seriesFileNameTemplate: '{{ title }} ({{ year }})',
|
||||
gameFileNameTemplate: '{{ title }} ({{ year }})',
|
||||
wikiFileNameTemplate: '{{ title }}',
|
||||
musicReleaseFileNameTemplate: '{{ title }} (by {{ ENUM:artists }} - {{ year }})',
|
||||
|
||||
templates: true,
|
||||
};
|
||||
|
||||
|
|
@ -57,6 +73,29 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('OMDb API key')
|
||||
.setDesc('API key for "www.omdbapi.com".')
|
||||
.addText(cb => {
|
||||
cb.setPlaceholder('API key')
|
||||
.setValue(this.plugin.settings.OMDbKey)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.OMDbKey = data;
|
||||
this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('SFW filter')
|
||||
.setDesc('Only shows SFW results for APIs that offer filtering.')
|
||||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.sfwFilter)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.sfwFilter = data;
|
||||
this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Resolve {{ tags }} in templates')
|
||||
.setDesc('Whether to resolve {{ tags }} in templates. The spaces inside the curly braces are important.')
|
||||
|
|
@ -68,6 +107,9 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
containerEl.createEl('h3', {text: 'Template Settings'});
|
||||
// region templates
|
||||
new Setting(containerEl)
|
||||
.setName('Movie template')
|
||||
.setDesc('Template file to be used when creating a new note for a movie.')
|
||||
|
|
@ -132,29 +174,71 @@ export class MediaDbSettingTab extends PluginSettingTab {
|
|||
this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
// endregion
|
||||
|
||||
containerEl.createEl('h3', {text: 'File Name Settings'});
|
||||
// region file name templates
|
||||
new Setting(containerEl)
|
||||
.setName('OMDb API key')
|
||||
.setDesc('API key for "www.omdbapi.com".')
|
||||
.addText(cb => {
|
||||
cb.setPlaceholder('API key')
|
||||
.setValue(this.plugin.settings.OMDbKey)
|
||||
.setName('Movie file name template')
|
||||
.setDesc('Template for the file name used when creating a new note for a movie.')
|
||||
.addSearch(cb => {
|
||||
cb.setPlaceholder(`Example: ${DEFAULT_SETTINGS.movieFileNameTemplate}`)
|
||||
.setValue(this.plugin.settings.movieFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.OMDbKey = data;
|
||||
this.plugin.settings.movieFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('SFW filter')
|
||||
.setDesc('Only shows SFW results for APIs that offer filtering.')
|
||||
.addToggle(cb => {
|
||||
cb.setValue(this.plugin.settings.sfwFilter)
|
||||
.setName('Series file name template')
|
||||
.setDesc('Template for the file name used when creating a new note for a series.')
|
||||
.addSearch(cb => {
|
||||
cb.setPlaceholder(`Example: ${DEFAULT_SETTINGS.seriesFileNameTemplate}`)
|
||||
.setValue(this.plugin.settings.seriesFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.sfwFilter = data;
|
||||
this.plugin.settings.seriesFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Game file name template')
|
||||
.setDesc('Template for the file name used when creating a new note for a game.')
|
||||
.addSearch(cb => {
|
||||
cb.setPlaceholder(`Example: ${DEFAULT_SETTINGS.gameFileNameTemplate}`)
|
||||
.setValue(this.plugin.settings.gameFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.gameFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Wiki file name template')
|
||||
.setDesc('Template for the file name used when creating a new note for a wiki entry.')
|
||||
.addSearch(cb => {
|
||||
cb.setPlaceholder(`Example: ${DEFAULT_SETTINGS.wikiFileNameTemplate}`)
|
||||
.setValue(this.plugin.settings.wikiFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.wikiFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Music Release file name template')
|
||||
.setDesc('Template for the file name used when creating a new note for a music release.')
|
||||
.addSearch(cb => {
|
||||
cb.setPlaceholder(`Example: ${DEFAULT_SETTINGS.musicReleaseFileNameTemplate}`)
|
||||
.setValue(this.plugin.settings.musicReleaseFileNameTemplate)
|
||||
.onChange(data => {
|
||||
this.plugin.settings.musicReleaseFileNameTemplate = data;
|
||||
this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
// endregion
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
27
src/tests/TestAPI.ts
Normal file
27
src/tests/TestAPI.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import {APIModel} from '../api/APIModel';
|
||||
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||
import MediaDbPlugin from '../main';
|
||||
|
||||
export class TestAPI extends APIModel {
|
||||
plugin: MediaDbPlugin;
|
||||
|
||||
|
||||
constructor(plugin: MediaDbPlugin) {
|
||||
super();
|
||||
|
||||
this.plugin = plugin;
|
||||
this.apiName = 'TestAPI';
|
||||
this.apiDescription = 'A test API for automated testing.';
|
||||
this.apiUrl = '';
|
||||
this.types = [];
|
||||
}
|
||||
|
||||
|
||||
async getById(item: MediaTypeModel): Promise<MediaTypeModel> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
|
||||
return [] as MediaTypeModel[];
|
||||
}
|
||||
}
|
||||
7
src/utils/MediaType.ts
Normal file
7
src/utils/MediaType.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export enum MediaType {
|
||||
Movie = 'movie',
|
||||
Series = 'series',
|
||||
Game = 'game',
|
||||
MusicRelease = 'musicRelease',
|
||||
Wiki = 'wiki',
|
||||
}
|
||||
73
src/utils/MediaTypeManager.ts
Normal file
73
src/utils/MediaTypeManager.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import {MediaDbPluginSettings} from '../settings/Settings';
|
||||
import {MediaType} from './MediaType';
|
||||
import {MediaTypeModel} from '../models/MediaTypeModel';
|
||||
import {replaceTags} from './Utils';
|
||||
import {App, TFile} from 'obsidian';
|
||||
import {MovieModel} from '../models/MovieModel';
|
||||
import {SeriesModel} from '../models/SeriesModel';
|
||||
import {GameModel} from '../models/GameModel';
|
||||
import {WikiModel} from '../models/WikiModel';
|
||||
import {MusicReleaseModel} from '../models/MusicReleaseModel';
|
||||
|
||||
export class MediaTypeManager {
|
||||
mediaFileNameTemplateMap: Map<MediaType, string>;
|
||||
mediaTemplateMap: Map<MediaType, string>;
|
||||
|
||||
constructor(settings: MediaDbPluginSettings) {
|
||||
this.updateTemplates(settings);
|
||||
}
|
||||
|
||||
updateTemplates(settings: MediaDbPluginSettings) {
|
||||
this.mediaFileNameTemplateMap = new Map<MediaType, string>();
|
||||
this.mediaFileNameTemplateMap.set(MediaType.Movie, settings.movieFileNameTemplate);
|
||||
this.mediaFileNameTemplateMap.set(MediaType.Series, settings.seriesFileNameTemplate);
|
||||
this.mediaFileNameTemplateMap.set(MediaType.Game, settings.gameFileNameTemplate);
|
||||
this.mediaFileNameTemplateMap.set(MediaType.Wiki, settings.wikiFileNameTemplate);
|
||||
this.mediaFileNameTemplateMap.set(MediaType.MusicRelease, settings.musicReleaseFileNameTemplate);
|
||||
|
||||
this.mediaTemplateMap = new Map<MediaType, string>();
|
||||
this.mediaTemplateMap.set(MediaType.Movie, settings.movieTemplate);
|
||||
this.mediaTemplateMap.set(MediaType.Series, settings.seriesTemplate);
|
||||
this.mediaTemplateMap.set(MediaType.Game, settings.gameTemplate);
|
||||
this.mediaTemplateMap.set(MediaType.Wiki, settings.wikiTemplate);
|
||||
this.mediaTemplateMap.set(MediaType.MusicRelease, settings.musicReleaseTemplate);
|
||||
}
|
||||
|
||||
getFileName(mediaTypeModel: MediaTypeModel): string {
|
||||
return replaceTags(this.mediaFileNameTemplateMap.get(mediaTypeModel.getMediaType()), mediaTypeModel);
|
||||
}
|
||||
|
||||
async getContent(mediaTypeModel: MediaTypeModel, app: App) {
|
||||
const templateFileName = this.mediaTemplateMap.get(mediaTypeModel.getMediaType());
|
||||
|
||||
if (!templateFileName) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const templateFile: TFile = app.vault.getFiles().filter((f: TFile) => f.name === templateFileName).first();
|
||||
|
||||
if (!templateFile) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const template = await app.vault.cachedRead(templateFile);
|
||||
// console.log(template);
|
||||
return replaceTags(template, mediaTypeModel);
|
||||
}
|
||||
|
||||
createMediaTypeModelFromMediaType(obj: any, mediaType: MediaType): MediaTypeModel {
|
||||
if (mediaType === MediaType.Movie) {
|
||||
return new MovieModel(obj);
|
||||
} else if (mediaType === MediaType.Series) {
|
||||
return new SeriesModel(obj);
|
||||
} else if (mediaType === MediaType.Game) {
|
||||
return new GameModel(obj);
|
||||
} else if (mediaType === MediaType.Wiki) {
|
||||
return new WikiModel(obj);
|
||||
} else if (mediaType === MediaType.MusicRelease) {
|
||||
return new MusicReleaseModel(obj);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ export const mediaDbTag: string = 'mediaDB';
|
|||
export const mediaDbVersion: string = '0.1.8';
|
||||
export const debug: boolean = false;
|
||||
|
||||
|
||||
export function wrapAround(value: number, size: number): number {
|
||||
return ((value % size) + size) % size;
|
||||
}
|
||||
|
|
|
|||
39
src/utils/YAMLConverter.ts
Normal file
39
src/utils/YAMLConverter.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
export class YAMLConverter {
|
||||
static toYaml(obj: any): string {
|
||||
let output = '';
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
output += `${key}: ${YAMLConverter.toYamlString(value, 0)}\n`;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private static toYamlString(value: any, indentation: number): string {
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? 'true' : 'false';
|
||||
} else if (typeof value === 'number') {
|
||||
return value.toString();
|
||||
} else if (typeof value === 'string') {
|
||||
return '"' + value + '"';
|
||||
} else if (typeof value === 'object') {
|
||||
let output = '';
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
for (const valueElement of value) {
|
||||
output += `\n${YAMLConverter.calculateSpacing(indentation)} - ${YAMLConverter.toYamlString(valueElement, indentation + 1)}`;
|
||||
}
|
||||
} else {
|
||||
for (const [objKey, objValue] of Object.entries(value)) {
|
||||
output += `\n${YAMLConverter.calculateSpacing(indentation)} ${objKey}: ${YAMLConverter.toYamlString(objValue, indentation + 1)}`;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
private static calculateSpacing(indentation: number): string {
|
||||
return ' '.repeat(indentation * 4);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue