Movie Sync Alpha version
This commit is contained in:
parent
488c1a0a19
commit
88f649d36d
4 changed files with 620 additions and 97 deletions
|
@ -50,6 +50,8 @@ from threading import Thread
|
|||
import Queue
|
||||
import traceback
|
||||
|
||||
import re
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as etree
|
||||
except ImportError:
|
||||
|
@ -85,6 +87,8 @@ class PlexAPI():
|
|||
self.plexversion = client.getVersion()
|
||||
self.platform = client.getPlatform()
|
||||
|
||||
self.doUtils = downloadutils.DownloadUtils()
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
@ -156,7 +160,7 @@ class PlexAPI():
|
|||
url = url + '/clients'
|
||||
self.logMsg("CheckConnection called for url %s with a token" % url, 2)
|
||||
|
||||
r = downloadutils.DownloadUtils().downloadUrl(
|
||||
r = self.doUtils.downloadUrl(
|
||||
url,
|
||||
authenticate=False,
|
||||
headerOptions={'X-Plex-Token': token}
|
||||
|
@ -1233,33 +1237,467 @@ class PlexAPI():
|
|||
})
|
||||
return serverlist
|
||||
|
||||
def GetPlexCollections(self, type):
|
||||
# Build a list of the user views
|
||||
def GetPlexCollections(self, mediatype):
|
||||
"""
|
||||
Input:
|
||||
mediatype String or list of strings with possible values
|
||||
'movie', 'show', 'artist', 'photo'
|
||||
Output:
|
||||
Collection containing only mediatype. List with entry of the form:
|
||||
{
|
||||
'name': xxx Plex title for the media section
|
||||
'type': xxx Plex type: 'movie', 'show', 'artist', 'photo'
|
||||
'id': xxx Plex unique key for the section
|
||||
}
|
||||
"""
|
||||
collections = []
|
||||
|
||||
url = "{server}/library/sections"
|
||||
jsondata = self.doUtils.downloadUrl(url)
|
||||
try:
|
||||
result = jsondata['_children']
|
||||
except:
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
for item in result:
|
||||
|
||||
# name = item['Name']
|
||||
name = item['title']
|
||||
# contentType = item['Type']
|
||||
contentType = item['type']
|
||||
itemtype = contentType
|
||||
content = itemtype
|
||||
contentID = item['key']
|
||||
|
||||
if itemtype == type and name not in ("Collections", "Trailers"):
|
||||
if contentType in mediatype:
|
||||
name = item['title']
|
||||
contentId = item['key']
|
||||
collections.append({
|
||||
|
||||
'title': name,
|
||||
'type': itemtype,
|
||||
'id': contentID,
|
||||
'content': content
|
||||
'name': name,
|
||||
'type': contentType,
|
||||
'id': str(contentId)
|
||||
})
|
||||
return collections
|
||||
|
||||
def GetPlexSectionResults(self, viewId):
|
||||
"""
|
||||
Returns a list (raw API dump) of all Plex movies in the Plex section
|
||||
with key = viewId.
|
||||
"""
|
||||
result = []
|
||||
url = "{server}/library/sections/%s/all" % viewId
|
||||
jsondata = self.doUtils.downloadUrl(url)
|
||||
try:
|
||||
result = jsondata['_children']
|
||||
except KeyError:
|
||||
pass
|
||||
return result
|
||||
|
||||
def GetPlexMetadata(self, key):
|
||||
"""
|
||||
Returns raw API metadata for key.
|
||||
"""
|
||||
result = []
|
||||
url = "{server}" + key
|
||||
jsondata = self.doUtils.downloadUrl(url)
|
||||
try:
|
||||
result = jsondata['_children'][0]
|
||||
except KeyError:
|
||||
self.logMsg("Error retrieving metadata for %s" % url, 1)
|
||||
pass
|
||||
return result
|
||||
|
||||
|
||||
class API():
|
||||
|
||||
def __init__(self, item):
|
||||
|
||||
self.item = item
|
||||
self.clientinfo = clientinfo.ClientInfo()
|
||||
self.addonName = self.clientinfo.getAddonName()
|
||||
|
||||
def logMsg(self, msg, lvl=1):
|
||||
|
||||
className = self.__class__.__name__
|
||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||
|
||||
def convert_date(self, stamp):
|
||||
"""
|
||||
convert_date(stamp) converts a Unix time stamp (seconds passed since
|
||||
January 1 1970) to a propper, human-readable time stamp
|
||||
"""
|
||||
# DATEFORMAT = xbmc.getRegion('dateshort')
|
||||
# TIMEFORMAT = xbmc.getRegion('meridiem')
|
||||
# date_time = time.localtime(stamp)
|
||||
# if DATEFORMAT[1] == 'd':
|
||||
# localdate = time.strftime('%d-%m-%Y', date_time)
|
||||
# elif DATEFORMAT[1] == 'm':
|
||||
# localdate = time.strftime('%m-%d-%Y', date_time)
|
||||
# else:
|
||||
# localdate = time.strftime('%Y-%m-%d', date_time)
|
||||
# if TIMEFORMAT != '/':
|
||||
# localtime = time.strftime('%I:%M%p', date_time)
|
||||
# else:
|
||||
# localtime = time.strftime('%H:%M', date_time)
|
||||
# return localtime + ' ' + localdate
|
||||
DATEFORMAT = xbmc.getRegion('dateshort')
|
||||
TIMEFORMAT = xbmc.getRegion('meridiem')
|
||||
date_time = time.localtime(stamp)
|
||||
localdate = time.strftime('%Y-%m-%d', date_time)
|
||||
return localdate
|
||||
|
||||
def getChecksum(self):
|
||||
"""
|
||||
Maybe get rid of viewOffset = (resume point)?!?
|
||||
"""
|
||||
item = self.item
|
||||
checksum = "%s%s%s%s%s" % (
|
||||
item['key'],
|
||||
item['updatedAt'],
|
||||
item.get('viewCount', ""),
|
||||
item.get('lastViewedAt', ""),
|
||||
item.get('viewOffset', "")
|
||||
)
|
||||
return checksum
|
||||
|
||||
def getDateCreated(self):
|
||||
item = self.item
|
||||
try:
|
||||
dateadded = item['addedAt']
|
||||
dateadded = self.convert_date(dateadded)
|
||||
except KeyError:
|
||||
dateadded = None
|
||||
return dateadded
|
||||
|
||||
def getUserData(self):
|
||||
item = self.item
|
||||
# Default
|
||||
favorite = False
|
||||
playcount = None
|
||||
played = False
|
||||
lastPlayedDate = None
|
||||
resume = 0
|
||||
rating = 0
|
||||
|
||||
try:
|
||||
playcount = int(item['viewCount'])
|
||||
except KeyError:
|
||||
playcount = None
|
||||
|
||||
if playcount:
|
||||
played = True
|
||||
|
||||
try:
|
||||
lastPlayedDate = int(item['lastViewedAt'])
|
||||
lastPlayedDate = self.convert_date(lastPlayedDate)
|
||||
except KeyError:
|
||||
lastPlayedDate = None
|
||||
|
||||
try:
|
||||
resume = int(item['viewOffset'])
|
||||
except KeyError:
|
||||
resume = 0
|
||||
|
||||
return {
|
||||
'Favorite': favorite,
|
||||
'PlayCount': playcount,
|
||||
'Played': played,
|
||||
'LastPlayedDate': lastPlayedDate,
|
||||
'Resume': resume,
|
||||
'Rating': rating
|
||||
}
|
||||
|
||||
def getPeople(self):
|
||||
"""
|
||||
returns a dictionary of lists of people found in item.
|
||||
|
||||
{
|
||||
'Director': list,
|
||||
'Writer': list,
|
||||
'Cast': list,
|
||||
'Producer': list
|
||||
}
|
||||
"""
|
||||
item = self.item
|
||||
item = item['_children']
|
||||
# Process People
|
||||
director = []
|
||||
writer = []
|
||||
cast = []
|
||||
producer = []
|
||||
for entry in item:
|
||||
if entry['_elementType'] == 'Director':
|
||||
director.append(entry['tag'])
|
||||
elif entry['_elementType'] == 'Writer':
|
||||
writer.append(entry['tag'])
|
||||
elif entry['_elementType'] == 'Role':
|
||||
cast.append(entry['tag'])
|
||||
elif entry['_elementType'] == 'Producer':
|
||||
producer.append(entry['tag'])
|
||||
return {
|
||||
'Director': director,
|
||||
'Writer': writer,
|
||||
'Cast': cast,
|
||||
'Producer': producer
|
||||
}
|
||||
|
||||
def getPeopleList(self):
|
||||
"""
|
||||
Returns a list of people from item, with a list item of the form
|
||||
{
|
||||
'Name': xxx,
|
||||
'Type': xxx,
|
||||
'Id': xxx
|
||||
('Role': xxx for cast/actors only)
|
||||
}
|
||||
"""
|
||||
item = self.item['_children']
|
||||
people = []
|
||||
# Key of library: Plex-identifier. Value represents the Kodi/emby side
|
||||
people_of_interest = {
|
||||
'Director': 'Director',
|
||||
'Writer': 'Writer',
|
||||
'Role': 'Actor',
|
||||
'Producer': 'Producer'
|
||||
}
|
||||
for entry in item:
|
||||
if entry['_elementType'] in people_of_interest.keys():
|
||||
name = entry['tag']
|
||||
name_id = entry['id']
|
||||
Type = entry['_elementType']
|
||||
Type = people_of_interest[Type]
|
||||
if Type == 'Actor':
|
||||
Role = entry['role']
|
||||
people.append({
|
||||
'Name': name,
|
||||
'Type': Type,
|
||||
'Id': name_id,
|
||||
'Role': Role
|
||||
})
|
||||
else:
|
||||
people.append({
|
||||
'Name': name,
|
||||
'Type': Type,
|
||||
'Id': name_id
|
||||
})
|
||||
return people
|
||||
|
||||
def getGenres(self):
|
||||
"""
|
||||
returns a list of genres found in item. (Not a string!!)
|
||||
"""
|
||||
item = self.item
|
||||
item = item['_children']
|
||||
genre = []
|
||||
for entry in item:
|
||||
if entry['_elementType'] == 'Genre':
|
||||
genre.append(entry['tag'])
|
||||
return genre
|
||||
|
||||
def getProvider(self, providername):
|
||||
"""
|
||||
provider = getProvider(self, item, providername)
|
||||
|
||||
providername: imdb, tvdb, musicBrainzArtist, musicBrainzAlbum,
|
||||
musicBrainzTrackId
|
||||
|
||||
Return IMDB: "tt1234567"
|
||||
"""
|
||||
item = self.item
|
||||
imdb_regex = re.compile(r'''(
|
||||
imdb:// # imdb tag, which will be followed be tt1234567
|
||||
(tt\d{7}) # actual IMDB ID, e.g. tt1234567
|
||||
\?? # zero or one ?
|
||||
(.*) # rest, e.g. language setting
|
||||
)''', re.VERBOSE)
|
||||
try:
|
||||
if "Imdb" in providername:
|
||||
provider = imdb_regex.findall(item['guid'])
|
||||
provider = provider[0][1]
|
||||
elif "tvdb" in providername:
|
||||
provider = item['ProviderIds']['Tvdb']
|
||||
elif "musicBrainzArtist" in providername:
|
||||
provider = item['ProviderIds']['MusicBrainzArtist']
|
||||
elif "musicBrainzAlbum" in providername:
|
||||
provider = item['ProviderIds']['MusicBrainzAlbum']
|
||||
elif "musicBrainzTrackId" in providername:
|
||||
provider = item['ProviderIds']['MusicBrainzTrackId']
|
||||
except:
|
||||
provider = None
|
||||
return provider
|
||||
|
||||
def GetTitle(self):
|
||||
item = self.item
|
||||
title = item['title']
|
||||
try:
|
||||
sorttitle = item['titleSort']
|
||||
except KeyError:
|
||||
sorttitle = title
|
||||
return title, sorttitle
|
||||
|
||||
def getRuntime(self):
|
||||
"""
|
||||
Resume point of time and runtime/totaltime. Rounded to 6th decimal.
|
||||
|
||||
Assumption: time for both resume and runtime is measured in
|
||||
milliseconds on the Plex side and in seconds on the Kodi side.
|
||||
"""
|
||||
item = self.item
|
||||
time_factor = 1/1000
|
||||
runtime = item['duration'] * time_factor
|
||||
try:
|
||||
resume = item['viewOffset'] * time_factor
|
||||
except KeyError:
|
||||
resume = 0
|
||||
resume = round(float(resume), 6)
|
||||
runtime = round(float(runtime), 6)
|
||||
return resume, runtime
|
||||
|
||||
def getMpaa(self):
|
||||
# Convert more complex cases
|
||||
item = self.item
|
||||
try:
|
||||
mpaa = item['contentRating']
|
||||
except KeyError:
|
||||
mpaa = None
|
||||
if mpaa in ("NR", "UR"):
|
||||
# Kodi seems to not like NR, but will accept Rated Not Rated
|
||||
mpaa = "Rated Not Rated"
|
||||
return mpaa
|
||||
|
||||
def getCountry(self):
|
||||
"""
|
||||
Returns a list of all countries found in item.
|
||||
"""
|
||||
item = self.item
|
||||
item = item['_children']
|
||||
country = []
|
||||
for entry in item:
|
||||
if entry['_elementType'] == 'Country':
|
||||
country.append(entry['tag'])
|
||||
return country
|
||||
|
||||
def getStudios(self):
|
||||
item = self.item
|
||||
studio = []
|
||||
try:
|
||||
studio.append(self.getStudio(item['studio']))
|
||||
except KeyError:
|
||||
pass
|
||||
return studio
|
||||
|
||||
def getStudio(self, studioName):
|
||||
# Convert studio for Kodi to properly detect them
|
||||
studios = {
|
||||
'abc (us)': "ABC",
|
||||
'fox (us)': "FOX",
|
||||
'mtv (us)': "MTV",
|
||||
'showcase (ca)': "Showcase",
|
||||
'wgn america': "WGN"
|
||||
}
|
||||
return studios.get(studioName.lower(), studioName)
|
||||
|
||||
def joinList(self, listobject):
|
||||
"""
|
||||
Smart-joins the list into a single string using a " / " separator.
|
||||
If the list is empty, smart_join returns an empty string.
|
||||
"""
|
||||
string = " / ".join(listobject)
|
||||
return string
|
||||
|
||||
def getFilePath(self):
|
||||
item = self.item
|
||||
try:
|
||||
filepath = item['key']
|
||||
|
||||
except KeyError:
|
||||
filepath = ""
|
||||
|
||||
else:
|
||||
if "\\\\" in filepath:
|
||||
# append smb protocol
|
||||
filepath = filepath.replace("\\\\", "smb://")
|
||||
filepath = filepath.replace("\\", "/")
|
||||
|
||||
if item.get('VideoType'):
|
||||
videotype = item['VideoType']
|
||||
# Specific format modification
|
||||
if 'Dvd'in videotype:
|
||||
filepath = "%s/VIDEO_TS/VIDEO_TS.IFO" % filepath
|
||||
elif 'Bluray' in videotype:
|
||||
filepath = "%s/BDMV/index.bdmv" % filepath
|
||||
|
||||
if "\\" in filepath:
|
||||
# Local path scenario, with special videotype
|
||||
filepath = filepath.replace("/", "\\")
|
||||
|
||||
return filepath
|
||||
|
||||
def getMediaStreams(self):
|
||||
item = self.item
|
||||
item = item['_children']
|
||||
videotracks = []
|
||||
audiotracks = []
|
||||
subtitlelanguages = []
|
||||
|
||||
MediaStreams = []
|
||||
aspectratio = None
|
||||
for entry in item:
|
||||
if entry['_elementType'] == 'Media':
|
||||
MediaStreams.append(entry)
|
||||
try:
|
||||
aspectratio = entry['aspectRatio']
|
||||
except KeyError:
|
||||
pass
|
||||
# Abort if no Media found
|
||||
if not MediaStreams:
|
||||
return
|
||||
# Loop over parts:
|
||||
# TODO: what if several Media tags exist?!?
|
||||
for part in MediaStreams[0]['_children']:
|
||||
container = part['container'].lower()
|
||||
for mediaStream in part['_children']:
|
||||
try:
|
||||
type = mediaStream['streamType']
|
||||
except KeyError:
|
||||
type = None
|
||||
if type == 1: # Video streams
|
||||
videotrack = {}
|
||||
videotrack['videocodec'] = mediaStream['codec'].lower()
|
||||
if "msmpeg4" in videotrack['videocodec']:
|
||||
videotrack['videocodec'] = "divx"
|
||||
elif "mpeg4" in videotrack['videocodec']:
|
||||
# if "simple profile" in profile or profile == "":
|
||||
# videotrack['videocodec'] = "xvid"
|
||||
pass
|
||||
elif "h264" in videotrack['videocodec']:
|
||||
if container in ("mp4", "mov", "m4v"):
|
||||
videotrack['videocodec'] = "avc1"
|
||||
videotrack['height'] = mediaStream.get('height')
|
||||
videotrack['width'] = mediaStream.get('width')
|
||||
# TODO: 3d Movies?!?
|
||||
# videotrack['Video3DFormat'] = item.get('Video3DFormat')
|
||||
try:
|
||||
aspectratio = mediaStream['aspectRatio']
|
||||
except KeyError:
|
||||
if not aspectratio:
|
||||
aspectratio = round(float(videotrack['width'] / videotrack['height']), 6)
|
||||
videotrack['aspectratio'] = aspectratio
|
||||
# TODO: Video 3d format
|
||||
videotrack['video3DFormat'] = None
|
||||
videotracks.append(videotrack)
|
||||
|
||||
elif type == 2: # Audio streams
|
||||
audiotrack = {}
|
||||
audiotrack['audiocodec'] = mediaStream['codec'].lower()
|
||||
profile = mediaStream['codecID'].lower()
|
||||
if "dca" in audiotrack['audiocodec'] and "dts-hd ma" in profile:
|
||||
audiotrack['audiocodec'] = "dtshd_ma"
|
||||
audiotrack['channels'] = mediaStream.get('channels')
|
||||
try:
|
||||
audiotrack['audiolanguage'] = mediaStream.get('language')
|
||||
except KeyError:
|
||||
audiotrack['audiolanguage'] = 'unknown'
|
||||
audiotracks.append(audiotrack)
|
||||
|
||||
elif type == 3: # Subtitle streams
|
||||
try:
|
||||
subtitlelanguages.append(mediaStream['language'])
|
||||
except:
|
||||
subtitlelanguages.append("Unknown")
|
||||
return {
|
||||
'video': videotracks,
|
||||
'audio': audiotracks,
|
||||
'subtitle': subtitlelanguages
|
||||
}
|
||||
|
|
|
@ -427,9 +427,7 @@ class Artwork():
|
|||
|
||||
server = self.server
|
||||
|
||||
id = item['Id']
|
||||
artworks = item['ImageTags']
|
||||
backdrops = item['BackdropImageTags']
|
||||
id = item['key']
|
||||
|
||||
maxHeight = 10000
|
||||
maxWidth = 10000
|
||||
|
@ -453,26 +451,20 @@ class Artwork():
|
|||
}
|
||||
|
||||
# Process backdrops
|
||||
backdropIndex = 0
|
||||
for backdroptag in backdrops:
|
||||
artwork = (
|
||||
"%s/emby/Items/%s/Images/Backdrop/%s?"
|
||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||
% (server, id, backdropIndex,
|
||||
maxWidth, maxHeight, backdroptag, customquery))
|
||||
allartworks['Backdrop'].append(artwork)
|
||||
backdropIndex += 1
|
||||
|
||||
# Process the rest of the artwork
|
||||
for art in artworks:
|
||||
# Filter backcover
|
||||
if art != "BoxRear":
|
||||
tag = artworks[art]
|
||||
artwork = (
|
||||
"%s/emby/Items/%s/Images/%s/0?"
|
||||
"MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s"
|
||||
% (server, id, art, maxWidth, maxHeight, tag, customquery))
|
||||
allartworks[art] = artwork
|
||||
# Get background artwork URL
|
||||
try:
|
||||
background = item['art']
|
||||
background = "%s%s" % (server, background)
|
||||
except KeyError:
|
||||
background = ""
|
||||
allartworks['Backdrop'].append(background)
|
||||
# Get primary "thumb" pictures:
|
||||
try:
|
||||
primary = item['thumb']
|
||||
primary = "%s%s" % (server, primary)
|
||||
except KeyError:
|
||||
primary = ""
|
||||
allartworks['Primary'] = primary
|
||||
|
||||
# Process parent items if the main item is missing artwork
|
||||
if parentInfo:
|
||||
|
|
|
@ -19,6 +19,8 @@ import embydb_functions as embydb
|
|||
import kodidb_functions as kodidb
|
||||
import read_embyserver as embyserver
|
||||
|
||||
import PlexAPI
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
|
@ -270,12 +272,12 @@ class Movies(Items):
|
|||
emby_db = self.emby_db
|
||||
kodi_db = self.kodi_db
|
||||
artwork = self.artwork
|
||||
API = api.API(item)
|
||||
API = PlexAPI.API(item)
|
||||
|
||||
# If the item already exist in the local Kodi DB we'll perform a full item update
|
||||
# If the item doesn't exist, we'll add it to the database
|
||||
update_item = True
|
||||
itemid = item['Id']
|
||||
itemid = item['key']
|
||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
||||
try:
|
||||
movieid = emby_dbitem[0]
|
||||
|
@ -290,11 +292,6 @@ class Movies(Items):
|
|||
kodicursor.execute("select coalesce(max(idMovie),0) from movie")
|
||||
movieid = kodicursor.fetchone()[0] + 1
|
||||
|
||||
if not viewtag or not viewid:
|
||||
# Get view tag from emby
|
||||
viewtag, viewid, mediatype = self.emby.getView_embyId(itemid)
|
||||
self.logMsg("View tag found: %s" % viewtag, 2)
|
||||
|
||||
# fileId information
|
||||
checksum = API.getChecksum()
|
||||
dateadded = API.getDateCreated()
|
||||
|
@ -304,51 +301,30 @@ class Movies(Items):
|
|||
|
||||
# item details
|
||||
people = API.getPeople()
|
||||
writer = " / ".join(people['Writer'])
|
||||
director = " / ".join(people['Director'])
|
||||
genres = item['Genres']
|
||||
title = item['Name']
|
||||
plot = API.getOverview()
|
||||
shortplot = item.get('ShortOverview')
|
||||
tagline = API.getTagline()
|
||||
votecount = item.get('VoteCount')
|
||||
rating = item.get('CommunityRating')
|
||||
year = item.get('ProductionYear')
|
||||
writer = API.joinList(people['Writer'])
|
||||
director = API.joinList(people['Director'])
|
||||
genres = API.getGenres()
|
||||
title, sorttitle = API.GetTitle()
|
||||
plot = item['summary']
|
||||
shortplot = None
|
||||
tagline = item.get('tagline', '')
|
||||
votecount = 0
|
||||
rating = item.get('audienceRating', None)
|
||||
year = item.get('year', None)
|
||||
imdb = API.getProvider('Imdb')
|
||||
sorttitle = item['SortName']
|
||||
runtime = API.getRuntime()
|
||||
resume, runtime = API.getRuntime()
|
||||
mpaa = API.getMpaa()
|
||||
genre = " / ".join(genres)
|
||||
country = API.getCountry()
|
||||
genre = API.joinList(genres)
|
||||
countries = API.getCountry()
|
||||
country = API.joinList(countries)
|
||||
studios = API.getStudios()
|
||||
try:
|
||||
studio = studios[0]
|
||||
except IndexError:
|
||||
studio = None
|
||||
|
||||
if item.get('LocalTrailerCount'):
|
||||
# There's a local trailer
|
||||
url = (
|
||||
"{server}/emby/Users/{UserId}/Items/%s/LocalTrailers?format=json"
|
||||
% itemid
|
||||
)
|
||||
result = self.doUtils.downloadUrl(url)
|
||||
trailer = "plugin://plugin.video.plexkodiconnect/trailer/?id=%s&mode=play" % result[0]['Id']
|
||||
else:
|
||||
# Try to get the youtube trailer
|
||||
try:
|
||||
trailer = item['RemoteTrailers'][0]['Url']
|
||||
except (KeyError, IndexError):
|
||||
trailer = None
|
||||
else:
|
||||
try:
|
||||
trailerId = trailer.rsplit('=', 1)[1]
|
||||
except IndexError:
|
||||
self.logMsg("Failed to process trailer: %s" % trailer)
|
||||
trailer = None
|
||||
else:
|
||||
trailer = "plugin://plugin.video.youtube/play/?video_id=%s" % trailerId
|
||||
|
||||
# TODO: trailers
|
||||
trailer = None
|
||||
|
||||
##### GET THE FILE AND PATH #####
|
||||
playurl = API.getFilePath()
|
||||
|
@ -455,9 +431,11 @@ class Movies(Items):
|
|||
kodicursor.execute(query, (pathid, filename, dateadded, fileid))
|
||||
|
||||
# Process countries
|
||||
kodi_db.addCountries(movieid, item['ProductionLocations'], "movie")
|
||||
kodi_db.addCountries(movieid, countries, "movie")
|
||||
# Process cast
|
||||
people = artwork.getPeopleArtwork(item['People'])
|
||||
people = API.getPeopleList()
|
||||
# TODO: get IMDB pictures?
|
||||
people = artwork.getPeopleArtwork(people)
|
||||
kodi_db.addPeople(movieid, people, "movie")
|
||||
# Process genres
|
||||
kodi_db.addGenres(movieid, genres, "movie")
|
||||
|
@ -469,13 +447,13 @@ class Movies(Items):
|
|||
# Process studios
|
||||
kodi_db.addStudios(movieid, studios, "movie")
|
||||
# Process tags: view, emby tags
|
||||
tags = [viewtag]
|
||||
tags.extend(item['Tags'])
|
||||
if userdata['Favorite']:
|
||||
tags.append("Favorite movies")
|
||||
kodi_db.addTags(movieid, tags, "movie")
|
||||
# tags = [viewtag]
|
||||
# tags.extend(item['Tags'])
|
||||
# if userdata['Favorite']:
|
||||
# tags.append("Favorite movies")
|
||||
# kodi_db.addTags(movieid, tags, "movie")
|
||||
# Process playstates
|
||||
resume = API.adjustResume(userdata['Resume'])
|
||||
# resume = API.adjustResume(userdata['Resume'])
|
||||
total = round(float(runtime), 6)
|
||||
kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ import read_embyserver as embyserver
|
|||
import userclient
|
||||
import videonodes
|
||||
|
||||
import PlexAPI
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
|
@ -51,6 +53,7 @@ class LibrarySync(threading.Thread):
|
|||
self.user = userclient.UserClient()
|
||||
self.emby = embyserver.Read_EmbyServer()
|
||||
self.vnodes = videonodes.VideoNodes()
|
||||
self.plx = PlexAPI.PlexAPI()
|
||||
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
|
@ -76,7 +79,7 @@ class LibrarySync(threading.Thread):
|
|||
if utils.settings('SyncInstallRunDone') == "true":
|
||||
|
||||
# Validate views
|
||||
self.refreshViews()
|
||||
# self.refreshViews()
|
||||
completed = False
|
||||
# Verify if server plugin is installed.
|
||||
if utils.settings('serverSync') == "true":
|
||||
|
@ -235,7 +238,7 @@ class LibrarySync(threading.Thread):
|
|||
}
|
||||
|
||||
process = {
|
||||
'movies': self.movies,
|
||||
'movies': self.PlexMovies,
|
||||
}
|
||||
for itemtype in process:
|
||||
startTime = datetime.now()
|
||||
|
@ -461,6 +464,118 @@ class LibrarySync(threading.Thread):
|
|||
utils.window('Emby.nodes.total', str(totalnodes))
|
||||
|
||||
|
||||
|
||||
def PlexMovies(self, embycursor, kodicursor, pdialog, compare=False):
|
||||
# Get movies from emby
|
||||
emby = self.emby
|
||||
emby_db = embydb.Embydb_Functions(embycursor)
|
||||
movies = itemtypes.Movies(embycursor, kodicursor)
|
||||
|
||||
views = self.plx.GetPlexCollections('movies')
|
||||
self.logMsg("Media folders: %s" % views, 1)
|
||||
|
||||
if compare:
|
||||
# Pull the list of movies and boxsets in Kodi
|
||||
try:
|
||||
all_kodimovies = dict(emby_db.getChecksum('Movie'))
|
||||
except ValueError:
|
||||
all_kodimovies = {}
|
||||
|
||||
try:
|
||||
all_kodisets = dict(emby_db.getChecksum('BoxSet'))
|
||||
except ValueError:
|
||||
all_kodisets = {}
|
||||
|
||||
all_embymoviesIds = set()
|
||||
all_embyboxsetsIds = set()
|
||||
updatelist = []
|
||||
|
||||
##### PROCESS MOVIES #####
|
||||
for view in views:
|
||||
|
||||
if self.shouldStop():
|
||||
return False
|
||||
|
||||
# Get items per view
|
||||
viewId = view['id']
|
||||
viewName = view['name']
|
||||
|
||||
if pdialog:
|
||||
pdialog.update(
|
||||
heading=self.addonName,
|
||||
message="Gathering movies from view: %s..." % viewName)
|
||||
|
||||
if compare:
|
||||
# Manual sync
|
||||
if pdialog:
|
||||
pdialog.update(
|
||||
heading=self.addonName,
|
||||
message="Comparing movies from view: %s..." % viewName)
|
||||
|
||||
all_embymovies = self.plx.GetPlexSectionResults(viewId)
|
||||
embymovies = []
|
||||
for embymovie in all_embymovies:
|
||||
|
||||
if self.shouldStop():
|
||||
return False
|
||||
|
||||
API = PlexAPI.API(embymovie)
|
||||
itemid = embymovie['key']
|
||||
all_embymoviesIds.add(itemid)
|
||||
|
||||
if all_kodimovies.get(itemid) != API.getChecksum():
|
||||
# Only update if movie is not in Kodi or checksum is different
|
||||
updatelist.append(itemid)
|
||||
embymovies.append(embymovie)
|
||||
|
||||
self.logMsg("Movies to update for %s: %s" % (viewName, updatelist), 1)
|
||||
total = len(updatelist)
|
||||
del updatelist[:]
|
||||
else:
|
||||
# Initial or repair sync
|
||||
embymovies = self.plx.GetPlexMovies(viewId)
|
||||
total = len(embymovies)
|
||||
|
||||
if pdialog:
|
||||
pdialog.update(heading="Processing %s / %s items" % (viewName, total))
|
||||
|
||||
count = 0
|
||||
for embymovie in embymovies:
|
||||
# Process individual movies
|
||||
if self.shouldStop():
|
||||
return False
|
||||
|
||||
title = embymovie['title']
|
||||
if pdialog:
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
pdialog.update(percentage, message=title)
|
||||
count += 1
|
||||
detailed = self.plx.GetPlexMetadata(embymovie['key'])
|
||||
movies.add_update(detailed, viewName, viewId)
|
||||
else:
|
||||
self.logMsg("Movies finished.", 2)
|
||||
|
||||
##### PROCESS DELETES #####
|
||||
if compare:
|
||||
# Manual sync, process deletes
|
||||
for kodimovie in all_kodimovies:
|
||||
if kodimovie not in all_embymoviesIds:
|
||||
movies.remove(kodimovie)
|
||||
else:
|
||||
self.logMsg("Movies compare finished.", 1)
|
||||
|
||||
for boxset in all_kodisets:
|
||||
if boxset not in all_embyboxsetsIds:
|
||||
movies.remove(boxset)
|
||||
else:
|
||||
self.logMsg("Boxsets compare finished.", 1)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def movies(self, embycursor, kodicursor, pdialog, compare=False):
|
||||
# Get movies from emby
|
||||
emby = self.emby
|
||||
|
|
Loading…
Reference in a new issue