UNPKG

clams-omdb

Version:

A module to access OMDb API.

235 lines (190 loc) 7.34 kB
var needle = require('needle'), stream = require('stream'), HOST = 'http://www.omdbapi.com/', TYPES = [ 'movie', 'series', 'episode' ]; // Series have a different format to describe years, so account for that // when we format it. For example, // "1989" == 1998 // "1989-" == { from: 1989, to: undefined } // "1989-2014" == { from: 1989, to: 2014 } function formatYear(year) { var from, to; year = year.split('–'); if (year.length === 2) { from = +year[0]; if (year[1]) { to = +year[1]; } return { from: from, to: to }; } return +year; } // Search for movies by titles. module.exports.search = function (terms, done) { var query = {}; if (typeof terms === 'string') { query.s = terms; } else { query.s = terms.terms || terms.s; query.y = terms.year || terms.y; query.type = terms.type; } if (!query.s) { return done(new Error('No search terms specified.')); } if (query.type) { if (TYPES.indexOf(query.type) < 0) { return done(new Error( 'Invalid type specified. Valid types are: ' + TYPES.join(', ') + '.' )); } } if (query.y) { query.y = parseInt(query.y, 10); if (isNaN(query.y)) { return done(new Error('Year is not an integer.')); } } needle.request('get', HOST, query, function (err, res, movies) { if (err) { return done(err); } if (res.statusCode !== 200) { return done(new Error('status code: ' + res.statusCode)); } // If no movies are found, the API returns // "{"Response":"False","Error":"Movie not found!"}" instead of an // empty array. So in this case, return an empty array to be consistent. if (movies.Response === 'False') { return done(null, []); } // Fix the ugly capitalized naming and cast the year as a Number. done(null, movies.Search.map(function (movie) { return { title: movie.Title, year: formatYear(movie.Year), imdb: movie.imdbID, type: movie.Type, poster : movie.Poster }; })); }); }; // Find a movie by title, title & year or IMDB ID. The second argument is // optional and determines whether or not to return an extended plot synopsis. module.exports.get = (function () { var formatRuntime, formatVotes; // Format strings of hours & minutes into minutes. For example, // "1 h 30 min" == 90. formatRuntime = function (runtime) { var hours = runtime.match(/(\d+) h/), minutes = runtime.match(/(\d+) min/); hours = hours ? hours[1] : 0; minutes = minutes ? +minutes[1] : 0; return (hours * 60) + minutes; }; // Strip foreign characters from the string and return a casted Number. formatVotes = function (votes) { return +votes.match(/\d/g).join(''); }; return function (terms, done) { var query = {}; query.plot = 'full'; // Select query based on explicit IMDB ID, explicit title, title & year, // IMDB ID and title, respectively. if (terms.imdb) { query.i = terms.imdb; } else if (terms.title) { query.t = terms.title; // In order to search with a year, a title must be present. if (terms.year) { query.y = terms.year; } if (terms.season) { query.Season = terms.season; if (terms.episode) { query.Episode = terms.episode; } } // Assume anything beginning with "tt" and ending with digits is an // IMDB ID. } else if (/^tt\d+$/.test(terms)) { query.i = terms; // Finally, assume options is a string repesenting the title. } else { query.t = terms; } needle.request('get', HOST, query, function (err, res, media) { if (err) { return done(err); } if (res.statusCode !== 200) { return done(new Error('status code: ' + res.statusCode)); } // The movie being searched for could not be found. if (media.Response === 'False') { return done(null, null); } // Replace 'N/A' strings with null for simple checks in the return // value. Object.keys(media).forEach(function (key) { if (media[key] === 'N/A') { media[key] = null; } }); // Beautify and normalize the ugly results the API returns. done(null, { title: media.Title, year: formatYear(media.Year), rated: media.Rated, season: (media.Season) ? media.Season : null, episode: (media.Episode) ? media.Episode : null, // Cast the API's release date as a native JavaScript Date type. released: media.Released ? new Date(media.Released) : null, // Return runtime as minutes casted as a Number instead of an // arbitrary string. runtime: media.Runtime ? formatRuntime(media.Runtime) : null, countries: media.Country ? media.Country.split(', ') : null, genres: media.Genre ? media.Genre.split(', ') : null, director: media.Director, writers: media.Writer ? media.Writer.split(', ') : null, actors: media.Actors ? media.Actors.split(', ') : null, plot: media.Plot, // A hotlink to a JPG of the movie poster on IMDB. poster: media.Poster, imdb: { id: media.imdbID, rating: media.imdbRating ? +media.imdbRating : null, // Convert votes from a US formatted string of a number to // a JavaScript Number. votes: media.imdbVotes ? formatVotes(media.imdbVotes) : null }, type: media.Type }); }); }; }()); // Get a Readable Stream with the jpg image data of the poster to the movie, // identified by title, title & year or IMDB ID. module.exports.poster = function (show) { var out = new stream.PassThrough(), req; module.exports.get(show, false, function (err, res) { if (err) { out.emit('error', err); } else if (!res) { out.emit('error', new Error('Movie not found')); } else { req = needle.get(res.poster); req.on('error', function (err) { out.emit('error', err); }); req.pipe(out); } }); return out; };