Skip to content

Commit 37b83ef

Browse files
committed
Use openapi schemas for TMDBSeries & TMDBMovie
1 parent fae46f6 commit 37b83ef

File tree

6 files changed

+22928
-65
lines changed

6 files changed

+22928
-65
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ Now you select the result you want, and the plugin will cast its magic, creating
119119
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
120120
| [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 |
121121
| [OMDb](https://www.omdbapi.com/) | OMDb is an API that offers metadata for movies, 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 |
122-
| [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 |
122+
| [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 Read Access Token` (__not__ `API Key`) [here](https://www.themoviedb.org/settings/api) | 50 per second | Yes |
123123
| [MusicBrainz](https://musicbrainz.org/) | MusicBrainz is an API that offers information about music releases. | music releases | No | 50 per second | No |
124124
| [Wikipedia](https://en.wikipedia.org/wiki/Main_Page) | The Wikipedia API allows access to all Wikipedia articles. | wiki articles | No | None | No |
125125
| [Steam](https://store.steampowered.com/) | The Steam API offers information on all Steam games. | games | No | 10000 per day | No |

automation/fetchSchemas.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ async function fetchSchema() {
1212

1313
// https://github.com/internetarchive/openlibrary-api/blob/main/swagger.yaml
1414
await $('bun openapi-typescript ./src/api/schemas/OpenLibrary.json -o ./src/api/schemas/OpenLibrary.ts');
15+
16+
// https://developer.themoviedb.org/openapi
17+
await $('bun openapi-typescript https://developer.themoviedb.org/openapi/tmdb-api.json -o ./src/api/schemas/TMDB.ts')
1518
}
1619

1720
await fetchSchema();

src/api/apis/TMDBMovieAPI.ts

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { Notice, renderResults } from 'obsidian';
1+
import createClient from 'openapi-fetch';
22
import type MediaDbPlugin from '../../main';
33
import type { MediaTypeModel } from '../../models/MediaTypeModel';
44
import { MovieModel } from '../../models/MovieModel';
55
import { MediaType } from '../../utils/MediaType';
66
import { APIModel } from '../APIModel';
7+
import type { paths } from '../schemas/TMDB';
78

89
export class TMDBMovieAPI extends APIModel {
910
plugin: MediaDbPlugin;
@@ -29,26 +30,34 @@ export class TMDBMovieAPI extends APIModel {
2930
throw new Error(`MDB | API key for ${this.apiName} missing.`);
3031
}
3132

32-
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'}`;
33-
const fetchData = await fetch(searchUrl);
33+
const client = createClient<paths>({ baseUrl: 'https://api.themoviedb.org' });
34+
const response = await client.GET('/3/search/movie', {
35+
headers: {
36+
Authorization: `Bearer ${this.plugin.settings.TMDBKey}`
37+
},
38+
params: {
39+
query: {
40+
query: encodeURIComponent(title),
41+
include_adult: this.plugin.settings.sfwFilter ? false : true
42+
},
43+
},
44+
fetch: fetch,
45+
});
3446

35-
if (fetchData.status === 401) {
47+
if (response.response.status === 401) {
3648
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
3749
}
38-
if (fetchData.status !== 200) {
39-
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
50+
if (response.response.status !== 200) {
51+
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
4052
}
4153

42-
const data = await fetchData.json();
54+
const data = response.data
4355

44-
if (data.total_results === 0) {
45-
if (data.Error === 'Movie not found!') {
46-
return [];
47-
}
48-
49-
throw Error(`MDB | Received error from ${this.apiName}: \n${JSON.stringify(data, undefined, 4)}`);
56+
if(!data) {
57+
throw Error(`MDB | No data received from ${this.apiName}.`);
5058
}
51-
if (!data.results) {
59+
60+
if ( data.total_results === 0 || !data.results ) {
5261
return [];
5362
}
5463

@@ -64,7 +73,7 @@ export class TMDBMovieAPI extends APIModel {
6473
englishTitle: result.title,
6574
year: result.release_date ? new Date(result.release_date).getFullYear().toString() : 'unknown',
6675
dataSource: this.apiName,
67-
id: result.id,
76+
id: result.id.toString(),
6877
}),
6978
);
7079
}
@@ -79,17 +88,32 @@ export class TMDBMovieAPI extends APIModel {
7988
throw Error(`MDB | API key for ${this.apiName} missing.`);
8089
}
8190

82-
const searchUrl = `https://api.themoviedb.org/3/movie/${encodeURIComponent(id)}?api_key=${this.plugin.settings.TMDBKey}&append_to_response=credits`;
83-
const fetchData = await fetch(searchUrl);
91+
const client = createClient<paths>({ baseUrl: 'https://api.themoviedb.org' });
92+
const response = await client.GET('/3/movie/{movie_id}', {
93+
headers: {
94+
Authorization: `Bearer ${this.plugin.settings.TMDBKey}`
95+
},
96+
params: {
97+
path: { movie_id: parseInt(id) },
98+
query: {
99+
append_to_response: 'credits'
100+
},
101+
},
102+
fetch: fetch,
103+
});
84104

85-
if (fetchData.status === 401) {
105+
if (response.response.status === 401) {
86106
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
87107
}
88-
if (fetchData.status !== 200) {
89-
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
108+
if (response.response.status !== 200) {
109+
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
90110
}
91111

92-
const result = await fetchData.json();
112+
const result = response.data
113+
114+
if (!result) {
115+
throw Error(`MDB | No data received from ${this.apiName}.`);
116+
}
93117
// console.debug(result);
94118

95119
return new MovieModel({
@@ -100,20 +124,24 @@ export class TMDBMovieAPI extends APIModel {
100124
premiere: this.plugin.dateFormatter.format(result.release_date, this.apiDateFormat) ?? 'unknown',
101125
dataSource: this.apiName,
102126
url: `https://www.themoviedb.org/movie/${result.id}`,
103-
id: result.id,
127+
id: result.id.toString(),
104128

105129
plot: result.overview ?? '',
106-
genres: result.genres.map((g: any) => g.name) ?? [],
107-
writer: result.credits.crew.filter((c: any) => c.job === 'Screenplay').map((c: any) => c.name) ?? [],
108-
director: result.credits.crew.filter((c: any) => c.job === 'Director').map((c: any) => c.name) ?? [],
109-
studio: result.production_companies.map((s: any) => s.name) ?? [],
130+
genres: result.genres?.map((g: any) => g.name) ?? [],
131+
// TMDB's spec allows for 'append_to_response' but doesn't seem to account for it in the type
132+
// @ts-ignore
133+
writer: result.credits.crew?.filter((c: any) => c.job === 'Screenplay').map((c: any) => c.name) ?? [],
134+
// @ts-ignore
135+
director: result.credits.crew?.filter((c: any) => c.job === 'Director').map((c: any) => c.name) ?? [],
136+
studio: result.production_companies?.map((s: any) => s.name) ?? [],
110137

111-
duration: result.runtime ?? 'unknown',
138+
duration: result.runtime?.toString() ?? 'unknown',
112139
onlineRating: result.vote_average,
140+
// @ts-ignore
113141
actors: result.credits.cast.map((c: any) => c.name).slice(0, 5) ?? [],
114142
image: `https://image.tmdb.org/t/p/w780${result.poster_path}`,
115143

116-
released:['Released'].includes(result.status),
144+
released:['Released'].includes(result.status!),
117145
streamingServices: [],
118146

119147
userData: {
@@ -126,6 +154,6 @@ export class TMDBMovieAPI extends APIModel {
126154
}
127155

128156
getDisabledMediaTypes(): MediaType[] {
129-
return this.plugin.settings.TMDBMovieAPI_disabledMediaTypes as MediaType[];
157+
return this.plugin.settings.TMDBSeriesAPI_disabledMediaTypes;
130158
}
131159
}

src/api/apis/TMDBSeriesAPI.ts

Lines changed: 58 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { Notice, renderResults } from 'obsidian';
1+
import createClient from 'openapi-fetch';
22
import type MediaDbPlugin from '../../main';
33
import type { MediaTypeModel } from '../../models/MediaTypeModel';
44
import { SeriesModel } from '../../models/SeriesModel';
55
import { MediaType } from '../../utils/MediaType';
66
import { APIModel } from '../APIModel';
7+
import type { paths } from '../schemas/TMDB';
78

89
export class TMDBSeriesAPI extends APIModel {
910
plugin: MediaDbPlugin;
@@ -29,26 +30,34 @@ export class TMDBSeriesAPI extends APIModel {
2930
throw new Error(`MDB | API key for ${this.apiName} missing.`);
3031
}
3132

32-
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'}`;
33-
const fetchData = await fetch(searchUrl);
33+
const client = createClient<paths>({ baseUrl: 'https://api.themoviedb.org' });
34+
const response = await client.GET('/3/search/tv', {
35+
headers: {
36+
Authorization: `Bearer ${this.plugin.settings.TMDBKey}`
37+
},
38+
params: {
39+
query: {
40+
query: encodeURIComponent(title),
41+
include_adult: this.plugin.settings.sfwFilter ? false : true
42+
},
43+
},
44+
fetch: fetch,
45+
});
3446

35-
if (fetchData.status === 401) {
47+
if (response.response.status === 401) {
3648
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
3749
}
38-
if (fetchData.status !== 200) {
39-
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
50+
if (response.response.status !== 200) {
51+
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
4052
}
4153

42-
const data = await fetchData.json();
54+
const data = response.data
4355

44-
if (data.total_results === 0) {
45-
if (data.Error === 'Series not found!') {
46-
return [];
47-
}
48-
49-
throw Error(`MDB | Received error from ${this.apiName}: \n${JSON.stringify(data, undefined, 4)}`);
56+
if(!data) {
57+
throw Error(`MDB | No data received from ${this.apiName}.`);
5058
}
51-
if (!data.results) {
59+
60+
if ( data.total_results === 0 || !data.results ) {
5261
return [];
5362
}
5463

@@ -64,7 +73,7 @@ export class TMDBSeriesAPI extends APIModel {
6473
englishTitle: result.name,
6574
year: result.first_air_date ? new Date(result.first_air_date).getFullYear().toString() : 'unknown',
6675
dataSource: this.apiName,
67-
id: result.id,
76+
id: result.id.toString(),
6877
}),
6978
);
7079
}
@@ -79,17 +88,32 @@ export class TMDBSeriesAPI extends APIModel {
7988
throw Error(`MDB | API key for ${this.apiName} missing.`);
8089
}
8190

82-
const searchUrl = `https://api.themoviedb.org/3/tv/${encodeURIComponent(id)}?api_key=${this.plugin.settings.TMDBKey}&append_to_response=credits`;
83-
const fetchData = await fetch(searchUrl);
91+
const client = createClient<paths>({ baseUrl: 'https://api.themoviedb.org' });
92+
const response = await client.GET('/3/tv/{series_id}', {
93+
headers: {
94+
Authorization: `Bearer ${this.plugin.settings.TMDBKey}`
95+
},
96+
params: {
97+
path: { series_id: parseInt(id) },
98+
query: {
99+
append_to_response: 'credits'
100+
},
101+
},
102+
fetch: fetch,
103+
});
84104

85-
if (fetchData.status === 401) {
105+
if (response.response.status === 401) {
86106
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
87107
}
88-
if (fetchData.status !== 200) {
89-
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
108+
if (response.response.status !== 200) {
109+
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
90110
}
91111

92-
const result = await fetchData.json();
112+
const result = response.data
113+
114+
if (!result) {
115+
throw Error(`MDB | No data received from ${this.apiName}.`);
116+
}
93117
// console.debug(result);
94118

95119
return new SeriesModel({
@@ -99,23 +123,25 @@ export class TMDBSeriesAPI extends APIModel {
99123
year: result.first_air_date ? new Date(result.first_air_date).getFullYear().toString() : 'unknown',
100124
dataSource: this.apiName,
101125
url: `https://www.themoviedb.org/tv/${result.id}`,
102-
id: result.id,
126+
id: result.id.toString(),
103127

104128
plot: result.overview ?? '',
105-
genres: result.genres.map((g: any) => g.name) ?? [],
106-
writer: result.created_by.map((c: any) => c.name) ?? [],
107-
studio: result.production_companies.map((s: any) => s.name) ?? [],
129+
genres: result.genres?.map((g: any) => g.name) ?? [],
130+
writer: result.created_by?.map((c: any) => c.name) ?? [],
131+
studio: result.production_companies?.map((s: any) => s.name) ?? [],
108132
episodes: result.number_of_episodes,
109-
duration: result.episode_run_time[0] ?? 'unknown',
133+
duration: result.episode_run_time?.[0]?.toString() ?? 'unknown',
110134
onlineRating: result.vote_average,
111-
actors: result.credits.cast.map((c: any) => c.name).slice(0, 5) ?? [],
112-
image: `https://image.tmdb.org/t/p/w780${result.poster_path}`,
135+
// TMDB's spec allows for 'append_to_response' but doesn't seem to account for it in the type
136+
// @ts-ignore
137+
actors: result.credits?.cast.map((c: any) => c.name).slice(0, 5) ?? [],
138+
image: result.poster_path ? `https://image.tmdb.org/t/p/w780${result.poster_path}` : null,
113139

114-
released:['Returning Series','Cancelled','Ended'].includes(result.status),
140+
released:['Returning Series','Cancelled','Ended'].includes(result.status!),
115141
streamingServices: [],
116-
airing: ['Returning Series'].includes(result.status),
142+
airing: ['Returning Series'].includes(result.status!),
117143
airedFrom: this.plugin.dateFormatter.format(result.first_air_date, this.apiDateFormat) ?? 'unknown',
118-
airedTo: ['Returning Series'].includes(result.status) ? 'unknown' : this.plugin.dateFormatter.format(result.last_air_date, this.apiDateFormat) ?? 'unknown',
144+
airedTo: ['Returning Series'].includes(result.status!) ? 'unknown' : this.plugin.dateFormatter.format(result.last_air_date, this.apiDateFormat) ?? 'unknown',
119145

120146
userData: {
121147
watched: false,
@@ -127,6 +153,6 @@ export class TMDBSeriesAPI extends APIModel {
127153
}
128154

129155
getDisabledMediaTypes(): MediaType[] {
130-
return this.plugin.settings.TMDBSeriesAPI_disabledMediaTypes as MediaType[];
156+
return this.plugin.settings.TMDBSeriesAPI_disabledMediaTypes;
131157
}
132158
}

0 commit comments

Comments
 (0)