Code reduction (#66)

This commit is contained in:
angelblue05 2016-10-10 03:19:00 -05:00 committed by GitHub
parent 64766bcb71
commit 3288d21bef
10 changed files with 3031 additions and 2935 deletions

File diff suppressed because it is too large Load diff

View file

@ -21,7 +21,8 @@ import kodidb_functions as kodidb
import read_embyserver as embyserver
import userclient
import videonodes
from utils import window, settings, language as lang
from objects import Movies, MusicVideos, TVShows, Music
from utils import window, settings, language as lang, should_stop
from ga_client import GoogleAnalytics
##################################################################################################
@ -186,15 +187,6 @@ class LibrarySync(threading.Thread):
finally:
settings('LastIncrementalSync', value=lastSync)
def shouldStop(self):
# Checkpoint during the syncing process
if self.monitor.abortRequested():
return True
elif window('emby_shouldStop') == "true":
return True
else: # Keep going
return False
def dbCommit(self, connection):
# Central commit, verifies if Kodi database update is running
kodidb_scan = window('emby_kodiScan') == "true"
@ -209,7 +201,7 @@ class LibrarySync(threading.Thread):
log.info("Flag still active, but will try to commit")
window('emby_kodiScan', clear=True)
if self.shouldStop():
if should_stop():
log.info("Commit unsuccessful. Sync terminated.")
break
@ -618,7 +610,7 @@ class LibrarySync(threading.Thread):
# Get movies from emby
emby_db = embydb.Embydb_Functions(embycursor)
movies = itemtypes.Movies(embycursor, kodicursor)
movies = Movies(embycursor, kodicursor, pdialog)
views = emby_db.getView_byType('movies')
views += emby_db.getView_byType('mixed')
@ -628,39 +620,18 @@ class LibrarySync(threading.Thread):
for view in views:
log.info("Processing: %s", view)
if self.shouldStop():
return False
view_name = view['name']
# Get items per view
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33017), view['name']))
message="%s %s..." % (lang(33017), view_name))
# Initial or repair sync
all_embymovies = self.emby.getMovies(view['id'], dialog=pdialog)
total = all_embymovies['TotalRecordCount']
embymovies = all_embymovies['Items']
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (view['name'], total))
count = 0
for embymovie in embymovies:
# Process individual movies
if self.shouldStop():
return False
title = embymovie['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
movies.add_update(embymovie, view['name'], view['id'])
else:
log.debug("Movies finished.")
all_movies = self.emby.getMovies(view['id'], dialog=pdialog)
movies.add_all("Movie", all_movies, view)
log.debug("Movies finished.")
##### PROCESS BOXSETS #####
if pdialog:
@ -668,25 +639,9 @@ class LibrarySync(threading.Thread):
boxsets = self.emby.getBoxset(dialog=pdialog)
total = boxsets['TotalRecordCount']
embyboxsets = boxsets['Items']
if pdialog:
pdialog.update(heading="Processing Boxsets / %s items" % total)
count = 0
for boxset in embyboxsets:
# Process individual boxset
if self.shouldStop():
return False
title = boxset['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
movies.add_updateBoxset(boxset)
else:
log.debug("Boxsets finished.")
movies.process_all("BoxSet", "added", boxsets['Items'], total)
log.debug("Boxsets finished.")
return True
@ -694,15 +649,13 @@ class LibrarySync(threading.Thread):
# Get musicvideos from emby
emby_db = embydb.Embydb_Functions(embycursor)
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
mvideos = MusicVideos(embycursor, kodicursor, pdialog)
views = emby_db.getView_byType('musicvideos')
log.info("Media folders: %s" % views)
for view in views:
if self.shouldStop():
return False
log.info("Processing: %s", view)
# Get items per view
viewId = view['id']
@ -714,25 +667,9 @@ class LibrarySync(threading.Thread):
message="%s %s..." % (lang(33019), viewName))
# Initial or repair sync
all_embymvideos = self.emby.getMusicVideos(viewId, dialog=pdialog)
total = all_embymvideos['TotalRecordCount']
embymvideos = all_embymvideos['Items']
all_mvideos = self.emby.getMusicVideos(viewId, dialog=pdialog)
mvideos.add_all("MusicVideo", all_mvideos, view)
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (viewName, total))
count = 0
for embymvideo in embymvideos:
# Process individual musicvideo
if self.shouldStop():
return False
title = embymvideo['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
mvideos.add_update(embymvideo, viewName, viewId)
else:
log.debug("MusicVideos finished.")
@ -742,7 +679,7 @@ class LibrarySync(threading.Thread):
# Get shows from emby
emby_db = embydb.Embydb_Functions(embycursor)
tvshows = itemtypes.TVShows(embycursor, kodicursor)
tvshows = TVShows(embycursor, kodicursor, pdialog)
views = emby_db.getView_byType('tvshows')
views += emby_db.getView_byType('mixed')
@ -750,47 +687,15 @@ class LibrarySync(threading.Thread):
for view in views:
if self.shouldStop():
return False
# Get items per view
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33020), view['name']))
all_embytvshows = self.emby.getShows(view['id'], dialog=pdialog)
total = all_embytvshows['TotalRecordCount']
embytvshows = all_embytvshows['Items']
all_tvshows = self.emby.getShows(view['id'], dialog=pdialog)
tvshows.add_all("Series", all_tvshows, view)
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (view['name'], total))
count = 0
for embytvshow in embytvshows:
# Process individual show
if self.shouldStop():
return False
title = embytvshow['Name']
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
tvshows.add_update(embytvshow, view['name'], view['id'])
# Process episodes
all_episodes = self.emby.getEpisodesbyShow(embytvshow['Id'])
for episode in all_episodes['Items']:
# Process individual show
if self.shouldStop():
return False
episodetitle = episode['Name']
if pdialog:
pdialog.update(percentage, message="%s - %s" % (title, episodetitle))
tvshows.add_updateEpisode(episode)
else:
log.debug("TVShows finished.")
@ -799,41 +704,21 @@ class LibrarySync(threading.Thread):
def music(self, embycursor, kodicursor, pdialog):
# Get music from emby
emby_db = embydb.Embydb_Functions(embycursor)
music = itemtypes.Music(embycursor, kodicursor)
music = Music(embycursor, kodicursor, pdialog)
process = {
views = emby_db.getView_byType('music')
log.info("Media folders: %s", views)
'artists': [self.emby.getArtists, music.add_updateArtist],
'albums': [self.emby.getAlbums, music.add_updateAlbum],
'songs': [self.emby.getSongs, music.add_updateSong]
}
for itemtype in ['artists', 'albums', 'songs']:
# Add music artists and everything will fall into place
if pdialog:
pdialog.update(heading=lang(29999),
message="%s Music..." % lang(33021))
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33021), itemtype))
for view in views:
all_artists = self.emby.getArtists(view['id'], dialog=pdialog)
music.add_all("MusicArtist", all_artists)
all_embyitems = process[itemtype][0](dialog=pdialog)
total = all_embyitems['TotalRecordCount']
embyitems = all_embyitems['Items']
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (itemtype, total))
count = 0
for embyitem in embyitems:
# Process individual item
if self.shouldStop():
return False
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=embyitem['Name'])
count += 1
process[itemtype][1](embyitem)
else:
log.debug("%s finished." % itemtype)
log.debug("Finished syncing music")
return True
@ -1123,447 +1008,13 @@ class ManualSync(LibrarySync):
def movies(self, embycursor, kodicursor, pdialog):
# Get movies from emby
emby_db = embydb.Embydb_Functions(embycursor)
movies = itemtypes.Movies(embycursor, kodicursor)
views = emby_db.getView_byType('movies')
views += emby_db.getView_byType('mixed')
log.info("Media folders: %s" % views)
# Pull the list of movies and boxsets in Kodi
try:
all_kodimovies = dict(emby_db.get_checksum('Movie'))
except ValueError:
all_kodimovies = {}
try:
all_kodisets = dict(emby_db.get_checksum('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=lang(29999),
message="%s %s..." % (lang(33026), viewName))
all_embymovies = self.emby.getMovies(viewId, basic=True, dialog=pdialog)
for embymovie in all_embymovies['Items']:
if self.shouldStop():
return False
API = api.API(embymovie)
itemid = embymovie['Id']
all_embymoviesIds.add(itemid)
if all_kodimovies.get(itemid) != API.get_checksum():
# Only update if movie is not in Kodi or checksum is different
updatelist.append(itemid)
log.info("Movies to update for %s: %s" % (viewName, updatelist))
embymovies = self.emby.getFullItems(updatelist)
total = len(updatelist)
del updatelist[:]
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
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=embymovie['Name'])
count += 1
movies.add_update(embymovie, viewName, viewId)
##### PROCESS BOXSETS #####
boxsets = self.emby.getBoxset(dialog=pdialog)
embyboxsets = []
if pdialog:
pdialog.update(heading=lang(29999), message=lang(33027))
for boxset in boxsets['Items']:
if self.shouldStop():
return False
# Boxset has no real userdata, so using etag to compare
itemid = boxset['Id']
all_embyboxsetsIds.add(itemid)
if all_kodisets.get(itemid) != boxset['Etag']:
# Only update if boxset is not in Kodi or boxset['Etag'] is different
updatelist.append(itemid)
embyboxsets.append(boxset)
log.info("Boxsets to update: %s" % updatelist)
total = len(updatelist)
if pdialog:
pdialog.update(heading="Processing Boxsets / %s items" % total)
count = 0
for boxset in embyboxsets:
# Process individual boxset
if self.shouldStop():
return False
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=boxset['Name'])
count += 1
movies.add_updateBoxset(boxset)
##### PROCESS DELETES #####
for kodimovie in all_kodimovies:
if kodimovie not in all_embymoviesIds:
movies.remove(kodimovie)
else:
log.info("Movies compare finished.")
for boxset in all_kodisets:
if boxset not in all_embyboxsetsIds:
movies.remove(boxset)
else:
log.info("Boxsets compare finished.")
return True
return Movies(embycursor, kodicursor, pdialog).compare_all()
def musicvideos(self, embycursor, kodicursor, pdialog):
# Get musicvideos from emby
emby_db = embydb.Embydb_Functions(embycursor)
mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
views = emby_db.getView_byType('musicvideos')
log.info("Media folders: %s" % views)
# Pull the list of musicvideos in Kodi
try:
all_kodimvideos = dict(emby_db.get_checksum('MusicVideo'))
except ValueError:
all_kodimvideos = {}
all_embymvideosIds = set()
updatelist = []
for view in views:
if self.shouldStop():
return False
# Get items per view
viewId = view['id']
viewName = view['name']
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33028), viewName))
all_embymvideos = self.emby.getMusicVideos(viewId, basic=True, dialog=pdialog)
for embymvideo in all_embymvideos['Items']:
if self.shouldStop():
return False
API = api.API(embymvideo)
itemid = embymvideo['Id']
all_embymvideosIds.add(itemid)
if all_kodimvideos.get(itemid) != API.get_checksum():
# Only update if musicvideo is not in Kodi or checksum is different
updatelist.append(itemid)
log.info("MusicVideos to update for %s: %s" % (viewName, updatelist))
embymvideos = self.emby.getFullItems(updatelist)
total = len(updatelist)
del updatelist[:]
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (viewName, total))
count = 0
for embymvideo in embymvideos:
# Process individual musicvideo
if self.shouldStop():
return False
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=embymvideo['Name'])
count += 1
mvideos.add_update(embymvideo, viewName, viewId)
##### PROCESS DELETES #####
for kodimvideo in all_kodimvideos:
if kodimvideo not in all_embymvideosIds:
mvideos.remove(kodimvideo)
else:
log.info("MusicVideos compare finished.")
return True
return MusicVideos(embycursor, kodicursor, pdialog).compare_all()
def tvshows(self, embycursor, kodicursor, pdialog):
# Get shows from emby
emby_db = embydb.Embydb_Functions(embycursor)
tvshows = itemtypes.TVShows(embycursor, kodicursor)
views = emby_db.getView_byType('tvshows')
views += emby_db.getView_byType('mixed')
log.info("Media folders: %s" % views)
# Pull the list of tvshows and episodes in Kodi
try:
all_koditvshows = dict(emby_db.get_checksum('Series'))
except ValueError:
all_koditvshows = {}
log.info("all_koditvshows = %s", all_koditvshows)
try:
all_kodiepisodes = dict(emby_db.get_checksum('Episode'))
except ValueError:
all_kodiepisodes = {}
all_embytvshowsIds = set()
all_embyepisodesIds = set()
updatelist = []
for view in views:
if self.shouldStop():
return False
# Get items per view
viewId = view['id']
viewName = view['name']
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33029), viewName))
all_embytvshows = self.emby.getShows(viewId, basic=True, dialog=pdialog)
for embytvshow in all_embytvshows['Items']:
if self.shouldStop():
return False
API = api.API(embytvshow)
itemid = embytvshow['Id']
all_embytvshowsIds.add(itemid)
if all_koditvshows.get(itemid) != API.get_checksum():
# Only update if movie is not in Kodi or checksum is different
updatelist.append(itemid)
log.info("TVShows to update for %s: %s" % (viewName, updatelist))
embytvshows = self.emby.getFullItems(updatelist)
total = len(updatelist)
del updatelist[:]
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (viewName, total))
count = 0
for embytvshow in embytvshows:
# Process individual show
if self.shouldStop():
return False
itemid = embytvshow['Id']
title = embytvshow['Name']
all_embytvshowsIds.add(itemid)
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=title)
count += 1
tvshows.add_update(embytvshow, viewName, viewId)
else:
# Get all episodes in view
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33030), viewName))
all_embyepisodes = self.emby.getEpisodes(viewId, basic=True, dialog=pdialog)
for embyepisode in all_embyepisodes['Items']:
if self.shouldStop():
return False
API = api.API(embyepisode)
itemid = embyepisode['Id']
all_embyepisodesIds.add(itemid)
if "SeriesId" in embyepisode:
all_embytvshowsIds.add(embyepisode['SeriesId'])
if all_kodiepisodes.get(itemid) != API.get_checksum():
# Only update if movie is not in Kodi or checksum is different
updatelist.append(itemid)
log.info("Episodes to update for %s: %s" % (viewName, updatelist))
embyepisodes = self.emby.getFullItems(updatelist)
total = len(updatelist)
del updatelist[:]
count = 0
for episode in embyepisodes:
# Process individual episode
if self.shouldStop():
return False
if pdialog:
percentage = int((float(count) / float(total))*100)
title = "%s - %s" % (episode.get('SeriesName', "Unknown"), episode['Name'])
pdialog.update(percentage, message=title)
count += 1
tvshows.add_updateEpisode(episode)
##### PROCESS DELETES #####
log.info("all_embytvshowsIds = %s " % all_embytvshowsIds)
for koditvshow in all_koditvshows:
if koditvshow not in all_embytvshowsIds:
tvshows.remove(koditvshow)
else:
log.info("TVShows compare finished.")
for kodiepisode in all_kodiepisodes:
if kodiepisode not in all_embyepisodesIds:
tvshows.remove(kodiepisode)
else:
log.info("Episodes compare finished.")
return True
return TVShows(embycursor, kodicursor, pdialog).compare_all()
def music(self, embycursor, kodicursor, pdialog):
# Get music from emby
emby_db = embydb.Embydb_Functions(embycursor)
music = itemtypes.Music(embycursor, kodicursor)
# Pull the list of artists, albums, songs
try:
all_kodiartists = dict(emby_db.get_checksum('MusicArtist'))
except ValueError:
all_kodiartists = {}
try:
all_kodialbums = dict(emby_db.get_checksum('MusicAlbum'))
except ValueError:
all_kodialbums = {}
try:
all_kodisongs = dict(emby_db.get_checksum('Audio'))
except ValueError:
all_kodisongs = {}
all_embyartistsIds = set()
all_embyalbumsIds = set()
all_embysongsIds = set()
updatelist = []
process = {
'artists': [self.emby.getArtists, music.add_updateArtist],
'albums': [self.emby.getAlbums, music.add_updateAlbum],
'songs': [self.emby.getSongs, music.add_updateSong]
}
for data_type in ['artists', 'albums', 'songs']:
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33031), data_type))
if data_type != "artists":
all_embyitems = process[data_type][0](basic=True, dialog=pdialog)
else:
all_embyitems = process[data_type][0](dialog=pdialog)
for embyitem in all_embyitems['Items']:
if self.shouldStop():
return False
API = api.API(embyitem)
itemid = embyitem['Id']
if data_type == "artists":
all_embyartistsIds.add(itemid)
if all_kodiartists.get(itemid) != API.get_checksum():
# Only update if artist is not in Kodi or checksum is different
updatelist.append(itemid)
elif data_type == "albums":
all_embyalbumsIds.add(itemid)
if all_kodialbums.get(itemid) != API.get_checksum():
# Only update if album is not in Kodi or checksum is different
updatelist.append(itemid)
else:
all_embysongsIds.add(itemid)
if all_kodisongs.get(itemid) != API.get_checksum():
# Only update if songs is not in Kodi or checksum is different
updatelist.append(itemid)
log.info("%s to update: %s" % (data_type, updatelist))
embyitems = self.emby.getFullItems(updatelist)
total = len(updatelist)
del updatelist[:]
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (data_type, total))
count = 0
for embyitem in embyitems:
# Process individual item
if self.shouldStop():
return False
if pdialog:
percentage = int((float(count) / float(total))*100)
pdialog.update(percentage, message=embyitem['Name'])
count += 1
process[data_type][1](embyitem)
##### PROCESS DELETES #####
for kodiartist in all_kodiartists:
if kodiartist not in all_embyartistsIds and all_kodiartists[kodiartist] is not None:
music.remove(kodiartist)
else:
log.info("Artist compare finished.")
for kodialbum in all_kodialbums:
if kodialbum not in all_embyalbumsIds:
music.remove(kodialbum)
else:
log.info("Albums compare finished.")
for kodisong in all_kodisongs:
if kodisong not in all_embysongsIds:
music.remove(kodisong)
else:
log.info("Songs compare finished.")
return True
return Music(embycursor, kodicursor).compare_all()

View file

@ -0,0 +1,5 @@
# Dummy file to make this directory a package.
from movies import Movies
from musicvideos import MusicVideos
from tvshows import TVShows
from music import Music

View file

@ -0,0 +1,126 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import xbmc
import xbmcgui
import xbmcvfs
import artwork
import downloadutils
import read_embyserver as embyserver
from utils import window, settings, dialog, language as lang, should_stop
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class Items(object):
pdialog = None
title = None
count = 0
total = 0
def __init__(self, **kwargs):
self.artwork = artwork.Artwork()
self.emby = embyserver.Read_EmbyServer()
self.do_url = downloadutils.DownloadUtils().downloadUrl
self.should_stop = should_stop
self.kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
self.direct_path = settings('useDirectPaths') == "1"
self.content_msg = settings('newContent') == "true"
def path_validation(self, path):
# Verify if direct path is accessible or not
if window('emby_pathverified') != "true" and not xbmcvfs.exists(path):
if dialog(type_="yesno",
heading="{emby}",
line1="%s %s. %s" % (lang(33047), path, lang(33048))):
window('emby_shouldStop', value="true")
return False
return True
def content_pop(self):
# It's possible for the time to be 0. It should be considered disabled in this case.
if not self.pdialog and self.content_msg and self.new_time:
dialog(type_="notification",
heading="{emby}",
message="%s %s" % (lang(33049), name),
icon="{emby}",
time=time,
sound=False)
def update_pdialog(self):
if self.pdialog:
percentage = int((float(self.count) / float(self.total))*100)
self.pdialog.update(percentage, message=self.title)
def add_all(self, item_type, items, view=None):
if self.should_stop():
return False
total = items['TotalRecordCount'] if 'TotalRecordCount' in items else len(items)
items = items['Items'] if 'Items' in items else items
if self.pdialog and view:
self.pdialog.update(heading="Processing %s / %s items" % (view['name'], total))
action = self._get_func(item_type, "added")
if view:
action(items, total, view)
else:
action(items, total)
def process_all(self, item_type, action, items, total=None, view=None):
log.debug("Processing %s: %s", action, items)
process = self._get_func(item_type, action)
self.total = total or len(items)
self.count = 0
for item in items:
if self.should_stop():
return False
if not process:
continue
self.title = item.get('Name', "unknown")
self.update_pdialog()
process(item)
self.count += 1
def added(self, items, total=None, update=True):
# Generator for newly added content
if update:
self.total = total or len(items)
self.count = 0
for item in items:
if self.should_stop():
break
self.title = item.get('Name', "unknown")
yield item
self.update_pdialog()
if update:
self.count += 1

View file

@ -0,0 +1,572 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import urllib
from ntpath import dirname
from datetime import datetime
import api
import common
import downloadutils
import embydb_functions as embydb
import kodidb_functions as kodidb
from utils import window, settings, language as lang, catch_except
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class Movies(common.Items):
def __init__(self, embycursor, kodicursor, pdialog=None):
self.embycursor = embycursor
self.emby_db = embydb.Embydb_Functions(self.embycursor)
self.kodicursor = kodicursor
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
self.pdialog = pdialog
self.new_time = int(settings('newvideotime'))*1000
common.Items.__init__(self)
def _get_func(self, item_type, action):
if item_type == "Movie":
actions = {
'added': self.added,
'update': self.add_update,
'userdata': self.updateUserdata,
'remove': self.remove
}
elif item_type == "BoxSet":
actions = {
'added': self.add_updateBoxset,
'update': self.add_updateBoxset,
'remove': self.remove
}
else:
log.info("Unsupported item_type: %s", item_type)
actions = {}
return actions.get(action)
def compare_all(self):
# Pull the list of movies and boxsets in Kodi
pdialog = self.pdialog
views = self.emby_db.getView_byType('movies')
views += self.emby_db.getView_byType('mixed')
log.info("Media folders: %s" % views)
try:
all_kodisets = dict(self.emby_db.get_checksum('BoxSet'))
except ValueError:
all_kodisets = {}
try:
all_kodimovies = dict(self.emby_db.get_checksum('Movie'))
except ValueError:
all_kodimovies = {}
all_embymoviesIds = set()
all_embyboxsetsIds = set()
updatelist = []
for view in views:
if self.should_stop():
return False
# Get items per view
viewId = view['id']
viewName = view['name']
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33026), viewName))
all_embymovies = self.emby.getMovies(viewId, basic=True, dialog=pdialog)
for embymovie in all_embymovies['Items']:
if self.should_stop():
return False
API = api.API(embymovie)
itemid = embymovie['Id']
all_embymoviesIds.add(itemid)
if all_kodimovies.get(itemid) != API.get_checksum():
# Only update if movie is not in Kodi or checksum is different
updatelist.append(itemid)
log.info("Movies to update for %s: %s" % (viewName, updatelist))
embymovies = self.emby.getFullItems(updatelist)
total = len(updatelist)
del updatelist[:]
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (viewName, total))
self.added(embymovies, total, view)
boxsets = self.emby.getBoxset(dialog=pdialog)
embyboxsets = []
if pdialog:
pdialog.update(heading=lang(29999), message=lang(33027))
for boxset in boxsets['Items']:
if self.should_stop():
return False
# Boxset has no real userdata, so using etag to compare
itemid = boxset['Id']
all_embyboxsetsIds.add(itemid)
if all_kodisets.get(itemid) != boxset['Etag']:
# Only update if boxset is not in Kodi or boxset['Etag'] is different
updatelist.append(itemid)
embyboxsets.append(boxset)
log.info("Boxsets to update: %s" % updatelist)
self.total = len(updatelist)
if pdialog:
pdialog.update(heading="Processing Boxsets / %s items" % total)
self.count = 0
for boxset in embyboxsets:
# Process individual boxset
if self.should_stop():
return False
self.title = boxset['Name']
self.update_pdialog()
self.add_updateBoxset(boxset)
self.count += 1
##### PROCESS DELETES #####
for kodimovie in all_kodimovies:
if kodimovie not in all_embymoviesIds:
self.remove(kodimovie)
else:
log.info("Movies compare finished.")
for boxset in all_kodisets:
if boxset not in all_embyboxsetsIds:
self.remove(boxset)
else:
log.info("Boxsets compare finished.")
return True
def added(self, items, total=None, view=None):
for item in super(Movies, self).added(items, total):
if self.add_update(item, view):
self.content_pop()
@catch_except()
def add_update(self, item, view=None):
# Process single movie
kodicursor = self.kodicursor
emby_db = self.emby_db
artwork = self.artwork
API = api.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']
emby_dbitem = emby_db.getItem_byId(itemid)
try:
movieid = emby_dbitem[0]
fileid = emby_dbitem[1]
pathid = emby_dbitem[2]
log.info("movieid: %s fileid: %s pathid: %s" % (movieid, fileid, pathid))
except TypeError:
update_item = False
log.debug("movieid: %s not found." % itemid)
# movieid
kodicursor.execute("select coalesce(max(idMovie),0) from movie")
movieid = kodicursor.fetchone()[0] + 1
else:
# Verification the item is still in Kodi
query = "SELECT * FROM movie WHERE idMovie = ?"
kodicursor.execute(query, (movieid,))
try:
kodicursor.fetchone()[0]
except TypeError:
# item is not found, let's recreate it.
update_item = False
log.info("movieid: %s missing from Kodi, repairing the entry." % movieid)
if not view:
# Get view tag from emby
viewtag, viewid, mediatype = self.emby.getView_embyId(itemid)
log.debug("View tag found: %s" % viewtag)
else:
viewtag = view['name']
viewid = view['id']
# fileId information
checksum = API.get_checksum()
dateadded = API.get_date_created()
userdata = API.get_userdata()
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
# item details
people = API.get_people()
writer = " / ".join(people['Writer'])
director = " / ".join(people['Director'])
genres = item['Genres']
title = item['Name']
plot = API.get_overview()
shortplot = item.get('ShortOverview')
tagline = API.get_tagline()
votecount = item.get('VoteCount')
rating = item.get('CommunityRating')
year = item.get('ProductionYear')
imdb = API.get_provider('Imdb')
sorttitle = item['SortName']
runtime = API.get_runtime()
mpaa = API.get_mpaa()
genre = " / ".join(genres)
country = API.get_country()
studios = API.get_studios()
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.do_url(url)
try:
trailer = "plugin://plugin.video.emby/trailer/?id=%s&mode=play" % result[0]['Id']
except IndexError:
log.info("Failed to process local trailer.")
trailer = None
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:
log.info("Failed to process trailer: %s" % trailer)
trailer = None
else:
trailer = "plugin://plugin.video.youtube/play/?video_id=%s" % trailerId
##### GET THE FILE AND PATH #####
playurl = API.get_file_path()
if "\\" in playurl:
# Local path
filename = playurl.rsplit("\\", 1)[1]
else: # Network share
filename = playurl.rsplit("/", 1)[1]
if self.direct_path:
# Direct paths is set the Kodi way
if not self.path_validation(playurl):
return False
path = playurl.replace(filename, "")
window('emby_pathverified', value="true")
else:
# Set plugin path and media flags using real filename
path = "plugin://plugin.video.emby.movies/"
params = {
'filename': filename.encode('utf-8'),
'id': itemid,
'dbid': movieid,
'mode': "play"
}
filename = "%s?%s" % (path, urllib.urlencode(params))
##### UPDATE THE MOVIE #####
if update_item:
log.info("UPDATE movie itemid: %s - Title: %s" % (itemid, title))
# Update the movie entry
if self.kodi_version > 16:
query = ' '.join((
"UPDATE movie",
"SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?, c06 = ?,",
"c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?, c14 = ?, c15 = ?,",
"c16 = ?, c18 = ?, c19 = ?, c21 = ?, premiered = ?",
"WHERE idMovie = ?"
))
kodicursor.execute(query, (title, plot, shortplot, tagline, votecount, rating,
writer, year, imdb, sorttitle, runtime, mpaa, genre, director, title, studio,
trailer, country, year, movieid))
else:
query = ' '.join((
"UPDATE movie",
"SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?, c06 = ?,",
"c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?, c14 = ?, c15 = ?,",
"c16 = ?, c18 = ?, c19 = ?, c21 = ?",
"WHERE idMovie = ?"
))
kodicursor.execute(query, (title, plot, shortplot, tagline, votecount, rating,
writer, year, imdb, sorttitle, runtime, mpaa, genre, director, title, studio,
trailer, country, movieid))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
##### OR ADD THE MOVIE #####
else:
log.info("ADD movie itemid: %s - Title: %s" % (itemid, title))
# Add path
pathid = self.kodi_db.addPath(path)
# Add the file
fileid = self.kodi_db.addFile(filename, pathid)
# Create the movie entry
if self.kodi_version > 16:
query = (
'''
INSERT INTO movie(
idMovie, idFile, c00, c01, c02, c03, c04, c05, c06, c07,
c09, c10, c11, c12, c14, c15, c16, c18, c19, c21, premiered)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (movieid, fileid, title, plot, shortplot, tagline,
votecount, rating, writer, year, imdb, sorttitle, runtime, mpaa, genre,
director, title, studio, trailer, country, year))
else:
query = (
'''
INSERT INTO movie(
idMovie, idFile, c00, c01, c02, c03, c04, c05, c06, c07,
c09, c10, c11, c12, c14, c15, c16, c18, c19, c21)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (movieid, fileid, title, plot, shortplot, tagline,
votecount, rating, writer, year, imdb, sorttitle, runtime, mpaa, genre,
director, title, studio, trailer, country))
# Create the reference in emby table
emby_db.addReference(itemid, movieid, "Movie", "movie", fileid, pathid, None, checksum, viewid)
# Update the path
query = ' '.join((
"UPDATE path",
"SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?",
"WHERE idPath = ?"
))
kodicursor.execute(query, (path, "movies", "metadata.local", 1, pathid))
# Update the file
query = ' '.join((
"UPDATE files",
"SET idPath = ?, strFilename = ?, dateAdded = ?",
"WHERE idFile = ?"
))
kodicursor.execute(query, (pathid, filename, dateadded, fileid))
# Process countries
if 'ProductionLocations' in item:
self.kodi_db.addCountries(movieid, item['ProductionLocations'], "movie")
# Process cast
people = artwork.get_people_artwork(item['People'])
self.kodi_db.addPeople(movieid, people, "movie")
# Process genres
self.kodi_db.addGenres(movieid, genres, "movie")
# Process artwork
artwork.add_artwork(artwork.get_all_artwork(item), movieid, "movie", kodicursor)
# Process stream details
streams = API.get_media_streams()
self.kodi_db.addStreams(fileid, streams, runtime)
# Process studios
self.kodi_db.addStudios(movieid, studios, "movie")
# Process tags: view, emby tags
tags = [viewtag]
tags.extend(item['Tags'])
if userdata['Favorite']:
tags.append("Favorite movies")
log.info("Applied tags: %s", tags)
self.kodi_db.addTags(movieid, tags, "movie")
# Process playstates
resume = API.adjust_resume(userdata['Resume'])
total = round(float(runtime), 6)
self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
return True
def add_updateBoxset(self, boxset):
emby = self.emby
emby_db = self.emby_db
artwork = self.artwork
boxsetid = boxset['Id']
title = boxset['Name']
checksum = boxset['Etag']
emby_dbitem = emby_db.getItem_byId(boxsetid)
try:
setid = emby_dbitem[0]
except TypeError:
setid = self.kodi_db.createBoxset(title)
# Process artwork
artwork.add_artwork(artwork.get_all_artwork(boxset), setid, "set", self.kodicursor)
# Process movies inside boxset
current_movies = emby_db.getItemId_byParentId(setid, "movie")
process = []
try:
# Try to convert tuple to dictionary
current = dict(current_movies)
except ValueError:
current = {}
# Sort current titles
for current_movie in current:
process.append(current_movie)
# New list to compare
for movie in emby.getMovies_byBoxset(boxsetid)['Items']:
itemid = movie['Id']
if not current.get(itemid):
# Assign boxset to movie
emby_dbitem = emby_db.getItem_byId(itemid)
try:
movieid = emby_dbitem[0]
except TypeError:
log.info("Failed to add: %s to boxset." % movie['Name'])
continue
log.info("New addition to boxset %s: %s" % (title, movie['Name']))
self.kodi_db.assignBoxset(setid, movieid)
# Update emby reference
emby_db.updateParentId(itemid, setid)
else:
# Remove from process, because the item still belongs
process.remove(itemid)
# Process removals from boxset
for movie in process:
movieid = current[movie]
log.info("Remove from boxset %s: %s" % (title, movieid))
self.kodi_db.removefromBoxset(movieid)
# Update emby reference
emby_db.updateParentId(movie, None)
# Update the reference in the emby table
emby_db.addReference(boxsetid, setid, "BoxSet", mediatype="set", checksum=checksum)
def updateUserdata(self, item):
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
# Poster with progress bar
emby_db = self.emby_db
API = api.API(item)
# Get emby information
itemid = item['Id']
checksum = API.get_checksum()
userdata = API.get_userdata()
runtime = API.get_runtime()
# Get Kodi information
emby_dbitem = emby_db.getItem_byId(itemid)
try:
movieid = emby_dbitem[0]
fileid = emby_dbitem[1]
log.info("Update playstate for movie: %s fileid: %s" % (item['Name'], fileid))
except TypeError:
return
# Process favorite tags
if userdata['Favorite']:
self.kodi_db.addTag(movieid, "Favorite movies", "movie")
else:
self.kodi_db.removeTag(movieid, "Favorite movies", "movie")
# Process playstates
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
resume = API.adjust_resume(userdata['Resume'])
total = round(float(runtime), 6)
log.debug("%s New resume point: %s" % (itemid, resume))
self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
emby_db.updateReference(itemid, checksum)
def remove(self, itemid):
# Remove movieid, fileid, emby reference
emby_db = self.emby_db
kodicursor = self.kodicursor
artwork = self.artwork
emby_dbitem = emby_db.getItem_byId(itemid)
try:
kodiid = emby_dbitem[0]
fileid = emby_dbitem[1]
mediatype = emby_dbitem[4]
log.info("Removing %sid: %s fileid: %s" % (mediatype, kodiid, fileid))
except TypeError:
return
# Remove the emby reference
emby_db.removeItem(itemid)
# Remove artwork
artwork.delete_artwork(kodiid, mediatype, kodicursor)
if mediatype == "movie":
# Delete kodi movie and file
kodicursor.execute("DELETE FROM movie WHERE idMovie = ?", (kodiid,))
kodicursor.execute("DELETE FROM files WHERE idFile = ?", (fileid,))
elif mediatype == "set":
# Delete kodi boxset
boxset_movies = emby_db.getItem_byParentId(kodiid, "movie")
for movie in boxset_movies:
embyid = movie[0]
movieid = movie[1]
self.kodi_db.removefromBoxset(movieid)
# Update emby reference
emby_db.updateParentId(embyid, None)
kodicursor.execute("DELETE FROM sets WHERE idSet = ?", (kodiid,))
log.info("Deleted %s %s from kodi database" % (mediatype, itemid))

View file

@ -0,0 +1,909 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import urllib
from ntpath import dirname
from datetime import datetime
import api
import common
import downloadutils
import embydb_functions as embydb
import kodidb_functions as kodidb
import musicutils
from utils import window, settings, language as lang, catch_except
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class Music(common.Items):
def __init__(self, embycursor, kodicursor, pdialog=None):
self.embycursor = embycursor
self.emby_db = embydb.Embydb_Functions(self.embycursor)
self.kodicursor = kodicursor
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
self.pdialog = pdialog
self.new_time = int(settings('newmusictime'))*1000
self.directstream = settings('streamMusic') == "true"
self.enableimportsongrating = settings('enableImportSongRating') == "true"
self.enableexportsongrating = settings('enableExportSongRating') == "true"
self.enableupdatesongrating = settings('enableUpdateSongRating') == "true"
self.userid = window('emby_currUser')
self.server = window('emby_server%s' % self.userid)
common.Items.__init__(self)
def _get_func(self, item_type, action):
if item_type == "MusicAlbum":
actions = {
'added': self.added_album,
'update': self.add_updateAlbum,
'userdata': self.updateUserdata,
'remove': self.remove
}
elif item_type in ("MusicArtist", "AlbumArtist"):
actions = {
'added': self.added,
'update': self.add_updateArtist,
'remove': self.remove
}
elif item_type == "Audio":
actions = {
'added': self.added_song,
'update': self.add_updateSong,
'userdata': self.updateUserdata,
'remove': self.remove
}
else:
log.info("Unsupported item_type: %s", item_type)
actions = {}
return actions.get(action)
def compare_all(self):
# Pull the list of artists, albums, songs
pdialog = self.pdialog
views = self.emby_db.getView_byType('music')
try:
all_kodiartists = dict(self.emby_db.get_checksum('MusicArtist'))
except ValueError:
all_kodiartists = {}
try:
all_kodialbums = dict(self.emby_db.get_checksum('MusicAlbum'))
except ValueError:
all_kodialbums = {}
try:
all_kodisongs = dict(self.emby_db.get_checksum('Audio'))
except ValueError:
all_kodisongs = {}
all_embyartistsIds = set()
all_embyalbumsIds = set()
all_embysongsIds = set()
updatelist = []
process = {
'artists': [self.emby.getArtists, self.add_updateArtist],
'albums': [self.emby.getAlbums, self.add_updateAlbum],
'songs': [self.emby.getSongs, self.add_updateSong]
}
for view in views:
for data_type in ['artists', 'albums', 'songs']:
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33031), data_type))
if data_type != "artists":
all_embyitems = process[data_type][0](basic=True, dialog=pdialog)
else:
all_embyitems = process[data_type][0](view['id'], dialog=pdialog)
for embyitem in all_embyitems['Items']:
if self.should_stop():
return False
API = api.API(embyitem)
itemid = embyitem['Id']
if data_type == "artists":
all_embyartistsIds.add(itemid)
if all_kodiartists.get(itemid) != API.get_checksum():
# Only update if artist is not in Kodi or checksum is different
updatelist.append(itemid)
elif data_type == "albums":
all_embyalbumsIds.add(itemid)
if all_kodialbums.get(itemid) != API.get_checksum():
# Only update if album is not in Kodi or checksum is different
updatelist.append(itemid)
else:
all_embysongsIds.add(itemid)
if all_kodisongs.get(itemid) != API.get_checksum():
# Only update if songs is not in Kodi or checksum is different
updatelist.append(itemid)
log.info("%s to update: %s" % (data_type, updatelist))
embyitems = self.emby.getFullItems(updatelist)
self.total = len(updatelist)
del updatelist[:]
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (data_type, self.total))
self.count = 0
for embyitem in embyitems:
# Process individual item
if self.should_stop():
return False
self.title = embyitem['Name']
self.update_pdialog()
process[data_type][1](embyitem)
self.count += 1
##### PROCESS DELETES #####
for kodiartist in all_kodiartists:
if kodiartist not in all_embyartistsIds and all_kodiartists[kodiartist] is not None:
self.remove(kodiartist)
else:
log.info("Artist compare finished.")
for kodialbum in all_kodialbums:
if kodialbum not in all_embyalbumsIds:
self.remove(kodialbum)
else:
log.info("Albums compare finished.")
for kodisong in all_kodisongs:
if kodisong not in all_embysongsIds:
self.remove(kodisong)
else:
log.info("Songs compare finished.")
return True
def added(self, items, total=None):
for item in super(Music, self).added(items, total):
if self.add_updateArtist(item):
# Add albums
all_albums = self.emby.getAlbumsbyArtist(item['Id'])
self.added_album(all_albums['Items'])
def added_album(self, items, total=None):
update = True if not self.total else False
for item in super(Music, self).added(items, total, update):
self.title = "%s - %s" % (item.get('AlbumArtist', "unknown"), self.title)
if self.add_updateAlbum(item):
# Add songs
all_songs = self.emby.getSongsbyAlbum(item['Id'])
self.added_song(all_songs['Items'])
def added_song(self, items, total=None):
update = True if not self.total else False
for item in super(Music, self).added(items, total, update):
self.title = "%s - %s" % (item.get('AlbumArtist', "unknown"), self.title)
if self.add_updateSong(item):
self.content_pop()
@catch_except()
def add_updateArtist(self, item, artisttype="MusicArtist"):
# Process a single artist
kodicursor = self.kodicursor
emby_db = self.emby_db
artwork = self.artwork
API = api.API(item)
update_item = True
itemid = item['Id']
emby_dbitem = emby_db.getItem_byId(itemid)
try:
artistid = emby_dbitem[0]
except TypeError:
update_item = False
log.debug("artistid: %s not found." % itemid)
##### The artist details #####
lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
dateadded = API.get_date_created()
checksum = API.get_checksum()
name = item['Name']
musicBrainzId = API.get_provider('MusicBrainzArtist')
genres = " / ".join(item.get('Genres'))
bio = API.get_overview()
# Associate artwork
artworks = artwork.get_all_artwork(item, parent_info=True)
thumb = artworks['Primary']
backdrops = artworks['Backdrop'] # List
if thumb:
thumb = "<thumb>%s</thumb>" % thumb
if backdrops:
fanart = "<fanart>%s</fanart>" % backdrops[0]
else:
fanart = ""
##### UPDATE THE ARTIST #####
if update_item:
log.info("UPDATE artist itemid: %s - Name: %s" % (itemid, name))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
##### OR ADD THE ARTIST #####
else:
log.info("ADD artist itemid: %s - Name: %s" % (itemid, name))
# safety checks: It looks like Emby supports the same artist multiple times.
# Kodi doesn't allow that. In case that happens we just merge the artist entries.
artistid = self.kodi_db.addArtist(name, musicBrainzId)
# Create the reference in emby table
emby_db.addReference(itemid, artistid, artisttype, "artist", checksum=checksum)
# Process the artist
if self.kodi_version in (16, 17):
query = ' '.join((
"UPDATE artist",
"SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?,",
"lastScraped = ?",
"WHERE idArtist = ?"
))
kodicursor.execute(query, (genres, bio, thumb, fanart, lastScraped, artistid))
else:
query = ' '.join((
"UPDATE artist",
"SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?,",
"lastScraped = ?, dateAdded = ?",
"WHERE idArtist = ?"
))
kodicursor.execute(query, (genres, bio, thumb, fanart, lastScraped,
dateadded, artistid))
# Update artwork
artwork.add_artwork(artworks, artistid, "artist", kodicursor)
return True
@catch_except()
def add_updateAlbum(self, item):
# Process a single artist
emby = self.emby
kodicursor = self.kodicursor
emby_db = self.emby_db
artwork = self.artwork
API = api.API(item)
update_item = True
itemid = item['Id']
emby_dbitem = emby_db.getItem_byId(itemid)
try:
albumid = emby_dbitem[0]
except TypeError:
update_item = False
log.debug("albumid: %s not found." % itemid)
##### The album details #####
lastScraped = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
dateadded = API.get_date_created()
userdata = API.get_userdata()
checksum = API.get_checksum()
name = item['Name']
musicBrainzId = API.get_provider('MusicBrainzAlbum')
year = item.get('ProductionYear')
genres = item.get('Genres')
genre = " / ".join(genres)
bio = API.get_overview()
rating = userdata['UserRating']
artists = item['AlbumArtists']
artistname = []
for artist in artists:
artistname.append(artist['Name'])
artistname = " / ".join(artistname)
# Associate artwork
artworks = artwork.get_all_artwork(item, parent_info=True)
thumb = artworks['Primary']
if thumb:
thumb = "<thumb>%s</thumb>" % thumb
##### UPDATE THE ALBUM #####
if update_item:
log.info("UPDATE album itemid: %s - Name: %s" % (itemid, name))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
##### OR ADD THE ALBUM #####
else:
log.info("ADD album itemid: %s - Name: %s" % (itemid, name))
# safety checks: It looks like Emby supports the same artist multiple times.
# Kodi doesn't allow that. In case that happens we just merge the artist entries.
albumid = self.kodi_db.addAlbum(name, musicBrainzId)
# Create the reference in emby table
emby_db.addReference(itemid, albumid, "MusicAlbum", "album", checksum=checksum)
# Process the album info
if self.kodi_version == 17:
# Kodi Krypton
query = ' '.join((
"UPDATE album",
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
"iUserrating = ?, lastScraped = ?, strReleaseType = ?",
"WHERE idAlbum = ?"
))
kodicursor.execute(query, (artistname, year, genre, bio, thumb, rating, lastScraped,
"album", albumid))
elif self.kodi_version == 16:
# Kodi Jarvis
query = ' '.join((
"UPDATE album",
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
"iRating = ?, lastScraped = ?, strReleaseType = ?",
"WHERE idAlbum = ?"
))
kodicursor.execute(query, (artistname, year, genre, bio, thumb, rating, lastScraped,
"album", albumid))
elif self.kodi_version == 15:
# Kodi Isengard
query = ' '.join((
"UPDATE album",
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
"iRating = ?, lastScraped = ?, dateAdded = ?, strReleaseType = ?",
"WHERE idAlbum = ?"
))
kodicursor.execute(query, (artistname, year, genre, bio, thumb, rating, lastScraped,
dateadded, "album", albumid))
else:
# Kodi Helix
query = ' '.join((
"UPDATE album",
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
"iRating = ?, lastScraped = ?, dateAdded = ?",
"WHERE idAlbum = ?"
))
kodicursor.execute(query, (artistname, year, genre, bio, thumb, rating, lastScraped,
dateadded, albumid))
# Assign main artists to album
for artist in item['AlbumArtists']:
artistname = artist['Name']
artistId = artist['Id']
emby_dbartist = emby_db.getItem_byId(artistId)
try:
artistid = emby_dbartist[0]
except TypeError:
# Artist does not exist in emby database, create the reference
artist = emby.getItem(artistId)
self.add_updateArtist(artist, artisttype="AlbumArtist")
emby_dbartist = emby_db.getItem_byId(artistId)
artistid = emby_dbartist[0]
else:
# Best take this name over anything else.
query = "UPDATE artist SET strArtist = ? WHERE idArtist = ?"
kodicursor.execute(query, (artistname, artistid,))
# Add artist to album
query = (
'''
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
VALUES (?, ?, ?)
'''
)
kodicursor.execute(query, (artistid, albumid, artistname))
# Update emby reference with parentid
emby_db.updateParentId(artistId, albumid)
for artist in item['ArtistItems']:
artistId = artist['Id']
emby_dbartist = emby_db.getItem_byId(artistId)
try:
artistid = emby_dbartist[0]
except TypeError:
pass
else:
# Update discography
query = (
'''
INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
VALUES (?, ?, ?)
'''
)
kodicursor.execute(query, (artistid, name, year))
# Add genres
self.kodi_db.addMusicGenres(albumid, genres, "album")
# Update artwork
artwork.add_artwork(artworks, albumid, "album", kodicursor)
return True
@catch_except()
def add_updateSong(self, item):
# Process single song
kodicursor = self.kodicursor
emby = self.emby
emby_db = self.emby_db
artwork = self.artwork
API = api.API(item)
update_item = True
itemid = item['Id']
emby_dbitem = emby_db.getItem_byId(itemid)
try:
songid = emby_dbitem[0]
pathid = emby_dbitem[2]
albumid = emby_dbitem[3]
except TypeError:
update_item = False
log.debug("songid: %s not found." % itemid)
##### The song details #####
checksum = API.get_checksum()
dateadded = API.get_date_created()
userdata = API.get_userdata()
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
# item details
title = item['Name']
musicBrainzId = API.get_provider('MusicBrainzTrackId')
genres = item.get('Genres')
genre = " / ".join(genres)
artists = " / ".join(item['Artists'])
tracknumber = item.get('IndexNumber', 0)
disc = item.get('ParentIndexNumber', 1)
if disc == 1:
track = tracknumber
else:
track = disc*2**16 + tracknumber
year = item.get('ProductionYear')
duration = API.get_runtime()
rating = userdata['UserRating']
#if enabled, try to get the rating from file and/or emby
if not self.directstream:
rating, comment, hasEmbeddedCover = musicutils.getAdditionalSongTags(itemid, rating, API, kodicursor, emby_db, self.enableimportsongrating, self.enableexportsongrating, self.enableupdatesongrating)
else:
hasEmbeddedCover = False
comment = API.get_overview()
##### GET THE FILE AND PATH #####
if self.directstream:
path = "%s/emby/Audio/%s/" % (self.server, itemid)
extensions = ['mp3', 'aac', 'ogg', 'oga', 'webma', 'wma', 'flac']
if 'Container' in item and item['Container'].lower() in extensions:
filename = "stream.%s?static=true" % item['Container']
else:
filename = "stream.mp3?static=true"
else:
playurl = API.get_file_path()
if "\\" in playurl:
# Local path
filename = playurl.rsplit("\\", 1)[1]
else: # Network share
filename = playurl.rsplit("/", 1)[1]
# Direct paths is set the Kodi way
if not self.path_validation(playurl):
return False
path = playurl.replace(filename, "")
window('emby_pathverified', value="true")
##### UPDATE THE SONG #####
if update_item:
log.info("UPDATE song itemid: %s - Title: %s" % (itemid, title))
# Update path
query = "UPDATE path SET strPath = ? WHERE idPath = ?"
kodicursor.execute(query, (path, pathid))
# Update the song entry
query = ' '.join((
"UPDATE song",
"SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?,",
"iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,",
"rating = ?, comment = ?",
"WHERE idSong = ?"
))
kodicursor.execute(query, (albumid, artists, genre, title, track, duration, year,
filename, playcount, dateplayed, rating, comment, songid))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
##### OR ADD THE SONG #####
else:
log.info("ADD song itemid: %s - Title: %s" % (itemid, title))
# Add path
pathid = self.kodi_db.addPath(path)
try:
# Get the album
emby_dbalbum = emby_db.getItem_byId(item['AlbumId'])
albumid = emby_dbalbum[0]
except KeyError:
# Verify if there's an album associated.
album_name = item.get('Album')
if album_name:
log.info("Creating virtual music album for song: %s." % itemid)
albumid = self.kodi_db.addAlbum(album_name, API.get_provider('MusicBrainzAlbum'))
emby_db.addReference("%salbum%s" % (itemid, albumid), albumid, "MusicAlbum_", "album")
else:
# No album Id associated to the song.
log.error("Song itemid: %s has no albumId associated." % itemid)
return False
except TypeError:
# No album found. Let's create it
log.info("Album database entry missing.")
emby_albumId = item['AlbumId']
album = emby.getItem(emby_albumId)
self.add_updateAlbum(album)
emby_dbalbum = emby_db.getItem_byId(emby_albumId)
try:
albumid = emby_dbalbum[0]
log.info("Found albumid: %s" % albumid)
except TypeError:
# No album found, create a single's album
log.info("Failed to add album. Creating singles.")
kodicursor.execute("select coalesce(max(idAlbum),0) from album")
albumid = kodicursor.fetchone()[0] + 1
if self.kodi_version == 16:
# Kodi Jarvis
query = (
'''
INSERT INTO album(idAlbum, strGenres, iYear, strReleaseType)
VALUES (?, ?, ?, ?)
'''
)
kodicursor.execute(query, (albumid, genre, year, "single"))
elif self.kodi_version == 15:
# Kodi Isengard
query = (
'''
INSERT INTO album(idAlbum, strGenres, iYear, dateAdded, strReleaseType)
VALUES (?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (albumid, genre, year, dateadded, "single"))
else:
# Kodi Helix
query = (
'''
INSERT INTO album(idAlbum, strGenres, iYear, dateAdded)
VALUES (?, ?, ?, ?)
'''
)
kodicursor.execute(query, (albumid, genre, year, dateadded))
# Create the song entry
kodicursor.execute("select coalesce(max(idSong),0) from song")
songid = kodicursor.fetchone()[0] + 1
query = (
'''
INSERT INTO song(
idSong, idAlbum, idPath, strArtists, strGenres, strTitle, iTrack,
iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed,
rating)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (songid, albumid, pathid, artists, genre, title, track,
duration, year, filename, musicBrainzId, playcount, dateplayed, rating))
# Create the reference in emby table
emby_db.addReference(itemid, songid, "Audio", "song", pathid=pathid, parentid=albumid,
checksum=checksum)
# Link song to album
query = (
'''
INSERT OR REPLACE INTO albuminfosong(
idAlbumInfoSong, idAlbumInfo, iTrack, strTitle, iDuration)
VALUES (?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (songid, albumid, track, title, duration))
# Link song to artists
for index, artist in enumerate(item['ArtistItems']):
artist_name = artist['Name']
artist_eid = artist['Id']
artist_edb = emby_db.getItem_byId(artist_eid)
try:
artistid = artist_edb[0]
except TypeError:
# Artist is missing from emby database, add it.
artist_full = emby.getItem(artist_eid)
self.add_updateArtist(artist_full)
artist_edb = emby_db.getItem_byId(artist_eid)
artistid = artist_edb[0]
finally:
if self.kodi_version >= 17:
# Kodi Krypton
query = (
'''
INSERT OR REPLACE INTO song_artist(idArtist, idSong, idRole, iOrder, strArtist)
VALUES (?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (artistid, songid, 1, index, artist_name))
# May want to look into only doing this once?
query = (
'''
INSERT OR REPLACE INTO role(idRole, strRole)
VALUES (?, ?)
'''
)
kodicursor.execute(query, (1, 'Composer'))
else:
query = (
'''
INSERT OR REPLACE INTO song_artist(idArtist, idSong, iOrder, strArtist)
VALUES (?, ?, ?, ?)
'''
)
kodicursor.execute(query, (artistid, songid, index, artist_name))
# Verify if album artist exists
album_artists = []
for artist in item['AlbumArtists']:
artist_name = artist['Name']
album_artists.append(artist_name)
artist_eid = artist['Id']
artist_edb = emby_db.getItem_byId(artist_eid)
try:
artistid = artist_edb[0]
except TypeError:
# Artist is missing from emby database, add it.
artist_full = emby.getItem(artist_eid)
self.add_updateArtist(artist_full)
artist_edb = emby_db.getItem_byId(artist_eid)
artistid = artist_edb[0]
finally:
query = (
'''
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
VALUES (?, ?, ?)
'''
)
kodicursor.execute(query, (artistid, albumid, artist_name))
# Update discography
if item.get('Album'):
query = (
'''
INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
VALUES (?, ?, ?)
'''
)
kodicursor.execute(query, (artistid, item['Album'], 0))
else:
album_artists = " / ".join(album_artists)
query = ' '.join((
"SELECT strArtists",
"FROM album",
"WHERE idAlbum = ?"
))
kodicursor.execute(query, (albumid,))
result = kodicursor.fetchone()
if result and result[0] != album_artists:
# Field is empty
if self.kodi_version in (16, 17):
# Kodi Jarvis, Krypton
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
kodicursor.execute(query, (album_artists, albumid))
elif self.kodi_version == 15:
# Kodi Isengard
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
kodicursor.execute(query, (album_artists, albumid))
else:
# Kodi Helix
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
kodicursor.execute(query, (album_artists, albumid))
# Add genres
self.kodi_db.addMusicGenres(songid, genres, "song")
# Update artwork
allart = artwork.get_all_artwork(item, parent_info=True)
if hasEmbeddedCover:
allart["Primary"] = "image://music@" + artwork.single_urlencode( playurl )
artwork.add_artwork(allart, songid, "song", kodicursor)
if item.get('AlbumId') is None:
# Update album artwork
artwork.add_artwork(allart, albumid, "album", kodicursor)
return True
def updateUserdata(self, item):
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
# Poster with progress bar
kodicursor = self.kodicursor
emby_db = self.emby_db
API = api.API(item)
# Get emby information
itemid = item['Id']
checksum = API.get_checksum()
userdata = API.get_userdata()
runtime = API.get_runtime()
rating = userdata['UserRating']
# Get Kodi information
emby_dbitem = emby_db.getItem_byId(itemid)
try:
kodiid = emby_dbitem[0]
mediatype = emby_dbitem[4]
log.info("Update playstate for %s: %s" % (mediatype, item['Name']))
except TypeError:
return
if mediatype == "song":
#should we ignore this item ?
#happens when userdata updated by ratings method
if window("ignore-update-%s" %itemid):
window("ignore-update-%s" %itemid,clear=True)
return
# Process playstates
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
#process item ratings
rating, comment, hasEmbeddedCover = musicutils.getAdditionalSongTags(itemid, rating, API, kodicursor, emby_db, self.enableimportsongrating, self.enableexportsongrating, self.enableupdatesongrating)
query = "UPDATE song SET iTimesPlayed = ?, lastplayed = ?, rating = ? WHERE idSong = ?"
kodicursor.execute(query, (playcount, dateplayed, rating, kodiid))
elif mediatype == "album":
# Process playstates
if self.kodi_version >= 17:
query = "UPDATE album SET fRating = ? WHERE idAlbum = ?"
else:
query = "UPDATE album SET iRating = ? WHERE idAlbum = ?"
kodicursor.execute(query, (rating, kodiid))
emby_db.updateReference(itemid, checksum)
def remove(self, itemid):
# Remove kodiid, fileid, pathid, emby reference
emby_db = self.emby_db
kodicursor = self.kodicursor
artwork = self.artwork
emby_dbitem = emby_db.getItem_byId(itemid)
try:
kodiid = emby_dbitem[0]
mediatype = emby_dbitem[4]
log.info("Removing %s kodiid: %s" % (mediatype, kodiid))
except TypeError:
return
##### PROCESS ITEM #####
# Remove the emby reference
emby_db.removeItem(itemid)
##### IF SONG #####
if mediatype == "song":
# Delete song
self.removeSong(kodiid)
# This should only address single song scenario, where server doesn't actually
# create an album for the song.
emby_db.removeWildItem(itemid)
for item in emby_db.getItem_byWildId(itemid):
item_kid = item[0]
item_mediatype = item[1]
if item_mediatype == "album":
childs = emby_db.getItem_byParentId(item_kid, "song")
if not childs:
# Delete album
self.removeAlbum(item_kid)
##### IF ALBUM #####
elif mediatype == "album":
# Delete songs, album
album_songs = emby_db.getItem_byParentId(kodiid, "song")
for song in album_songs:
self.removeSong(song[1])
else:
# Remove emby songs
emby_db.removeItems_byParentId(kodiid, "song")
# Remove the album
self.removeAlbum(kodiid)
##### IF ARTIST #####
elif mediatype == "artist":
# Delete songs, album, artist
albums = emby_db.getItem_byParentId(kodiid, "album")
for album in albums:
albumid = album[1]
album_songs = emby_db.getItem_byParentId(albumid, "song")
for song in album_songs:
self.removeSong(song[1])
else:
# Remove emby song
emby_db.removeItems_byParentId(albumid, "song")
# Remove emby artist
emby_db.removeItems_byParentId(albumid, "artist")
# Remove kodi album
self.removeAlbum(albumid)
else:
# Remove emby albums
emby_db.removeItems_byParentId(kodiid, "album")
# Remove artist
self.removeArtist(kodiid)
log.info("Deleted %s: %s from kodi database" % (mediatype, itemid))
def removeSong(self, kodiId):
kodicursor = self.kodicursor
self.artwork.delete_artwork(kodiId, "song", self.kodicursor)
self.kodicursor.execute("DELETE FROM song WHERE idSong = ?", (kodiId,))
def removeAlbum(self, kodiId):
self.artwork.delete_artwork(kodiId, "album", self.kodicursor)
self.kodicursor.execute("DELETE FROM album WHERE idAlbum = ?", (kodiId,))
def removeArtist(self, kodiId):
self.artwork.delete_artwork(kodiId, "artist", self.kodicursor)
self.kodicursor.execute("DELETE FROM artist WHERE idArtist = ?", (kodiId,))

View file

@ -0,0 +1,415 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import urllib
from ntpath import dirname
from datetime import datetime
import api
import common
import downloadutils
import embydb_functions as embydb
import kodidb_functions as kodidb
from utils import window, settings, language as lang, catch_except
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class MusicVideos(common.Items):
def __init__(self, embycursor, kodicursor, pdialog=None):
self.embycursor = embycursor
self.emby_db = embydb.Embydb_Functions(self.embycursor)
self.kodicursor = kodicursor
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
self.pdialog = pdialog
common.Items.__init__(self)
def _get_func(self, item_type, action):
if item_type == "MusicVideo":
actions = {
'added': self.added,
'update': self.add_update,
'userdata': self.updateUserdata,
'remove': self.remove
}
else:
log.info("Unsupported item_type: %s", item_type)
actions = {}
return actions.get(action)
def compare_all(self):
pdialog = self.pdialog
# Pull the list of musicvideos in Kodi
views = self.emby_db.getView_byType('musicvideos')
log.info("Media folders: %s" % views)
try:
all_kodimvideos = dict(self.emby_db.get_checksum('MusicVideo'))
except ValueError:
all_kodimvideos = {}
all_embymvideosIds = set()
updatelist = []
for view in views:
if self.should_stop():
return False
# Get items per view
viewId = view['id']
viewName = view['name']
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33028), viewName))
all_embymvideos = self.emby.getMusicVideos(viewId, basic=True, dialog=pdialog)
for embymvideo in all_embymvideos['Items']:
if self.should_stop():
return False
API = api.API(embymvideo)
itemid = embymvideo['Id']
all_embymvideosIds.add(itemid)
if all_kodimvideos.get(itemid) != API.get_checksum():
# Only update if musicvideo is not in Kodi or checksum is different
updatelist.append(itemid)
log.info("MusicVideos to update for %s: %s" % (viewName, updatelist))
embymvideos = self.emby.getFullItems(updatelist)
self.total = len(updatelist)
del updatelist[:]
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (viewName, self.total))
self.count = 0
for embymvideo in embymvideos:
# Process individual musicvideo
if self.should_stop():
return False
self.title = embymvideo['Name']
self.update_pdialog()
self.add_update(embymvideo, view)
self.count += 1
##### PROCESS DELETES #####
for kodimvideo in all_kodimvideos:
if kodimvideo not in all_embymvideosIds:
self.remove(kodimvideo)
else:
log.info("MusicVideos compare finished.")
return True
def added(self, items, total=None, view=None):
for item in super(MusicVideos, self).added(items, total, True):
if self.add_update(item, view):
self.content_pop()
@catch_except
def add_update(self, item, view=None):
# Process single music video
kodicursor = self.kodicursor
emby_db = self.emby_db
artwork = self.artwork
API = api.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']
emby_dbitem = emby_db.getItem_byId(itemid)
try:
mvideoid = emby_dbitem[0]
fileid = emby_dbitem[1]
pathid = emby_dbitem[2]
log.info("mvideoid: %s fileid: %s pathid: %s" % (mvideoid, fileid, pathid))
except TypeError:
update_item = False
log.debug("mvideoid: %s not found." % itemid)
# mvideoid
kodicursor.execute("select coalesce(max(idMVideo),0) from musicvideo")
mvideoid = kodicursor.fetchone()[0] + 1
else:
# Verification the item is still in Kodi
query = "SELECT * FROM musicvideo WHERE idMVideo = ?"
kodicursor.execute(query, (mvideoid,))
try:
kodicursor.fetchone()[0]
except TypeError:
# item is not found, let's recreate it.
update_item = False
log.info("mvideoid: %s missing from Kodi, repairing the entry." % mvideoid)
if not view:
# Get view tag from emby
viewtag, viewid, mediatype = self.emby.getView_embyId(itemid)
log.debug("View tag found: %s" % viewtag)
else:
viewtag = view['name']
viewid = view['id']
# fileId information
checksum = API.get_checksum()
dateadded = API.get_date_created()
userdata = API.get_userdata()
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
# item details
runtime = API.get_runtime()
plot = API.get_overview()
title = item['Name']
year = item.get('ProductionYear')
genres = item['Genres']
genre = " / ".join(genres)
studios = API.get_studios()
studio = " / ".join(studios)
artist = " / ".join(item.get('Artists'))
album = item.get('Album')
track = item.get('Track')
people = API.get_people()
director = " / ".join(people['Director'])
##### GET THE FILE AND PATH #####
playurl = API.get_file_path()
if "\\" in playurl:
# Local path
filename = playurl.rsplit("\\", 1)[1]
else: # Network share
filename = playurl.rsplit("/", 1)[1]
if self.direct_path:
# Direct paths is set the Kodi way
if not self.path_validation(playurl):
return False
path = playurl.replace(filename, "")
window('emby_pathverified', value="true")
else:
# Set plugin path and media flags using real filename
path = "plugin://plugin.video.emby.musicvideos/"
params = {
'filename': filename.encode('utf-8'),
'id': itemid,
'dbid': mvideoid,
'mode': "play"
}
filename = "%s?%s" % (path, urllib.urlencode(params))
##### UPDATE THE MUSIC VIDEO #####
if update_item:
log.info("UPDATE mvideo itemid: %s - Title: %s" % (itemid, title))
# Update path
query = "UPDATE path SET strPath = ? WHERE idPath = ?"
kodicursor.execute(query, (path, pathid))
# Update the filename
query = "UPDATE files SET strFilename = ?, dateAdded = ? WHERE idFile = ?"
kodicursor.execute(query, (filename, dateadded, fileid))
# Update the music video entry
query = ' '.join((
"UPDATE musicvideo",
"SET c00 = ?, c04 = ?, c05 = ?, c06 = ?, c07 = ?, c08 = ?, c09 = ?, c10 = ?,",
"c11 = ?, c12 = ?"
"WHERE idMVideo = ?"
))
kodicursor.execute(query, (title, runtime, director, studio, year, plot, album,
artist, genre, track, mvideoid))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
##### OR ADD THE MUSIC VIDEO #####
else:
log.info("ADD mvideo itemid: %s - Title: %s" % (itemid, title))
# Add path
query = ' '.join((
"SELECT idPath",
"FROM path",
"WHERE strPath = ?"
))
kodicursor.execute(query, (path,))
try:
pathid = kodicursor.fetchone()[0]
except TypeError:
kodicursor.execute("select coalesce(max(idPath),0) from path")
pathid = kodicursor.fetchone()[0] + 1
query = (
'''
INSERT OR REPLACE INTO path(
idPath, strPath, strContent, strScraper, noUpdate)
VALUES (?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (pathid, path, "musicvideos", "metadata.local", 1))
# Add the file
kodicursor.execute("select coalesce(max(idFile),0) from files")
fileid = kodicursor.fetchone()[0] + 1
query = (
'''
INSERT INTO files(
idFile, idPath, strFilename, dateAdded)
VALUES (?, ?, ?, ?)
'''
)
kodicursor.execute(query, (fileid, pathid, filename, dateadded))
# Create the musicvideo entry
query = (
'''
INSERT INTO musicvideo(
idMVideo, idFile, c00, c04, c05, c06, c07, c08, c09, c10, c11, c12)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (mvideoid, fileid, title, runtime, director, studio,
year, plot, album, artist, genre, track))
# Create the reference in emby table
emby_db.addReference(itemid, mvideoid, "MusicVideo", "musicvideo", fileid, pathid,
checksum=checksum, mediafolderid=viewid)
# Process cast
people = item['People']
artists = item['ArtistItems']
for artist in artists:
artist['Type'] = "Artist"
people.extend(artists)
people = artwork.get_people_artwork(people)
self.kodi_db.addPeople(mvideoid, people, "musicvideo")
# Process genres
self.kodi_db.addGenres(mvideoid, genres, "musicvideo")
# Process artwork
artwork.add_artwork(artwork.get_all_artwork(item), mvideoid, "musicvideo", kodicursor)
# Process stream details
streams = API.get_media_streams()
self.kodi_db.addStreams(fileid, streams, runtime)
# Process studios
self.kodi_db.addStudios(mvideoid, studios, "musicvideo")
# Process tags: view, emby tags
tags = [viewtag]
tags.extend(item['Tags'])
if userdata['Favorite']:
tags.append("Favorite musicvideos")
self.kodi_db.addTags(mvideoid, tags, "musicvideo")
# Process playstates
resume = API.adjust_resume(userdata['Resume'])
total = round(float(runtime), 6)
self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
return True
def updateUserdata(self, item):
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
# Poster with progress bar
emby_db = self.emby_db
API = api.API(item)
# Get emby information
itemid = item['Id']
checksum = API.get_checksum()
userdata = API.get_userdata()
runtime = API.get_runtime()
# Get Kodi information
emby_dbitem = emby_db.getItem_byId(itemid)
try:
mvideoid = emby_dbitem[0]
fileid = emby_dbitem[1]
log.info(
"Update playstate for musicvideo: %s fileid: %s"
% (item['Name'], fileid))
except TypeError:
return
# Process favorite tags
if userdata['Favorite']:
self.kodi_db.addTag(mvideoid, "Favorite musicvideos", "musicvideo")
else:
self.kodi_db.removeTag(mvideoid, "Favorite musicvideos", "musicvideo")
# Process playstates
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
resume = API.adjust_resume(userdata['Resume'])
total = round(float(runtime), 6)
self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
emby_db.updateReference(itemid, checksum)
def remove(self, itemid):
# Remove mvideoid, fileid, pathid, emby reference
emby_db = self.emby_db
kodicursor = self.kodicursor
artwork = self.artwork
emby_dbitem = emby_db.getItem_byId(itemid)
try:
mvideoid = emby_dbitem[0]
fileid = emby_dbitem[1]
pathid = emby_dbitem[2]
log.info("Removing mvideoid: %s fileid: %s" % (mvideoid, fileid, pathid))
except TypeError:
return
# Remove artwork
query = ' '.join((
"SELECT url, type",
"FROM art",
"WHERE media_id = ?",
"AND media_type = 'musicvideo'"
))
kodicursor.execute(query, (mvideoid,))
for row in kodicursor.fetchall():
url = row[0]
imagetype = row[1]
if imagetype in ("poster", "fanart"):
artwork.delete_cached_artwork(url)
kodicursor.execute("DELETE FROM musicvideo WHERE idMVideo = ?", (mvideoid,))
kodicursor.execute("DELETE FROM files WHERE idFile = ?", (fileid,))
if self.direct_path:
kodicursor.execute("DELETE FROM path WHERE idPath = ?", (pathid,))
self.embycursor.execute("DELETE FROM emby WHERE emby_id = ?", (itemid,))
log.info("Deleted musicvideo %s from kodi database" % itemid)

View file

@ -0,0 +1,924 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import urllib
from ntpath import dirname
from datetime import datetime
import api
import common
import downloadutils
import embydb_functions as embydb
import kodidb_functions as kodidb
from utils import window, settings, language as lang, catch_except
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class TVShows(common.Items):
def __init__(self, embycursor, kodicursor, pdialog=None):
self.embycursor = embycursor
self.emby_db = embydb.Embydb_Functions(self.embycursor)
self.kodicursor = kodicursor
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
self.pdialog = pdialog
self.new_time = int(settings('newvideotime'))*1000
common.Items.__init__(self)
def _get_func(self, item_type, action):
if item_type == "Series":
actions = {
'added': self.added,
'update': self.add_update,
'userdata': self.updateUserdata,
'remove': self.remove
}
elif item_type == "Season":
actions = {
'added': self.added_season,
'update': self.add_updateSeason,
'remove': self.remove
}
elif item_type == "Episode":
actions = {
'added': self.added_episode,
'update': self.add_updateEpisode,
'userdata': self.updateUserdata,
'remove': self.remove
}
else:
log.info("Unsupported item_type: %s", item_type)
actions = {}
return actions.get(action)
def compare_all(self):
# Pull the list of movies and boxsets in Kodi
pdialog = self.pdialog
views = self.emby_db.getView_byType('tvshows')
views += self.emby_db.getView_byType('mixed')
log.info("Media folders: %s" % views)
# Pull the list of tvshows and episodes in Kodi
try:
all_koditvshows = dict(self.emby_db.get_checksum('Series'))
except ValueError:
all_koditvshows = {}
log.info("all_koditvshows = %s", all_koditvshows)
try:
all_kodiepisodes = dict(self.emby_db.get_checksum('Episode'))
except ValueError:
all_kodiepisodes = {}
all_embytvshowsIds = set()
all_embyepisodesIds = set()
updatelist = []
for view in views:
if self.should_stop():
return False
# Get items per view
viewId = view['id']
viewName = view['name']
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33029), viewName))
all_embytvshows = self.emby.getShows(viewId, basic=True, dialog=pdialog)
for embytvshow in all_embytvshows['Items']:
if self.should_stop():
return False
API = api.API(embytvshow)
itemid = embytvshow['Id']
all_embytvshowsIds.add(itemid)
if all_koditvshows.get(itemid) != API.get_checksum():
# Only update if movie is not in Kodi or checksum is different
updatelist.append(itemid)
log.info("TVShows to update for %s: %s" % (viewName, updatelist))
embytvshows = self.emby.getFullItems(updatelist)
self.total = len(updatelist)
del updatelist[:]
if pdialog:
pdialog.update(heading="Processing %s / %s items" % (viewName, self.total))
self.count = 0
for embytvshow in embytvshows:
# Process individual show
if self.should_stop():
return False
itemid = embytvshow['Id']
title = embytvshow['Name']
all_embytvshowsIds.add(itemid)
self.update_pdialog()
self.add_update(embytvshow, view)
self.count += 1
else:
# Get all episodes in view
if pdialog:
pdialog.update(
heading=lang(29999),
message="%s %s..." % (lang(33030), viewName))
all_embyepisodes = self.emby.getEpisodes(viewId, basic=True, dialog=pdialog)
for embyepisode in all_embyepisodes['Items']:
if self.should_stop():
return False
API = api.API(embyepisode)
itemid = embyepisode['Id']
all_embyepisodesIds.add(itemid)
if "SeriesId" in embyepisode:
all_embytvshowsIds.add(embyepisode['SeriesId'])
if all_kodiepisodes.get(itemid) != API.get_checksum():
# Only update if movie is not in Kodi or checksum is different
updatelist.append(itemid)
log.info("Episodes to update for %s: %s" % (viewName, updatelist))
embyepisodes = self.emby.getFullItems(updatelist)
self.total = len(updatelist)
del updatelist[:]
self.count = 0
for episode in embyepisodes:
# Process individual episode
if self.should_stop():
return False
self.title = "%s - %s" % (episode.get('SeriesName', "Unknown"), episode['Name'])
self.add_updateEpisode(episode)
self.count += 1
##### PROCESS DELETES #####
log.info("all_embytvshowsIds = %s " % all_embytvshowsIds)
for koditvshow in all_koditvshows:
if koditvshow not in all_embytvshowsIds:
self.remove(koditvshow)
else:
log.info("TVShows compare finished.")
for kodiepisode in all_kodiepisodes:
if kodiepisode not in all_embyepisodesIds:
self.remove(kodiepisode)
else:
log.info("Episodes compare finished.")
return True
def added(self, items, total=None, view=None):
for item in super(TVShows, self).added(items, total):
if self.add_update(item, view):
# Add episodes
all_episodes = self.emby.getEpisodesbyShow(item['Id'])
self.added_episode(all_episodes['Items'])
def added_season(self, items, total=None, view=None):
update = True if not self.total else False
for item in super(TVShows, self).added(items, total, update):
self.title = "%s - %s" % (item.get('SeriesName', "Unknown"), self.title)
if self.add_updateSeason(item):
# Add episodes
all_episodes = self.emby.getEpisodesbySeason(item['Id'])
self.added_episode(all_episodes['Items'])
def added_episode(self, items, total=None, view=None):
update = True if not self.total else False
for item in super(TVShows, self).added(items, total, update):
self.title = "%s - %s" % (item.get('SeriesName', "Unknown"), self.title)
if self.add_updateEpisode(item):
self.content_pop()
@catch_except()
def add_update(self, item, view=None):
# Process single tvshow
kodicursor = self.kodicursor
emby = self.emby
emby_db = self.emby_db
artwork = self.artwork
API = api.API(item)
if settings('syncEmptyShows') == "false" and not item.get('RecursiveItemCount'):
log.info("Skipping empty show: %s" % item['Name'])
return
# 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
force_episodes = False
itemid = item['Id']
emby_dbitem = emby_db.getItem_byId(itemid)
try:
showid = emby_dbitem[0]
pathid = emby_dbitem[2]
log.info("showid: %s pathid: %s" % (showid, pathid))
except TypeError:
update_item = False
log.debug("showid: %s not found." % itemid)
kodicursor.execute("select coalesce(max(idShow),0) from tvshow")
showid = kodicursor.fetchone()[0] + 1
else:
# Verification the item is still in Kodi
query = "SELECT * FROM tvshow WHERE idShow = ?"
kodicursor.execute(query, (showid,))
try:
kodicursor.fetchone()[0]
except TypeError:
# item is not found, let's recreate it.
update_item = False
log.info("showid: %s missing from Kodi, repairing the entry." % showid)
# Force re-add episodes after the show is re-created.
force_episodes = True
if view is None:
# Get view tag from emby
viewtag, viewid, mediatype = emby.getView_embyId(itemid)
log.debug("View tag found: %s" % viewtag)
else:
viewtag = view['name']
viewid = view['id']
# fileId information
checksum = API.get_checksum()
dateadded = API.get_date_created()
userdata = API.get_userdata()
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
# item details
genres = item['Genres']
title = item['Name']
plot = API.get_overview()
rating = item.get('CommunityRating')
premieredate = API.get_premiere_date()
tvdb = API.get_provider('Tvdb')
sorttitle = item['SortName']
mpaa = API.get_mpaa()
genre = " / ".join(genres)
studios = API.get_studios()
studio = " / ".join(studios)
# Verify series pooling
if not update_item and tvdb:
query = "SELECT idShow FROM tvshow WHERE C12 = ?"
kodicursor.execute(query, (tvdb,))
try:
temp_showid = kodicursor.fetchone()[0]
except TypeError:
pass
else:
emby_other = emby_db.getItem_byKodiId(temp_showid, "tvshow")
if emby_other and viewid == emby_other[2]:
log.info("Applying series pooling for %s", title)
emby_other_item = emby_db.getItem_byId(emby_other[0])
showid = emby_other_item[0]
pathid = emby_other_item[2]
log.info("showid: %s pathid: %s" % (showid, pathid))
# Create the reference in emby table
emby_db.addReference(itemid, showid, "Series", "tvshow", pathid=pathid,
checksum=checksum, mediafolderid=viewid)
update_item = True
##### GET THE FILE AND PATH #####
playurl = API.get_file_path()
if self.direct_path:
# Direct paths is set the Kodi way
if "\\" in playurl:
# Local path
path = "%s\\" % playurl
toplevelpath = "%s\\" % dirname(dirname(path))
else:
# Network path
path = "%s/" % playurl
toplevelpath = "%s/" % dirname(dirname(path))
if not self.path_validation(path):
return False
window('emby_pathverified', value="true")
else:
# Set plugin path
toplevelpath = "plugin://plugin.video.emby.tvshows/"
path = "%s%s/" % (toplevelpath, itemid)
##### UPDATE THE TVSHOW #####
if update_item:
log.info("UPDATE tvshow itemid: %s - Title: %s" % (itemid, title))
# Update the tvshow entry
query = ' '.join((
"UPDATE tvshow",
"SET c00 = ?, c01 = ?, c04 = ?, c05 = ?, c08 = ?, c09 = ?,",
"c12 = ?, c13 = ?, c14 = ?, c15 = ?",
"WHERE idShow = ?"
))
kodicursor.execute(query, (title, plot, rating, premieredate, genre, title,
tvdb, mpaa, studio, sorttitle, showid))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
##### OR ADD THE TVSHOW #####
else:
log.info("ADD tvshow itemid: %s - Title: %s" % (itemid, title))
# Add top path
toppathid = self.kodi_db.addPath(toplevelpath)
query = ' '.join((
"UPDATE path",
"SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?",
"WHERE idPath = ?"
))
kodicursor.execute(query, (toplevelpath, "tvshows", "metadata.local", 1, toppathid))
# Add path
pathid = self.kodi_db.addPath(path)
# Create the tvshow entry
query = (
'''
INSERT INTO tvshow(
idShow, c00, c01, c04, c05, c08, c09, c12, c13, c14, c15)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (showid, title, plot, rating, premieredate, genre,
title, tvdb, mpaa, studio, sorttitle))
# Link the path
query = "INSERT INTO tvshowlinkpath(idShow, idPath) values(?, ?)"
kodicursor.execute(query, (showid, pathid))
# Create the reference in emby table
emby_db.addReference(itemid, showid, "Series", "tvshow", pathid=pathid,
checksum=checksum, mediafolderid=viewid)
# Update the path
query = ' '.join((
"UPDATE path",
"SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?",
"WHERE idPath = ?"
))
kodicursor.execute(query, (path, None, None, 1, pathid))
# Process cast
people = artwork.get_people_artwork(item['People'])
self.kodi_db.addPeople(showid, people, "tvshow")
# Process genres
self.kodi_db.addGenres(showid, genres, "tvshow")
# Process artwork
artwork.add_artwork(artwork.get_all_artwork(item), showid, "tvshow", kodicursor)
# Process studios
self.kodi_db.addStudios(showid, studios, "tvshow")
# Process tags: view, emby tags
tags = [viewtag]
tags.extend(item['Tags'])
if userdata['Favorite']:
tags.append("Favorite tvshows")
self.kodi_db.addTags(showid, tags, "tvshow")
# Process seasons
all_seasons = emby.getSeasons(itemid)
for season in all_seasons['Items']:
self.add_updateSeason(season, showid=showid)
else:
# Finally, refresh the all season entry
seasonid = self.kodi_db.addSeason(showid, -1)
# Process artwork
artwork.add_artwork(artwork.get_all_artwork(item), seasonid, "season", kodicursor)
if force_episodes:
# We needed to recreate the show entry. Re-add episodes now.
log.info("Repairing episodes for showid: %s %s" % (showid, title))
all_episodes = emby.getEpisodesbyShow(itemid)
self.added_episode(all_episodes['Items'], None)
return True
def add_updateSeason(self, item, showid=None):
kodicursor = self.kodicursor
emby_db = self.emby_db
artwork = self.artwork
seasonnum = item.get('IndexNumber', 1)
if showid is None:
try:
seriesId = item['SeriesId']
showid = emby_db.getItem_byId(seriesId)[0]
except KeyError:
return
except TypeError:
# Show is missing, update show instead.
show = self.emby.getItem(seriesId)
self.add_update(show)
return
seasonid = self.kodi_db.addSeason(showid, seasonnum, item['Name'])
if item['LocationType'] != "Virtual":
# Create the reference in emby table
emby_db.addReference(item['Id'], seasonid, "Season", "season", parentid=showid)
# Process artwork
artwork.add_artwork(artwork.get_all_artwork(item), seasonid, "season", kodicursor)
return True
@catch_except()
def add_updateEpisode(self, item):
# Process single episode
kodicursor = self.kodicursor
emby_db = self.emby_db
artwork = self.artwork
API = api.API(item)
if item.get('LocationType') == "Virtual": # TODO: Filter via api instead
log.info("Skipping virtual episode: %s", item['Name'])
return
# 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']
emby_dbitem = emby_db.getItem_byId(itemid)
try:
episodeid = emby_dbitem[0]
fileid = emby_dbitem[1]
pathid = emby_dbitem[2]
log.info("episodeid: %s fileid: %s pathid: %s" % (episodeid, fileid, pathid))
except TypeError:
update_item = False
log.debug("episodeid: %s not found." % itemid)
# episodeid
kodicursor.execute("select coalesce(max(idEpisode),0) from episode")
episodeid = kodicursor.fetchone()[0] + 1
else:
# Verification the item is still in Kodi
query = "SELECT * FROM episode WHERE idEpisode = ?"
kodicursor.execute(query, (episodeid,))
try:
kodicursor.fetchone()[0]
except TypeError:
# item is not found, let's recreate it.
update_item = False
log.info("episodeid: %s missing from Kodi, repairing the entry." % episodeid)
# fileId information
checksum = API.get_checksum()
dateadded = API.get_date_created()
userdata = API.get_userdata()
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
# item details
people = API.get_people()
writer = " / ".join(people['Writer'])
director = " / ".join(people['Director'])
title = item['Name']
plot = API.get_overview()
rating = item.get('CommunityRating')
runtime = API.get_runtime()
premieredate = API.get_premiere_date()
# episode details
try:
seriesId = item['SeriesId']
except KeyError:
# Missing seriesId, skip
log.error("Skipping: %s. SeriesId is missing." % itemid)
return False
season = item.get('ParentIndexNumber')
episode = item.get('IndexNumber', -1)
if season is None:
if item.get('AbsoluteEpisodeNumber'):
# Anime scenario
season = 1
episode = item['AbsoluteEpisodeNumber']
else:
season = -1 if "Specials" not in item['Path'] else 0
# Specials ordering within season
if item.get('AirsAfterSeasonNumber'):
airsBeforeSeason = item['AirsAfterSeasonNumber']
airsBeforeEpisode = 4096 # Kodi default number for afterseason ordering
else:
airsBeforeSeason = item.get('AirsBeforeSeasonNumber')
airsBeforeEpisode = item.get('AirsBeforeEpisodeNumber')
# Append multi episodes to title
if item.get('IndexNumberEnd'):
title = "| %02d | %s" % (item['IndexNumberEnd'], title)
# Get season id
show = emby_db.getItem_byId(seriesId)
try:
showid = show[0]
except TypeError:
# Show is missing from database
show = self.emby.getItem(seriesId)
self.add_update(show)
show = emby_db.getItem_byId(seriesId)
try:
showid = show[0]
except TypeError:
log.error("Skipping: %s. Unable to add series: %s." % (itemid, seriesId))
return False
seasonid = self.kodi_db.addSeason(showid, season)
##### GET THE FILE AND PATH #####
playurl = API.get_file_path()
if "\\" in playurl:
# Local path
filename = playurl.rsplit("\\", 1)[1]
else: # Network share
filename = playurl.rsplit("/", 1)[1]
if self.direct_path:
# Direct paths is set the Kodi way
if not self.path_validation(playurl):
return False
path = playurl.replace(filename, "")
window('emby_pathverified', value="true")
else:
# Set plugin path and media flags using real filename
path = "plugin://plugin.video.emby.tvshows/%s/" % seriesId
params = {
'filename': filename.encode('utf-8'),
'id': itemid,
'dbid': episodeid,
'mode': "play"
}
filename = "%s?%s" % (path, urllib.urlencode(params))
##### UPDATE THE EPISODE #####
if update_item:
log.info("UPDATE episode itemid: %s - Title: %s" % (itemid, title))
# Update the movie entry
if self.kodi_version in (16, 17):
# Kodi Jarvis, Krypton
query = ' '.join((
"UPDATE episode",
"SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?, c10 = ?,",
"c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?, idSeason = ?, idShow = ?",
"WHERE idEpisode = ?"
))
kodicursor.execute(query, (title, plot, rating, writer, premieredate,
runtime, director, season, episode, title, airsBeforeSeason,
airsBeforeEpisode, seasonid, showid, episodeid))
else:
query = ' '.join((
"UPDATE episode",
"SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?, c10 = ?,",
"c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?, idShow = ?",
"WHERE idEpisode = ?"
))
kodicursor.execute(query, (title, plot, rating, writer, premieredate,
runtime, director, season, episode, title, airsBeforeSeason,
airsBeforeEpisode, showid, episodeid))
# Update the checksum in emby table
emby_db.updateReference(itemid, checksum)
# Update parentid reference
emby_db.updateParentId(itemid, seasonid)
##### OR ADD THE EPISODE #####
else:
log.info("ADD episode itemid: %s - Title: %s" % (itemid, title))
# Add path
pathid = self.kodi_db.addPath(path)
# Add the file
fileid = self.kodi_db.addFile(filename, pathid)
# Create the episode entry
if self.kodi_version in (16, 17):
# Kodi Jarvis, Krypton
query = (
'''
INSERT INTO episode(
idEpisode, idFile, c00, c01, c03, c04, c05, c09, c10, c12, c13, c14,
idShow, c15, c16, idSeason)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (episodeid, fileid, title, plot, rating, writer,
premieredate, runtime, director, season, episode, title, showid,
airsBeforeSeason, airsBeforeEpisode, seasonid))
else:
query = (
'''
INSERT INTO episode(
idEpisode, idFile, c00, c01, c03, c04, c05, c09, c10, c12, c13, c14,
idShow, c15, c16)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
kodicursor.execute(query, (episodeid, fileid, title, plot, rating, writer,
premieredate, runtime, director, season, episode, title, showid,
airsBeforeSeason, airsBeforeEpisode))
# Create the reference in emby table
emby_db.addReference(itemid, episodeid, "Episode", "episode", fileid, pathid,
seasonid, checksum)
# Update the path
query = ' '.join((
"UPDATE path",
"SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?",
"WHERE idPath = ?"
))
kodicursor.execute(query, (path, None, None, 1, pathid))
# Update the file
query = ' '.join((
"UPDATE files",
"SET idPath = ?, strFilename = ?, dateAdded = ?",
"WHERE idFile = ?"
))
kodicursor.execute(query, (pathid, filename, dateadded, fileid))
# Process cast
people = artwork.get_people_artwork(item['People'])
self.kodi_db.addPeople(episodeid, people, "episode")
# Process artwork
artworks = artwork.get_all_artwork(item)
artwork.add_update_art(artworks['Primary'], episodeid, "episode", "thumb", kodicursor)
# Process stream details
streams = API.get_media_streams()
self.kodi_db.addStreams(fileid, streams, runtime)
# Process playstates
resume = API.adjust_resume(userdata['Resume'])
total = round(float(runtime), 6)
self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
if not self.direct_path and resume:
# Create additional entry for widgets. This is only required for plugin/episode.
temppathid = self.kodi_db.getPath("plugin://plugin.video.emby.tvshows/")
tempfileid = self.kodi_db.addFile(filename, temppathid)
query = ' '.join((
"UPDATE files",
"SET idPath = ?, strFilename = ?, dateAdded = ?",
"WHERE idFile = ?"
))
kodicursor.execute(query, (temppathid, filename, dateadded, tempfileid))
self.kodi_db.addPlaystate(tempfileid, resume, total, playcount, dateplayed)
return True
def updateUserdata(self, item):
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
# Poster with progress bar
emby_db = self.emby_db
API = api.API(item)
# Get emby information
itemid = item['Id']
checksum = API.get_checksum()
userdata = API.get_userdata()
runtime = API.get_runtime()
dateadded = API.get_date_created()
# Get Kodi information
emby_dbitem = emby_db.getItem_byId(itemid)
try:
kodiid = emby_dbitem[0]
fileid = emby_dbitem[1]
mediatype = emby_dbitem[4]
log.info(
"Update playstate for %s: %s fileid: %s"
% (mediatype, item['Name'], fileid))
except TypeError:
return
# Process favorite tags
if mediatype == "tvshow":
if userdata['Favorite']:
self.kodi_db.addTag(kodiid, "Favorite tvshows", "tvshow")
else:
self.kodi_db.removeTag(kodiid, "Favorite tvshows", "tvshow")
elif mediatype == "episode":
# Process playstates
playcount = userdata['PlayCount']
dateplayed = userdata['LastPlayedDate']
resume = API.adjust_resume(userdata['Resume'])
total = round(float(runtime), 6)
log.debug("%s New resume point: %s" % (itemid, resume))
self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed)
if not self.direct_path and not resume:
# Make sure there's no other bookmarks created by widget.
filename = self.kodi_db.getFile(fileid)
self.kodi_db.removeFile("plugin://plugin.video.emby.tvshows/", filename)
if not self.direct_path and resume:
# Create additional entry for widgets. This is only required for plugin/episode.
filename = self.kodi_db.getFile(fileid)
temppathid = self.kodi_db.getPath("plugin://plugin.video.emby.tvshows/")
tempfileid = self.kodi_db.addFile(filename, temppathid)
query = ' '.join((
"UPDATE files",
"SET idPath = ?, strFilename = ?, dateAdded = ?",
"WHERE idFile = ?"
))
self.kodicursor.execute(query, (temppathid, filename, dateadded, tempfileid))
self.kodi_db.addPlaystate(tempfileid, resume, total, playcount, dateplayed)
emby_db.updateReference(itemid, checksum)
def remove(self, itemid):
# Remove showid, fileid, pathid, emby reference
emby_db = self.emby_db
embycursor = self.embycursor
kodicursor = self.kodicursor
artwork = self.artwork
emby_dbitem = emby_db.getItem_byId(itemid)
try:
kodiid = emby_dbitem[0]
fileid = emby_dbitem[1]
pathid = emby_dbitem[2]
parentid = emby_dbitem[3]
mediatype = emby_dbitem[4]
log.info("Removing %s kodiid: %s fileid: %s" % (mediatype, kodiid, fileid))
except TypeError:
return
##### PROCESS ITEM #####
# Remove the emby reference
emby_db.removeItem(itemid)
##### IF EPISODE #####
if mediatype == "episode":
# Delete kodi episode and file, verify season and tvshow
self.removeEpisode(kodiid, fileid)
# Season verification
season = emby_db.getItem_byKodiId(parentid, "season")
try:
showid = season[1]
except TypeError:
return
season_episodes = emby_db.getItem_byParentId(parentid, "episode")
if not season_episodes:
self.removeSeason(parentid)
emby_db.removeItem(season[0])
# Show verification
show = emby_db.getItem_byKodiId(showid, "tvshow")
query = ' '.join((
"SELECT totalCount",
"FROM tvshowcounts",
"WHERE idShow = ?"
))
kodicursor.execute(query, (showid,))
result = kodicursor.fetchone()
if result and result[0] is None:
# There's no episodes left, delete show and any possible remaining seasons
seasons = emby_db.getItem_byParentId(showid, "season")
for season in seasons:
self.removeSeason(season[1])
else:
# Delete emby season entries
emby_db.removeItems_byParentId(showid, "season")
self.removeShow(showid)
emby_db.removeItem(show[0])
##### IF TVSHOW #####
elif mediatype == "tvshow":
# Remove episodes, seasons, tvshow
seasons = emby_db.getItem_byParentId(kodiid, "season")
for season in seasons:
seasonid = season[1]
season_episodes = emby_db.getItem_byParentId(seasonid, "episode")
for episode in season_episodes:
self.removeEpisode(episode[1], episode[2])
else:
# Remove emby episodes
emby_db.removeItems_byParentId(seasonid, "episode")
else:
# Remove emby seasons
emby_db.removeItems_byParentId(kodiid, "season")
# Remove tvshow
self.removeShow(kodiid)
##### IF SEASON #####
elif mediatype == "season":
# Remove episodes, season, verify tvshow
season_episodes = emby_db.getItem_byParentId(kodiid, "episode")
for episode in season_episodes:
self.removeEpisode(episode[1], episode[2])
else:
# Remove emby episodes
emby_db.removeItems_byParentId(kodiid, "episode")
# Remove season
self.removeSeason(kodiid)
# Show verification
seasons = emby_db.getItem_byParentId(parentid, "season")
if not seasons:
# There's no seasons, delete the show
self.removeShow(parentid)
emby_db.removeItem_byKodiId(parentid, "tvshow")
log.info("Deleted %s: %s from kodi database" % (mediatype, itemid))
def removeShow(self, kodiid):
kodicursor = self.kodicursor
self.artwork.delete_artwork(kodiid, "tvshow", kodicursor)
kodicursor.execute("DELETE FROM tvshow WHERE idShow = ?", (kodiid,))
log.debug("Removed tvshow: %s." % kodiid)
def removeSeason(self, kodiid):
kodicursor = self.kodicursor
self.artwork.delete_artwork(kodiid, "season", kodicursor)
kodicursor.execute("DELETE FROM seasons WHERE idSeason = ?", (kodiid,))
log.debug("Removed season: %s." % kodiid)
def removeEpisode(self, kodiid, fileid):
kodicursor = self.kodicursor
self.artwork.delete_artwork(kodiid, "episode", kodicursor)
kodicursor.execute("DELETE FROM episode WHERE idEpisode = ?", (kodiid,))
kodicursor.execute("DELETE FROM files WHERE idFile = ?", (fileid,))
log.debug("Removed episode: %s." % kodiid)

View file

@ -186,7 +186,7 @@ class Read_EmbyServer():
url = "{server}/emby/LiveTv/Recordings/?userid={UserId}&format=json"
return self.doUtils(url, parameters=params)
def getSection(self, parentid, itemtype=None, sortby="SortName", basic=False, dialog=None):
def getSection(self, parentid, itemtype=None, sortby="SortName", artist_id=None, basic=False, dialog=None):
items = {
@ -199,6 +199,7 @@ class Read_EmbyServer():
params = {
'ParentId': parentid,
'ArtistIds': artist_id,
'IncludeItemTypes': itemtype,
'CollapseBoxSetItems': False,
'IsVirtualUnaired': False,
@ -225,6 +226,7 @@ class Read_EmbyServer():
params = {
'ParentId': parentid,
'ArtistIds': artist_id,
'IncludeItemTypes': itemtype,
'CollapseBoxSetItems': False,
'IsVirtualUnaired': False,
@ -435,10 +437,11 @@ class Read_EmbyServer():
return self.getSection(seasonId, "Episode")
def getArtists(self, dialog=None):
def getArtists(self, parent_id=None, dialog=None):
items = {
'ParentId': parent_id,
'Items': [],
'TotalRecordCount': 0
}
@ -459,13 +462,14 @@ class Read_EmbyServer():
log.debug("%s:%s Failed to retrieve the server response." % (url, params))
else:
index = 1
index = 0
jump = self.limitIndex
while index < total:
# Get items by chunk to increase retrieval speed at scale
params = {
'ParentId': parent_id,
'Recursive': True,
'IsVirtualUnaired': False,
'IsMissing': False,
@ -495,7 +499,7 @@ class Read_EmbyServer():
def getAlbumsbyArtist(self, artistId):
return self.getSection(artistId, "MusicAlbum", sortby="DateCreated")
return self.getSection(None, "MusicAlbum", sortby="DateCreated", artist_id=artistId)
def getSongs(self, basic=False, dialog=None):

View file

@ -109,6 +109,15 @@ class JSONRPC(object):
#################################################################################################
# Database related methods
def should_stop():
# Checkpoint during the syncing process
if xbmc.Monitor().abortRequested():
return True
elif window('emby_shouldStop') == "true":
return True
else: # Keep going
return False
def kodiSQL(media_type="video"):
if media_type == "emby":
@ -309,6 +318,21 @@ def indent(elem, level=0):
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def catch_except(errors=(Exception, ), default_value=False):
# Will wrap method with try/except and print parameters for easier debugging
def decorator(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except errors as error:
log.exception(error)
log.error("function: %s \n args: %s \n kwargs: %s",
func.__name__, args, kwargs)
return default_value
return wrapper
return decorator
def profiling(sortby="cumulative"):
# Will print results to Kodi log
def decorator(func):