-
Notifications
You must be signed in to change notification settings - Fork 0
/
__init__.py
276 lines (235 loc) · 9.92 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
"""A Mycroft skill to provide information about movies.
The information is sourced from The Movie Database (TMDb) API. Documentation
for this API can be found at https://www.themoviedb.org/. The API is free to
use. It can be accessed using the API key specified in the __api__ variable
or by an API specified by the user in skill settings.
"""
from datetime import datetime
import os
from mycroft import MycroftSkill, intent_file_handler
from mycroft.util.format import pronounce_number, nice_date, nice_number
from mycroft.util.log import LOG
import tmdbv3api
DEFAULT_API_KEY = "6b064259b900f7d4fd32f3c74ac35207"
LOGGER = LOG(__name__)
TMDB = tmdbv3api.TMDb()
MOVIE = tmdbv3api.Movie()
class MovieMaster(MycroftSkill):
def __init__(self):
super(MovieMaster, self).__init__(name="MovieMaster")
self.search_depth = None
self.movie_db = tmdbv3api.TMDb()
self.movie = tmdbv3api.Movie()
def initialize(self):
"""Set some variables that do not change during the execution of the script"""
self.apply_user_settings()
self.movie_db.language = self.lang
self.settings_change_callback = self.apply_user_settings
def apply_user_settings(self):
"""Apply user-defined settings from https://home.mycroft.ai.
The API key defined at the top of this file can be overridden by the
user if they have their own. The search depth is used to limit the
number of search results included in a response to the user. For
example, if there are 10 movies on the popular movies list and search
depth is set to 5, only five movies will be included in response
"""
self._determine_api_key()
self.searchDepth = self.settings.get("searchDepth")
def _determine_api_key(self):
"""Use the API key in settings, if provided. Otherwise use developer key."""
settings_api_key = self.settings.get("apiv3")
if settings_api_key in ("Default", "", None):
self.movie_db.api_key = DEFAULT_API_KEY
else:
self.movie_db.api_key = settings_api_key
try:
# Make a call to the API to verify the key
self.movie.popular()
except Exception:
self.log.exception(
'API Key {} invalid. Reverting to default'.format(settings_api_key)
)
self.movie_db.api_key = DEFAULT_API_KEY
@intent_file_handler("movie.description.intent")
def handle_movie_description(self, message):
""" Gets the long version of the requested movie.
"""
movie = message.data.get("movie")
try:
movieDetails = MOVIE.details(MOVIE.search(movie)[:1][0])
if movieDetails.overview is not "":
self.speak_dialog("movie.description", {"movie": movie})
for sentence in movieDetails.overview.split(". "):
self.speak(sentence)
else:
self.speak_dialog("no.info", {"movie": movie})
# If the title can not be found, it creates an IndexError
except IndexError:
self.speak_dialog("no.info", {"movie": movie})
@intent_file_handler("movie.information.intent")
def handle_movie_information(self, message):
""" Gets the short version and adds the TagLine for good measure.
"""
movie = message.data.get("movie")
try:
movieDetails = MOVIE.details(MOVIE.search(movie)[:1][0].id)
self.speak_dialog("movie.info.response", {"movie": movieDetails.title, "year": nice_date(datetime.strptime(movieDetails.release_date.replace("-", " "), "%Y %m %d")), "budget": nice_number(movieDetails.budget)})
self.speak(movieDetails.tagline)
# If the title can not be found, it creates an IndexError
except IndexError:
self.speak_dialog("no.info", {"movie": movie})
@intent_file_handler("movie.year.intent")
def handle_movie_year(self, message):
""" Gets the year the movie was released.
"""
movie = message.data.get("movie")
try:
movieDetails = MOVIE.details(MOVIE.search(movie)[:1][0].id)
self.speak_dialog("movie.year", {"movie": movieDetails.title, "year": nice_date(datetime.strptime(movieDetails.release_date.replace("-", " "), "%Y %m %d"))})
## If the title can not be found, it creates an IndexError
except IndexError:
self.speak_dialog("no.info", {"movie": movie})
@intent_file_handler("movie.cast.intent")
def handle_movie_cast(self, message):
""" Gets the cast of the requested movie.
The search_depth setting is avaliable at home.mycroft.ai
"""
movie = message.data.get("movie")
try:
movieDetails = MOVIE.details(MOVIE.search(movie)[:1][0].id)
cast = movieDetails.casts["cast"][:self.searchDepth]
# Create a list to store the cast to be included in the dialog
actorList = ""
# Get the last actor in the list so that the dialog can say it properly
lastInList = cast.pop()
lastActor = " {} as {}".format(lastInList["name"], lastInList["character"])
# Format the rest of the list for the dialog
for person in cast:
actor = " {} as {},".format(person["name"], person["character"])
# Add the formated sentence to the actor list
actorList = actorList + actor
self.speak_dialog("movie.cast", {"movie": movie, "actorlist": actorList, "lastactor": lastActor})
# If the title can not be found, it creates an IndexError
except IndexError:
self.speak_dialog("no.info", {"movie": movie})
@intent_file_handler("movie.production.intent")
def handle_movie_production(self, message):
""" Gets the production companies that made the movie.
The search_depth setting is avaliable at home.mycroft.ai
"""
movie = message.data.get("movie")
try:
movieDetails = MOVIE.details(MOVIE.search(movie)[:1][0].id)
companyList = movieDetails.production_companies[:self.searchDepth]
# If there is only one production company, say the dialog differently
if len(companyList) == 1:
self.speak_dialog("movie.production.single", {"movie": movie, "company": companyList[0]["name"]})
# If there is more, get the last in the list and set up the dialog
if len(companyList) > 1:
companies = ""
lastCompany = companyList.pop()["name"]
for company in companyList:
companies = companies + company["name"] + ", "
self.speak_dialog("movie.production.multiple", {"companies": companies, "movie": movie, "lastcompany": lastCompany})
# If the title can not be found, it creates an IndexError
except IndexError:
self.speak_dialog("no.info", {"movie": movie})
@intent_file_handler("movie.genres.intent")
def handle_movie_genre(self, message):
""" Gets the genres the movie belongs to.
The search_depth setting is avaliable at home.mycroft.ai
"""
movie = message.data.get("movie")
try:
movieDetails = MOVIE.details(MOVIE.search(movie)[:1][0].id)
genreList = movieDetails.genres[:self.searchDepth]
# Set up dialog AGAIN just like above. Is there a better way?
if len(genreList) == 1:
self.speak_dialog("movie.genre.single", {"movie": movie, "genre": genreList[0]["name"]})
if len(genreList) > 1:
genreDialog = ""
lastGenre = genreList.pop()["name"]
for genre in genreList:
genreDialog = genreDialog + genre["name"] + ", "
self.speak_dialog("movie.genre.multiple", {"genrelist": genreDialog, "genrelistlast": lastGenre})
# If the title can not be found, it creates an IndexError
except IndexError:
self.speak_dialog("no.info", {"movie": movie})
@intent_file_handler("movie.runtime.intent")
def handle_movie_length(self, message):
""" Gets the runtime of the searched movie.
"""
movie = message.data.get("movie")
try:
movieDetails = MOVIE.details(MOVIE.search(movie)[:1][0].id)
self.speak_dialog("movie.runtime", {"movie": movie, "runtime": movieDetails.runtime})
# If the title can not be found, it creates an IndexError
except IndexError:
self.speak_dialog("no.info", {"movie": movie})
@intent_file_handler("movie.recommendations.intent")
def handle_movie_recommendations(self, message):
""" Gets the top movies that are similar to the suggested movie.
"""
try:
movie = message.data.get("movie")
# Create a list to store the dialog
movieDialog = ""
movieRecommendations = MOVIE.recommendations(MOVIE.search(movie)[:1][0].id)[:self.searchDepth]
# Get the last movie
lastMovie = movieRecommendations.pop()
for film in movieRecommendations:
if movieDialog == "":
movieDialog = film.title
else:
movieDialog = movieDialog + ", " + film.title
movieDialog = movieDialog + " and {}".format(lastMovie.title)
self.speak_dialog("movie.recommendations", {"movielist": movieDialog, "movie": movie})
# If the title can not be found, it creates an IndexError
except IndexError:
self.speak_dialog("no.info", {"movie": movie.title})
@intent_file_handler("movie.popular.intent")
def handle_popular_movies(self, message):
""" Gets the daily popular movies.
The list changes daily, and are not just recent movies.
The search_depth setting is avaliable at home.mycroft.ai
"""
try:
movie = message.data.get("movie")
popularMovies = MOVIE.popular()[:self.searchDepth]
# Lets see...I think we will set up the dialog again.
lastMovie = popularMovies.pop()
popularDialog = ""
for movie in popularMovies:
if popularDialog == "":
popularDialog = movie.title
else:
popularDialog = popularDialog + ", " + movie.title
popularDialog = popularDialog + " and {}".format(lastMovie.title)
self.speak_dialog("movie.popular", {"popularlist": popularDialog})
except:
self.speak_dialog("no.info", {"movie": movie.title})
pass
@intent_file_handler("movie.top.intent")
def handle_top_movies(self, message):
""" Gets the top rated movies of the day.
The list changes daily, and are not just recent movies.
The search_depth setting is avaliable at home.mycroft.ai
"""
try:
movie = message.data.get("movie")
topMovies = MOVIE.top_rated()[:self.searchDepth]
# Set up the dialog
lastMovie = topMovies.pop()
topDialog = ""
for movie in topMovies:
if topDialog == "":
topDialog = movie.title
else:
topDialog = topDialog + ", {}".format(movie.title)
topDialog = topDialog + " and {}".format(lastMovie.title)
self.speak_dialog("movie.top", {"toplist": topDialog})
except:
self.speak_dialog("no.info", {"movie": movie.title})
pass
def create_skill():
return MovieMaster()