From e2b616829faa7744acc342e5d93b9973a569af4e Mon Sep 17 00:00:00 2001 From: Joseph Schmitt Date: Sun, 27 Mar 2016 14:35:30 -0400 Subject: [PATCH] Couch Potato Alexa Skill 1.0 --- .env | 12 + .gitignore | 3 + README.md | 53 ++ event.json | 26 + icon-108x108.png | Bin 0 -> 15688 bytes index.js | 4 + interaction_model/intent_schema.json | 31 ++ interaction_model/sample_utterances.txt | 34 ++ lib/handlers.js | 133 +++++ lib/index.js | 15 + lib/old.js | 160 ++++++ lib/utils.js | 35 ++ package.json | 21 + test_events/couch_potato_add_movie.json | 27 + test_events/couch_potato_find_movie.json | 26 + test_events/yes_reprompt.json | 601 +++++++++++++++++++++++ 16 files changed, 1181 insertions(+) create mode 100644 .env create mode 100644 .gitignore create mode 100644 README.md create mode 100644 event.json create mode 100644 icon-108x108.png create mode 100644 index.js create mode 100644 interaction_model/intent_schema.json create mode 100644 interaction_model/sample_utterances.txt create mode 100644 lib/handlers.js create mode 100644 lib/index.js create mode 100644 lib/old.js create mode 100644 lib/utils.js create mode 100644 package.json create mode 100644 test_events/couch_potato_add_movie.json create mode 100644 test_events/couch_potato_find_movie.json create mode 100644 test_events/yes_reprompt.json 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 0000000000000000000000000000000000000000..de826df8be0b8cd63e33af85eae06bab3fbdd2ad GIT binary patch literal 15688 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 IqK3i$3o#GRq5uE@ literal 0 HcmV?d00001 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": {} + } + } +}