Browse Source

Added TMDB support

master
David Ludwig 4 years ago
parent
commit
56a387d22d
3 changed files with 230 additions and 0 deletions
  1. +23
    -0
      src/lib/tmdb/index.ts
  2. +126
    -0
      src/lib/tmdb/request.ts
  3. +81
    -0
      src/lib/tmdb/schema.ts

+ 23
- 0
src/lib/tmdb/index.ts View File

@ -0,0 +1,23 @@
import ApiRequestManager from "./request"
import * as Schema from "./schema";
export default class TheMovieDb
{
protected requestManager!: ApiRequestManager;
public constructor(apiKey: string) {
this.requestManager = new ApiRequestManager(apiKey);
}
public async configuration() {
return await this.requestManager.get("/configuration");
}
public async searchMovie(query: string, year?: number, page?: number) {
return await this.requestManager.get<Schema.IMovieSearchResult>("/search/movie", { query, year });
}
public async movie(id: number) {
return await this.requestManager.get<Schema.IMovieDetails>(`/movie/${id}`);
}
}

+ 126
- 0
src/lib/tmdb/request.ts View File

@ -0,0 +1,126 @@
import https, { RequestOptions } from "https";
/**
* The API URL
*/
const API_URL = "https://api.themoviedb.org/3";
/**
* A status error is used to indicate responses with non-200 status codes
*/
export class StatusError<T = any> extends Error
{
/**
* The resulting body of a response
*/
public readonly response: T;
/**
* The resulting status code of a response
*/
public readonly statusCode?: number;
/**
* Create a new error indicating non-200 status
*/
public constructor(response: T, statusCode: number) {
super();
Object.setPrototypeOf(this, StatusError.prototype);
this.response = response;
this.statusCode = statusCode;
}
}
/**
* A request manager with atomic/persistent request options
*/
export default class ApiRequestManager
{
private __api_key: string;
/**
* Store additional request options
*/
protected options: RequestOptions;
/**
* Create a new API request manager
*
* @param options Additional request options
*/
public constructor(apiKey: string, options: RequestOptions = {}) {
this.__api_key = apiKey;
this.options = options;
}
/**
* Perform a generic HTTPS request
*
* @param method The HTTP method
* @param url The URL to request
* @param apiKey An optional bearer token
* @param params Optional parameters
* @param body Optional body
*/
protected request<T>(method: string, url: string, params?: any, body?: string)
{
return new Promise<T>((resolve, reject) => {
// Create request options
let options = Object.assign({ method, headers: {} }, this.options);
if (body) {
options.headers["Content-Type"] = "application/json";
options.headers["Content-Length"] = body.length;
}
// Add search parameters if necessary
let requestUrl = new URL(url);
requestUrl.searchParams.set("api_key", this.__api_key);
if (params) {
Object.keys(params).forEach((key) => {
if (params[key] !== undefined) {
requestUrl.searchParams.set(key, params[key]);
}
});
}
// Create the request
let request = https.request(<any>requestUrl, options, (res) => {
let rawData: string = "";
res.setEncoding("utf8");
res.on("data", chunk => {rawData += chunk});
res.on("error", reject);
res.on("end", () => {
let response: T = JSON.parse(rawData);
if (res.statusCode == 200) {
resolve(response)
} else {
reject(new StatusError(response, <number>res.statusCode));
}
});
})
.on("error", reject)
.on("timeout", () => reject("timeout"));
if (body) {
request.write(body);
}
request.end();
});
}
/**
* Perform a generic GET request
*/
public async get<T = any>(path: string, params?: any) {
return await this.request<T>("GET", `${API_URL}${path}`, params);
}
/**
* Perform a generic POST request
*/
public async post<T = any>(path: string, params?: any, body?: any) {
if (body !== undefined) {
body = JSON.stringify(body);
}
return await this.request<T>("POST", `${API_URL}${path}`, params, body);
}
}

+ 81
- 0
src/lib/tmdb/schema.ts View File

@ -0,0 +1,81 @@
export enum Status {
Rumored = "Rumored",
Planned = "Planned",
InProduction = "InProduction",
PostProduction = "PostProduction",
Released = "Released",
Canceled = "Canceled"
}
export interface IGenre {
id : number,
name: string
}
export interface ILanguage {
iso_639_1: string,
name: string
}
export interface IMovieSearchResult {
adult : boolean,
backdrop_path : string | null,
genre_ids : number[],
id : number,
original_string: string,
original_title : string,
overview : string,
popularity : number,
poster_path : string | null,
release_date : string,
title : string,
video : boolean,
vote_average : number
vote_count : number,
}
export interface IMovieDetails {
adult : boolean,
backdrop_path : string | null,
belongs_to_collection: any,
budget : number,
genres : IGenre[]
imdb_id : string | null,
original_language : string,
original_title : string,
overview : string | null,
popularity : number,
poster_path : string | null,
production_companies : IProductionCompany[],
production_countries : IProductionCountry[],
release_date : string,
revenue : number,
runtime : number | null,
spoken_languages : ILanguage[],
status : Status,
tagline : string | null,
title : string,
video : boolean,
vote_average : number,
vote_count : number
}
export interface IProductionCompany {
name : string,
id : number,
logo_path : string | null,
origin_country: string
}
export interface IProductionCountry {
release_date: string,
revenue : number,
runtime : number | null
}
export interface IPaginatedResponse<T> {
page : number,
results : T[],
total_results: number,
total_pages : number
}

Loading…
Cancel
Save