movie-metadata
Advanced tools
Comparing version 1.0.4 to 2.1.4
#! /usr/bin/env node | ||
const { getMetadata } = require('../src/') | ||
const commandLineArgs = require('command-line-args') | ||
const { getMetadata } = require('../src/'); | ||
const commandLineArgs = require('command-line-args'); | ||
@@ -22,9 +22,7 @@ /** | ||
// {String} Path where to save the updated movies to | ||
{ name: 'destination', alias: 'd', type: String }, | ||
{ name: 'dest', alias: 'd', type: String }, | ||
// {String} Path where to save the movies that are not found on the omdb API server | ||
{ name: 'notfound', alias: 'n', type: String }, | ||
// {String} Path where to save the movies that are not found on the omdb API server | ||
{ name: 'timeout', alias: 't', type: Number } | ||
]) | ||
{ name: 'notFound', alias: 'n', type: String } | ||
]); | ||
getMetadata(options).catch(err => console.error(err)) | ||
getMetadata(options).catch(err => console.error(err)); |
@@ -0,1 +1,7 @@ | ||
## [1.2.4] -2018-11-29 | ||
### Added | ||
- Can now accept an `Array` of `Objects` containing the movie title and year (for more accurate searches) | ||
- `titleKey` and `yearKey` parameters which represent the property for movie title and year in the above Objects | ||
## [1.0.3] -2018-11-29 | ||
@@ -2,0 +8,0 @@ |
{ | ||
"name": "movie-metadata", | ||
"version": "1.0.4", | ||
"version": "2.1.4", | ||
"description": "A simple utility to easily fetch movie metadata, given an Array of movie titles, using the API from the Open Movie Database.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
module.exports = { | ||
// TODO: Add a getPoster method to fetch movie posters | ||
getMetadata: require('./metadata') | ||
} | ||
}; |
@@ -1,510 +0,275 @@ | ||
const { resolve } = require('path') | ||
const url = require('url') | ||
const fs = require('fs-extra') | ||
const _cliProgress = require('cli-progress') | ||
const fetch = require('node-fetch') | ||
const _ = require('lodash') | ||
const AbortContoller = require('abort-controller') | ||
const { resolve } = require('path'); | ||
const fs = require('fs-extra'); | ||
const _cliProgress = require('cli-progress'); | ||
const fetch = require('node-fetch'); | ||
const _ = require('lodash'); | ||
const AbortContoller = require('abort-controller'); | ||
const signalController = new AbortContoller() | ||
const _errors = { | ||
noSource: new Error('\x1b[31m[MISSING PARAMETER:\x1b[0m The "source" parameter is required!]'), | ||
noKey: new Error('\x1b[31m[MISSING PARAMETER:\x1b[0m The "key" parameter is required!]') | ||
}; | ||
const _signalController = new AbortContoller(); | ||
const isJsonPath = filePath => { | ||
return (typeof filePath === 'string' && /\.json$/i.test(filePath)); | ||
}; | ||
/** | ||
* Will be the progress bar instance, if enabled | ||
* | ||
* @type {Object} | ||
*/ | ||
let progressBar = {} | ||
let _progressBar = {}; | ||
let _updatedMovies = []; | ||
let _notFoundMovies = []; | ||
/** | ||
* Titles of movies that have been updated. This is in the | ||
* global scope so if we need to abort the connection to the API server, we can | ||
* still continue without skipping a movie. | ||
* Merge the user options with the default options | ||
* | ||
* @type {Array} Array of Objects | ||
* @param {Object} userOptions User provided options | ||
* | ||
* @return {Object} { options, state } | ||
*/ | ||
let UPDATED_MOVIES = [] | ||
async function mergeOptions(userOptions) { | ||
const defaults = { | ||
dest: '%source%-metadata.json', | ||
notFound: '%source%-notFound.json', | ||
verbose: false, | ||
progress: true, | ||
overwrite: false, | ||
timeout: 30000, | ||
splitter: '\n', | ||
keys: { title: 'title', year: 'year' }, | ||
onUpdate() {} | ||
}; | ||
return validateOptions(_.defaults(userOptions, defaults)); | ||
}; | ||
/** | ||
* Titles of movies that were not found on the omdb API server. This is in the | ||
* global scope so if we need to abort the connection to the API server, we can | ||
* still continue without skipping a movie. | ||
* Validate and update the options Object | ||
* | ||
* @type {Array} Array of Strings | ||
* @param {Object} options Merged options | ||
* | ||
* @return {Object} { options, state } | ||
*/ | ||
let NOT_FOUND_MOVIES = [] | ||
async function validateOptions (options) { | ||
// Ensure the {source} and {key} parameters were provided | ||
if (_.isUndefined(options.source) || _.isUndefined(options.key)) { | ||
throw _.isUndefined(options.source) ? _errors.noSource : _errors.noKey; | ||
}; | ||
module.exports = async (userOptions) => { | ||
/** | ||
* Default user options | ||
* | ||
* @type {Object} | ||
*/ | ||
const _defaults = { | ||
/** | ||
* Where to save the JSON file, with the fetched metadata, to. | ||
* "%source%" is a placeholder for the source file. | ||
* | ||
* If set to false the fetched metadata will be returned as a | ||
* Promise Object. | ||
* =========================================================================== | ||
* @default '%source%-metadata.json' | ||
* @type {String|Boolean} | ||
*/ | ||
destination: '%source%-metadata.json', | ||
const state = { | ||
isSaveToFile : options.dest && options.notFound, | ||
isArraySource : _.isArray(options.source) | ||
}; | ||
/** | ||
* Path where to save the movies that are not found on the omdb API server | ||
* "%source%" is a placeholder for the source file (minus the .json extension) | ||
* | ||
* If set to false the not found movies will be returned as a | ||
* Promise Array of movie titles. | ||
* =========================================================================== | ||
* @default '%source%-notfound.json' | ||
* @type {String|Boolean} | ||
*/ | ||
notfound: '%source%-notfound.json', | ||
if (isJsonPath(options.source)) { | ||
options.sourcePath = options.source; | ||
options.source = await fs.readJson(resolve(options.source)).catch(err => console.error(err)); | ||
} else if (!state.isArraySource) { | ||
options.source = options.source.split(options.splitter); | ||
}; | ||
/** | ||
* Whether or not to show each movie as its metadata is being fetched. | ||
* Enabling this will disable the {progress} option. | ||
* | ||
* @default false | ||
* @type {Boolean} | ||
*/ | ||
verbose: false, | ||
// Only {progress} OR {verbose} can be enabled (not both) | ||
options.progress = !options.verbose; | ||
/** | ||
* Whether or not to show a progress bar while the movie metadata | ||
* is being fetched. Enabling this will disable the {verbose} option. | ||
* | ||
* @default true | ||
* @type {Boolean} | ||
*/ | ||
progress: true, | ||
if (state.isSaveToFile) { | ||
// Set {destination} to {source} (overwriting {source} file) | ||
options.dest = options.overwrite ? options.source : options.dest; | ||
}; | ||
/** | ||
* Whether or not to overwrite the {source} JSON file with the updated | ||
* JSON metadata. | ||
* | ||
* If the "destination" or "notfound" option is set to false, this is | ||
* automatically disabled, and the metadata is returned as a Promise instead. | ||
* | ||
* @default false | ||
* @type {Boolean} | ||
*/ | ||
overwrite: false, | ||
if (options.progress) { | ||
// Initialize the CLI progress bar | ||
_progressBar = new _cliProgress.Bar({ hideCursor: true }, _cliProgress.Presets.shades_classic); | ||
}; | ||
/** | ||
* Amount of time to wait before aborting the request for metadata from the | ||
* API server. Will automatically continue, and the movie metadata will | ||
* not be skipped. | ||
* | ||
* @default 120000 (2 minutes) | ||
* @type {Number} | ||
*/ | ||
timeout: 30000, | ||
return { options, state }; | ||
} | ||
/** | ||
* What String to use to split {source} into an Arraym, if it is a string | ||
* and not a JSON file path or an Array. | ||
* | ||
* @default '\n' (newline) | ||
* @type {String} | ||
*/ | ||
splitter: '\n', | ||
/** | ||
* Different steps in which data is displayed to the user | ||
* | ||
* @type {Object} | ||
*/ | ||
const step = { | ||
init(options) { | ||
if (options.progress) { | ||
_progressBar.start(options.source.length, 0); | ||
} else if (options.verbose) { | ||
console.log('---------------------------------------------------------------'); | ||
console.log(`--------------- FETCHING METADATA FOR ${options.source.length} MOVIES ---------------`); | ||
console.log('---------------------------------------------------------------'); | ||
}; | ||
}, | ||
updateFound({ options, movie }) { | ||
const processedMovies = (_updatedMovies.length + _notFoundMovies.length).toString().padStart(options.source.length.toString().length, '0'); | ||
/** | ||
* Method to call everytime metadata is either successfully fetched or | ||
* the movie is not found. Takes an Object with properties: | ||
* { | ||
* movie: fetchedOrNotFoundMovie // Object|String, | ||
* status: true|false // Boolean | ||
* } | ||
*/ | ||
onUpdate() {} | ||
} | ||
if (options.progress) { | ||
// Increment the progress bar by one (1) if enabled | ||
_progressBar.increment(1); | ||
} else { | ||
console.log(`${processedMovies}/${options.source.length} - \t\x1b[32mUpdated:\x1b[0m\t"${movie}"`); | ||
} | ||
}, | ||
updateNotFound({ options, movie }) { | ||
const processedMovies = (_updatedMovies.length + _notFoundMovies.length).toString().padStart(options.source.length.toString().length, '0'); | ||
/** | ||
* Merge the default options and the users' options | ||
* | ||
* @type {Object} | ||
*/ | ||
const options = _.defaults(userOptions, _defaults) | ||
if (options.progress) { | ||
// Increment the progress bar by one (1) if enabled | ||
_progressBar.increment(1); | ||
} else { | ||
console.log(`${processedMovies}/${options.source.length} - \t\x1b[31mNot Found:\x1b[0m\t"${movie}"`); | ||
}; | ||
}, | ||
final(options) { | ||
if (options.progress) { | ||
_progressBar.stop(); | ||
}; | ||
// Check if {source} option is missing | ||
if(typeof options.source === 'undefined') { | ||
throw new Error('\x1b[31m[MISSING PARAMETER:\x1b[0m The "source" parameter is required!]') | ||
} | ||
// End - Check if {source} option is missing | ||
// Check if {verbose} or {progress} parameter is enabled | ||
if (options.verbose || options.progress) { | ||
console.log('\n'); | ||
if (_updatedMovies.length) { | ||
console.log(`\x1b[32mFetched metadata for ${_updatedMovies.length} of ${options.source.length} movies\x1b[0m`); | ||
} | ||
// Check if {key} option is missing | ||
if(typeof options.key === 'undefined') { | ||
throw new Error('\x1b[31m[MISSING PARAMETER:\x1b[0m The "key" parameter is required!]') | ||
if (_notFoundMovies.length) { | ||
console.log(`\x1b[31m${_notFoundMovies.length} movies were not found\x1b[0m\n\n`); | ||
}; | ||
}; | ||
} | ||
// End - Check if {key} option is missing | ||
}; | ||
// Check if {destination} and {notfound} parameters are not set to false (CLI) | ||
if(options.destination && options.notfound) { | ||
/** | ||
* Set {destination} to either {source} (if {overwite} is enabled) or | ||
* {destination} with the placeholder replaced by {source} with its extension | ||
* | ||
* @type {String} | ||
*/ | ||
options.destination = options.overwrite | ||
? options.source | ||
: options.destination.replace('%source%', options.source.replace(/\.json$/, '')) | ||
function getRemainingMovies (options) { | ||
let hasYear = false; | ||
let year = hasYear; | ||
return { remainingMovies: options.source.filter(currentMovie => { | ||
hasYear = !_.isUndefined(currentMovie[options.keys.year]); | ||
/** | ||
* Set {notfound} to the {notfound} path with the placeholder replaced with the source filename | ||
* {destination} with the placeholder replaced by {source} with its extension | ||
* | ||
* @type {String} | ||
*/ | ||
options.notfound = options.notfound.replace('%source%', options.source.replace(/\.json$/, '')) | ||
} | ||
// End - Check if {destination} and {notfound} parameters are not set to false (CLI) | ||
year = hasYear ? currentMovie[options.keys.year] : false; | ||
const title = hasYear ? currentMovie[options.keys.title] : currentMovie; | ||
// Check if {progress} or {verbose} parameter is enabled | ||
if(options.progress || options.verbose) { | ||
options.progress = !options.verbose | ||
} | ||
// End - Check if {progress} or {verbose} parameter is enabled | ||
const justTitlesFetched = _updatedMovies.map(data => _.toLower(data.Title)); | ||
const justTitlesNotFound = _notFoundMovies.map(data => _.toLower(data.title)); | ||
const justYearsFetched = _updatedMovies.map(data => data.Year); | ||
const justYearsNotFound = _notFoundMovies.map(data => data.year); | ||
// Check if {progress} parameter is enabled and {verbose} is disabled | ||
if(options.progress && !options.verbose) { | ||
progressBar = new _cliProgress.Bar({ hideCursor: true }, _cliProgress.Presets.shades_classic) | ||
} | ||
// End - Check if {progress} parameter is enabled and {verbose} is disabled | ||
if (hasYear) { | ||
return (justTitlesFetched.indexOf(_.toLower(title)) === -1 && justYearsFetched.indexOf(year.toString()) === -1) | ||
&& (justTitlesNotFound.indexOf(_.toLower(title)) === -1 && justYearsNotFound.indexOf(year.toString()) === -1); | ||
} else { | ||
return justTitlesFetched.indexOf(_.toLower(title)) === -1 | ||
&& justTitlesNotFound.indexOf(_.toLower(title)) === -1; | ||
} | ||
}), movieYear: year }; | ||
}; | ||
// Check if {source} parameter is a JSON path | ||
if(typeof options.source === 'string' && /\.json$/i.test(options.source)) { | ||
// End - Check if {source} parameter is a JSON path | ||
/** | ||
* Resolve the {source} path | ||
* | ||
* @type {String} | ||
*/ | ||
options.source = resolve(options.source) | ||
} | ||
function beforeFetch ({ options, currentMovie, movieYear }) { | ||
let title = currentMovie; | ||
let year = movieYear; | ||
// Check if {destination} parameter is a JSON path | ||
if(typeof options.destination === 'string' && /\.json$/i.test(options.destination)) { | ||
/** | ||
* Resolve the {destination} path | ||
* | ||
* @type {String} | ||
*/ | ||
options.destination = resolve(options.source, '../', options.destination) | ||
if (movieYear) { | ||
title = currentMovie[options.keys.title]; | ||
year = currentMovie[options.keys.year]; | ||
} | ||
// End - Check if {destination} parameter is a JSON path | ||
// Check if {notfound} parameter is a JSON path | ||
if(typeof options.notfound === 'string' && /\.json$/i.test(options.notfound)) { | ||
/** | ||
* Resolve the {notfound} path | ||
* | ||
* @type {String} | ||
*/ | ||
options.notfound = resolve(options.source, '../', options.notfound) | ||
} | ||
// End - Check if {notfound} parameter is a JSON path | ||
let abortTimeout = setTimeout(() => { | ||
_signalController.abort(); | ||
getMetadata(options); | ||
}, options.timeout); | ||
/** | ||
***************************************************************************** | ||
********************************* MAIN LOGIC ******************************** | ||
***************************************************************************** | ||
*/ | ||
return { title, year, abortTimeout }; | ||
}; | ||
/** | ||
* Set {movieTitles} to the {source} parameter. | ||
* (changed later if it's a JSON path instead of an Array) | ||
* | ||
* @type {Array|Path} | ||
*/ | ||
let movieTitles = options.source | ||
async function getMetadata (options) { | ||
const { remainingMovies, movieYear } = getRemainingMovies(options); | ||
// Check if {source} is a JSON path | ||
if(typeof options.source === 'string' && /\.json$/i.test(options.source)) { | ||
/** | ||
* Get the movie titles from the {source} JSON file | ||
* | ||
* @type {Array} | ||
*/ | ||
movieTitles = await fs.readJson(options.source).catch(err => console.error(err)) | ||
} else if (typeof movieTitles === 'string') { | ||
// If {source} is not a JSON file, split it into an Array based on the {splitter} parameter | ||
movieTitles = movieTitles.split(options.splitter) | ||
} | ||
// End - Check if {source} is a JSON path | ||
for (let i = 0; i < remainingMovies.length; i++) { | ||
const currentMovie = remainingMovies[i]; | ||
const { title, year, abortTimeout } = beforeFetch({ options, currentMovie, movieYear }); | ||
// Check if the {progress} parameter is enabled | ||
if(options.progress && !options.verbose) { | ||
// Start the progress bar, starting at 0 | ||
progressBar.start(movieTitles.length, 0) | ||
} else if(options.verbose && !options.progress) { | ||
// Display a message to the user stating that we have started fetching metadata | ||
console.log('---------------------------------------------------------------') | ||
console.log(`--------------- FETCHING METADATA FOR ${movieTitles.length} MOVIES ---------------`) | ||
console.log('---------------------------------------------------------------') | ||
} | ||
// End - Check if the {progress} parameter is enabled | ||
const metadata = await fetchMetadata({ options, title, year }); | ||
/** | ||
* Set the metadata for each movie in the {movieTitles} Array | ||
* | ||
* {Array} updatedMovies = Movies that were successfully tagged with their metadata | ||
* {Array} notFoundMovies = Movie titles that were not found on the omdb API server | ||
* | ||
* @type {Array} | ||
*/ | ||
const { updatedMovies, notFoundMovies } = await setMetadata(options, movieTitles) | ||
clearTimeout(abortTimeout); | ||
// Check if {destination} parameter is a JSON path | ||
if((typeof options.destination === 'string' && /\.json$/i.test(options.destination) | ||
&&(typeof options.notfound === 'string' && /\.json$/i.test(options.notfound)))) { | ||
/** | ||
* Save the movie metadata to the {destination} parameter | ||
* | ||
* | ||
* @type {Object} | ||
*/ | ||
await saveUpdatedMovies(options, { updatedMovies, notFoundMovies }) | ||
} | ||
// End - Check if {destination} parameter is a JSON path | ||
const status = afterFetch({ options, metadata, title, year }); | ||
// Check if the {progress} parameter is enabled | ||
if(options.progress && !options.verbose) { | ||
/** | ||
* Stop / finish the progress bar once all the metadata has been fetched and saved | ||
*/ | ||
progressBar.stop() | ||
} | ||
// End - Check if the {progress} parameter is enabled | ||
if (status.isAborted) { | ||
break; | ||
}; | ||
}; | ||
// Check if {verbose} or {progress} parameter is enabled | ||
if(options.verbose || options.progress) { | ||
console.log('\n---------------------------------------------------------------') | ||
console.log(`\x1b[32mSuccessfully fetched metadata for ${updatedMovies.length} of ${movieTitles.length} movies\x1b[0m`) | ||
return { fetched: _updatedMovies, notFound: _notFoundMovies }; | ||
}; | ||
// Check if any movies couldn't be found | ||
if(notFoundMovies.length > 0) { | ||
console.log(`\x1b[31m${notFoundMovies.length} movies were not found on the server\x1b[0m`) | ||
} | ||
// End - Check if any movies couldn't be found | ||
console.log('---------------------------------------------------------------\n') | ||
} | ||
// End - Check if {verbose} or {progress} parameter is enabled | ||
async function fetchMetadata ({ options, title, year }) { | ||
title = encodeURIComponent(title); | ||
year = year ? `&y=${year}` : ''; | ||
const apiUrl = new URL(`http://www.omdbapi.com/?t=${title}&type=movie&apikey=${options.key}${year}`); | ||
let jsonData = null; | ||
// @return {Object} Return the {updatedMovies}, and {notFoundMovies} Arrays/Dictionaries | ||
return { fetchedMetadata: updatedMovies, notFoundMovies } | ||
} | ||
// End - {module.exports} Function | ||
try { | ||
const response = await fetch(apiUrl, { signal: _signalController.signal }); | ||
jsonData = await response.json(); | ||
} catch (err) { | ||
throw err; | ||
}; | ||
/** | ||
* Set the metadata for each item in the {movieTitles} Array | ||
* | ||
* @param {Object} {options} User options | ||
* @param {Array} {movieTitles} Movie titles to search for | ||
* | ||
* @return {Object} Array of Objects with the fetched metadata | ||
*/ | ||
async function setMetadata(options, movieTitles) { | ||
/** | ||
* Total amount of movies to search for | ||
* | ||
* @type {Number} | ||
*/ | ||
const totalMoviesCount = movieTitles.length | ||
return jsonData.Response === 'True' ? jsonData : 'Not Found'; | ||
}; | ||
/** | ||
* Will contain the fetched movie metadata | ||
* | ||
* @type {Object} | ||
*/ | ||
let fetchedMetadata = null | ||
function afterFetch ({ options, metadata, title, year }) { | ||
const isAborted = metadata === 'AbortError'; | ||
const isFound = metadata !== 'Not Found'; | ||
let movie = !year ? title : { title, year }; | ||
movie = isFound ? metadata : movie; | ||
/** | ||
* Run the {_setMetadata} function internally so we can recall it later (when aborting a connection) | ||
* | ||
* @param {Object} options User options | ||
* @param {Array} movieTitles Movie titles to fetch metadata for | ||
*/ | ||
async function _setMetadata(options, movieTitles) { | ||
/** | ||
* Set to only movies that have not already been looked up / updated with metadata | ||
* | ||
* @type {Array} | ||
*/ | ||
let remainingMovies = movieTitles.filter(currentMovie => { | ||
return UPDATED_MOVIES.map(data => data.Title).indexOf(currentMovie) === -1 && NOT_FOUND_MOVIES.indexOf(currentMovie) === -1 | ||
}) | ||
if (isAborted) { | ||
return { isFound, isAborted }; | ||
}; | ||
// Loop through each of the {remainingMovies} | ||
for(let i=0;i<remainingMovies.length;i++) { | ||
if (isFound) { | ||
_updatedMovies.push(movie); | ||
/** | ||
* The current movie title in the loop | ||
* | ||
* @type {String} | ||
*/ | ||
const currentMovie = remainingMovies[i] | ||
step.updateFound({ options, movie: movie.Title }); | ||
} else { | ||
_notFoundMovies.push(movie); | ||
/** | ||
* Set a request timeout, if the {fetchMetadata} Function takes more than 90 seconds, | ||
* then abort the connection and run this function again | ||
* | ||
* @type {Timeout Object} | ||
*/ | ||
let abortTimeout = setTimeout(() => { | ||
signalController.abort() | ||
_setMetadata(options,movieTitles) | ||
}, options.timeout) | ||
step.updateNotFound({ options, movie: title }); | ||
}; | ||
/** | ||
* Fetch the metadata the current movie in the loop | ||
* | ||
* @type {Object} | ||
*/ | ||
fetchedMetadata = await fetchMetadata(options.key, currentMovie) | ||
options.onUpdate({ movie, isFound }); | ||
/** | ||
* Clear the timeout after the metadata has been fetched and the request didn't timeout (resolved) | ||
*/ | ||
clearTimeout(abortTimeout) | ||
return { isAborted, isFound }; | ||
}; | ||
// Check if the request was aborted | ||
if(fetchedMetadata === 'AbortError') { | ||
// Break out of the loop, allowing the Function to restart | ||
break | ||
} | ||
// End - Check if the request was aborted | ||
async function saveMetadataToJson ({ options }) { | ||
const sourceName = options.sourcePath.replace('.json', ''); | ||
options.dest = options.dest.replace('%source%', sourceName); | ||
options.notFound = options.notFound.replace('%source%', sourceName); | ||
// Ensure that there was no errors when retrieving the metadata (returned true) | ||
if(fetchedMetadata !== 'Not Found') { | ||
// Add the updated movie Object to the {UPDATED_MOVIES} Array | ||
UPDATED_MOVIES.push(fetchedMetadata) | ||
if (isJsonPath(options.sourcePath)) { | ||
if (_updatedMovies.length) { | ||
fs.writeJson(resolve(options.sourcePath, '../', options.dest), _updatedMovies, {spaces: '\t'}); | ||
} | ||
// Check if {verbose} parameter is enabled | ||
if(options.verbose && !options.progress) { | ||
console.log(`${(UPDATED_MOVIES.length + NOT_FOUND_MOVIES.length).toString().padStart(totalMoviesCount.toString().length, '0')}/${totalMoviesCount} - \x1b[32mUpdated:\x1b[0m\t"${fetchedMetadata.Title}"`) | ||
} | ||
// End - Check if {verbose} parameter is enabled | ||
if (_notFoundMovies.length) { | ||
fs.writeJson(resolve(options.sourcePath, '../', options.notFound), _notFoundMovies, {spaces: '\t'}); | ||
}; | ||
}; | ||
}; | ||
/** | ||
* Run the {onUpdate} Method parameter, with the metadata just fetched, and | ||
* whether or not the metadata was fetched (true) or not found (false) | ||
* | ||
* @type {Object} | ||
*/ | ||
options.onUpdate({ movie: fetchedMetadata, fetched: true }) | ||
} | ||
else if(fetchedMetadata === 'Not Found') { | ||
// Add the not found movie title to the {NOT_FOUND_MOVIES} Array | ||
NOT_FOUND_MOVIES.push(currentMovie) | ||
async function main(userOptions) { | ||
const { options } = await mergeOptions(userOptions); | ||
// Check if {verbose} parameter is enabled | ||
if(options.verbose && !options.progress) { | ||
console.log(`${(UPDATED_MOVIES.length + NOT_FOUND_MOVIES.length).toString().padStart(totalMoviesCount.toString().length, '0')}/${totalMoviesCount} - \x1b[31mNot Found:\x1b[0m\t"${currentMovie}"`) | ||
} | ||
// End - Check if {verbose} parameter is enabled | ||
// Run the initial step | ||
step.init(options); | ||
/** | ||
* Run the {onUpdate} Method parameter, with the metadata just fetched, and | ||
* whether or not the metadata was fetched (true) or not found (false) | ||
* | ||
* @type {Object} | ||
*/ | ||
options.onUpdate({ movie: currentMovie, fetched: false }) | ||
} | ||
// End - Ensure that there was no errors when retrieving the metadata | ||
await getMetadata(options); | ||
// Check if {progress} parameter is enabled | ||
if(options.progress && !options.verbose) { | ||
// Increment the progress bar by one (1) if enabled | ||
progressBar.increment() | ||
} | ||
// End - Check if {progress} parameter is enabled | ||
} | ||
// End - Loop through each of the {remainingMovies} | ||
await saveMetadataToJson({ options }); | ||
// Check if the metadata fetch was aborted | ||
if(fetchedMetadata === 'AbortError' || fetchedMetadata === null) { | ||
await _setMetadata(options,movieTitles) | ||
} | ||
// End - Check if the metadata fetch was aborted | ||
} | ||
// Run the end step | ||
step.final(options); | ||
// Check if the metadata fetch was aborted | ||
if(fetchedMetadata === 'AbortError' || fetchedMetadata === null) { | ||
await _setMetadata(options,movieTitles) | ||
} | ||
// End - Check if the metadata fetch was aborted | ||
return { fetched: _updatedMovies, notFound: _notFoundMovies }; | ||
}; | ||
// Check if the metadata fetch was aborted | ||
if(fetchedMetadata !== 'AbortError' && fetchedMetadata !== null) { | ||
return { | ||
updatedMovies: UPDATED_MOVIES, | ||
notFoundMovies: NOT_FOUND_MOVIES | ||
} | ||
} | ||
// End - Check if the metadata fetch was aborted | ||
} | ||
// End - {setMetadata} Function | ||
/** | ||
* Fetch metadata for the specified movie title | ||
* | ||
* @param {String} {apiKey} Developers API key for omdb API | ||
* @param {String} {movieTitle} Movie title to search for | ||
* | ||
* @return {Object} Metadata for the specified movie | ||
*/ | ||
async function fetchMetadata(apiKey, movieTitle) { | ||
// @return {Object} Metadata Object for the specified movie | ||
return fetch(new URL(`http://www.omdbapi.com/?t=${encodeURIComponent(movieTitle)}&type=movie&apikey=${apiKey}`), | ||
{ signal: signalController.signal }) | ||
.then(request => request.json()) | ||
.then(jsonData => { | ||
// Ensure there were no errors when retrieving the metadata | ||
if(jsonData.Response === 'True') { | ||
// Return JSON data if there were no errors | ||
return jsonData | ||
} else { | ||
// Otherwise, return 'Not Found' if the movie was not found | ||
return 'Not Found' | ||
} | ||
// End - Ensure there were no errors when retrieving the metadata | ||
}, | ||
err => { | ||
return err.name | ||
}) | ||
.catch(err => err) | ||
} | ||
// End - {fetchMetadata} Function | ||
/** | ||
* Save the metadata to the {destination} JSON file, | ||
* and the not found movies to the {notFound} JSON file. | ||
* | ||
* @param {Object} {options} User options | ||
* @param {Object} {movieMetadata} { updatedMovies, notFoundMovies } Array of Objects, and Array of Strings | ||
*/ | ||
async function saveUpdatedMovies(options, movieMetadata) { | ||
/** | ||
* Write the update movie metadata to the {destination} JSON file path | ||
*/ | ||
fs.writeJson(options.destination, movieMetadata.updatedMovies, {spaces: '\t'}) | ||
// Check if any movies were not found | ||
if(movieMetadata.notFoundMovies.length > 0) { | ||
/** | ||
* Write the not found movies to the {notfound} JSON file path | ||
*/ | ||
fs.writeJson(options.notfound, movieMetadata.notFoundMovies, {spaces: '\t'}) | ||
} | ||
// End - Check if any movies were not found | ||
} | ||
// End - {saveUpdatedMovies} Function | ||
module.exports = main; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
10
50778
254
2