commit e2b616829faa7744acc342e5d93b9973a569af4e Author: Joseph Schmitt <me@joe.sh> Date: Sun Mar 27 14:35:30 2016 -0400 Couch Potato Alexa Skill 1.0 diff --git a/.env b/.env new file mode 100644 index 0000000..82ef408 --- /dev/null +++ b/.env @@ -0,0 +1,12 @@ +AWS_ENVIRONMENT= +AWS_SESSION_TOKEN= +AWS_REGION=us-east-1 +AWS_FUNCTION_NAME=couchpotato +AWS_HANDLER=index.handler +AWS_MODE=event +AWS_MEMORY_SIZE=128 +AWS_TIMEOUT=10 +AWS_DESCRIPTION= +AWS_RUNTIME=nodejs +CP_URL=http://url-to-couch-potato-server +CP_API_KEY=apiKey diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..babf4c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +deploy.env +lambda.zip +node_modules/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..5a40b5a --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# Couch Potato Alexa Skill + +This is a skill built for Amazon's Alexa service that tells you about your Couch Potato queue. It +allows you to ask Alexa the following: + +> Alexa, ask Couch Potato to add The Godfather + +> Alexa, ask Couch Potato if The Dark Knight is on the list + +If you're just getting started developing skills for Alexa, I'd recommend reading [Getting Started +with the Alexa Skills +Kit](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/getting-started-guide) and +[Developing an Alexa Skill as a Lambda +Function](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/developing-an-alexa-skill-as-a-lambda-function) to get familiar with the process. + +## Configuring The Skill + +To configure the skill, open up the `.env` file and fill in the correct values for `CP_URL`, which +should point to your Couch Potato server, and `CP_API_KEY` which should have your server's API key. + +## Testing The Skill Locally + +You can use [node-lambda](https://github.com/motdotla/node-lambda) to test this skill locally. In +the `test_events` directory are several event files you can use for testing, and they should map +pretty well to each Intent. To test an intent, simply copy the contents of one of the json files in +that directory and overwrite the contents of `event.json`. Then run `node-lambda run` from the +command line. + +## Setting up the Skill + +To set up the skill, head on over to [Alexa skills kit +development console](https://developer.amazon.com/edw/home.html) and add a new skill. Fill in the +basic skill information however you choose. For Endpoint, you'll need to fill in your Lambda ARN +which you'll get in the next step. Next, head on over to Interaction Model. In the Intent +Schema field, copy and paste the contents of the `interaction_model/intent_schema.json` file. Then +in the Sample Utterances field, copy and paste the contents of +`interaction_model/sample_utterances.txt`. + +## Hosting the Skill + +The skill is built to be easily hosted on Amazon's [AWS +Lambda service](https://aws.amazon.com/lambda/). Create your Lambda function (using the +alexa-skills-kit-color-expert blueprint) and make sure you choose Node.js as the runtime. + +To deploy to Lambda, first makes sure you do an `npm install` at the root of the project. +Once all the dependencies are installed, run `npm run bundle`, which will create a lambda.zip file. +You can then upload that zip file to Lambda for use in your function and skill. + +You can also use [node-lambda](https://github.com/motdotla/node-lambda) to deploy to your Lambda +function directly from the command line. Simply add a deploy.env file with your environment +configuration (and double check the supplied .env file in this repository) and then run +`node-lambda deploy`. Please visit the [node-lambda](https://github.com/motdotla/node-lambda) +project page for more information on deploying from the command line. diff --git a/event.json b/event.json new file mode 100644 index 0000000..836a206 --- /dev/null +++ b/event.json @@ -0,0 +1,26 @@ +{ + "session": { + "sessionId": "SessionId.1234", + "application": { + "applicationId": "amzn1.echo-sdk-ams.app.1234" + }, + "user": { + "userId": "amzn1.echo-sdk-account.1234" + }, + "new": true + }, + "request": { + "type": "IntentRequest", + "requestId": "EdwRequestId.1234", + "timestamp": "2016-01-01T00:00:00Z", + "intent": { + "name": "FindMovie", + "slots": { + "movieName": { + "name": "movieName", + "value": "the godfather" + } + } + } + } +} diff --git a/icon-108x108.png b/icon-108x108.png new file mode 100644 index 0000000..de826df Binary files /dev/null and b/icon-108x108.png differ diff --git a/index.js b/index.js new file mode 100644 index 0000000..5581cfc --- /dev/null +++ b/index.js @@ -0,0 +1,4 @@ +'use strict'; + +var app = require('./lib'); +exports.handler = app.lambda(); diff --git a/interaction_model/intent_schema.json b/interaction_model/intent_schema.json new file mode 100644 index 0000000..28620ea --- /dev/null +++ b/interaction_model/intent_schema.json @@ -0,0 +1,31 @@ +{ + "intents": [ + { + "intent": "AddMovie", + "slots": [ + { + "name": "movieName", + "type": "AMAZON.LITERAL" + } + ] + }, + { + "intent": "FindMovie", + "slots": [ + { + "name": "movieName", + "type": "AMAZON.LITERAL" + } + ] + }, + { + "intent": "AMAZON.YesIntent" + }, + { + "intent": "AMAZON.NoIntent" + }, + { + "intent": "AMAZON.CancelIntent" + } + ] +} diff --git a/interaction_model/sample_utterances.txt b/interaction_model/sample_utterances.txt new file mode 100644 index 0000000..eef428f --- /dev/null +++ b/interaction_model/sample_utterances.txt @@ -0,0 +1,34 @@ +AddMovie add {The Godfather|movieName} +AddMovie add {Star Wars|movieName} +AddMovie add {Star Wars Episode Four A New Hope|movieName} +AddMovie add {The Avengers|movieName} +AddMovie add {The Dark Knight|movieName} +AddMovie add {Live or Let Die|movieName} + +FindMovie is {The Godfather|movieName} on the list +FindMovie is {Star Wars|movieName} on the list +FindMovie is {Star Wars Episode Four A New Hope|movieName} on the list +FindMovie is {The Avengers|movieName} on the list +FindMovie is {The Dark Knight|movieName} on the list +FindMovie is {Live or Let Die|movieName} on the list + +FindMovie if {The Godfather|movieName} is on the list +FindMovie if {Star Wars|movieName} is on the list +FindMovie if {Star Wars Episode Four A New Hope|movieName} is on the list +FindMovie if {The Avengers|movieName} is on the list +FindMovie if {The Dark Knight|movieName} is on the list +FindMovie if {Live or Let Die|movieName} is on the list + +FindMovie is {The Godfather|movieName} already +FindMovie is {Star Wars|movieName} already +FindMovie is {Star Wars Episode Four A New Hope|movieName} already +FindMovie is {The Avengers|movieName} already +FindMovie is {The Dark Knight|movieName} already +FindMovie is {Live or Let Die|movieName} already + +FindMovie if {The Godfather|movieName} is already +FindMovie if {Star Wars|movieName} is already +FindMovie if {Star Wars Episode Four A New Hope|movieName} is already +FindMovie if {The Avengers|movieName} is already +FindMovie if {The Dark Knight|movieName} is already +FindMovie if {Live or Let Die| movieName} is already diff --git a/lib/handlers.js b/lib/handlers.js new file mode 100644 index 0000000..121af06 --- /dev/null +++ b/lib/handlers.js @@ -0,0 +1,133 @@ +'use strict'; + +var CouchPotato = require('node-couchpotato'); +var utils = require('./utils.js'); + +var WELCOME_DESCRIPTION = 'This skill allows you to manage your Couch Potato movie list.'; +var HELP_RESPONSE = ['You can ask Couch Potato about the movies in your queue or add new movies', + 'to it. Try asking "is The Godfather on the list?". If it\'s not and you want to add it, try', + 'saying "add The Godfather".'].join(' '); + +var config = require('dotenv').config(); +var cp = new CouchPotato({ + url: config.CP_URL, + apikey: config.CP_API_KEY +}); + +function handleLaunchIntent(req, resp) { + resp + .say(WELCOME_DESCRIPTION) + .say(HELP_RESPONSE) + .send(); +} + +function handleFindMovieIntent(req, resp) { + var movieName = req.slot('movieName'); + + cp.movie.list({search: movieName, limit_offset: 5}).then(function (searchResp) { + var movies = searchResp.movies; + var result; + + if (!movies || !movies.length) { + resp.say('Couldn\'t find ' + movieName + ' queued for download. '); + + cp.movie.search(movieName).then(function (searchResults) { + utils.sendSearchResponse(searchResults, resp); + }); + } + else { + result = movies[0].info; + resp + .say([ + 'It looks like', result.original_title, + '(' + result.year + ')', 'is already on your list.' + ].join(' ')) + .send(); + } + }); + + //Async response + return false; +} + +function handleAddMovieIntent(req, resp) { + var movieName = req.slot('movieName'); + + cp.movie.search(movieName).then(function (movies) { + utils.sendSearchResponse(movies, resp); + }); + + //Async response + return false; +} + +function handleYesIntent(req, resp) { + var promptData = req.session('promptData'); + var movie; + + if (!promptData) { + console.log('Got a AMAZON.YesIntent but no promptData. Ending session.'); + resp.send(); + } + else if (promptData.yesAction === 'addMovie') { + movie = promptData.searchResults[0]; + + cp.movie.add({ + title: movie.titles[0], + identifier: movie.imdb + }).then(function () { + resp + .say(promptData.yesResponse) + .send(); + }); + + //Async response + return false; + } + else { + console.log("Got an unexpected yesAction. PromptData:"); + console.log(promptData); + resp.send(); + } +} + +function handleNoIntent(req, resp) { + var promptData = req.session('promptData'); + + if (!promptData) { + console.log('Got a AMAZON.YesIntent but no promptData. Ending session.'); + resp.send(); + } + else if (promptData.noAction === 'endSession') { + resp.say(promptData.noResponse).send(); + } + else if (promptData.noAction === 'suggestNextMovie') { + var movies = promptData.searchResults; + resp + .say(promptData.noResponse) + .session('promptData', utils.buildPrompt(movies.slice(1))) + .shouldEndSession(false) + .send(); + } + else { + console.log("Got an unexpected noAction. PromptData:"); + console.log(promptData); + resp.send(); + } +} + +function handleCancelIntent(req, resp) { + resp.shouldEndSession(true).send(); +} + +function handleCancelIntent(req, resp) { + resp.say(HELP_RESPONSE).send(); +} + +module.exports = { + handleFindMovieIntent: handleFindMovieIntent, + handleAddMovieIntent: handleAddMovieIntent, + handleYesIntent: handleYesIntent, + handleNoIntent: handleNoIntent, + handleCancelIntent: handleCancelIntent +}; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..4579cef --- /dev/null +++ b/lib/index.js @@ -0,0 +1,15 @@ +'use strict'; + +var handlers = require('./handlers.js'); + +var alexa = require('alexa-app'); +var app = new alexa.app('couchPotato'); + +app.launch(handlers.handleLaunchIntent); +app.intent('FindMovie', handlers.handleFindMovieIntent); +app.intent('AddMovie', handlers.handleAddMovieIntent); +app.intent('AMAZON.YesIntent', handlers.handleYesIntent); +app.intent('AMAZON.NoIntent', handlers.handleNoIntent); +app.intent('AMAZON.CancelIntent', handlers.handleCancelIntent); + +module.exports = app; diff --git a/lib/old.js b/lib/old.js new file mode 100644 index 0000000..2e31b57 --- /dev/null +++ b/lib/old.js @@ -0,0 +1,160 @@ +var alexa = require('alexa-app'); +var request = require('request'); +var dotenv = require('dotenv').config(); +var CouchPotato = require('node-couchpotato'); + +var app = new alexa.app(); +var cp = new CouchPotato({ + url: dotenv.CP_URL, + apikey: dotenv.CP_API_KEY, + debug: true +}); + +function buildNextMovieSuggestionPrompt(movies) { + var promptData = { + searchResults: movies.slice(0, 5), + yesAction : 'addMovie', + yesResponse: 'Added ' + movies[0].original_title + ' (' + movies[0].year + ')' + ' to your list of movies to download.' + }; + + if (movies.length > 1) { + promptData.noAction = 'suggestNextMovie'; + promptData.noResponse = 'Ok, did you mean ' + movies[1].original_title + ' (' + movies[1].year + ')' + '?'; + } + else { + promptData.noAction = 'endSession'; + promptData.noResponse = 'Ok. I\'m out of suggestions. Sorry about that.'; + } + + return promptData; +} + +function formatMovieSearchResponse(movies, resp) { + if(!movies || !movies.length) { + resp.say("No movie found for " + movieName); + return resp.send(); + } + + var speechText = 'Add ' + movies[0].original_title + ' (' + movies[0].year + ')' + ' to your list?'; + // console.log('movie found', speechText); + // console.log('reprompt data', buildNextMovieSuggestionPrompt(movies.slice(0, 5))); + + resp + .say(speechText) + .session('promptData', buildNextMovieSuggestionPrompt(movies.slice(0, 5))) + .shouldEndSession(false) + .send(); +} + +app.intent('FindMovie', function (req, resp) { + var movieName = req.slot('movieName'); + + // console.log('search for movie in list', movieName); + cp.movie.list({search: movieName, limit_offset: 5}).then(function (searchResp) { + var movies = searchResp.movies; + var result; + var speechText; + + if (!movies || !movies.length) { + speechText = "Couldn't find " + movieName + " queued for download. "; + resp.say(speechText); + + // console.log('no movie found', speechText); + + cp.movie.search(movieName).then(function (searchResults) { + formatMovieSearchResponse(searchResults, resp); + }); + } + else { + result = movies[0].info; + speechText = 'It looks like ' + result.original_title + ' (' + result.year + ')' + ' is already on your list.'; + + // console.log('movie found', speechText); + + resp + .say(speechText) + .send(); + } + + return false; + // console.log('reprompt data', buildNextMovieSuggestionPrompt(movies.slice(0, 5))); + }); + + return false; +}); + +app.intent('AddMovie', function (req, resp) { + var movieName = req.slot('movieName'); + + // console.log('search for movie', movieName); + cp.movie.search(movieName).then(function (movies) { + formatMovieSearchResponse(movies, resp); + }); + + return false; +}); + +app.intent('AMAZON.YesIntent', function (req, resp) { + var promptData = req.session('promptData'); + + if (!promptData) { + console.log('Got a AMAZON.YesIntent but no promptData. Ending session.'); + return resp.send(); + } + + if (promptData.yesAction === 'addMovie') { + // console.log('promptData', promptData); + var movie = promptData.searchResults[0]; + + // console.log('Add the movie', movie.original_title); + cp.movie.add({ + title: movie.titles[0], + identifier: movie.imdb + }).then(function () { + resp + .say(promptData.yesResponse) + .send(); + }); + } + else { + console.log("Got an unexpected yesAction. PromptData:"); + console.log(promptData); + return resp.send(); + } + + return false; +}); + +app.intent('AMAZON.NoIntent', function (req, resp) { + var promptData = req.session('promptData'); + + if (!promptData) { + console.log('Got a AMAZON.YesIntent but no promptData. Ending session.'); + return resp.send(); + } + + if (promptData.noAction === 'endSession') { + return resp.say(promptData.noResponse).send(); + } + else if (promptData.noAction === 'suggestNextMovie') { + var movies = promptData.searchResults; + return resp + .say(promptData.noResponse) + .session('promptData', buildNextMovieSuggestionPrompt(movies.slice(1))) + .shouldEndSession(false) + .send(); + } + else { + console.log("Got an unexpected noAction. PromptData:"); + console.log(promptData); + return resp.send(); + } + + return false; +}); + +app.intent('AMAZON.CancelIntent', function (req, resp) { + resp.shouldEndSession(true).send(); +}); + +exports.handler = app.lambda(); diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 0000000..3820c87 --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,35 @@ +function buildPrompt(movies) { + var promptData = { + searchResults: movies.slice(0, 5), + yesAction : 'addMovie', + yesResponse: 'Added ' + movies[0].original_title + ' (' + movies[0].year + ')' + ' to your list of movies to download.' + }; + + if (movies.length > 1) { + promptData.noAction = 'suggestNextMovie'; + promptData.noResponse = 'Ok, did you mean ' + movies[1].original_title + ' (' + movies[1].year + ')' + '?'; + } + else { + promptData.noAction = 'endSession'; + promptData.noResponse = 'Ok. I\'m out of suggestions. Sorry about that.'; + } + + return promptData; +} + +function sendSearchResponse(movies, resp) { + if(!movies || !movies.length) { + return resp.say('No movie found for ' + movieName).send(); + } + + resp + .say(['Add', movies[0].original_title, '(' + movies[0].year + ')', 'to your list?'].join(' ')) + .session('promptData', buildPrompt(movies.slice(0, 5))) + .shouldEndSession(false) + .send(); +} + +module.exports = { + buildPrompt: buildPrompt, + sendSearchResponse: sendSearchResponse +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..d4a07a7 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "alexa-couchpotato", + "version": "1.0.0", + "description": "A skill to ask Alexa about your Couch Potato queue.", + "main": "index.js", + "scripts": { + "bundle": "mkdir -p bundle && cp -r {.env,index.js,lib,node_modules} bundle/ && cd bundle && bestzip ../lambda.zip * .env && rm -rf ../bundle" + }, + "author": "Joe Schmitt", + "license": "MIT", + "dependencies": { + "alexa-app": "^2.3.2", + "dotenv": "^2.0.0", + "node-couchpotato": "^0.1.4", + "node-lambda": "^0.7.1" + }, + "devDependencies": { + "bestzip": "^1.1.3", + "node-lambda": "^0.7.1" + } +} diff --git a/test_events/couch_potato_add_movie.json b/test_events/couch_potato_add_movie.json new file mode 100644 index 0000000..dc12913 --- /dev/null +++ b/test_events/couch_potato_add_movie.json @@ -0,0 +1,27 @@ +{ + "session": { + "sessionId": "SessionId.1234", + "application": { + "applicationId": "amzn1.echo-sdk-ams.app.1234" + }, + "user": { + "userId": "amzn1.echo-sdk-account.1234" + }, + "new": true + }, + "request": { + "type": "IntentRequest", + "requestId": "EdwRequestId.1234", + "timestamp": "2016-01-01T00:00:00Z", + "intent": { + "name": "AddMovie", + "slots": { + "movieName": { + "name": "movieName", + "value": "the godfather" + } + } + } + }, + "version": "1.0" +} diff --git a/test_events/couch_potato_find_movie.json b/test_events/couch_potato_find_movie.json new file mode 100644 index 0000000..836a206 --- /dev/null +++ b/test_events/couch_potato_find_movie.json @@ -0,0 +1,26 @@ +{ + "session": { + "sessionId": "SessionId.1234", + "application": { + "applicationId": "amzn1.echo-sdk-ams.app.1234" + }, + "user": { + "userId": "amzn1.echo-sdk-account.1234" + }, + "new": true + }, + "request": { + "type": "IntentRequest", + "requestId": "EdwRequestId.1234", + "timestamp": "2016-01-01T00:00:00Z", + "intent": { + "name": "FindMovie", + "slots": { + "movieName": { + "name": "movieName", + "value": "the godfather" + } + } + } + } +} diff --git a/test_events/yes_reprompt.json b/test_events/yes_reprompt.json new file mode 100644 index 0000000..b225619 --- /dev/null +++ b/test_events/yes_reprompt.json @@ -0,0 +1,601 @@ +{ + "session": { + "sessionId": "SessionId.1234", + "application": { + "applicationId": "amzn1.echo-sdk-ams.app.1234" + }, + "attributes": { + "promptData": { + "searchResults": [ + { + "rating": { + "imdb": [ + 6.9, + 992 + ] + }, + "tmdb_id": 321258, + "actor_roles": { + "Diana Hardcastle": "Mrs. Heelshire", + "Rupert Evans": "Malcolm", + "James Russell": "James", + "Jim Norton": "Mr. Heelshire", + "Lauren Cohan": "Greta" + }, + "via_imdb": true, + "via_tmdb": true, + "titles": [ + "The Boy", + "The Inhabitant", + "?? ?????" + ], + "imdb": "tt3882082", + "year": 2016, + "images": { + "poster": [ + "https://image.tmdb.org/t/p/w154/lISBWF6UUF3cQPcoLE8pMxnyJty.jpg", + "http://ia.media-imdb.com/images/M/MV5BMTc1MjcxNzcwMV5BMl5BanBnXkFtZTgwMTE0NTE2NzE@._V1_.jpg", + "http://ia.media-imdb.com/images/M/MV5BMTQ1NzAwNTg1M15BMl5BanBnXkFtZTgwNDQwOTQ0NzE@._V1_SX300.jpg" + ], + "extra_thumbs": [], + "poster_original": [ + "https://image.tmdb.org/t/p/original/lISBWF6UUF3cQPcoLE8pMxnyJty.jpg" + ], + "actors": { + "Diana Hardcastle": "https://image.tmdb.org/t/p/w185/hl58zPrWbo1mfatgVxEnGsKGZ1b.jpg", + "Rupert Evans": "https://image.tmdb.org/t/p/w185/AuPOKHD18bE2jKXONvrMLGVQ744.jpg", + "Jim Norton": "https://image.tmdb.org/t/p/w185/lndFtmSwtCzoeWkgjqJBqVRKGk2.jpg", + "Lauren Cohan": "https://image.tmdb.org/t/p/w185/5W4AV3ZXn38NlEMqPy9QPjwRRz8.jpg" + }, + "backdrop_original": [ + "https://image.tmdb.org/t/p/original/aOvgn3E5OjJDepApd1WtS2doqMF.jpg" + ], + "backdrop": [ + "https://image.tmdb.org/t/p/w1280/aOvgn3E5OjJDepApd1WtS2doqMF.jpg" + ] + }, + "plot": "A nanny, working for a family whose son has just passed away, finds herself put in charge of caring for a lifelike doll that the couple treat as a real child.", + "genres": [ + "Horror", + "Thriller" + ], + "in_library": false, + "released": "2016-01-22", + "mpaa": "PG-13", + "original_title": "The Boy", + "directors": [ + "William Brent Bell" + ], + "writers": [ + "Stacey Menear" + ], + "in_wanted": { + "status": "active", + "info": { + "rating": { + "imdb": [ + 6.9, + 815 + ] + }, + "genres": [ + "Horror", + "Thriller" + ], + "tmdb_id": 321258, + "plot": "An American nanny is shocked that her new English family's boy is actually a life-sized doll. After violating a list of strict rules, disturbing events make her believe that the doll is really alive.", + "tagline": "", + "release_date": { + "dvd": 1459461600, + "expires": 1453930027, + "theater": 1453417200, + "bluray": true + }, + "year": 2016, + "original_title": "The Boy", + "actor_roles": { + "Diana Hardcastle": "Mrs. Heelshire", + "Rupert Evans": "Malcolm", + "Jim Norton": "Mr. Heelshire", + "James Russell": "James", + "Lauren Cohan": "Greta" + }, + "via_imdb": true, + "images": { + "disc_art": [], + "poster": [ + "http://ia.media-imdb.com/images/M/MV5BMTQ1NzAwNTg1M15BMl5BanBnXkFtZTgwNDQwOTQ0NzE@._V1_SX300.jpg" + ], + "backdrop": [ + "https://image.tmdb.org/t/p/w1280/aOvgn3E5OjJDepApd1WtS2doqMF.jpg" + ], + "extra_thumbs": [], + "poster_original": [ + "https://image.tmdb.org/t/p/original/lISBWF6UUF3cQPcoLE8pMxnyJty.jpg" + ], + "actors": { + "Diana Hardcastle": "https://image.tmdb.org/t/p/w185/hl58zPrWbo1mfatgVxEnGsKGZ1b.jpg", + "Rupert Evans": "https://image.tmdb.org/t/p/w185/AuPOKHD18bE2jKXONvrMLGVQ744.jpg", + "Lauren Cohan": "https://image.tmdb.org/t/p/w185/5W4AV3ZXn38NlEMqPy9QPjwRRz8.jpg", + "Jim Norton": "https://image.tmdb.org/t/p/w185/lndFtmSwtCzoeWkgjqJBqVRKGk2.jpg" + }, + "backdrop_original": [ + "https://image.tmdb.org/t/p/original/aOvgn3E5OjJDepApd1WtS2doqMF.jpg" + ], + "clear_art": [], + "logo": [], + "banner": [], + "landscape": [], + "extra_fanart": [] + }, + "directors": [ + "William Brent Bell" + ], + "titles": [ + "The Boy", + "The Inhabitant", + "?? ?????" + ], + "imdb": "tt3882082", + "mpaa": "PG-13", + "via_tmdb": true, + "actors": [ + "Lauren Cohan", + "Rupert Evans", + "Ben Robson", + "Diana Hardcastle" + ], + "writers": [ + "Stacey Menear" + ], + "runtime": 97, + "type": "movie", + "released": "2016-01-22" + }, + "_t": "media", + "title": "The Boy", + "_rev": "000c14b5", + "profile_id": "e57930f7c49e4419a7ce764460921cd1", + "_id": "0a39772ec4484b628b739791a7b43b9f", + "tags": [], + "profile": { + "core": false, + "_rev": "0002f338", + "finish": [ + true, + true + ], + "minimum_score": 1, + "_id": "e57930f7c49e4419a7ce764460921cd1", + "_t": "profile", + "label": "HD", + "qualities": [ + "1080p", + "720p" + ], + "stop_after": [ + 0, + 0 + ], + "wait_for": [ + 5, + 5 + ], + "order": 0, + "3d": [ + 0, + 0 + ] + }, + "last_edit": 1453695473, + "type": "movie", + "files": { + "image_poster": [ + "/Users/jayjo/Library/Application Support/CouchPotato/cache/d8f54038b9a50cdd52642a92916299da.jpg" + ] + }, + "identifiers": { + "imdb": "tt3882082" + } + }, + "actors": [ + "Lauren Cohan", + "Rupert Evans", + "Ben Robson", + "Diana Hardcastle" + ], + "runtime": 97, + "type": "movie" + }, + { + "rating": { + "imdb": [ + 4.6, + 23396 + ] + }, + "tmdb_id": 241251, + "actor_roles": { + "John Corbett": "Garrett Peterson", + "Hill Harper": "Principal Edward Warren", + "Bailey Chase": "Benny", + "Jennifer Lopez": "Claire Peterson", + "Fran?ois Chau": "Detective Johnny Chou", + "Lexi Atkins": "Allie Callahan", + "Raquel Gardner": "Barbara", + "Ian Nelson": "Kevin Peterson", + "Ryan Guzman": "Noah Sandborn", + "Kristin Chenoweth": "Vicky Lansing" + }, + "via_imdb": true, + "via_tmdb": true, + "titles": [ + "The Boy Next Door", + "Le gar?on d'? c?t?", + "Obsesi?n" + ], + "imdb": "tt3181822", + "year": 2015, + "images": { + "poster": [ + "https://image.tmdb.org/t/p/w154/h28t2JNNGrZx0fIuAw8aHQFhIxR.jpg", + "http://ia.media-imdb.com/images/M/MV5BMTgxNTEyMTYzOV5BMl5BanBnXkFtZTgwNzQ4OTg5MjE@._V1_SX300.jpg" + ], + "extra_thumbs": [], + "poster_original": [ + "https://image.tmdb.org/t/p/original/h28t2JNNGrZx0fIuAw8aHQFhIxR.jpg" + ], + "actors": { + "John Corbett": "https://image.tmdb.org/t/p/w185/r66d7CFycEOwzFnRlAgv5HX8r1R.jpg", + "Hill Harper": "https://image.tmdb.org/t/p/w185/6Dz2mV1Jdfnv0789JTFY8G08ju2.jpg", + "Bailey Chase": "https://image.tmdb.org/t/p/w185/1Lmy7WGlthUPwjUDbj1ODTdWUZA.jpg", + "Jennifer Lopez": "https://image.tmdb.org/t/p/w185/mxBDIyt8u4q5eJcQkGipNYTtlvz.jpg", + "Fran?ois Chau": "https://image.tmdb.org/t/p/w185/r8EocoGAJVky0bQ6gyfxcabuTPz.jpg", + "Lexi Atkins": "https://image.tmdb.org/t/p/w185/fRq4XQ2X52lDqeDiHTRdSHU9xQC.jpg", + "Raquel Gardner": "https://image.tmdb.org/t/p/w185/rONhpmIXABWcJJgKjicQ87tJLEu.jpg", + "Ian Nelson": "https://image.tmdb.org/t/p/w185/e5UxvFTHRFI2o8RDiTAjb9tJXLL.jpg", + "Ryan Guzman": "https://image.tmdb.org/t/p/w185/yJR4t5PlBdsiPAP9JdGjqomkVv7.jpg", + "Kristin Chenoweth": "https://image.tmdb.org/t/p/w185/nZuynlyNec2G1QEIj0iHbKToaft.jpg" + }, + "backdrop_original": [ + "https://image.tmdb.org/t/p/original/vj4IhmH4HCMZYYjTMiYBybTWR5o.jpg" + ], + "backdrop": [ + "https://image.tmdb.org/t/p/w1280/vj4IhmH4HCMZYYjTMiYBybTWR5o.jpg" + ] + }, + "plot": "A recently cheated on married woman falls for a younger man who has moved in next door, but their torrid affair soon takes a dangerous turn.", + "genres": [ + "Thriller", + "Mystery" + ], + "in_library": false, + "released": "2015-01-23", + "tagline": "A Moment She Couldn't Resist. An Obsession He Can't Control.", + "original_title": "The Boy Next Door", + "directors": [ + "Rob Cohen" + ], + "writers": [ + "Barbara Curry" + ], + "in_wanted": false, + "mpaa": "R", + "actors": [ + "Jennifer Lopez", + "Ryan Guzman", + "Ian Nelson", + "John Corbett" + ], + "runtime": 91, + "type": "movie" + }, + { + "rating": { + "imdb": [ + 6.9, + 72919 + ] + }, + "tmdb_id": 9319, + "actor_roles": { + "Halle Berry": "Cory", + "Bill Medley": "Himself", + "Billy Blanks": "Billy Cole", + "Bruce Willis": "Joe Hallenbeck", + "Dick Butkus": "Himself", + "Bruce McGill": "Mike Matthews", + "Badja Djola": "Alley Thug", + "Verne Lundquist": "Himself", + "Ken Kells": "Head Coach", + "Joe El Rady": "Kid", + "Michael J. Fisher": "Wounded Player", + "Lynn Swann": "Himself", + "Morris Chestnut": "Locker Room Kid", + "Danielle Harris": "Darian Hallenbeck", + "Doug Simpson": "Wounded Player", + "Chelcie Ross": "Senator Baynard", + "Clarence Felder": "McCoskey", + "Chelsea Field": "Sarah Hallenbeck", + "Damon Wayans": "James Alexander 'Jimmy' Dix", + "Frank Collison": "Pablo", + "Kim Coates": "Chet", + "Noble Willingham": "Sheldon Marcone", + "Taylor Negron": "Milo", + "Joe Santos": "Bessalo", + "Tony Longo": "Big Ray Walston" + }, + "via_imdb": true, + "via_tmdb": true, + "titles": [ + "The Last Boy Scout", + "Den siste scouten", + "????????? ????????", + "Last Boy Scout", + "Le Dernier boy scout" + ], + "imdb": "tt0102266", + "year": 1991, + "images": { + "poster": [ + "https://image.tmdb.org/t/p/w154/cTl2WqsERuZ5vYmAmtfsPIJBLr0.jpg", + "http://ia.media-imdb.com/images/M/MV5BMTUxNjQxMTAwMV5BMl5BanBnXkFtZTcwNTI5ODMyMQ@@._V1_.jpg", + "http://ia.media-imdb.com/images/M/MV5BMTUxNjQxMTAwMV5BMl5BanBnXkFtZTcwNTI5ODMyMQ@@._V1_SX300.jpg" + ], + "extra_thumbs": [], + "poster_original": [ + "https://image.tmdb.org/t/p/original/cTl2WqsERuZ5vYmAmtfsPIJBLr0.jpg" + ], + "actors": { + "Halle Berry": "https://image.tmdb.org/t/p/w185/AmCXHowNbUXpNf41dNrxNB0naM2.jpg", + "Bill Medley": "https://image.tmdb.org/t/p/w185/gZix2FrZ9Dw4OvAh0Spu8IfRp6Z.jpg", + "Billy Blanks": "https://image.tmdb.org/t/p/w185/hkbllU1QR9irZ2AmW0eLelKoZSS.jpg", + "Bruce Willis": "https://image.tmdb.org/t/p/w185/kI1OluWhLJk3pnR19VjOfABpnTY.jpg", + "Dick Butkus": "https://image.tmdb.org/t/p/w185/eF8Bg3YPzJw9DH1B3QlAJfdsZgH.jpg", + "Bruce McGill": "https://image.tmdb.org/t/p/w185/n5lXakx8RP8LkRl4rXtHSOSIjPl.jpg", + "Badja Djola": "https://image.tmdb.org/t/p/w185/i5j8WpNKydvBs2Vu3cYJyXNBy3V.jpg", + "Verne Lundquist": "https://image.tmdb.org/t/p/w185/AkUVqg2rkExTneawEUSYFjYnv8O.jpg", + "Ken Kells": "https://image.tmdb.org/t/p/w185/n0hKRviE0p0I5UqJ5BVJ1gamM15.jpg", + "Lynn Swann": "https://image.tmdb.org/t/p/w185/hK8KMKumG0kCARGUBmN3P0UrBRN.jpg", + "Morris Chestnut": "https://image.tmdb.org/t/p/w185/xHgWktsqPDsSsq8RWTe98IwxIIM.jpg", + "Danielle Harris": "https://image.tmdb.org/t/p/w185/q3wQP4ZwVjfBerFK0iVZPzaF5BL.jpg", + "Chelcie Ross": "https://image.tmdb.org/t/p/w185/gtUOc36bK5MrjdRPQJi1b0cc0LT.jpg", + "Clarence Felder": "https://image.tmdb.org/t/p/w185/hbioULG0wwkHlXcjXy89eZIPt2J.jpg", + "Chelsea Field": "https://image.tmdb.org/t/p/w185/9NnerSpNB4Ro3f3iMRorv1uvYlA.jpg", + "Damon Wayans": "https://image.tmdb.org/t/p/w185/rQ1KV896Hm0jzLTOdksbHoXh67l.jpg", + "Frank Collison": "https://image.tmdb.org/t/p/w185/mrFWmvxe72WwrjyYOZXtNpJLLfQ.jpg", + "Kim Coates": "https://image.tmdb.org/t/p/w185/xGoPaGsqRWCsDDDmY1gNR8eoxTA.jpg", + "Noble Willingham": "https://image.tmdb.org/t/p/w185/stdQUjYtyjfuJRgA89DyzkZEyai.jpg", + "Taylor Negron": "https://image.tmdb.org/t/p/w185/wk0bOBqw47cMntR7IJvlE9hE372.jpg", + "Joe Santos": "https://image.tmdb.org/t/p/w185/7D7ddztwCyW1D0DIxNR0JDAWu0d.jpg", + "Tony Longo": "https://image.tmdb.org/t/p/w185/1kRcEeiz6cFAX41xz9ttCDE2MfG.jpg" + }, + "backdrop_original": [ + "https://image.tmdb.org/t/p/original/3BRLjwxF2rmVFedrQkjNdPcd8Uw.jpg" + ], + "backdrop": [ + "https://image.tmdb.org/t/p/w1280/3BRLjwxF2rmVFedrQkjNdPcd8Uw.jpg" + ] + }, + "plot": "When the girl that detective Joe Hallenback is protecting gets murdered, the boyfriend of the murdered girl (ex-football player Jimmy Dix) attempts to investigate and solve the case. What they discover is that there is deep seated corruption going on between a crooked politician and the owner of a pro football team.", + "genres": [ + "Adventure", + "Action", + "Comedy", + "Thriller", + "Crime", + "Mystery" + ], + "in_library": false, + "released": "1991-12-12", + "tagline": "Everyone had counted them out. But they're about to get back in the game.", + "original_title": "The Last Boy Scout", + "directors": [ + "Tony Scott" + ], + "writers": [ + "Shane Black (story)", + "Greg Hicks (story)", + "Shane Black (screenplay)" + ], + "in_wanted": false, + "mpaa": "R", + "actors": [ + "Bruce Willis", + "Damon Wayans", + "Chelsea Field", + "Noble Willingham" + ], + "runtime": 105, + "type": "movie" + }, + { + "rating": { + "imdb": [ + 6.3, + 1175 + ] + }, + "genres": [ + "Fantasy", + "Adventure", + "Comedy", + "Sci-Fi" + ], + "in_library": false, + "titles": [ + "The Invisible Boy", + "Il ragazzo invisibile" + ], + "tmdb_id": 303542, + "plot": "Michele is thirteen year old, shy, unpopular at school, and in love with Stella. After wearing a costume for a Halloween party, he finds out that he's invisible.", + "mpaa": "T", + "original_title": "Il ragazzo invisibile", + "actor_roles": { + "Fabrizio Bentivoglio": "Basilli", + "Valeria Golino": "Giovanna", + "Kseniya Rappoport": "Yelena", + "Assil Kandil": "Candela", + "Noa Zatta": "Stella", + "Vilius Tumalavicius": "Biondo", + "Ludovico Girardello": "Michele", + "Christo Jivkov": "Andreij" + }, + "via_imdb": true, + "images": { + "poster_original": [ + "https://image.tmdb.org/t/p/original/eYkOI2sLAoUhj8UgVhY03LAR8yW.jpg" + ], + "poster": [ + "https://image.tmdb.org/t/p/w154/eYkOI2sLAoUhj8UgVhY03LAR8yW.jpg", + "http://ia.media-imdb.com/images/M/MV5BYWMzNzAxOTktMmE3ZS00OTQxLWJiZWQtYjM3MWQxZmJlMTE5XkEyXkFqcGdeQXVyMTAxNTc3Nzk@._V1_.jpg" + ], + "actors": { + "Kseniya Rappoport": "https://image.tmdb.org/t/p/w185/8CRRe5lTddGGUTF2pjNHAeYw12n.jpg", + "Fabrizio Bentivoglio": "https://image.tmdb.org/t/p/w185/uODahcGkpEJJNPPZNw06ET5V9d5.jpg", + "Christo Jivkov": "https://image.tmdb.org/t/p/w185/rRoYhDGVsXhCeVyhVa0lnhl6HSc.jpg", + "Valeria Golino": "https://image.tmdb.org/t/p/w185/kK13NzfxGQBJ8Th9SiPbu0FFJq7.jpg" + }, + "backdrop_original": [ + "https://image.tmdb.org/t/p/original/woLLDo3tal0R2jBykWyw6QJiDJk.jpg" + ], + "backdrop": [ + "https://image.tmdb.org/t/p/w1280/woLLDo3tal0R2jBykWyw6QJiDJk.jpg" + ] + }, + "directors": [ + "Gabriele Salvatores" + ], + "writers": [ + "Alessandro Fabbri", + "Ludovica Rampoldi", + "Stefano Sardo" + ], + "in_wanted": false, + "year": 2014, + "via_tmdb": true, + "imdb": "tt3078296", + "actors": [ + "Ludovico Girardello", + "Valeria Golino", + "Fabrizio Bentivoglio", + "Christo Jivkov" + ], + "type": "movie", + "runtime": 100 + }, + { + "rating": { + "imdb": [ + 7.8, + 123130 + ] + }, + "genres": [ + "Drama", + "War" + ], + "in_library": false, + "titles": [ + "The Boy in the Striped Pajamas", + "The Boy in the Striped Pyjamas", + "O Rapaz do Pijama ?s Riscas", + "???????", + "????????", + "O Menino do Pijama Listrado" + ], + "tmdb_id": 14574, + "plot": "Set during World War II, a story seen through the innocent eyes of Bruno, the eight-year-old son of the commandant at a concentration camp, whose forbidden friendship with a Jewish boy on the other side of the camp fence has startling and unexpected consequences.", + "tagline": "Lines may divide us, but hope will unite us.", + "year": 2008, + "original_title": "The Boy in the Striped Pyjamas", + "actor_roles": { + "Zsuzsa Holl": "Berlin Cook", + "Richard Johnson": "Grandpa", + "Cara Horgan": "Maria", + "Jack Scanlon": "Shmuel", + "Asa Butterfield": "Bruno", + "Vera Farmiga": "Mother", + "Rupert Friend": "Lieutenant Kurt Kotler", + "Iv?n Vereb?ly": "Meinberg", + "Charlie Baker": "Palm Court Singer", + "B?la Fesztbaum": "Schultz", + "L?szl? ?ron": "Lars", + "Sheila Hancock": "Grandma", + "Zac Mattoon O'Brien": "Leon", + "Henry Kingsmill": "Karl", + "Amber Beattie": "Gretel", + "David Thewlis": "Father" + }, + "via_imdb": true, + "images": { + "poster_original": [ + "https://image.tmdb.org/t/p/original/f40R9to84FGF7T5bp3KGB84Lklb.jpg" + ], + "poster": [ + "https://image.tmdb.org/t/p/w154/f40R9to84FGF7T5bp3KGB84Lklb.jpg", + "http://ia.media-imdb.com/images/M/MV5BMTMzMTc3MjA5NF5BMl5BanBnXkFtZTcwOTk3MDE5MQ@@._V1_.jpg", + "http://ia.media-imdb.com/images/M/MV5BMTMzMTc3MjA5NF5BMl5BanBnXkFtZTcwOTk3MDE5MQ@@._V1_SX300.jpg" + ], + "actors": { + "Rupert Friend": "https://image.tmdb.org/t/p/w185/tYDs9R1TDHUgIT3bnXZo6GoyzTI.jpg", + "Richard Johnson": "https://image.tmdb.org/t/p/w185/miDA5sN3ijfED6M4oRZ0NTdBfBR.jpg", + "Cara Horgan": "https://image.tmdb.org/t/p/w185/wXPFyDpKemvplSq1X9YFUrZc0ev.jpg", + "Jack Scanlon": "https://image.tmdb.org/t/p/w185/lZXxoJ2dyv3tD0ookIKNx5hkxsF.jpg", + "Asa Butterfield": "https://image.tmdb.org/t/p/w185/zvaIeivqGmWgCtWusKmItMq3eeC.jpg", + "Vera Farmiga": "https://image.tmdb.org/t/p/w185/dNeyqIgD29efuzdoDFGqPZYa1Y0.jpg", + "Charlie Baker": "https://image.tmdb.org/t/p/w185/dweX5EVhUh4JfcKgVuaMAgjbyaJ.jpg", + "Sheila Hancock": "https://image.tmdb.org/t/p/w185/jZv0I2HNE4BbFTatSXPotNvZWRS.jpg", + "Zac Mattoon O'Brien": "https://image.tmdb.org/t/p/w185/oiVl4lzBroQbCa4iOydICQF2Tz6.jpg", + "Amber Beattie": "https://image.tmdb.org/t/p/w185/6wl7bpIIvkSG5fFUHjHTc62liv8.jpg", + "David Thewlis": "https://image.tmdb.org/t/p/w185/ozGL5i87RkvO2FTd43awnoJzFo7.jpg" + }, + "backdrop_original": [ + "https://image.tmdb.org/t/p/original/prhrYZvQN1eXxBeZdLlNSyLfPQb.jpg" + ], + "backdrop": [ + "https://image.tmdb.org/t/p/w1280/prhrYZvQN1eXxBeZdLlNSyLfPQb.jpg" + ] + }, + "directors": [ + "Mark Herman" + ], + "writers": [ + "John Boyne (novel)", + "Mark Herman (screenplay)" + ], + "in_wanted": false, + "mpaa": "PG-13", + "via_tmdb": true, + "imdb": "tt0914798", + "actors": [ + "Asa Butterfield", + "Zac Mattoon O'Brien", + "Domonkos N?meth", + "Henry Kingsmill" + ], + "type": "movie", + "runtime": 94 + } + ], + "yesAction": "addMovie", + "yesResponse": "Added The Boy (2016) to your list of movies to download.", + "noAction": "suggestNextMovie", + "noResponse": "Ok, did you mean The Boy Next Door (2015)?" + } + }, + "user": { + "userId": "amzn1.echo-sdk-account.1234" + }, + "new": false + }, + "request": { + "type": "IntentRequest", + "requestId": "EdwRequestId.1234", + "timestamp": "2016-01-01T00:00:00Z", + "intent": { + "name": "AMAZON.NoIntent", + "slots": {} + } + } +}