WIP season search

This commit is contained in:
ltctceplrm 2025-09-18 18:57:33 +02:00
parent 813c165b88
commit 0bb17177ee
9 changed files with 579 additions and 0 deletions

View file

@ -115,6 +115,7 @@ Now you select the result you want and the plugin will cast it's magic and creat
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| [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, manga, manwha, novels | 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 |
| [TMDB](https://www.themoviedb.org/) | TMDB is a API that offers community editable metadata for movies and series. | series, movies | Yes, by making an account [here](https://www.themoviedb.org/signup) and getting your `API Key` (**not** `API Read Access Token`) [here](https://www.themoviedb.org/settings/api) | 50 per second | Yes |
| [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 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 |

View file

@ -0,0 +1,130 @@
import { Notice, renderResults } from 'obsidian';
import type MediaDbPlugin from '../../main';
import type { MediaTypeModel } from '../../models/MediaTypeModel';
import { MovieModel } from '../../models/MovieModel';
import { MediaType } from '../../utils/MediaType';
import { APIModel } from '../APIModel';
export class TMDBMovieAPI extends APIModel {
plugin: MediaDbPlugin;
typeMappings: Map<string, string>;
apiDateFormat: string = 'YYYY-MM-DD';
constructor(plugin: MediaDbPlugin) {
super();
this.plugin = plugin;
this.apiName = 'TMDBMovieAPI';
this.apiDescription = 'A community built Movie DB.';
this.apiUrl = 'https://www.themoviedb.org/';
this.types = [MediaType.Movie];
this.typeMappings = new Map<string, string>();
this.typeMappings.set('movie', 'movie');
}
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
console.log(`MDB | api "${this.apiName}" queried by Title`);
if (!this.plugin.settings.TMDBKey) {
throw new Error(`MDB | API key for ${this.apiName} missing.`);
}
const searchUrl = `https://api.themoviedb.org/3/search/movie?api_key=${this.plugin.settings.TMDBKey}&query=${encodeURIComponent(title)}&include_adult=${this.plugin.settings.sfwFilter ? 'false' : 'true'}`;
const fetchData = await fetch(searchUrl);
if (fetchData.status === 401) {
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
}
if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
}
const data = await fetchData.json();
if (data.total_results === 0) {
if (data.Error === 'Movie not found!') {
return [];
}
throw Error(`MDB | Received error from ${this.apiName}: \n${JSON.stringify(data, undefined, 4)}`);
}
if (!data.results) {
return [];
}
// console.debug(data.results);
const ret: MediaTypeModel[] = [];
for (const result of data.results) {
ret.push(
new MovieModel({
type: 'movie',
title: result.original_title,
englishTitle: result.title,
year: result.release_date ? new Date(result.release_date).getFullYear().toString() : 'unknown',
dataSource: this.apiName,
id: result.id,
}),
);
}
return ret;
}
async getById(id: string): Promise<MediaTypeModel> {
console.log(`MDB | api "${this.apiName}" queried by ID`);
if (!this.plugin.settings.TMDBKey) {
throw Error(`MDB | API key for ${this.apiName} missing.`);
}
const searchUrl = `https://api.themoviedb.org/3/movie/${encodeURIComponent(id)}?api_key=${this.plugin.settings.TMDBKey}&append_to_response=credits`;
const fetchData = await fetch(searchUrl);
if (fetchData.status === 401) {
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
}
if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
}
const result = await fetchData.json();
// console.debug(result);
return new MovieModel({
type: 'movie',
title: result.title,
englishTitle: result.title,
year: result.release_date ? new Date(result.release_date).getFullYear().toString() : 'unknown',
premiere: this.plugin.dateFormatter.format(result.release_date, this.apiDateFormat) ?? 'unknown',
dataSource: this.apiName,
url: `https://www.themoviedb.org/movie/${result.id}`,
id: result.id,
plot: result.overview ?? '',
genres: result.genres.map((g: any) => g.name) ?? [],
writer: result.credits.crew.filter((c: any) => c.job === 'Screenplay').map((c: any) => c.name) ?? [],
director: result.credits.crew.filter((c: any) => c.job === 'Director').map((c: any) => c.name) ?? [],
studio: result.production_companies.map((s: any) => s.name) ?? [],
duration: result.runtime ?? 'unknown',
onlineRating: result.vote_average,
actors: result.credits.cast.map((c: any) => c.name).slice(0, 5) ?? [],
image: `https://image.tmdb.org/t/p/w780${result.poster_path}`,
released: ['Released'].includes(result.status),
streamingServices: [],
userData: {
watched: false,
lastWatched: '',
personalRating: 0,
},
});
}
getDisabledMediaTypes(): MediaType[] {
return this.plugin.settings.TMDBMovieAPI_disabledMediaTypes as MediaType[];
}
}

View file

@ -0,0 +1,169 @@
import { Notice, renderResults } from 'obsidian';
import type MediaDbPlugin from '../../main';
import type { MediaTypeModel } from '../../models/MediaTypeModel';
import { MediaType } from '../../utils/MediaType';
import { APIModel } from '../APIModel';
import { SeasonModel } from '../../models/SeasonModel';
export class TMDBSeasonAPI extends APIModel {
plugin: MediaDbPlugin;
typeMappings: Map<string, string>;
apiDateFormat: string = 'YYYY-MM-DD';
constructor(plugin: MediaDbPlugin) {
super();
this.plugin = plugin;
this.apiName = 'TMDBSeasonAPI';
this.apiDescription = 'A community built Series DB (seasons).';
this.apiUrl = 'https://www.themoviedb.org/';
this.types = [MediaType.Season];
this.typeMappings = new Map<string, string>();
this.typeMappings.set('tv', 'season');
}
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
console.log(`MDB | api "${this.apiName}" queried by Title`);
if (!this.plugin.settings.TMDBKey) {
throw new Error(`MDB | API key for ${this.apiName} missing.`);
}
// 1) Search for series
const searchUrl = `https://api.themoviedb.org/3/search/tv?api_key=${this.plugin.settings.TMDBKey}&query=${encodeURIComponent(title)}&include_adult=${this.plugin.settings.sfwFilter ? 'false' : 'true'}`;
const searchResp = await fetch(searchUrl);
if (searchResp.status === 401) {
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
}
if (searchResp.status !== 200) {
throw Error(`MDB | Received status code ${searchResp.status} from ${this.apiName}.`);
}
const searchData = await searchResp.json();
if (searchData.total_results === 0 || !searchData.results) {
return [];
}
const ret: MediaTypeModel[] = [];
// 2) For each series result, fetch its seasons and flatten into SeasonModel entries (cap total to 20)
for (const series of searchData.results) {
if (ret.length >= 20) break;
const tvId = series.id;
const seriesUrl = `https://api.themoviedb.org/3/tv/${encodeURIComponent(tvId)}?api_key=${this.plugin.settings.TMDBKey}`;
const tvResp = await fetch(seriesUrl);
if (tvResp.status === 401) {
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
}
if (tvResp.status !== 200) {
// Skip this series if it fails; do not abort the whole search
console.warn(`MDB | Skipping series ${tvId} due to status ${tvResp.status}`);
continue;
}
const tvData = await tvResp.json();
const seriesName = tvData?.name ?? series?.name ?? series?.original_name ?? '';
if (Array.isArray(tvData?.seasons)) {
for (const season of tvData.seasons) {
if (ret.length >= 20) break;
// Some seasons (e.g., specials) may have limited metadata; handle gracefully
const seasonNumber = season.season_number ?? 0;
const airDate = season.air_date ?? '';
const titleText = `${seriesName} - Season ${seasonNumber}`;
ret.push(
new SeasonModel({
// SeasonModel constructor sets type to MediaType.Series internally
title: titleText,
englishTitle: titleText,
year: airDate ? new Date(airDate).getFullYear().toString() : 'unknown',
dataSource: this.apiName,
id: `${tvId}-S${seasonNumber}`,
seasonTitle: season.name ?? titleText,
seasonNumber: seasonNumber,
episodes: season.episode_count ?? 0,
airedFrom: this.plugin.dateFormatter.format(airDate, this.apiDateFormat) ?? 'unknown',
airedTo: 'unknown',
plot: season.overview ?? '',
image: season.poster_path ? `https://image.tmdb.org/t/p/w780${season.poster_path}` : '',
userData: { watched: false, lastWatched: '', personalRating: 0 },
}),
);
}
}
}
return ret;
}
async getById(id: string): Promise<MediaTypeModel> {
console.log(`MDB | api "${this.apiName}" queried by ID`);
if (!this.plugin.settings.TMDBKey) {
throw Error(`MDB | API key for ${this.apiName} missing.`);
}
// Expect season ids like "12345-S2"
const m = /^(\d+)-S(\d+)$/.exec(id);
if (!m) {
throw Error(`MDB | Invalid season id "${id}". Expected format "<tvId>-S<seasonNumber>".`);
}
const tvId = m[1];
const seasonNumber = m[2];
const seasonUrl = `https://api.themoviedb.org/3/tv/${encodeURIComponent(tvId)}/season/${encodeURIComponent(seasonNumber)}?api_key=${this.plugin.settings.TMDBKey}`;
const seasonResp = await fetch(seasonUrl);
if (seasonResp.status === 401) {
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
}
if (seasonResp.status !== 200) {
throw Error(`MDB | Received status code ${seasonResp.status} from ${this.apiName}.`);
}
const seasonData = await seasonResp.json();
// Fetch parent series to build consistent titles
const seriesUrl = `https://api.themoviedb.org/3/tv/${encodeURIComponent(tvId)}?api_key=${this.plugin.settings.TMDBKey}`;
const seriesResp = await fetch(seriesUrl);
if (seriesResp.status === 401) {
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
}
if (seriesResp.status !== 200) {
throw Error(`MDB | Received status code ${seriesResp.status} from ${this.apiName}.`);
}
const seriesData = await seriesResp.json();
const seriesName = seriesData?.name ?? '';
const airDate = seasonData.air_date ?? '';
const titleText = `${seriesName} - Season ${seasonData.season_number}`;
return new SeasonModel({
title: titleText,
englishTitle: titleText,
year: airDate ? new Date(airDate).getFullYear().toString() : 'unknown',
dataSource: this.apiName,
id: `${tvId}-S${seasonData.season_number}`,
seasonTitle: seasonData.name ?? titleText,
seasonNumber: seasonData.season_number ?? Number(seasonNumber),
episodes: Array.isArray(seasonData.episodes) ? seasonData.episodes.length : (seasonData.episodes ?? 0),
airedFrom: this.plugin.dateFormatter.format(airDate, this.apiDateFormat) ?? 'unknown',
airedTo: 'unknown',
plot: seasonData.overview ?? '',
image: seasonData.poster_path ? `https://image.tmdb.org/t/p/w780${seasonData.poster_path}` : '',
userData: { watched: false, lastWatched: '', personalRating: 0 },
});
}
// Settings didnt define TMDBSeasonAPIdisabledMediaTypes yet; return an empty list for now
getDisabledMediaTypes(): MediaType[] {
return [];
}
}

View file

@ -0,0 +1,131 @@
import { Notice, renderResults } from 'obsidian';
import type MediaDbPlugin from '../../main';
import type { MediaTypeModel } from '../../models/MediaTypeModel';
import { SeriesModel } from '../../models/SeriesModel';
import { MediaType } from '../../utils/MediaType';
import { APIModel } from '../APIModel';
export class TMDBSeriesAPI extends APIModel {
plugin: MediaDbPlugin;
typeMappings: Map<string, string>;
apiDateFormat: string = 'YYYY-MM-DD';
constructor(plugin: MediaDbPlugin) {
super();
this.plugin = plugin;
this.apiName = 'TMDBSeriesAPI';
this.apiDescription = 'A community built Series DB.';
this.apiUrl = 'https://www.themoviedb.org/';
this.types = [MediaType.Series];
this.typeMappings = new Map<string, string>();
this.typeMappings.set('tv', 'series');
}
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
console.log(`MDB | api "${this.apiName}" queried by Title`);
if (!this.plugin.settings.TMDBKey) {
throw new Error(`MDB | API key for ${this.apiName} missing.`);
}
const searchUrl = `https://api.themoviedb.org/3/search/tv?api_key=${this.plugin.settings.TMDBKey}&query=${encodeURIComponent(title)}&include_adult=${this.plugin.settings.sfwFilter ? 'false' : 'true'}`;
const fetchData = await fetch(searchUrl);
if (fetchData.status === 401) {
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
}
if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
}
const data = await fetchData.json();
if (data.total_results === 0) {
if (data.Error === 'Series not found!') {
return [];
}
throw Error(`MDB | Received error from ${this.apiName}: \n${JSON.stringify(data, undefined, 4)}`);
}
if (!data.results) {
return [];
}
// console.debug(data.results);
const ret: MediaTypeModel[] = [];
for (const result of data.results) {
ret.push(
new SeriesModel({
type: 'series',
title: result.original_name,
englishTitle: result.name,
year: result.first_air_date ? new Date(result.first_air_date).getFullYear().toString() : 'unknown',
dataSource: this.apiName,
id: result.id,
}),
);
}
return ret;
}
async getById(id: string): Promise<MediaTypeModel> {
console.log(`MDB | api "${this.apiName}" queried by ID`);
if (!this.plugin.settings.TMDBKey) {
throw Error(`MDB | API key for ${this.apiName} missing.`);
}
const searchUrl = `https://api.themoviedb.org/3/tv/${encodeURIComponent(id)}?api_key=${this.plugin.settings.TMDBKey}&append_to_response=credits`;
const fetchData = await fetch(searchUrl);
if (fetchData.status === 401) {
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
}
if (fetchData.status !== 200) {
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
}
const result = await fetchData.json();
// console.debug(result);
return new SeriesModel({
type: 'series',
title: result.original_name,
englishTitle: result.name,
year: result.first_air_date ? new Date(result.first_air_date).getFullYear().toString() : 'unknown',
dataSource: this.apiName,
url: `https://www.themoviedb.org/tv/${result.id}`,
id: result.id,
plot: result.overview ?? '',
genres: result.genres.map((g: any) => g.name) ?? [],
writer: result.created_by.map((c: any) => c.name) ?? [],
studio: result.production_companies.map((s: any) => s.name) ?? [],
episodes: result.number_of_episodes,
duration: result.episode_run_time[0] ?? 'unknown',
onlineRating: result.vote_average,
actors: result.credits.cast.map((c: any) => c.name).slice(0, 5) ?? [],
image: `https://image.tmdb.org/t/p/w780${result.poster_path}`,
released: ['Returning Series', 'Cancelled', 'Ended'].includes(result.status),
streamingServices: [],
airing: ['Returning Series'].includes(result.status),
airedFrom: this.plugin.dateFormatter.format(result.first_air_date, this.apiDateFormat) ?? 'unknown',
airedTo: ['Returning Series'].includes(result.status) ? 'unknown' : (this.plugin.dateFormatter.format(result.last_air_date, this.apiDateFormat) ?? 'unknown'),
userData: {
watched: false,
lastWatched: '',
personalRating: 0,
},
});
}
getDisabledMediaTypes(): MediaType[] {
return this.plugin.settings.TMDBSeriesAPI_disabledMediaTypes as MediaType[];
}
}

View file

@ -13,6 +13,9 @@ import { MusicBrainzAPI } from './api/apis/MusicBrainzAPI';
import { OMDbAPI } from './api/apis/OMDbAPI';
import { OpenLibraryAPI } from './api/apis/OpenLibraryAPI';
import { SteamAPI } from './api/apis/SteamAPI';
import { TMDBSeriesAPI } from './api/apis/TMDBSeriesAPI';
import { TMDBSeasonAPI } from './api/apis/TMDBSeasonAPI';
import { TMDBMovieAPI } from './api/apis/TMDBMovieAPI';
import { WikipediaAPI } from './api/apis/WikipediaAPI';
import { ConfirmOverwriteModal } from './modals/ConfirmOverwriteModal';
import type { MediaTypeModel } from './models/MediaTypeModel';
@ -56,6 +59,9 @@ export default class MediaDbPlugin extends Plugin {
this.apiManager.registerAPI(new WikipediaAPI(this));
this.apiManager.registerAPI(new MusicBrainzAPI(this));
this.apiManager.registerAPI(new SteamAPI(this));
this.apiManager.registerAPI(new TMDBSeriesAPI(this));
this.apiManager.registerAPI(new TMDBSeasonAPI(this));
this.apiManager.registerAPI(new TMDBMovieAPI(this));
this.apiManager.registerAPI(new BoardGameGeekAPI(this));
this.apiManager.registerAPI(new OpenLibraryAPI(this));
this.apiManager.registerAPI(new ComicVineAPI(this));

80
src/models/SeasonModel.ts Normal file
View file

@ -0,0 +1,80 @@
import { MediaType } from '../utils/MediaType';
import type { ModelToData } from '../utils/Utils';
import { mediaDbTag, migrateObject } from '../utils/Utils';
import { MediaTypeModel } from './MediaTypeModel';
export type SeasonData = ModelToData<SeasonModel>;
export class SeasonModel extends MediaTypeModel {
seasonNumber: number;
seasonTitle: string;
episodes: number;
plot: string;
genres: string[];
writer: string[];
studio: string[];
duration: string;
onlineRating: number;
actors: string[];
image: string;
released: boolean;
streamingServices: string[];
airing: boolean;
airedFrom: string;
airedTo: string;
userData: {
watched: boolean;
lastWatched: string;
personalRating: number;
};
constructor(obj: SeasonData) {
super();
this.seasonTitle = '';
this.seasonNumber = 0;
this.episodes = 0;
this.plot = '';
this.genres = [];
this.writer = [];
this.studio = [];
this.duration = '';
this.onlineRating = 0;
this.actors = [];
this.image = '';
this.released = false;
this.streamingServices = [];
this.airing = false;
this.airedFrom = '';
this.airedTo = '';
this.userData = {
watched: false,
lastWatched: '',
personalRating: 0,
};
migrateObject(this, obj, this);
if (!obj.hasOwnProperty('userData')) {
migrateObject(this.userData, obj, this.userData);
}
this.type = this.getMediaType();
}
getTags(): string[] {
return [mediaDbTag, 'tv', 'season'];
}
getMediaType(): MediaType {
return MediaType.Season;
}
getSummary(): string {
return 'Season ' + this.seasonNumber + '(' + this.year + ')';
}
}

View file

@ -13,6 +13,7 @@ import { FolderSuggest } from './suggesters/FolderSuggest';
export interface MediaDbPluginSettings {
OMDbKey: string;
TMDBKey: string;
MobyGamesKey: string;
GiantBombKey: string;
ComicVineKey: string;
@ -23,6 +24,9 @@ export interface MediaDbPluginSettings {
useDefaultFrontMatter: boolean;
enableTemplaterIntegration: boolean;
OMDbAPI_disabledMediaTypes: MediaType[];
TMDBSeriesAPI_disabledMediaTypes: MediaType[];
TMDBSeasonAPI_disabledMediaTypes: MediaType[];
TMDBMovieAPI_disabledMediaTypes: MediaType[];
MALAPI_disabledMediaTypes: MediaType[];
MALAPIManga_disabledMediaTypes: MediaType[];
ComicVineAPI_disabledMediaTypes: MediaType[];
@ -35,6 +39,7 @@ export interface MediaDbPluginSettings {
OpenLibraryAPI_disabledMediaTypes: MediaType[];
movieTemplate: string;
seriesTemplate: string;
seasonTemplate: string;
mangaTemplate: string;
gameTemplate: string;
wikiTemplate: string;
@ -44,6 +49,7 @@ export interface MediaDbPluginSettings {
movieFileNameTemplate: string;
seriesFileNameTemplate: string;
seasonFileNameTemplate: string;
mangaFileNameTemplate: string;
gameFileNameTemplate: string;
wikiFileNameTemplate: string;
@ -53,6 +59,7 @@ export interface MediaDbPluginSettings {
moviePropertyConversionRules: string;
seriesPropertyConversionRules: string;
seasonPropertyConversionRules: string;
mangaPropertyConversionRules: string;
gamePropertyConversionRules: string;
wikiPropertyConversionRules: string;
@ -62,6 +69,7 @@ export interface MediaDbPluginSettings {
movieFolder: string;
seriesFolder: string;
seasonFolder: string;
mangaFolder: string;
gameFolder: string;
wikiFolder: string;
@ -76,6 +84,7 @@ export interface MediaDbPluginSettings {
const DEFAULT_SETTINGS: MediaDbPluginSettings = {
OMDbKey: '',
TMDBKey: '',
MobyGamesKey: '',
GiantBombKey: '',
ComicVineKey: '',
@ -86,6 +95,9 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = {
useDefaultFrontMatter: true,
enableTemplaterIntegration: false,
OMDbAPI_disabledMediaTypes: [],
TMDBSeriesAPI_disabledMediaTypes: [],
TMDBSeasonAPI_disabledMediaTypes: [],
TMDBMovieAPI_disabledMediaTypes: [],
MALAPI_disabledMediaTypes: [],
MALAPIManga_disabledMediaTypes: [],
ComicVineAPI_disabledMediaTypes: [],
@ -98,6 +110,7 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = {
OpenLibraryAPI_disabledMediaTypes: [],
movieTemplate: '',
seriesTemplate: '',
seasonTemplate: '',
mangaTemplate: '',
gameTemplate: '',
wikiTemplate: '',
@ -107,6 +120,7 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = {
movieFileNameTemplate: '{{ title }} ({{ year }})',
seriesFileNameTemplate: '{{ title }} ({{ year }})',
seasonFileNameTemplate: '{{ title }} ({{ year }})',
mangaFileNameTemplate: '{{ title }} ({{ year }})',
gameFileNameTemplate: '{{ title }} ({{ year }})',
wikiFileNameTemplate: '{{ title }}',
@ -116,6 +130,7 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = {
moviePropertyConversionRules: '',
seriesPropertyConversionRules: '',
seasonPropertyConversionRules: '',
mangaPropertyConversionRules: '',
gamePropertyConversionRules: '',
wikiPropertyConversionRules: '',
@ -125,6 +140,7 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = {
movieFolder: 'Media DB/movies',
seriesFolder: 'Media DB/series',
seasonFolder: 'Media DB/series',
mangaFolder: 'Media DB/comics',
gameFolder: 'Media DB/games',
wikiFolder: 'Media DB/wiki',
@ -396,6 +412,19 @@ export class MediaDbSettingTab extends PluginSettingTab {
});
});
new Setting(containerEl)
.setName('Season folder')
.setDesc('Where newly imported seasons should be placed.')
.addSearch(cb => {
new FolderSuggest(this.app, cb.inputEl);
cb.setPlaceholder(DEFAULT_SETTINGS.seasonFolder)
.setValue(this.plugin.settings.seriesFolder)
.onChange(data => {
this.plugin.settings.seasonFolder = data;
void this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Comic and manga folder')
.setDesc('Where newly imported comics and manga should be placed.')
@ -503,6 +532,19 @@ export class MediaDbSettingTab extends PluginSettingTab {
});
});
new Setting(containerEl)
.setName('Season template')
.setDesc('Template file to be used when creating a new note for a season.')
.addSearch(cb => {
new FileSuggest(this.app, cb.inputEl);
cb.setPlaceholder('Example: seasonTemplate.md')
.setValue(this.plugin.settings.seasonTemplate)
.onChange(data => {
this.plugin.settings.seasonTemplate = data;
void this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Manga and Comics template')
.setDesc('Template file to be used when creating a new note for a manga or a comic.')
@ -609,6 +651,18 @@ export class MediaDbSettingTab extends PluginSettingTab {
});
});
new Setting(containerEl)
.setName('Season file name template')
.setDesc('Template for the file name used when creating a new note for a season.')
.addText(cb => {
cb.setPlaceholder(`Example: ${DEFAULT_SETTINGS.seasonFileNameTemplate}`)
.setValue(this.plugin.settings.seasonFileNameTemplate)
.onChange(data => {
this.plugin.settings.seasonFileNameTemplate = data;
void this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName('Manga and comic file name template')
.setDesc('Template for the file name used when creating a new note for a manga or comic.')

View file

@ -1,6 +1,7 @@
export enum MediaType {
Movie = 'movie',
Series = 'series',
Season = 'season',
ComicManga = 'comicManga',
Game = 'game',
MusicRelease = 'musicRelease',

View file

@ -8,6 +8,7 @@ import type { MediaTypeModel } from '../models/MediaTypeModel';
import { MovieModel } from '../models/MovieModel';
import { MusicReleaseModel } from '../models/MusicReleaseModel';
import { SeriesModel } from '../models/SeriesModel';
import { SeasonModel } from '../models/SeasonModel';
import { WikiModel } from '../models/WikiModel';
import type { MediaDbPluginSettings } from '../settings/Settings';
import { ILLEGAL_FILENAME_CHARACTERS } from './IllegalFilenameCharactersList';
@ -17,6 +18,7 @@ import { replaceTags } from './Utils';
export const MEDIA_TYPES: MediaType[] = [
MediaType.Movie,
MediaType.Series,
MediaType.Season,
MediaType.ComicManga,
MediaType.Game,
MediaType.Wiki,
@ -40,6 +42,7 @@ export class MediaTypeManager {
this.mediaFileNameTemplateMap = new Map<MediaType, string>();
this.mediaFileNameTemplateMap.set(MediaType.Movie, settings.movieFileNameTemplate);
this.mediaFileNameTemplateMap.set(MediaType.Series, settings.seriesFileNameTemplate);
this.mediaFileNameTemplateMap.set(MediaType.Season, settings.seasonFileNameTemplate);
this.mediaFileNameTemplateMap.set(MediaType.ComicManga, settings.mangaFileNameTemplate);
this.mediaFileNameTemplateMap.set(MediaType.Game, settings.gameFileNameTemplate);
this.mediaFileNameTemplateMap.set(MediaType.Wiki, settings.wikiFileNameTemplate);
@ -50,6 +53,7 @@ export class MediaTypeManager {
this.mediaTemplateMap = new Map<MediaType, string>();
this.mediaTemplateMap.set(MediaType.Movie, settings.movieTemplate);
this.mediaTemplateMap.set(MediaType.Series, settings.seriesTemplate);
this.mediaTemplateMap.set(MediaType.Season, settings.seasonTemplate);
this.mediaTemplateMap.set(MediaType.ComicManga, settings.mangaTemplate);
this.mediaTemplateMap.set(MediaType.Game, settings.gameTemplate);
this.mediaTemplateMap.set(MediaType.Wiki, settings.wikiTemplate);
@ -62,6 +66,7 @@ export class MediaTypeManager {
this.mediaFolderMap = new Map<MediaType, string>();
this.mediaFolderMap.set(MediaType.Movie, settings.movieFolder);
this.mediaFolderMap.set(MediaType.Series, settings.seriesFolder);
this.mediaFolderMap.set(MediaType.Season, settings.seasonFolder);
this.mediaFolderMap.set(MediaType.ComicManga, settings.mangaFolder);
this.mediaFolderMap.set(MediaType.Game, settings.gameFolder);
this.mediaFolderMap.set(MediaType.Wiki, settings.wikiFolder);
@ -138,6 +143,8 @@ export class MediaTypeManager {
return new MovieModel(obj);
} else if (mediaType === MediaType.Series) {
return new SeriesModel(obj);
} else if (mediaType === MediaType.Season) {
return new SeasonModel(obj);
} else if (mediaType === MediaType.ComicManga) {
return new ComicMangaModel(obj);
} else if (mediaType === MediaType.Game) {