This commit is contained in:
angelblue05 2016-10-28 00:02:47 -05:00 committed by GitHub
parent d582888ffb
commit ba22e26c06
80 changed files with 11580 additions and 6945 deletions

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,202 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import os
import xbmc
import xbmcvfs
import api
import artwork
import downloadutils
import read_embyserver as embyserver
from ga_client import GoogleAnalytics
from utils import window, settings, dialog, language as lang, should_stop
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
ga = GoogleAnalytics()
##################################################################################################
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:
errStrings = ga.formatException()
ga.sendEventData("Exception", errStrings[0], errStrings[1])
log.exception(error)
log.error("function: %s \n args: %s \n kwargs: %s",
func.__name__, args, kwargs)
return default_value
return wrapper
return decorator
class Items(object):
pdialog = None
title = None
count = 0
total = 0
def __init__(self):
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"
@classmethod
def path_validation(cls, path):
# Verify if direct path is accessible or not
if not os.path.supports_unicode_filenames:
path = path.encode('utf-8')
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, name):
# 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=self.new_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))
process = self._get_func(item_type, "added")
if view:
process(items, total, view)
else:
process(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 remove_all(self, item_type, items):
log.debug("Processing removal: %s", items)
process = self._get_func(item_type, "remove")
for item in items:
process(item)
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
def compare(self, item_type, items, compare_to, view=None):
view_name = view['name'] if view else item_type
update_list = self._compare_checksum(items, compare_to)
log.info("Update for %s: %s", view_name, update_list)
if self.should_stop():
return False
emby_items = self.emby.getFullItems(update_list)
total = len(update_list)
if self.pdialog:
self.pdialog.update(heading="Processing %s / %s items" % (view_name, total))
# Process additions and updates
if emby_items:
self.process_all(item_type, "update", emby_items, total, view)
# Process deletes
if compare_to:
self.remove_all(item_type, compare_to.items())
return True
def _compare_checksum(self, items, compare_to):
update_list = list()
for item in items:
if self.should_stop():
break
item_id = item['Id']
if compare_to.get(item_id) != api.API(item).get_checksum():
# Only update if item is not in Kodi or checksum is different
update_list.append(item_id)
compare_to.pop(item_id, None)
return update_list

View file

@ -0,0 +1,813 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import xbmc
import artwork
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class KodiItems(object):
def __init__(self):
self.artwork = artwork.Artwork()
self.kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
def create_entry_path(self):
self.cursor.execute("select coalesce(max(idPath),0) from path")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_file(self):
self.cursor.execute("select coalesce(max(idFile),0) from files")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_person(self):
self.cursor.execute("select coalesce(max(actor_id),0) from actor")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_genre(self):
self.cursor.execute("select coalesce(max(genre_id),0) from genre")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_studio(self):
self.cursor.execute("select coalesce(max(studio_id),0) from studio")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_bookmark(self):
self.cursor.execute("select coalesce(max(idBookmark),0) from bookmark")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_tag(self):
self.cursor.execute("select coalesce(max(tag_id),0) from tag")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def add_path(self, path):
path_id = self.get_path(path)
if path_id is None:
# Create a new entry
path_id = self.create_entry_path()
query = (
'''
INSERT INTO path(idPath, strPath)
VALUES (?, ?)
'''
)
self.cursor.execute(query, (path_id, path))
return path_id
def get_path(self, path):
query = ' '.join((
"SELECT idPath",
"FROM path",
"WHERE strPath = ?"
))
self.cursor.execute(query, (path,))
try:
path_id = self.cursor.fetchone()[0]
except TypeError:
path_id = None
return path_id
def update_path(self, path_id, path, media_type, scraper):
query = ' '.join((
"UPDATE path",
"SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?",
"WHERE idPath = ?"
))
self.cursor.execute(query, (path, media_type, scraper, 1, path_id))
def remove_path(self, path_id):
self.cursor.execute("DELETE FROM path WHERE idPath = ?", (path_id,))
def add_file(self, filename, path_id):
query = ' '.join((
"SELECT idFile",
"FROM files",
"WHERE strFilename = ?",
"AND idPath = ?"
))
self.cursor.execute(query, (filename, path_id,))
try:
file_id = self.cursor.fetchone()[0]
except TypeError:
# Create a new entry
file_id = self.create_entry_file()
query = (
'''
INSERT INTO files(idFile, idPath, strFilename)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (file_id, path_id, filename))
return file_id
def update_file(self, file_id, filename, path_id, date_added):
query = ' '.join((
"UPDATE files",
"SET idPath = ?, strFilename = ?, dateAdded = ?",
"WHERE idFile = ?"
))
self.cursor.execute(query, (path_id, filename, date_added, file_id))
def remove_file(self, path, filename):
path_id = self.get_path(path)
if path_id is not None:
query = ' '.join((
"DELETE FROM files",
"WHERE idPath = ?",
"AND strFilename = ?"
))
self.cursor.execute(query, (path_id, filename,))
def get_filename(self, file_id):
query = ' '.join((
"SELECT strFilename",
"FROM files",
"WHERE idFile = ?"
))
self.cursor.execute(query, (file_id,))
try:
filename = self.cursor.fetchone()[0]
except TypeError:
filename = ""
return filename
def add_people(self, kodi_id, people, media_type):
def add_thumbnail(person_id, person, type_):
thumbnail = person['imageurl']
if thumbnail:
art = type_.lower()
if "writing" in art:
art = "writer"
self.artwork.add_update_art(thumbnail, person_id, art, "thumb", self.cursor)
def add_link(link_type, person_id, kodi_id, media_type):
query = (
"INSERT OR REPLACE INTO " + link_type + "(actor_id, media_id, media_type)"
"VALUES (?, ?, ?)"
)
self.cursor.execute(query, (person_id, kodi_id, media_type))
cast_order = 1
if self.kodi_version > 14:
for person in people:
name = person['Name']
type_ = person['Type']
person_id = self._get_person(name)
# Link person to content
if type_ == "Actor":
role = person.get('Role')
query = (
'''
INSERT OR REPLACE INTO actor_link(
actor_id, media_id, media_type, role, cast_order)
VALUES (?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (person_id, kodi_id, media_type, role, cast_order))
cast_order += 1
elif type_ == "Director":
add_link("director_link", person_id, kodi_id, media_type)
elif type_ in ("Writing", "Writer"):
add_link("writer_link", person_id, kodi_id, media_type)
elif type_ == "Artist":
add_link("actor_link", person_id, kodi_id, media_type)
add_thumbnail(person_id, person, type_)
else:
# TODO: Remove Helix code when Krypton is RC
for person in people:
name = person['Name']
type_ = person['Type']
query = ' '.join((
"SELECT idActor",
"FROM actors",
"WHERE strActor = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (name,))
try:
person_id = self.cursor.fetchone()[0]
except TypeError:
# Cast entry does not exists
self.cursor.execute("select coalesce(max(idActor),0) from actors")
person_id = self.cursor.fetchone()[0] + 1
query = "INSERT INTO actors(idActor, strActor) values(?, ?)"
self.cursor.execute(query, (person_id, name))
log.debug("Add people to media, processing: %s", name)
finally:
# Link person to content
if type_ == "Actor":
role = person.get('Role')
if media_type == "movie":
query = (
'''
INSERT OR REPLACE INTO actorlinkmovie(
idActor, idMovie, strRole, iOrder)
VALUES (?, ?, ?, ?)
'''
)
elif media_type == "tvshow":
query = (
'''
INSERT OR REPLACE INTO actorlinktvshow(
idActor, idShow, strRole, iOrder)
VALUES (?, ?, ?, ?)
'''
)
elif media_type == "episode":
query = (
'''
INSERT OR REPLACE INTO actorlinkepisode(
idActor, idEpisode, strRole, iOrder)
VALUES (?, ?, ?, ?)
'''
)
else: return # Item is invalid
self.cursor.execute(query, (person_id, kodi_id, role, cast_order))
cast_order += 1
elif type_ == "Director":
if media_type == "movie":
query = (
'''
INSERT OR REPLACE INTO directorlinkmovie(idDirector, idMovie)
VALUES (?, ?)
'''
)
elif media_type == "tvshow":
query = (
'''
INSERT OR REPLACE INTO directorlinktvshow(idDirector, idShow)
VALUES (?, ?)
'''
)
elif media_type == "musicvideo":
query = (
'''
INSERT OR REPLACE INTO directorlinkmusicvideo(idDirector, idMVideo)
VALUES (?, ?)
'''
)
elif media_type == "episode":
query = (
'''
INSERT OR REPLACE INTO directorlinkepisode(idDirector, idEpisode)
VALUES (?, ?)
'''
)
else: return # Item is invalid
self.cursor.execute(query, (person_id, kodi_id))
elif type_ in ("Writing", "Writer"):
if media_type == "movie":
query = (
'''
INSERT OR REPLACE INTO writerlinkmovie(idWriter, idMovie)
VALUES (?, ?)
'''
)
elif media_type == "episode":
query = (
'''
INSERT OR REPLACE INTO writerlinkepisode(idWriter, idEpisode)
VALUES (?, ?)
'''
)
else: return # Item is invalid
self.cursor.execute(query, (person_id, kodi_id))
elif type_ == "Artist":
query = (
'''
INSERT OR REPLACE INTO artistlinkmusicvideo(idArtist, idMVideo)
VALUES (?, ?)
'''
)
self.cursor.execute(query, (person_id, kodi_id))
add_thumbnail(person_id, person, type_)
def _add_person(self, name):
person_id = self.create_entry_person()
query = "INSERT INTO actor(actor_id, name) values(?, ?)"
self.cursor.execute(query, (person_id, name))
log.debug("Add people to media, processing: %s", name)
return person_id
def _get_person(self, name):
query = ' '.join((
"SELECT actor_id",
"FROM actor",
"WHERE name = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (name,))
try:
person_id = self.cursor.fetchone()[0]
except TypeError:
person_id = self._add_person(name)
return person_id
def add_genres(self, kodi_id, genres, media_type):
if self.kodi_version > 14:
# Delete current genres for clean slate
query = ' '.join((
"DELETE FROM genre_link",
"WHERE media_id = ?",
"AND media_type = ?"
))
self.cursor.execute(query, (kodi_id, media_type,))
# Add genres
for genre in genres:
genre_id = self._get_genre(genre)
query = (
'''
INSERT OR REPLACE INTO genre_link(
genre_id, media_id, media_type)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (genre_id, kodi_id, media_type))
else:
# TODO: Remove Helix code when Krypton is RC
# Delete current genres for clean slate
if media_type == "movie":
self.cursor.execute("DELETE FROM genrelinkmovie WHERE idMovie = ?", (kodi_id,))
elif media_type == "tvshow":
self.cursor.execute("DELETE FROM genrelinktvshow WHERE idShow = ?", (kodi_id,))
elif media_type == "musicvideo":
self.cursor.execute("DELETE FROM genrelinkmusicvideo WHERE idMVideo = ?", (kodi_id,))
# Add genres
for genre in genres:
query = ' '.join((
"SELECT idGenre",
"FROM genre",
"WHERE strGenre = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (genre,))
try:
genre_id = self.cursor.fetchone()[0]
except TypeError:
# Create genre in database
self.cursor.execute("select coalesce(max(idGenre),0) from genre")
genre_id = self.cursor.fetchone()[0] + 1
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
self.cursor.execute(query, (genre_id, genre))
log.debug("Add Genres to media, processing: %s", genre)
finally:
# Assign genre to item
if media_type == "movie":
query = (
'''
INSERT OR REPLACE into genrelinkmovie(idGenre, idMovie)
VALUES (?, ?)
'''
)
elif media_type == "tvshow":
query = (
'''
INSERT OR REPLACE into genrelinktvshow(idGenre, idShow)
VALUES (?, ?)
'''
)
elif media_type == "musicvideo":
query = (
'''
INSERT OR REPLACE into genrelinkmusicvideo(idGenre, idMVideo)
VALUES (?, ?)
'''
)
else: return # Item is invalid
self.cursor.execute(query, (genre_id, kodi_id))
def _add_genre(self, genre):
genre_id = self.create_entry_genre()
query = "INSERT INTO genre(genre_id, name) values(?, ?)"
self.cursor.execute(query, (genre_id, genre))
log.debug("Add Genres to media, processing: %s", genre)
return genre_id
def _get_genre(self, genre):
query = ' '.join((
"SELECT genre_id",
"FROM genre",
"WHERE name = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (genre,))
try:
genre_id = self.cursor.fetchone()[0]
except TypeError:
genre_id = self._add_genre(genre)
return genre_id
def add_studios(self, kodi_id, studios, media_type):
if self.kodi_version > 14:
for studio in studios:
studio_id = self._get_studio(studio)
query = (
'''
INSERT OR REPLACE INTO studio_link(studio_id, media_id, media_type)
VALUES (?, ?, ?)
''')
self.cursor.execute(query, (studio_id, kodi_id, media_type))
else:
# TODO: Remove Helix code when Krypton is RC
for studio in studios:
query = ' '.join((
"SELECT idstudio",
"FROM studio",
"WHERE strstudio = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (studio,))
try:
studio_id = self.cursor.fetchone()[0]
except TypeError:
# Studio does not exists.
self.cursor.execute("select coalesce(max(idstudio),0) from studio")
studio_id = self.cursor.fetchone()[0] + 1
query = "INSERT INTO studio(idstudio, strstudio) values(?, ?)"
self.cursor.execute(query, (studio_id, studio))
log.debug("Add Studios to media, processing: %s", studio)
finally: # Assign studio to item
if media_type == "movie":
query = (
'''
INSERT OR REPLACE INTO studiolinkmovie(idstudio, idMovie)
VALUES (?, ?)
''')
elif media_type == "musicvideo":
query = (
'''
INSERT OR REPLACE INTO studiolinkmusicvideo(idstudio, idMVideo)
VALUES (?, ?)
''')
elif media_type == "tvshow":
query = (
'''
INSERT OR REPLACE INTO studiolinktvshow(idstudio, idShow)
VALUES (?, ?)
''')
elif media_type == "episode":
query = (
'''
INSERT OR REPLACE INTO studiolinkepisode(idstudio, idEpisode)
VALUES (?, ?)
''')
self.cursor.execute(query, (studio_id, kodi_id))
def _add_studio(self, studio):
studio_id = self.create_entry_studio()
query = "INSERT INTO studio(studio_id, name) values(?, ?)"
self.cursor.execute(query, (studio_id, studio))
log.debug("Add Studios to media, processing: %s", studio)
return studio_id
def _get_studio(self, studio):
query = ' '.join((
"SELECT studio_id",
"FROM studio",
"WHERE name = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (studio,))
try:
studio_id = self.cursor.fetchone()[0]
except TypeError:
studio_id = self._add_studio(studio)
return studio_id
def add_streams(self, file_id, streams, runtime):
# First remove any existing entries
self.cursor.execute("DELETE FROM streamdetails WHERE idFile = ?", (file_id,))
if streams:
# Video details
for track in streams['video']:
query = (
'''
INSERT INTO streamdetails(
idFile, iStreamType, strVideoCodec, fVideoAspect,
iVideoWidth, iVideoHeight, iVideoDuration ,strStereoMode)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (file_id, 0, track['codec'], track['aspect'],
track['width'], track['height'], runtime,
track['video3DFormat']))
# Audio details
for track in streams['audio']:
query = (
'''
INSERT INTO streamdetails(
idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage)
VALUES (?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (file_id, 1, track['codec'], track['channels'],
track['language']))
# Subtitles details
for track in streams['subtitle']:
query = (
'''
INSERT INTO streamdetails(idFile, iStreamType, strSubtitleLanguage)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (file_id, 2, track))
def add_playstate(self, file_id, resume, total, playcount, date_played):
# Delete existing resume point
self.cursor.execute("DELETE FROM bookmark WHERE idFile = ?", (file_id,))
# Set watched count
self.set_playcount(file_id, playcount, date_played)
if resume:
bookmark_id = self.create_entry_bookmark()
query = (
'''
INSERT INTO bookmark(
idBookmark, idFile, timeInSeconds, totalTimeInSeconds, player, type)
VALUES (?, ?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (bookmark_id, file_id, resume, total, "DVDPlayer", 1))
def set_playcount(self, file_id, playcount, date_played):
query = ' '.join((
"UPDATE files",
"SET playCount = ?, lastPlayed = ?",
"WHERE idFile = ?"
))
self.cursor.execute(query, (playcount, date_played, file_id))
def add_tags(self, kodi_id, tags, media_type):
if self.kodi_version > 14:
query = ' '.join((
"DELETE FROM tag_link",
"WHERE media_id = ?",
"AND media_type = ?"
))
self.cursor.execute(query, (kodi_id, media_type))
# Add tags
log.debug("Adding Tags: %s", tags)
for tag in tags:
tag_id = self.get_tag(kodi_id, tag, media_type)
else:
# TODO: Remove Helix code when Krypton is RC
query = ' '.join((
"DELETE FROM taglinks",
"WHERE idMedia = ?",
"AND media_type = ?"
))
self.cursor.execute(query, (kodi_id, media_type))
# Add tags
log.debug("Adding Tags: %s", tags)
for tag in tags:
tag_id = self.get_tag_old(kodi_id, tag, media_type)
def _add_tag(self, tag):
tag_id = self.create_entry_tag()
query = "INSERT INTO tag(tag_id, name) values(?, ?)"
self.cursor.execute(query, (tag_id, tag))
log.debug("Create tag_id: %s name: %s", tag_id, tag)
return tag_id
def get_tag(self, kodi_id, tag, media_type):
if self.kodi_version > 14:
query = ' '.join((
"SELECT tag_id",
"FROM tag",
"WHERE name = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (tag,))
try:
tag_id = self.cursor.fetchone()[0]
except TypeError:
tag_id = self._add_tag(tag)
query = (
'''
INSERT OR REPLACE INTO tag_link(tag_id, media_id, media_type)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (tag_id, kodi_id, media_type))
else:
# TODO: Remove Helix code when Krypton is RC
tag_id = self.get_tag_old(kodi_id, tag, media_type)
return tag_id
def get_tag_old(self, kodi_id, tag, media_type):
# TODO: Remove Helix code when Krypton is RC
query = ' '.join((
"SELECT idTag",
"FROM tag",
"WHERE strTag = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (tag,))
try:
tag_id = self.cursor.fetchone()[0]
except TypeError:
# Create the tag
self.cursor.execute("select coalesce(max(idTag),0) from tag")
tag_id = self.cursor.fetchone()[0] + 1
query = "INSERT INTO tag(idTag, strTag) values(?, ?)"
self.cursor.execute(query, (tag_id, name))
log.debug("Create idTag: %s name: %s", tag_id, name)
finally:
# Assign tag to item
query = (
'''
INSERT OR REPLACE INTO taglinks(
idTag, idMedia, media_type)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (tag_id, kodi_id, media_type))
return tag_id
def remove_tag(self, kodi_id, tag, media_type):
if self.kodi_version > 14:
query = ' '.join((
"SELECT tag_id",
"FROM tag",
"WHERE name = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (tag,))
try:
tag_id = self.cursor.fetchone()[0]
except TypeError:
return
else:
query = ' '.join((
"DELETE FROM tag_link",
"WHERE media_id = ?",
"AND media_type = ?",
"AND tag_id = ?"
))
self.cursor.execute(query, (kodi_id, media_type, tag_id,))
else:
# TODO: Remove Helix code when Krypton is RC
query = ' '.join((
"SELECT idTag",
"FROM tag",
"WHERE strTag = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (tag,))
try:
tag_id = self.cursor.fetchone()[0]
except TypeError:
return
else:
query = ' '.join((
"DELETE FROM taglinks",
"WHERE idMedia = ?",
"AND media_type = ?",
"AND idTag = ?"
))
self.cursor.execute(query, (kodi_id, media_type, tag_id,))

View file

@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
from _kodi_common import KodiItems
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class KodiMovies(KodiItems):
def __init__(self, cursor):
self.cursor = cursor
KodiItems.__init__(self)
def create_entry(self):
self.cursor.execute("select coalesce(max(idMovie),0) from movie")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_set(self):
self.cursor.execute("select coalesce(max(idSet),0) from sets")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_country(self):
self.cursor.execute("select coalesce(max(country_id),0) from country")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def get_movie(self, kodi_id):
query = "SELECT * FROM movie WHERE idMovie = ?"
self.cursor.execute(query, (kodi_id,))
try:
kodi_id = self.cursor.fetchone()[0]
except TypeError:
kodi_id = None
return kodi_id
def add_movie(self, *args):
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (args))
def add_movie_17(self, *args):
# Create the movie entry
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (args))
def update_movie(self, *args):
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 = ?"
))
self.cursor.execute(query, (args))
def update_movie_17(self, *args):
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 = ?"
))
self.cursor.execute(query, (args))
def remove_movie(self, kodi_id, file_id):
self.cursor.execute("DELETE FROM movie WHERE idMovie = ?", (kodi_id,))
self.cursor.execute("DELETE FROM files WHERE idFile = ?", (file_id,))
def add_countries(self, kodi_id, countries):
if self.kodi_version > 14:
for country in countries:
country_id = self._get_country(country)
query = (
'''
INSERT OR REPLACE INTO country_link(country_id, media_id, media_type)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (country_id, kodi_id, "movie"))
else:
# TODO: Remove Helix code when Krypton is RC
for country in countries:
query = ' '.join((
"SELECT idCountry",
"FROM country",
"WHERE strCountry = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (country,))
try:
country_id = self.cursor.fetchone()[0]
except TypeError:
# Create a new entry
self.cursor.execute("select coalesce(max(idCountry),0) from country")
country_id = self.cursor.fetchone()[0] + 1
query = "INSERT INTO country(idCountry, strCountry) values(?, ?)"
self.cursor.execute(query, (country_id, country))
log.debug("Add country to media, processing: %s", country)
query = (
'''
INSERT OR REPLACE INTO countrylinkmovie(idCountry, idMovie)
VALUES (?, ?)
'''
)
self.cursor.execute(query, (country_id, kodi_id))
def _add_country(self, country):
country_id = self.create_entry_country()
query = "INSERT INTO country(country_id, name) values(?, ?)"
self.cursor.execute(query, (country_id, country))
log.debug("Add country to media, processing: %s", country)
return country_id
def _get_country(self, country):
query = ' '.join((
"SELECT country_id",
"FROM country",
"WHERE name = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (country,))
try:
country_id = self.cursor.fetchone()[0]
except TypeError:
country_id = self._add_country(country)
return country_id
def add_boxset(self, boxset):
query = ' '.join((
"SELECT idSet",
"FROM sets",
"WHERE strSet = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (boxset,))
try:
set_id = self.cursor.fetchone()[0]
except TypeError:
set_id = self._add_boxset(boxset)
return set_id
def _add_boxset(self, boxset):
set_id = self.create_entry_set()
query = "INSERT INTO sets(idSet, strSet) values(?, ?)"
self.cursor.execute(query, (set_id, boxset))
log.debug("Adding boxset: %s", boxset)
return set_id
def set_boxset(self, set_id, movie_id):
query = ' '.join((
"UPDATE movie",
"SET idSet = ?",
"WHERE idMovie = ?"
))
self.cursor.execute(query, (set_id, movie_id,))
def remove_from_boxset(self, movie_id):
query = ' '.join((
"UPDATE movie",
"SET idSet = null",
"WHERE idMovie = ?"
))
self.cursor.execute(query, (movie_id,))
def remove_boxset(self, kodi_id):
self.cursor.execute("DELETE FROM sets WHERE idSet = ?", (kodi_id,))

View file

@ -0,0 +1,406 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
from _kodi_common import KodiItems
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class KodiMusic(KodiItems):
def __init__(self, cursor):
self.cursor = cursor
KodiItems.__init__(self)
def create_entry(self):
self.cursor.execute("select coalesce(max(idArtist),0) from artist")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_album(self):
self.cursor.execute("select coalesce(max(idAlbum),0) from album")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_song(self):
self.cursor.execute("select coalesce(max(idSong),0) from song")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_genre(self):
self.cursor.execute("select coalesce(max(idGenre),0) from genre")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def update_path(self, path_id, path):
query = "UPDATE path SET strPath = ? WHERE idPath = ?"
self.cursor.execute(query, (path, path_id))
def add_role(self):
query = (
'''
INSERT OR REPLACE INTO role(idRole, strRole)
VALUES (?, ?)
'''
)
self.cursor.execute(query, (1, 'Composer'))
def get_artist(self, name, musicbrainz):
query = ' '.join((
"SELECT idArtist, strArtist",
"FROM artist",
"WHERE strMusicBrainzArtistID = ?"
))
self.cursor.execute(query, (musicbrainz,))
try:
result = self.cursor.fetchone()
artist_id = result[0]
artist_name = result[1]
except TypeError:
artist_id = self._add_artist(name, musicbrainz)
else:
if artist_name != name:
self.update_artist_name(artist_id, name)
return artist_id
def _add_artist(self, name, musicbrainz):
query = ' '.join((
# Safety check, when musicbrainz does not exist
"SELECT idArtist",
"FROM artist",
"WHERE strArtist = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (name,))
try:
artist_id = self.cursor.fetchone()[0]
except TypeError:
artist_id = self.create_entry()
query = (
'''
INSERT INTO artist(idArtist, strArtist, strMusicBrainzArtistID)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (artist_id, name, musicbrainz))
return artist_id
def update_artist_name(self, kodi_id, name):
query = "UPDATE artist SET strArtist = ? WHERE idArtist = ?"
self.cursor.execute(query, (name, kodi_id,))
def update_artist_16(self, *args):
query = ' '.join((
"UPDATE artist",
"SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?,",
"lastScraped = ?",
"WHERE idArtist = ?"
))
self.cursor.execute(query, (args))
def update_artist(self, *args):
query = ' '.join((
"UPDATE artist",
"SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?,",
"lastScraped = ?, dateAdded = ?",
"WHERE idArtist = ?"
))
self.cursor.execute(query, (args))
def link_artist(self, kodi_id, album_id, name):
query = (
'''
INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (kodi_id, album_id, name))
def add_discography(self, kodi_id, album, year):
query = (
'''
INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (kodi_id, album, year))
def get_album(self, name, musicbrainz):
query = ' '.join((
"SELECT idAlbum",
"FROM album",
"WHERE strMusicBrainzAlbumID = ?"
))
self.cursor.execute(query, (musicbrainz,))
try:
album_id = self.cursor.fetchone()[0]
except TypeError:
album_id = self._add_album(name, musicbrainz)
return album_id
def _add_album(self, name, musicbrainz):
album_id = self.create_entry_album()
if self.kodi_version > 14:
query = (
'''
INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strReleaseType)
VALUES (?, ?, ?, ?)
'''
)
self.cursor.execute(query, (album_id, name, musicbrainz, "album"))
else:
# TODO: Remove Helix code when Krypton is RC
query = (
'''
INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID)
VALUES (?, ?, ?)
'''
)
self.cursor.execute(query, (album_id, name, musicbrainz))
return album_id
def update_album(self, *args):
query = ' '.join((
"UPDATE album",
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
"iRating = ?, lastScraped = ?, strReleaseType = ?",
"WHERE idAlbum = ?"
))
self.cursor.execute(query, (args))
def update_album_17(self, *args):
query = ' '.join((
"UPDATE album",
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
"iUserrating = ?, lastScraped = ?, strReleaseType = ?",
"WHERE idAlbum = ?"
))
self.cursor.execute(query, (args))
def update_album_15(self, *args):
query = ' '.join((
"UPDATE album",
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
"iRating = ?, lastScraped = ?, dateAdded = ?, strReleaseType = ?",
"WHERE idAlbum = ?"
))
self.cursor.execute(query, (args))
def update_album_14(self, *args):
# TODO: Remove Helix code when Krypton is RC
query = ' '.join((
"UPDATE album",
"SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
"iRating = ?, lastScraped = ?, dateAdded = ?",
"WHERE idAlbum = ?"
))
self.cursor.execute(query, (args))
def get_album_artist(self, album_id, artists):
query = ' '.join((
"SELECT strArtists",
"FROM album",
"WHERE idAlbum = ?"
))
self.cursor.execute(query, (album_id,))
try:
curr_artists = self.cursor.fetchone()[0]
except TypeError:
return
if curr_artists != artists:
self._update_album_artist(album_id, artists)
def _update_album_artist(self, album_id, artists):
query = "UPDATE album SET strArtists = ? WHERE idAlbum = ?"
self.cursor.execute(query, (artists, album_id))
def add_single(self, *args):
query = (
'''
INSERT INTO album(idAlbum, strGenres, iYear, strReleaseType)
VALUES (?, ?, ?, ?)
'''
)
self.cursor.execute(query, (args))
def add_single_15(self, *args):
query = (
'''
INSERT INTO album(idAlbum, strGenres, iYear, dateAdded, strReleaseType)
VALUES (?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (args))
def add_single_14(self, *args):
# TODO: Remove Helix code when Krypton is RC
query = (
'''
INSERT INTO album(idAlbum, strGenres, iYear, dateAdded)
VALUES (?, ?, ?, ?)
'''
)
self.cursor.execute(query, (args))
def add_song(self, *args):
query = (
'''
INSERT INTO song(
idSong, idAlbum, idPath, strArtists, strGenres, strTitle, iTrack,
iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed,
rating)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (args))
def update_song(self, *args):
query = ' '.join((
"UPDATE song",
"SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?,",
"iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,",
"rating = ?, comment = ?",
"WHERE idSong = ?"
))
self.cursor.execute(query, (args))
def link_song_artist(self, kodi_id, song_id, index, artist):
if self.kodi_version > 16:
query = (
'''
INSERT OR REPLACE INTO song_artist(idArtist, idSong, idRole, iOrder, strArtist)
VALUES (?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (kodi_id, song_id, 1, index, artist))
else:
query = (
'''
INSERT OR REPLACE INTO song_artist(idArtist, idSong, iOrder, strArtist)
VALUES (?, ?, ?, ?)
'''
)
self.cursor.execute(query, (kodi_id, song_id, index, artist))
def link_song_album(self, song_id, album_id, track, title, duration):
query = (
'''
INSERT OR REPLACE INTO albuminfosong(
idAlbumInfoSong, idAlbumInfo, iTrack, strTitle, iDuration)
VALUES (?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (song_id, album_id, track, title, duration))
def rate_song(self, kodi_id, playcount, rating, date_played):
query = "UPDATE song SET iTimesPlayed = ?, lastplayed = ?, rating = ? WHERE idSong = ?"
self.cursor.execute(query, (playcount, date_played, rating, kodi_id))
def add_genres(self, kodi_id, genres, media_type):
if media_type == "album":
# Delete current genres for clean slate
query = ' '.join((
"DELETE FROM album_genre",
"WHERE idAlbum = ?"
))
self.cursor.execute(query, (kodi_id,))
for genre in genres:
genre_id = self.get_genre(genre)
query = "INSERT OR REPLACE INTO album_genre(idGenre, idAlbum) values(?, ?)"
self.cursor.execute(query, (genre_id, kodi_id))
elif media_type == "song":
# Delete current genres for clean slate
query = ' '.join((
"DELETE FROM song_genre",
"WHERE idSong = ?"
))
self.cursor.execute(query, (kodi_id,))
for genre in genres:
genre_id = self.get_genre(genre)
query = "INSERT OR REPLACE INTO song_genre(idGenre, idSong) values(?, ?)"
self.cursor.execute(query, (genre_id, kodi_id))
def get_genre(self, genre):
query = ' '.join((
"SELECT idGenre",
"FROM genre",
"WHERE strGenre = ?",
"COLLATE NOCASE"
))
self.cursor.execute(query, (genre,))
try:
genre_id = self.cursor.fetchone()[0]
except TypeError:
genre_id = self._add_genre(genre)
return genre_id
def _add_genre(self, genre):
genre_id = self.create_entry_genre()
query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)"
self.cursor.execute(query, (genre_id, genre))
return genre_id
def remove_artist(self, kodi_id):
self.cursor.execute("DELETE FROM artist WHERE idArtist = ?", (kodi_id,))
def remove_album(self, kodi_id):
self.cursor.execute("DELETE FROM album WHERE idAlbum = ?", (kodi_id,))
def remove_song(self, kodi_id):
self.cursor.execute("DELETE FROM song WHERE idSong = ?", (kodi_id,))

View file

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
from _kodi_common import KodiItems
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class KodiMusicVideos(KodiItems):
def __init__(self, cursor):
self.cursor = cursor
KodiItems.__init__(self)
def create_entry(self):
self.cursor.execute("select coalesce(max(idMVideo),0) from musicvideo")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def get_musicvideo(self, kodi_id):
query = "SELECT * FROM musicvideo WHERE idMVideo = ?"
self.cursor.execute(query, (kodi_id,))
try:
kodi_id = self.cursor.fetchone()[0]
except TypeError:
kodi_id = None
return kodi_id
def add_musicvideo(self, *args):
query = (
'''
INSERT INTO musicvideo(
idMVideo, idFile, c00, c04, c05, c06, c07, c08, c09, c10, c11, c12)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (args))
def update_musicvideo(self, *args):
query = ' '.join((
"UPDATE musicvideo",
"SET c00 = ?, c04 = ?, c05 = ?, c06 = ?, c07 = ?, c08 = ?, c09 = ?, c10 = ?,",
"c11 = ?, c12 = ?"
"WHERE idMVideo = ?"
))
self.cursor.execute(query, (args))
def remove_musicvideo(self, kodi_id, file_id):
self.cursor.execute("DELETE FROM musicvideo WHERE idMVideo = ?", (kodi_id,))
self.cursor.execute("DELETE FROM files WHERE idFile = ?", (file_id,))

View file

@ -0,0 +1,170 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
from _kodi_common import KodiItems
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class KodiTVShows(KodiItems):
def __init__(self, cursor):
self.cursor = cursor
KodiItems.__init__(self)
def create_entry(self):
self.cursor.execute("select coalesce(max(idShow),0) from tvshow")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_season(self):
self.cursor.execute("select coalesce(max(idSeason),0) from seasons")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def create_entry_episode(self):
self.cursor.execute("select coalesce(max(idEpisode),0) from episode")
kodi_id = self.cursor.fetchone()[0] + 1
return kodi_id
def get_tvshow(self, kodi_id):
query = "SELECT * FROM tvshow WHERE idShow = ?"
self.cursor.execute(query, (kodi_id,))
try:
kodi_id = self.cursor.fetchone()[0]
except TypeError:
kodi_id = None
return kodi_id
def get_episode(self, kodi_id):
query = "SELECT * FROM episode WHERE idEpisode = ?"
self.cursor.execute(query, (kodi_id,))
try:
kodi_id = self.cursor.fetchone()[0]
except TypeError:
kodi_id = None
return kodi_id
def add_tvshow(self, *args):
query = (
'''
INSERT INTO tvshow(idShow, c00, c01, c04, c05, c08, c09, c12, c13, c14, c15)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (args))
def update_tvshow(self, *args):
query = ' '.join((
"UPDATE tvshow",
"SET c00 = ?, c01 = ?, c04 = ?, c05 = ?, c08 = ?, c09 = ?,",
"c12 = ?, c13 = ?, c14 = ?, c15 = ?",
"WHERE idShow = ?"
))
self.cursor.execute(query, (args))
def link_tvshow(self, show_id, path_id):
query = "INSERT OR REPLACE INTO tvshowlinkpath(idShow, idPath) values(?, ?)"
self.cursor.execute(query, (show_id, path_id))
def get_season(self, show_id, number, name=None):
query = ' '.join((
"SELECT idSeason",
"FROM seasons",
"WHERE idShow = ?",
"AND season = ?"
))
self.cursor.execute(query, (show_id, number,))
try:
season_id = self.cursor.fetchone()[0]
except TypeError:
season_id = self._add_season(show_id, number)
if self.kodi_version > 15 and name is not None:
query = "UPDATE seasons SET name = ? WHERE idSeason = ?"
self.cursor.execute(query, (name, season_id))
return season_id
def _add_season(self, show_id, number):
season_id = self.create_entry_season()
query = "INSERT INTO seasons(idSeason, idShow, season) values(?, ?, ?)"
self.cursor.execute(query, (season_id, show_id, number))
return season_id
def add_episode(self, *args):
query = (
'''
INSERT INTO episode(
idEpisode, idFile, c00, c01, c03, c04, c05, c09, c10, c12, c13, c14,
idShow, c15, c16)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (args))
def add_episode_16(self, *args):
query = (
'''
INSERT INTO episode(
idEpisode, idFile, c00, c01, c03, c04, c05, c09, c10, c12, c13, c14,
idShow, c15, c16, idSeason)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
)
self.cursor.execute(query, (args))
def update_episode(self, *args):
query = ' '.join((
"UPDATE episode",
"SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?, c10 = ?,",
"c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?, idShow = ?",
"WHERE idEpisode = ?"
))
self.cursor.execute(query, (args))
def update_episode_16(self, *args):
query = ' '.join((
"UPDATE episode",
"SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?, c10 = ?,",
"c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?, idSeason = ?, idShow = ?",
"WHERE idEpisode = ?"
))
self.cursor.execute(query, (args))
def remove_tvshow(self, kodi_id):
self.cursor.execute("DELETE FROM tvshow WHERE idShow = ?", (kodi_id,))
def remove_season(self, kodi_id):
self.cursor.execute("DELETE FROM seasons WHERE idSeason = ?", (kodi_id,))
def remove_episode(self, kodi_id, file_id):
self.cursor.execute("DELETE FROM episode WHERE idEpisode = ?", (kodi_id,))
self.cursor.execute("DELETE FROM files WHERE idFile = ?", (file_id,))

View file

@ -0,0 +1,453 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import urllib
import api
import embydb_functions as embydb
import _kodi_movies
from _common import Items, catch_except
from utils import window, settings, language as lang
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class Movies(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 = _kodi_movies.KodiMovies(self.kodicursor)
self.pdialog = pdialog
self.new_time = int(settings('newvideotime'))*1000
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.added_boxset,
'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
views = self.emby_db.getView_byType('movies')
views += self.emby_db.getView_byType('mixed')
log.info("Media folders: %s", views)
# Process movies
for view in views:
if self.should_stop():
return False
if not self.compare_movies(view):
return False
# Process boxsets
if not self.compare_boxsets():
return False
return True
def compare_movies(self, view):
view_id = view['id']
view_name = view['name']
if self.pdialog:
self.pdialog.update(heading=lang(29999), message="%s %s..." % (lang(33026), view_name))
movies = dict(self.emby_db.get_checksum_by_view("Movie", view_id))
emby_movies = self.emby.getMovies(view_id, basic=True, dialog=self.pdialog)
return self.compare("Movie", emby_movies['Items'], movies, view)
def compare_boxsets(self):
if self.pdialog:
self.pdialog.update(heading=lang(29999), message=lang(33027))
boxsets = dict(self.emby_db.get_checksum('BoxSet'))
emby_boxsets = self.emby.getBoxset(dialog=self.pdialog)
return self.compare("BoxSet", emby_boxsets['Items'], boxsets)
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(item.get('Name', "unknown"))
def added_boxset(self, items, total=None):
for item in super(Movies, self).added(items, total):
self.add_updateBoxset(item)
@catch_except()
def add_update(self, item, view=None):
# Process single movie
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
movieid = self.kodi_db.create_entry()
else:
if self.kodi_db.get_movie(movieid) is None:
# 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:
trailer_id = 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" % trailer_id
##### 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:
self.kodi_db.update_movie_17(title, plot, shortplot, tagline, votecount, rating,
writer, year, imdb, sorttitle, runtime, mpaa, genre,
director, title, studio, trailer, country, year,
movieid)
else:
self.kodi_db.update_movie(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.add_path(path)
# Add the file
fileid = self.kodi_db.add_file(filename, pathid)
# Create the movie entry
if self.kodi_version > 16:
self.kodi_db.add_movie_17(movieid, fileid, title, plot, shortplot, tagline,
votecount, rating, writer, year, imdb, sorttitle,
runtime, mpaa, genre, director, title, studio, trailer,
country, year)
else:
self.kodi_db.add_movie(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
self.kodi_db.update_path(pathid, path, "movies", "metadata.local")
# Update the file
self.kodi_db.update_file(fileid, filename, pathid, dateadded)
# Process countries
if 'ProductionLocations' in item:
self.kodi_db.add_countries(movieid, item['ProductionLocations'])
# Process cast
people = artwork.get_people_artwork(item['People'])
self.kodi_db.add_people(movieid, people, "movie")
# Process genres
self.kodi_db.add_genres(movieid, genres, "movie")
# Process artwork
artwork.add_artwork(artwork.get_all_artwork(item), movieid, "movie", self.kodicursor)
# Process stream details
streams = API.get_media_streams()
self.kodi_db.add_streams(fileid, streams, runtime)
# Process studios
self.kodi_db.add_studios(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.add_tags(movieid, tags, "movie")
# Process playstates
resume = API.adjust_resume(userdata['Resume'])
total = round(float(runtime), 6)
self.kodi_db.add_playstate(fileid, resume, total, playcount, dateplayed)
return True
def add_updateBoxset(self, boxset):
emby = self.emby
emby_db = self.emby_db
artwork = self.artwork
API = api.API(boxset)
boxsetid = boxset['Id']
title = boxset['Name']
checksum = API.get_checksum()
emby_dbitem = emby_db.getItem_byId(boxsetid)
try:
setid = emby_dbitem[0]
except TypeError:
setid = self.kodi_db.add_boxset(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.set_boxset(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.remove_from_boxset(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.get_tag(movieid, "Favorite movies", "movie")
else:
self.kodi_db.remove_tag(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.add_playstate(fileid, resume, total, playcount, dateplayed)
emby_db.updateReference(itemid, checksum)
def remove(self, itemid):
# Remove movieid, fileid, emby reference
emby_db = self.emby_db
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, self.kodicursor)
if mediatype == "movie":
self.kodi_db.remove_movie(kodiid, 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.remove_from_boxset(movieid)
# Update emby reference
emby_db.updateParentId(embyid, None)
self.kodi_db.remove_boxset(kodiid)
log.info("Deleted %s %s from kodi database", mediatype, itemid)

View file

@ -0,0 +1,709 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
from datetime import datetime
import api
import embydb_functions as embydb
import musicutils
import _kodi_music
from _common import Items, catch_except
from utils import window, settings, language as lang
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class Music(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 = _kodi_music.KodiMusic(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)
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
views = self.emby_db.getView_byType('music')
for view in views:
# Process artists
self.compare_artists(view)
# Process albums
self.compare_albums()
# Process songs
self.compare_songs()
return True
def compare_artists(self, view):
all_embyartistsIds = set()
update_list = list()
if self.pdialog:
self.pdialog.update(heading=lang(29999), message="%s Artists..." % lang(33031))
artists = dict(self.emby_db.get_checksum('MusicArtist'))
album_artists = dict(self.emby_db.get_checksum('AlbumArtist'))
emby_artists = self.emby.getArtists(view['id'], dialog=self.pdialog)
for item in emby_artists['Items']:
if self.should_stop():
return False
item_id = item['Id']
API = api.API(item)
all_embyartistsIds.add(item_id)
if item_id in artists:
if artists[item_id] != API.get_checksum():
# Only update if artist is not in Kodi or checksum is different
update_list.append(item_id)
elif album_artists.get(item_id) != API.get_checksum():
# Only update if artist is not in Kodi or checksum is different
update_list.append(item_id)
#compare_to.pop(item_id, None)
log.info("Update for Artist: %s", update_list)
emby_items = self.emby.getFullItems(update_list)
total = len(update_list)
if self.pdialog:
self.pdialog.update(heading="Processing Artists / %s items" % total)
# Process additions and updates
if emby_items:
self.process_all("MusicArtist", "update", emby_items, total)
# Process removals
for artist in artists:
if artist not in all_embyartistsIds and artists[artist] is not None:
self.remove(artist)
def compare_albums(self):
if self.pdialog:
self.pdialog.update(heading=lang(29999), message="%s Albums..." % lang(33031))
albums = dict(self.emby_db.get_checksum('MusicAlbum'))
emby_albums = self.emby.getAlbums(basic=True, dialog=self.pdialog)
return self.compare("MusicAlbum", emby_albums['Items'], albums)
def compare_songs(self):
if self.pdialog:
self.pdialog.update(heading=lang(29999), message="%s Songs..." % lang(33031))
songs = dict(self.emby_db.get_checksum('Audio'))
emby_songs = self.emby.getSongs(basic=True, dialog=self.pdialog)
return self.compare("Audio", emby_songs['Items'], songs)
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(self.title)
@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)
else:
pass
##### 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.get_artist(name, musicBrainzId)
# Create the reference in emby table
emby_db.addReference(itemid, artistid, artisttype, "artist", checksum=checksum)
# Process the artist
if self.kodi_version > 15:
self.kodi_db.update_artist_16(genres, bio, thumb, fanart, lastScraped, artistid)
else:
self.kodi_db.update_artist(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 = 0
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.get_album(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
self.kodi_db.update_album_17(artistname, year, genre, bio, thumb, rating, lastScraped,
"album", albumid)
elif self.kodi_version == 16:
# Kodi Jarvis
self.kodi_db.update_album(artistname, year, genre, bio, thumb, rating, lastScraped,
"album", albumid)
elif self.kodi_version == 15:
# Kodi Isengard
self.kodi_db.update_album_15(artistname, year, genre, bio, thumb, rating, lastScraped,
dateadded, "album", albumid)
else:
# TODO: Remove Helix code when Krypton is RC
self.kodi_db.update_album_14(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.
self.kodi_db.update_artist_name(artistid, artistname)
# Add artist to album
self.kodi_db.link_artist(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
self.kodi_db.add_discography(artistid, name, year)
# Add genres
self.kodi_db.add_genres(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)
songid = self.kodi_db.create_entry_song()
##### 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 = 0
#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
self.kodi_db.update_path(pathid, path)
# Update the song entry
self.kodi_db.update_song(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.add_path(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.get_album(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.")
album_id = self.kodi_db.create_entry_album()
if self.kodi_version == 16:
self.kodi_db.add_single(albumid, genre, year, "single")
elif self.kodi_version == 15:
self.kodi_db.add_single_15(albumid, genre, year, dateadded, "single")
else:
# TODO: Remove Helix code when Krypton is RC
self.kodi_db.add_single_14(albumid, genre, year, dateadded)
# Create the song entry
self.kodi_db.add_song(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
self.kodi_db.link_song_album(songid, albumid, track, title, duration)
# Create default role
if self.kodi_version > 16:
self.kodi_db.add_role()
# 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:
# Link song to artist
self.kodi_db.link_song_artist(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:
# Link artist to album
self.kodi_db.link_artist(artistid, albumid, artist_name)
# Update discography
if item.get('Album'):
self.kodi_db.add_discography(artistid, item['Album'], 0)
# Artist names
album_artists = " / ".join(album_artists)
self.kodi_db.get_album_artist(albumid, album_artists)
# Add genres
self.kodi_db.add_genres(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()
rating = 0
# 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)
self.kodi_db.rate_song(playcount, dateplayed, rating, kodiid)
emby_db.updateReference(itemid, checksum)
def remove(self, itemid):
# Remove kodiid, fileid, pathid, emby reference
emby_db = self.emby_db
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, kodi_id):
self.artwork.delete_artwork(kodi_id, "song", self.kodicursor)
self.kodi_db.remove_song(kodi_id)
def removeAlbum(self, kodi_id):
self.artwork.delete_artwork(kodi_id, "album", self.kodicursor)
self.kodi_db.remove_album(kodi_id)
def removeArtist(self, kodi_id):
self.artwork.delete_artwork(kodi_id, "artist", self.kodicursor)
self.kodi_db.remove_artist(kodi_id)

View file

@ -0,0 +1,299 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import urllib
import api
import embydb_functions as embydb
import _kodi_musicvideos
from _common import Items, catch_except
from utils import window, settings, language as lang
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class MusicVideos(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 = _kodi_musicvideos.KodiMusicVideos(self.kodicursor)
self.pdialog = pdialog
self.new_time = int(settings('newvideotime'))*1000
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):
# Pull the list of musicvideos in Kodi
views = self.emby_db.getView_byType('musicvideos')
log.info("Media folders: %s", views)
for view in views:
if self.should_stop():
return False
if not self.compare_mvideos(view):
return False
return True
def compare_mvideos(self, view):
view_id = view['id']
view_name = view['name']
if self.pdialog:
self.pdialog.update(heading=lang(29999), message="%s %s..." % (lang(33028), view_name))
mvideos = dict(self.emby_db.get_checksum_by_view('MusicVideo', view_id))
emby_mvideos = self.emby.getMusicVideos(view_id, basic=True, dialog=self.pdialog)
return self.compare("MusicVideo", emby_mvideos['Items'], mvideos, view)
def added(self, items, total=None, view=None):
for item in super(MusicVideos, self).added(items, total):
if self.add_update(item, view):
self.content_pop(item.get('Name', "unknown"))
@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
mvideoid = self.kodi_db.create_entry()
else:
if self.kodi_db.get_musicvideo(mvideoid) is None:
# 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 the music video entry
self.kodi_db.update_musicvideo(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
pathid = self.kodi_db.add_path(path)
# Add the file
fileid = self.kodi_db.add_file(filename, pathid)
# Create the musicvideo entry
self.kodi_db.add_musicvideo(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)
# Update the path
self.kodi_db.update_path(pathid, path, "musicvideos", "metadata.local")
# Update the file
self.kodi_db.update_file(fileid, filename, pathid, dateadded)
# 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.add_people(mvideoid, people, "musicvideo")
# Process genres
self.kodi_db.add_genres(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.add_streams(fileid, streams, runtime)
# Process studios
self.kodi_db.add_studios(mvideoid, studios, "musicvideo")
# Process tags: view, emby tags
tags = [viewtag]
tags.extend(item['Tags'])
if userdata['Favorite']:
tags.append("Favorite musicvideos")
self.kodi_db.add_tags(mvideoid, tags, "musicvideo")
# Process playstates
resume = API.adjust_resume(userdata['Resume'])
total = round(float(runtime), 6)
self.kodi_db.add_playstate(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.get_tag(mvideoid, "Favorite musicvideos", "musicvideo")
else:
self.kodi_db.remove_tag(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.add_playstate(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 pathid: %s", mvideoid, fileid, pathid)
except TypeError:
return
# Remove the emby reference
emby_db.removeItem(itemid)
# Remove artwork
artwork.delete_artwork(mvideoid, "musicvideo", self.kodicursor)
self.kodi_db.remove_musicvideo(mvideoid, fileid)
if self.direct_path:
self.kodi_db.remove_path(pathid)
log.info("Deleted musicvideo %s from kodi database", itemid)

View file

@ -0,0 +1,818 @@
# -*- coding: utf-8 -*-
##################################################################################################
import logging
import urllib
from ntpath import dirname
import api
import embydb_functions as embydb
import _kodi_tvshows
from _common import Items, catch_except
from utils import window, settings, language as lang
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class TVShows(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 = _kodi_tvshows.KodiTVShows(self.kodicursor)
self.pdialog = pdialog
self.new_time = int(settings('newvideotime'))*1000
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 = []
# TODO: Review once series pooling is explicitely returned in api
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)
log.info("TVShows compare finished.")
for kodiepisode in all_kodiepisodes:
if kodiepisode not in all_embyepisodesIds:
self.remove(kodiepisode)
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(self.title)
@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)
showid = self.kodi_db.create_entry()
else:
# Verification the item is still in Kodi
if self.kodi_db.get_tvshow(showid) is None:
# 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()
userdata = API.get_userdata()
# 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
self.kodi_db.update_tvshow(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.add_path(toplevelpath)
self.kodi_db.update_path(toppathid, toplevelpath, "tvshows", "metadata.local")
# Add path
pathid = self.kodi_db.add_path(path)
# Create the tvshow entry
self.kodi_db.add_tvshow(showid, title, plot, rating, premieredate, genre,
title, tvdb, mpaa, studio, sorttitle)
# Create the reference in emby table
emby_db.addReference(itemid, showid, "Series", "tvshow", pathid=pathid,
checksum=checksum, mediafolderid=viewid)
# Link the path
self.kodi_db.link_tvshow(showid, pathid)
# Update the path
self.kodi_db.update_path(pathid, path, None, None)
# Process cast
people = artwork.get_people_artwork(item['People'])
self.kodi_db.add_people(showid, people, "tvshow")
# Process genres
self.kodi_db.add_genres(showid, genres, "tvshow")
# Process artwork
artwork.add_artwork(artwork.get_all_artwork(item), showid, "tvshow", kodicursor)
# Process studios
self.kodi_db.add_studios(showid, studios, "tvshow")
# Process tags: view, emby tags
tags = [viewtag]
tags.extend(item['Tags'])
if userdata['Favorite']:
tags.append("Favorite tvshows")
self.kodi_db.add_tags(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.get_season(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.get_season(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
episodeid = self.kodi_db.create_entry_episode()
else:
# Verification the item is still in Kodi
if self.kodi_db.get_episode(episodeid) is None:
# 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.get_season(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
self.kodi_db.update_episode_16(title, plot, rating, writer, premieredate, runtime,
director, season, episode, title, airsBeforeSeason,
airsBeforeEpisode, seasonid, showid, episodeid)
else:
self.kodi_db.update_episode(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.add_path(path)
# Add the file
fileid = self.kodi_db.add_file(filename, pathid)
# Create the episode entry
if self.kodi_version in (16, 17):
# Kodi Jarvis, Krypton
self.kodi_db.add_episode_16(episodeid, fileid, title, plot, rating, writer,
premieredate, runtime, director, season, episode, title,
showid, airsBeforeSeason, airsBeforeEpisode, seasonid)
else:
self.kodi_db.add_episode(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
self.kodi_db.update_path(pathid, path, None, None)
# Update the file
self.kodi_db.update_file(fileid, filename, pathid, dateadded)
# Process cast
people = artwork.get_people_artwork(item['People'])
self.kodi_db.add_people(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.add_streams(fileid, streams, runtime)
# Process playstates
resume = API.adjust_resume(userdata['Resume'])
total = round(float(runtime), 6)
self.kodi_db.add_playstate(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.get_path("plugin://plugin.video.emby.tvshows/")
tempfileid = self.kodi_db.add_file(filename, temppathid)
self.kodi_db.update_file(tempfileid, filename, temppathid, dateadded)
self.kodi_db.add_playstate(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.get_tag(kodiid, "Favorite tvshows", "tvshow")
else:
self.kodi_db.remove_tag(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.add_playstate(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.get_filename(fileid)
self.kodi_db.remove_file("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.get_filename(fileid)
temppathid = self.kodi_db.get_path("plugin://plugin.video.emby.tvshows/")
tempfileid = self.kodi_db.add_file(filename, temppathid)
self.kodi_db.update_file(tempfileid, filename, temppathid, dateadded)
self.kodi_db.add_playstate(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
kodicursor = self.kodicursor
emby_dbitem = emby_db.getItem_byId(itemid)
try:
kodiid = emby_dbitem[0]
fileid = emby_dbitem[1]
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)
self.kodi_db.remove_tvshow(kodiid)
log.debug("Removed tvshow: %s", kodiid)
def removeSeason(self, kodiid):
kodicursor = self.kodicursor
self.artwork.delete_artwork(kodiid, "season", kodicursor)
self.kodi_db.remove_season(kodiid)
log.debug("Removed season: %s", kodiid)
def removeEpisode(self, kodiid, fileid):
kodicursor = self.kodicursor
self.artwork.delete_artwork(kodiid, "episode", kodicursor)
self.kodi_db.remove_episode(kodiid, fileid)
log.debug("Removed episode: %s", kodiid)