mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2025-05-08 02:15:08 +00:00
2.3.0 (#70)
This commit is contained in:
parent
d582888ffb
commit
ba22e26c06
80 changed files with 11580 additions and 6945 deletions
5
resources/lib/objects/__init__.py
Normal file
5
resources/lib/objects/__init__.py
Normal 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
|
202
resources/lib/objects/_common.py
Normal file
202
resources/lib/objects/_common.py
Normal 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
|
813
resources/lib/objects/_kodi_common.py
Normal file
813
resources/lib/objects/_kodi_common.py
Normal 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,))
|
225
resources/lib/objects/_kodi_movies.py
Normal file
225
resources/lib/objects/_kodi_movies.py
Normal 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,))
|
406
resources/lib/objects/_kodi_music.py
Normal file
406
resources/lib/objects/_kodi_music.py
Normal 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,))
|
66
resources/lib/objects/_kodi_musicvideos.py
Normal file
66
resources/lib/objects/_kodi_musicvideos.py
Normal 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,))
|
170
resources/lib/objects/_kodi_tvshows.py
Normal file
170
resources/lib/objects/_kodi_tvshows.py
Normal 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,))
|
453
resources/lib/objects/movies.py
Normal file
453
resources/lib/objects/movies.py
Normal 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)
|
709
resources/lib/objects/music.py
Normal file
709
resources/lib/objects/music.py
Normal 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)
|
299
resources/lib/objects/musicvideos.py
Normal file
299
resources/lib/objects/musicvideos.py
Normal 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)
|
818
resources/lib/objects/tvshows.py
Normal file
818
resources/lib/objects/tvshows.py
Normal 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)
|
Loading…
Add table
Add a link
Reference in a new issue