12 Commits
v0.1 ... v0.2

Author SHA1 Message Date
53614c7b78 comments 2021-03-19 15:34:44 +01:00
1af7508eb8 movie id can be found ignoring the kodi skin 2021-03-19 15:24:43 +01:00
a561932844 comment 2021-03-19 15:22:54 +01:00
4ac68a6db6 shuffle recommendations, for more variety 2021-03-19 15:10:37 +01:00
646b62a149 reformat and comment 2021-03-19 15:07:55 +01:00
c3bf8117a6 refactored 2021-03-19 14:46:48 +01:00
89c1c1d7c5 refactored 2021-03-19 14:34:14 +01:00
5e238ba9b0 refactored 2021-03-19 13:54:04 +01:00
d9e185ba4f refactored tmdb stuff into a class 2021-03-19 01:21:09 +01:00
1dc369bc3b find tmdbid by imdbid if necessary 2021-03-18 22:11:37 +01:00
207601f395 require youtube plugin 2021-03-18 21:44:56 +01:00
227733fe55 make compatible to Estuary skin 2021-03-18 21:36:30 +01:00
4 changed files with 136 additions and 59 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.pyo

100
addon.py
View File

@ -5,12 +5,14 @@ import xbmcvfs
import json
import random
import sys
import urllib
import tmdb
ADDON = xbmcaddon.Addon()
CWD = ADDON.getAddonInfo('path').decode('utf-8')
#CWD = ADDON.getAddonInfo('path') # for kodi 19
# reads program names from JSON files and returns a dict
def list_programs(cinematic_path):
dirs, files = xbmcvfs.listdir(cinematic_path)
programs = {}
@ -23,6 +25,7 @@ def list_programs(cinematic_path):
programs[program_data['name']] = filename
return programs
# shows the selection dialog with program names, returns JSON filename
def show_dialog(programs):
entries = programs.keys()
dialog = xbmcgui.Dialog()
@ -30,6 +33,7 @@ def show_dialog(programs):
del dialog
return programs[entries[ret]]
# get a number of random files from a directory
def files_from_dir(count, location):
dirs, files = xbmcvfs.listdir(location)
files = random.sample(files, count)
@ -37,50 +41,7 @@ def files_from_dir(count, location):
files[i] = location + files[i]
return files
def get_recommendations(apikey, movieid, language, choice):
print("getting %s for %d" % (choice, movieid))
baseurl = 'https://api.themoviedb.org/3/'
url = baseurl + 'movie/%d/%s?api_key=%s&language=%s'
url = url % (movieid, choice, apikey, language)
json_url = urllib.urlopen(url)
data = json.loads(json_url.read())
results = []
for result in data['results']:
results.append({'title': result['title'], 'movieid': result['id']})
return results
def get_movie_trailers(apikey, movieid, language):
print("getting trailers for %d" % movieid)
baseurl = 'https://api.themoviedb.org/3/'
url = baseurl + 'movie/%d?api_key=%s&language=%s&append_to_response=videos'
url = url % (movieid, apikey, language)
json_url = urllib.urlopen(url)
data = json.loads(json_url.read())
results = []
for result in data['videos']['results']:
if result['site'] == 'YouTube':
location = 'plugin://plugin.video.youtube/play/?video_id=%s' % result['key']
else:
next
results.append({'title': result['name'], 'type': result['type'], 'location': location})
return results
def get_trailers(apikey, recommendations, language, cliptype, count):
results = []
for recommendation in recommendations[:10]:
all_trailers = get_movie_trailers(apikey, recommendation['movieid'], language)
trailers = []
for trailer in all_trailers:
if trailer['type'] == cliptype:
trailers.append(trailer)
if len(trailers) > 0:
random.shuffle(trailers)
trailer = trailers[0]
results.append(trailer)
if len(results) == count:
break
return results
# reads the JSON file and conducts a pre-program for the feature movie
def conduct_program(program_file, feature):
filehandle = xbmcvfs.File(program_file)
program_json = filehandle.read()
@ -97,16 +58,18 @@ def conduct_program(program_file, feature):
entry = {'type': 'video', 'data': location}
program.append(entry)
elif settings['source'] == 'tmdbtrailer':
apikey = settings['apikey']
TMDB = tmdb.Tmdb(apikey = settings['apikey'], language = settings['language'])
tmdbid = int(feature['tmdbid'])
language = settings['language']
imdbid = feature['imdbid']
choice = settings['choice']
trailertype = settings['type']
count = settings['count']
if not tmdbid:
tmdbid = TMDB.get_tmdbid(imdbid)
if tmdbid:
movies = get_recommendations(apikey, tmdbid, language, choice)
movies = TMDB.get_recommendations(tmdbid, choice)
random.shuffle(movies)
trailers = get_trailers(apikey, movies, language, trailertype, count)
trailers = TMDB.get_trailers(movies, trailertype, count)
else:
print("TODO: this feature has no tmdb id, find someting else to play")
trailers = []
@ -132,51 +95,70 @@ def conduct_program(program_file, feature):
program.append(entry)
return program
# fetches information on the feature movie
def get_feature(movieid):
query = '{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovieDetails", "params": {"movieid": %s, "properties": ["file", "mpaa", "uniqueid"]}, "id": "1"}' % movieid
json_response = xbmc.executeJSONRPC(query)
response = json.loads(json_response)
if not 'tmdb' in response['result']['moviedetails']['uniqueid']:
response['result']['moviedetails']['uniqueid']['tmdb'] = 0
if not 'imdb' in response['result']['moviedetails']['uniqueid']:
response['result']['moviedetails']['uniqueid']['tmdb'] = ''
feature = {
'label': response['result']['moviedetails']['label'],
'file': response['result']['moviedetails']['file'],
'mpaa': response['result']['moviedetails']['mpaa'],
'tmdbid': response['result']['moviedetails']['uniqueid']['tmdb']
'tmdbid': response['result']['moviedetails']['uniqueid']['tmdb'],
'imdbid': response['result']['moviedetails']['uniqueid']['imdb']
}
return feature
# entry point
if __name__ == '__main__':
for arg in sys.argv[1:]:
(name, value) = arg.split('=')
if name == 'dbid':
movieid = int(value)
cinematic_path = ADDON.getSettingString('cinematic_path')
# collect info about the selected feature movie
movieid = xbmc.getInfoLabel('ListItem.DBID').decode('utf-8')
feature = get_feature(movieid)
print(feature)
# let user select one of the JSON programs
programs = list_programs(cinematic_path)
program_file = cinematic_path + show_dialog(programs)
program = conduct_program(program_file, feature)
# conduct and show playlist
program = conduct_program(program_file, feature)
print('=== playlist')
for entry in program:
print(" * [%s] -- %s" % (entry['type'], entry['data']))
# close modal movie information window
if xbmc.getCondVisibility('Window.IsVisible(MovieInformation)'):
xbmc.executebuiltin('Dialog.Close(MovieInformation)')
print('=== playing')
"""
TODO: this is not the intended way of playing back the conducted playlist.
hints for a better implementation are welcome.
the plan is to play each video file in the list separately, so the addon is
able to perform further tasks in between, like controlling home automation
systems. unfortunately, research has to be done in order to find out how
that should be implemented.
"""
# build kodi style playlist
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
playlist.clear()
for entry in program:
print(" * [%s] -- %s" % (entry['type'], entry['data']))
if entry['type'] == 'video':
playlist.add(entry['data'])
else:
# it is planned to implement features other than video playback
# e. g. call scripts in home automation to dim the lights
print(" unable to handle %s yet" % entry['type'])
# start playback
xbmc.Player().play(playlist)
xbmc.sleep(500)
# wait until program is finished
while xbmc.getCondVisibility('Player.HasMedia'):
xbmc.sleep(100)
print('=== done playing')

View File

@ -2,6 +2,7 @@
<addon id="script.cinemavision" name="Cinematic" version="0.1" provider-name="Cinematic">
<requires>
<import addon="xbmc.python" version="2.25.0"/>
<import addon="plugin.video.youtube" version="6.8.10"/>
</requires>
<extension point="xbmc.python.script" library="addon.py">
<provides>executable</provides>

93
tmdb.py Normal file
View File

@ -0,0 +1,93 @@
# this is a small and single-purpose wrapper around the TMDB API
import json
import random
import urllib
class Tmdb:
def __init__(self, apikey, language):
self.baseurl = 'https://api.themoviedb.org/3/'
self.apikey = apikey
self.language = language
# fetches data from TMDB API
def fetch_from_tmdb(self, path, parameters):
apikey = "?api_key=%s" % self.apikey
url = self.baseurl + path + apikey + parameters
json_url = urllib.urlopen(url)
data = json.loads(json_url.read())
return data
# finds TMDB-id for a known IMDB-id, return 0 if nothing is found
def get_tmdbid(self, imdbid):
print("getting tmdbid for imdbid %s" % imdbid)
data = self.fetch_from_tmdb(
path = 'find/%s' % imdbid,
parameters = '&external_source=imdb_id'
)
try:
tmdbid = data['movie_results'][0]['id']
print("tmdbid is %d" % tmdbid)
except:
tmdbid = 0
print("tmdbid could not be found")
return tmdbid
# get recommendations for a movie id
# choice is either "recommendations" or "similar"
def get_recommendations(self, movieid, choice):
print("getting %s for %d" % (choice, movieid))
data = self.fetch_from_tmdb(
path = 'movie/%d/%s' % (movieid, choice),
parameters = '&language=%s' % self.language
)
results = []
for result in data['results']:
results.append({
'title': result['title'],
'movieid': result['id']
})
return results
# get trailers, teasers, clips... for a movie id
def get_movie_trailers(self, movieid):
print("getting trailers for %d" % movieid)
data = self.fetch_from_tmdb(
path = 'movie/%d' % movieid,
parameters = '&language=%s&append_to_response=videos' % self.language
)
results = []
for result in data['videos']['results']:
if result['site'] == 'YouTube':
location = 'plugin://plugin.video.youtube/play/?video_id=%s' % result['key']
else:
# skip other sites
next
results.append({
'title': result['name'],
'type': result['type'],
'location': location
})
return results
# get a number of trailers of a certain kind for a list of recommended movies
def get_trailers(self, recommendations, cliptype, count):
results = []
# loop over ten out of the list of recommendations
random.shuffle(recommendations)
for recommendation in recommendations[:10]:
all_trailers = self.get_movie_trailers(recommendation['movieid'])
# filter to get only a certain kind of trailer
trailers = []
for trailer in all_trailers:
if trailer['type'] == cliptype:
trailers.append(trailer)
# get a random clip for this recommendation
if len(trailers) > 0:
random.shuffle(trailers)
trailer = trailers[0]
results.append(trailer)
# quit if enough clips have been collected
if len(results) == count:
break
return results