mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2025-05-04 16:38:48 +00:00
New hybrid method
This commit is contained in:
parent
7f5084c62e
commit
ace50b34dc
279 changed files with 39526 additions and 19994 deletions
|
@ -1,5 +1,12 @@
|
|||
# Dummy file to make this directory a package.
|
||||
version = "1710760"
|
||||
|
||||
from movies import Movies
|
||||
from musicvideos import MusicVideos
|
||||
from tvshows import TVShows
|
||||
from music import Music
|
||||
from obj import Objects
|
||||
from actions import Actions
|
||||
from actions import PlaylistWorker
|
||||
from actions import on_play, on_update, special_listener
|
||||
|
||||
Objects().mapping()
|
||||
|
|
|
@ -1,207 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sqlite3
|
||||
|
||||
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 sqlite3.Error as error:
|
||||
raise
|
||||
except errors as error:
|
||||
"""
|
||||
if not (hasattr(error, 'quiet') and error.quiet):
|
||||
errStrings = ga.formatException()
|
||||
ga.sendEventData("Exception", errStrings[0], errStrings[1], True)
|
||||
"""
|
||||
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
|
||||
verify_path = path
|
||||
if not os.path.supports_unicode_filenames:
|
||||
verify_path = path.encode('utf-8')
|
||||
|
||||
if window('emby_pathverified') != "true" and not xbmcvfs.exists(verify_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 and (not xbmc.Player().isPlayingVideo() or xbmc.getCondVisibility('VideoPlayer.Content(livetv)')):
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
for item in items:
|
||||
|
||||
if self.should_stop():
|
||||
break
|
||||
|
||||
self.title = item.get('Name', "unknown")
|
||||
|
||||
self.update_pdialog()
|
||||
yield item
|
||||
|
||||
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.keys())
|
||||
|
||||
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
|
|
@ -1,813 +0,0 @@
|
|||
# -*- 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, tag))
|
||||
log.debug("Create idTag: %s name: %s", tag_id, tag)
|
||||
|
||||
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,))
|
|
@ -1,246 +0,0 @@
|
|||
# -*- 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_uniqueid(self):
|
||||
self.cursor.execute("select coalesce(max(uniqueid_id),0) from uniqueid")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def create_entry_rating(self):
|
||||
self.cursor.execute("select coalesce(max(rating_id),0) from rating")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
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):
|
||||
# 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 = ?, 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 get_ratingid(self, media_id):
|
||||
|
||||
query = "SELECT rating_id FROM rating WHERE media_type = ? AND media_id = ?"
|
||||
self.cursor.execute(query, ("movie", media_id,))
|
||||
try:
|
||||
ratingid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
ratingid = None
|
||||
|
||||
return ratingid
|
||||
|
||||
def add_ratings(self, *args):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO rating(
|
||||
rating_id, media_id, media_type, rating_type, rating, votes)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_ratings(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE rating",
|
||||
"SET media_id = ?, media_type = ?, rating_type = ?, rating = ?, votes = ?",
|
||||
"WHERE rating_id = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def get_uniqueid(self, media_id):
|
||||
|
||||
query = "SELECT uniqueid_id FROM uniqueid WHERE media_type = ? AND media_id = ?"
|
||||
self.cursor.execute(query, ("movie", media_id,))
|
||||
try:
|
||||
uniqueid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
uniqueid = None
|
||||
|
||||
return uniqueid
|
||||
|
||||
def add_uniqueid(self, *args):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO uniqueid(
|
||||
uniqueid_id, media_id, media_type, value, type)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_uniqueid(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE uniqueid",
|
||||
"SET media_id = ?, media_type = ?, value = ?, type = ?",
|
||||
"WHERE uniqueid_id = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def add_countries(self, kodi_id, countries):
|
||||
|
||||
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"))
|
||||
|
||||
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 update_boxset(self, set_id, boxset):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE sets",
|
||||
"SET strSet = ?",
|
||||
"WHERE idSet = ?"
|
||||
))
|
||||
self.cursor.execute(query, (boxset, 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,))
|
|
@ -1,455 +0,0 @@
|
|||
# -*- 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):
|
||||
# Krypton has a dummy first entry
|
||||
# idArtist: 1 strArtist: [Missing Tag] strMusicBrainzArtistID: Artist Tag Missing
|
||||
self.cursor.execute("select coalesce(max(idArtist),1) 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, artist_id=None):
|
||||
|
||||
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, artist_id)
|
||||
else:
|
||||
if artist_name != name:
|
||||
self.update_artist_name(artist_id, name)
|
||||
|
||||
return artist_id
|
||||
|
||||
def _add_artist(self, name, musicbrainz, artist_id=None):
|
||||
|
||||
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 = artist_id or 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 = ?",
|
||||
"WHERE idArtist = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_artist_18(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE artist",
|
||||
"SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?,",
|
||||
"lastScraped = ?",
|
||||
"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 validate_artist(self, kodi_id):
|
||||
|
||||
query = "SELECT * FROM artist WHERE idArtist = ?"
|
||||
self.cursor.execute(query, (kodi_id,))
|
||||
try:
|
||||
kodi_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
kodi_id = None
|
||||
|
||||
return kodi_id
|
||||
|
||||
def validate_album(self, kodi_id):
|
||||
|
||||
query = "SELECT * FROM album WHERE idAlbum = ?"
|
||||
self.cursor.execute(query, (kodi_id,))
|
||||
try:
|
||||
kodi_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
kodi_id = None
|
||||
|
||||
return kodi_id
|
||||
|
||||
def validate_song(self, kodi_id):
|
||||
|
||||
query = "SELECT * FROM song WHERE idSong = ?"
|
||||
self.cursor.execute(query, (kodi_id,))
|
||||
try:
|
||||
kodi_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
kodi_id = None
|
||||
|
||||
return kodi_id
|
||||
|
||||
def get_album(self, name, musicbrainz=None, album_id=None):
|
||||
|
||||
if musicbrainz is not None:
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idAlbum",
|
||||
"FROM album",
|
||||
"WHERE strMusicBrainzAlbumID = ?"
|
||||
))
|
||||
self.cursor.execute(query, (musicbrainz,))
|
||||
else:
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT idAlbum",
|
||||
"FROM album",
|
||||
"WHERE strAlbum = ?"
|
||||
))
|
||||
self.cursor.execute(query, (name,))
|
||||
|
||||
try:
|
||||
album_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
album_id = self._add_album(name, musicbrainz, album_id)
|
||||
|
||||
return album_id
|
||||
|
||||
def _add_album(self, name, musicbrainz, album_id=None):
|
||||
|
||||
album_id = album_id or self.create_entry_album()
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strReleaseType)
|
||||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (album_id, name, musicbrainz, "album"))
|
||||
|
||||
return album_id
|
||||
|
||||
def update_album(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_18(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE album",
|
||||
"SET strArtistDisp = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,",
|
||||
"iUserrating = ?, lastScraped = ?, strReleaseType = ?",
|
||||
"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 get_album_artist_18(self, album_id, artists):
|
||||
|
||||
query = ' '.join((
|
||||
|
||||
"SELECT strArtistDisp",
|
||||
"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_18(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 _update_album_artist_18(self, album_id, artists):
|
||||
|
||||
query = "UPDATE album SET strArtistDisp = ? 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_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 add_song_18(self, *args):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO song(
|
||||
idSong, idAlbum, idPath, strArtistDisp, 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 update_song_18(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE song",
|
||||
"SET idAlbum = ?, strArtistDisp = ?, 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,))
|
|
@ -1,90 +0,0 @@
|
|||
# -*- 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, premiered)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def add_musicvideo_16(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 = ?, premiered = ?"
|
||||
"WHERE idMVideo = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_musicvideo_16(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,))
|
|
@ -1,222 +0,0 @@
|
|||
# -*- 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_uniqueid(self):
|
||||
self.cursor.execute("select coalesce(max(uniqueid_id),0) from uniqueid")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
def create_entry_rating(self):
|
||||
self.cursor.execute("select coalesce(max(rating_id),0) from rating")
|
||||
kodi_id = self.cursor.fetchone()[0] + 1
|
||||
|
||||
return kodi_id
|
||||
|
||||
|
||||
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 get_ratingid(self, media_type, media_id):
|
||||
|
||||
query = "SELECT rating_id FROM rating WHERE media_type = ? AND media_id = ?"
|
||||
self.cursor.execute(query, (media_type, media_id,))
|
||||
try:
|
||||
ratingid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
ratingid = None
|
||||
|
||||
return ratingid
|
||||
|
||||
def add_ratings(self, *args):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO rating(
|
||||
rating_id, media_id, media_type, rating_type, rating, votes)
|
||||
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_ratings(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE rating",
|
||||
"SET media_id = ?, media_type = ?, rating_type = ?, rating = ?, votes = ?",
|
||||
"WHERE rating_id = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def get_uniqueid(self, media_type, media_id):
|
||||
|
||||
query = "SELECT uniqueid_id FROM uniqueid WHERE media_type = ? AND media_id = ?"
|
||||
self.cursor.execute(query, (media_type, media_id,))
|
||||
try:
|
||||
uniqueid = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
uniqueid = None
|
||||
|
||||
return uniqueid
|
||||
|
||||
def add_uniqueid(self, *args):
|
||||
query = (
|
||||
'''
|
||||
INSERT INTO uniqueid(uniqueid_id, media_id, media_type, value, type)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
def update_uniqueid(self, *args):
|
||||
query = ' '.join((
|
||||
|
||||
"UPDATE uniqueid",
|
||||
"SET media_id = ?, media_type = ?, value = ?, type = ?",
|
||||
"WHERE uniqueid_id = ?"
|
||||
))
|
||||
self.cursor.execute(query, (args))
|
||||
|
||||
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 name:
|
||||
|
||||
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, 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 = ?, 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,))
|
723
resources/lib/objects/actions.py
Normal file
723
resources/lib/objects/actions.py
Normal file
|
@ -0,0 +1,723 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
#################################################################################################
|
||||
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
import sys
|
||||
from datetime import timedelta
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
import xbmcaddon
|
||||
|
||||
import database
|
||||
from downloader import TheVoid
|
||||
from obj import Objects
|
||||
from helper import _, playutils, api, window, settings, dialog, JSONRPC
|
||||
from dialogs import resume
|
||||
from emby import Emby
|
||||
from utils import get_play_action
|
||||
|
||||
#################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
||||
class Actions(object):
|
||||
|
||||
def __init__(self, server_id=None):
|
||||
|
||||
self.server_id = server_id or None
|
||||
self.server = TheVoid('GetServerAddress', {'ServerId': self.server_id}).get()
|
||||
self.stack = []
|
||||
|
||||
def get_playlist(self, item):
|
||||
|
||||
if item['Type'] == 'Audio':
|
||||
return xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
|
||||
|
||||
return xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
|
||||
def play(self, item, db_id=None, transcode=False):
|
||||
|
||||
''' Play item based on if playback started from widget ot not.
|
||||
To get everything to work together, play the first item in the stack with setResolvedUrl,
|
||||
add the rest to the regular playlist.
|
||||
'''
|
||||
listitem = xbmcgui.ListItem()
|
||||
LOG.info("[ play/%s ] %s", item['Id'], item['Name'])
|
||||
|
||||
playlist = self.get_playlist(item)
|
||||
play = playutils.PlayUtils(item, transcode, self.server_id, self.server)
|
||||
source = play.select_source(play.get_sources())
|
||||
play.set_external_subs(source, listitem)
|
||||
|
||||
self.set_playlist(item, listitem, db_id, transcode)
|
||||
index = max(playlist.getposition(), 0) + 1 # Can return -1
|
||||
force_play = False
|
||||
|
||||
self.stack[0][1].setPath(self.stack[0][0])
|
||||
try:
|
||||
if self.detect_widgets(item):
|
||||
LOG.info(" [ play/widget ]")
|
||||
|
||||
raise IndexError
|
||||
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, self.stack[0][1])
|
||||
self.stack.pop(0)
|
||||
except IndexError:
|
||||
force_play = True
|
||||
|
||||
for stack in self.stack:
|
||||
|
||||
playlist.add(url=stack[0], listitem=stack[1], index=index)
|
||||
index += 1
|
||||
|
||||
if force_play:
|
||||
if len(sys.argv) > 1: xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, self.stack[0][1])
|
||||
xbmc.Player().play(playlist, windowed=False)
|
||||
|
||||
def set_playlist(self, item, listitem, db_id=None, transcode=False):
|
||||
|
||||
''' Verify seektime, set intros, set main item and set additional parts.
|
||||
Detect the seektime for video type content.
|
||||
Verify the default video action set in Kodi for accurate resume behavior.
|
||||
'''
|
||||
seektime = window('emby.resume.bool')
|
||||
window('emby.resume', clear=True)
|
||||
|
||||
if item['Type'] != 'Audio':
|
||||
|
||||
if get_play_action() == "Resume":
|
||||
seektime = True
|
||||
|
||||
if transcode and not seektime:
|
||||
resume = item['UserData'].get('PlaybackPositionTicks')
|
||||
|
||||
if resume:
|
||||
choice = self.resume_dialog(api.API(item, self.server).adjust_resume((resume or 0) / 10000000.0))
|
||||
|
||||
if choice is None:
|
||||
return
|
||||
|
||||
elif not choice:
|
||||
seektime = False
|
||||
|
||||
if settings('enableCinema.bool') and not seektime:
|
||||
self._set_intros(item)
|
||||
|
||||
self.set_listitem(item, listitem, db_id, seektime)
|
||||
playutils.set_properties(item, item['PlaybackInfo']['Method'], self.server_id)
|
||||
self.stack.append([item['PlaybackInfo']['Path'], listitem])
|
||||
|
||||
if item.get('PartCount'):
|
||||
self._set_additional_parts(item['Id'])
|
||||
|
||||
def _set_intros(self, item):
|
||||
|
||||
''' if we have any play them when the movie/show is not being resumed.
|
||||
'''
|
||||
intros = TheVoid('GetIntros', {'ServerId': self.server_id, 'Id': item['Id']}).get()
|
||||
|
||||
if intros['Items']:
|
||||
enabled = True
|
||||
|
||||
if settings('askCinema') == "true":
|
||||
|
||||
resp = dialog("yesno", heading="{emby}", line1=_(33016))
|
||||
if not resp:
|
||||
|
||||
enabled = False
|
||||
LOG.info("Skip trailers.")
|
||||
|
||||
if enabled:
|
||||
for intro in intros['Items']:
|
||||
|
||||
listitem = xbmcgui.ListItem()
|
||||
LOG.info("[ intro/%s ] %s", intro['Id'], intro['Name'])
|
||||
|
||||
play = playutils.PlayUtils(intro, False, self.server_id, self.server)
|
||||
source = play.select_source(play.get_sources())
|
||||
self.set_listitem(intro, listitem)
|
||||
listitem.setPath(intro['PlaybackInfo']['Path'])
|
||||
playutils.set_properties(intro, intro['PlaybackInfo']['Method'], self.server_id)
|
||||
|
||||
self.stack.append([intro['PlaybackInfo']['Path'], listitem])
|
||||
|
||||
window('emby.skip.%s' % intro['Id'], value="true")
|
||||
|
||||
def _set_additional_parts(self, item_id):
|
||||
|
||||
''' Create listitems and add them to the stack of playlist.
|
||||
'''
|
||||
parts = TheVoid('GetAdditionalParts', {'ServerId': self.server_id, 'Id': item_id}).get()
|
||||
|
||||
for part in parts['Items']:
|
||||
|
||||
listitem = xbmcgui.ListItem()
|
||||
LOG.info("[ part/%s ] %s", part['Id'], part['Name'])
|
||||
|
||||
play = playutils.PlayUtils(part, False, self.server_id, self.server)
|
||||
source = play.select_source(play.get_sources())
|
||||
play.set_external_subs(source, listitem)
|
||||
self.set_listitem(part, listitem)
|
||||
listitem.setPath(part['PlaybackInfo']['Path'])
|
||||
playutils.set_properties(part, part['PlaybackInfo']['Method'], self.server_id)
|
||||
|
||||
self.stack.append([part['PlaybackInfo']['Path'], listitem])
|
||||
|
||||
def play_playlist(self, items, clear=True, seektime=None, audio=None, subtitle=None):
|
||||
|
||||
''' Play a list of items. Creates a new playlist.
|
||||
'''
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
started = False
|
||||
|
||||
if clear:
|
||||
playlist.clear()
|
||||
index = 0
|
||||
else:
|
||||
index = max(playlist.getposition(), 0) + 1 # Can return -1
|
||||
|
||||
for order, item in enumerate(items):
|
||||
|
||||
listitem = xbmcgui.ListItem()
|
||||
LOG.info("[ playlist/%s ] %s", item['Id'], item['Name'])
|
||||
|
||||
play = playutils.PlayUtils(item, False, self.server_id, self.server)
|
||||
source = play.select_source(play.get_sources())
|
||||
play.set_external_subs(source, listitem)
|
||||
|
||||
if order == 0: # First item
|
||||
|
||||
item['PlaybackInfo']['AudioStreamIndex'] = audio or item['PlaybackInfo']['AudioStreamIndex']
|
||||
item['PlaybackInfo']['SubtitleStreamIndex'] = subtitle or item['PlaybackInfo'].get('SubtitleStreamIndex')
|
||||
|
||||
self.set_listitem(item, listitem, None, True if order == 0 and seektime else False)
|
||||
listitem.setPath(item['PlaybackInfo']['Path'])
|
||||
playutils.set_properties(item, item['PlaybackInfo']['Method'], self.server_id)
|
||||
|
||||
playlist.add(item['PlaybackInfo']['Path'], listitem, index)
|
||||
index += 1
|
||||
|
||||
if not started and clear:
|
||||
|
||||
started = True
|
||||
xbmc.Player().play(playlist)
|
||||
|
||||
def set_listitem(self, item, listitem, db_id=None, seektime=None):
|
||||
|
||||
objects = Objects()
|
||||
API = api.API(item, self.server)
|
||||
|
||||
if item['Type'] in ('MusicArtist', 'MusicAlbum', 'Audio'):
|
||||
|
||||
obj = objects.map(item, 'BrowseAudio')
|
||||
obj['DbId'] = db_id
|
||||
obj['Artwork'] = API.get_all_artwork(objects.map(item, 'ArtworkMusic'), True)
|
||||
self.listitem_music(obj, listitem, item)
|
||||
|
||||
elif item['Type'] in ('Photo', 'PhotoAlbum'):
|
||||
|
||||
obj = objects.map(item, 'BrowsePhoto')
|
||||
obj['Artwork'] = API.get_all_artwork(objects.map(item, 'Artwork'))
|
||||
self.listitem_photo(obj, listitem, item)
|
||||
|
||||
else:
|
||||
obj = objects.map(item, 'BrowseVideo')
|
||||
obj['DbId'] = db_id
|
||||
obj['Artwork'] = API.get_all_artwork(objects.map(item, 'ArtworkParent'), True)
|
||||
self.listitem_video(obj, listitem, item, seektime)
|
||||
|
||||
listitem.setContentLookup(False)
|
||||
|
||||
def listitem_video(self, obj, listitem, item, seektime=None):
|
||||
|
||||
''' Set listitem for video content. That also include streams.
|
||||
'''
|
||||
API = api.API(item, self.server)
|
||||
is_video = obj['MediaType'] == 'Video'
|
||||
|
||||
obj['Genres'] = " / ".join(obj['Genres'] or [])
|
||||
obj['Studios'] = [API.validate_studio(studio) for studio in (obj['Studios'] or [])]
|
||||
obj['Studios'] = " / ".join(obj['Studios'])
|
||||
obj['People'] = obj['People'] or []
|
||||
obj['Countries'] = " / ".join(obj['Countries'] or [])
|
||||
obj['Directors'] = " / ".join(obj['Directors'] or [])
|
||||
obj['Writers'] = " / ".join(obj['Writers'] or [])
|
||||
obj['Plot'] = API.get_overview(obj['Plot'])
|
||||
obj['ShortPlot'] = API.get_overview(obj['ShortPlot'])
|
||||
obj['DateAdded'] = obj['DateAdded'].split('.')[0].replace('T', " ")
|
||||
obj['Rating'] = obj['Rating'] or 0
|
||||
obj['FileDate'] = "%s.%s.%s" % tuple(reversed(obj['DateAdded'].split('T')[0].split('-')))
|
||||
obj['Runtime'] = round(float((obj['Runtime'] or 0) / 10000000.0), 6)
|
||||
obj['Resume'] = API.adjust_resume((obj['Resume'] or 0) / 10000000.0)
|
||||
obj['PlayCount'] = API.get_playcount(obj['Played'], obj['PlayCount']) or 0
|
||||
obj['Overlay'] = 7 if obj['Played'] else 6
|
||||
obj['Video'] = API.video_streams(obj['Video'] or [], obj['Container'])
|
||||
obj['Audio'] = API.audio_streams(obj['Audio'] or [])
|
||||
obj['Streams'] = API.media_streams(obj['Video'], obj['Audio'], obj['Subtitles'])
|
||||
obj['Artwork']['Primary'] = obj['Artwork']['Primary'] or "special://home/addons/plugin.video.emby/icon.png"
|
||||
obj['Artwork']['Thumb'] = obj['Artwork']['Thumb'] or "special://home/addons/plugin.video.emby/fanart.jpg"
|
||||
obj['Artwork']['Backdrop'] = obj['Artwork']['Backdrop'] or ["special://home/addons/plugin.video.emby/fanart.jpg"]
|
||||
obj['ChildCount'] = obj['ChildCount'] or 0
|
||||
obj['RecursiveCount'] = obj['RecursiveCount'] or 0
|
||||
obj['Unwatched'] = obj['Unwatched'] or 0
|
||||
|
||||
if obj['Premiere']:
|
||||
obj['Premiere'] = obj['Premiere'].split('T')[0]
|
||||
|
||||
if obj['DatePlayed']:
|
||||
obj['DatePlayed'] = obj['DatePlayed'].split('.')[0].replace('T', " ")
|
||||
|
||||
metadata = {
|
||||
'title': obj['Title'],
|
||||
'originaltitle': obj['Title'],
|
||||
'sorttitle': obj['SortTitle'],
|
||||
'country': obj['Countries'],
|
||||
'genre': obj['Genres'],
|
||||
'year': obj['Year'],
|
||||
'rating': obj['Rating'],
|
||||
'playcount': obj['PlayCount'],
|
||||
'overlay': obj['Overlay'],
|
||||
'director': obj['Directors'],
|
||||
'mpaa': obj['Mpaa'],
|
||||
'plot': obj['Plot'],
|
||||
'plotoutline': obj['ShortPlot'],
|
||||
'studio': obj['Studios'],
|
||||
'tagline': obj['Tagline'],
|
||||
'writer': obj['Writers'],
|
||||
'premiered': obj['Premiere'],
|
||||
'aired': obj['Premiere'],
|
||||
'votes': obj['Votes'],
|
||||
'dateadded': obj['DateAdded'],
|
||||
'date': obj['FileDate'],
|
||||
'dbid': obj['DbId']
|
||||
}
|
||||
listitem.setCast(API.get_actors())
|
||||
listitem.setIconImage(obj['Artwork']['Thumb'])
|
||||
listitem.setThumbnailImage(obj['Artwork']['Primary'])
|
||||
self.set_artwork(obj['Artwork'], listitem, obj['Type'])
|
||||
|
||||
if obj['Artwork']['Primary']:
|
||||
listitem.setThumbnailImage(obj['Artwork']['Primary'])
|
||||
|
||||
if not obj['Artwork']['Backdrop']:
|
||||
listitem.setArt({'fanart': obj['Artwork']['Primary']})
|
||||
|
||||
if obj['Premiere']:
|
||||
metadata['premieredate'] = obj['Premiere']
|
||||
metadata['date'] = obj['Premiere']
|
||||
|
||||
|
||||
if obj['Type'] == 'Episode':
|
||||
metadata.update({
|
||||
'mediatype': "episode",
|
||||
'tvshowtitle': obj['SeriesName'],
|
||||
'season': obj['Season'] or 0,
|
||||
'sortseason': obj['Season'] or 0,
|
||||
'episode': obj['Index'] or 0,
|
||||
'sortepisode': obj['Index'] or 0,
|
||||
'lastplayed': obj['DatePlayed'],
|
||||
'duration': obj['Runtime']
|
||||
})
|
||||
|
||||
elif obj['Type'] == 'Season':
|
||||
metadata.update({
|
||||
'mediatype': "season",
|
||||
'tvshowtitle': obj['SeriesName'],
|
||||
'season': obj['Index'] or 0,
|
||||
'sortseason': obj['Index'] or 0
|
||||
})
|
||||
listitem.setProperty('NumEpisodes', str(obj['RecursiveCount']))
|
||||
listitem.setProperty('WatchedEpisodes', str(obj['RecursiveCount'] - obj['Unwatched']))
|
||||
listitem.setProperty('UnWatchedEpisodes', str(obj['Unwatched']))
|
||||
listitem.setProperty('IsFolder', 'true')
|
||||
|
||||
elif obj['Type'] == 'Series':
|
||||
metadata.update({
|
||||
'mediatype': "tvshow",
|
||||
'tvshowtitle': obj['Title']
|
||||
})
|
||||
listitem.setProperty('TotalSeasons', str(obj['ChildCount']))
|
||||
listitem.setProperty('TotalEpisodes', str(obj['RecursiveCount']))
|
||||
listitem.setProperty('WatchedEpisodes', str(obj['RecursiveCount'] - obj['Unwatched']))
|
||||
listitem.setProperty('UnWatchedEpisodes', str(obj['Unwatched']))
|
||||
listitem.setProperty('IsFolder', 'true')
|
||||
|
||||
elif obj['Type'] == 'Movie':
|
||||
metadata.update({
|
||||
'mediatype': "movie",
|
||||
'imdbnumber': obj['UniqueId'],
|
||||
'lastplayed': obj['DatePlayed'],
|
||||
'duration': obj['Runtime']
|
||||
})
|
||||
|
||||
elif obj['Type'] == 'MusicVideo':
|
||||
metadata.update({
|
||||
'mediatype': "musicvideo",
|
||||
'album': obj['Album'],
|
||||
'artist': obj['Artists'],
|
||||
'lastplayed': obj['DatePlayed'],
|
||||
'duration': obj['Runtime']
|
||||
})
|
||||
|
||||
elif obj['Type'] == 'BoxSet':
|
||||
metadata['mediatype'] = "set"
|
||||
listitem.setProperty('IsFolder', 'true')
|
||||
else:
|
||||
metadata.update({
|
||||
'mediatype': "video",
|
||||
'lastplayed': obj['DatePlayed'],
|
||||
'duration': obj['Runtime']
|
||||
})
|
||||
|
||||
|
||||
if is_video:
|
||||
|
||||
listitem.setProperty('totaltime', str(obj['Runtime']))
|
||||
listitem.setProperty('IsPlayable', 'true')
|
||||
listitem.setProperty('IsFolder', 'false')
|
||||
|
||||
if obj['Resume'] and seektime != False:
|
||||
listitem.setProperty('resumetime', str(obj['Resume']))
|
||||
listitem.setProperty('StartPercent', str(((obj['Resume']/obj['Runtime']) * 100) - 0.40))
|
||||
else:
|
||||
listitem.setProperty('resumetime', '0')
|
||||
|
||||
for track in obj['Streams']['video']:
|
||||
listitem.addStreamInfo('video', {
|
||||
'duration': obj['Runtime'],
|
||||
'aspect': track['aspect'],
|
||||
'codec': track['codec'],
|
||||
'width': track['width'],
|
||||
'height': track['height']
|
||||
})
|
||||
|
||||
for track in obj['Streams']['audio']:
|
||||
listitem.addStreamInfo('audio', {'codec': track['codec'], 'channels': track['channels']})
|
||||
|
||||
for track in obj['Streams']['subtitle']:
|
||||
listitem.addStreamInfo('subtitle', {'language': track})
|
||||
|
||||
listitem.setLabel(obj['Title'])
|
||||
listitem.setInfo('video', metadata)
|
||||
listitem.setContentLookup(False)
|
||||
|
||||
def listitem_music(self, obj, listitem, item):
|
||||
API = api.API(item, self.server)
|
||||
|
||||
obj['Runtime'] = round(float((obj['Runtime'] or 0) / 10000000.0), 6)
|
||||
obj['PlayCount'] = API.get_playcount(obj['Played'], obj['PlayCount']) or 0
|
||||
obj['Rating'] = obj['Rating'] or 0
|
||||
|
||||
if obj['FileDate'] or obj['DatePlayed']:
|
||||
obj['DatePlayed'] = (obj['DatePlayed'] or obj['FileDate']).split('.')[0].replace('T', " ")
|
||||
|
||||
obj['FileDate'] = "%s.%s.%s" % tuple(reversed(obj['FileDate'].split('T')[0].split('-')))
|
||||
|
||||
metadata = {
|
||||
'title': obj['Title'],
|
||||
'genre': obj['Genre'],
|
||||
'year': obj['Year'],
|
||||
'album': obj['Album'],
|
||||
'artist': obj['Artists'],
|
||||
'rating': obj['Rating'],
|
||||
'comment': obj['Comment'],
|
||||
'date': obj['FileDate']
|
||||
}
|
||||
self.set_artwork(obj['Artwork'], listitem, obj['Type'])
|
||||
|
||||
if obj['Type'] == 'Audio':
|
||||
metadata.update({
|
||||
'mediatype': "song",
|
||||
'tracknumber': obj['Index'],
|
||||
'discnumber': obj['Disc'],
|
||||
'duration': obj['Runtime'],
|
||||
'playcount': obj['PlayCount'],
|
||||
'lastplayed': obj['DatePlayed'],
|
||||
'musicbrainztrackid': obj['UniqueId']
|
||||
})
|
||||
listitem.setProperty('IsPlayable', 'true')
|
||||
listitem.setProperty('IsFolder', 'false')
|
||||
|
||||
elif obj['Type'] == 'Album':
|
||||
metadata.update({
|
||||
'mediatype': "album",
|
||||
'musicbrainzalbumid': obj['UniqueId']
|
||||
})
|
||||
|
||||
elif obj['Type'] in ('Artist', 'MusicArtist'):
|
||||
metadata.update({
|
||||
'mediatype': "artist",
|
||||
'musicbrainzartistid': obj['UniqueId']
|
||||
})
|
||||
else:
|
||||
metadata['mediatype'] = "music"
|
||||
|
||||
listitem.setLabel(obj['Title'])
|
||||
listitem.setInfo('music', metadata)
|
||||
listitem.setContentLookup(False)
|
||||
|
||||
def listitem_photo(self, obj, listitem, item):
|
||||
API = api.API(item, self.server)
|
||||
|
||||
obj['Overview'] = API.get_overview(obj['Overview'])
|
||||
obj['FileDate'] = "%s.%s.%s" % tuple(reversed(obj['FileDate'].split('T')[0].split('-')))
|
||||
|
||||
metadata = {
|
||||
'title': obj['Title']
|
||||
}
|
||||
listitem.setProperty('path', obj['Artwork']['Primary'])
|
||||
listitem.setThumbnailImage(obj['Artwork']['Primary'])
|
||||
listitem.setIconImage(obj['Artwork']['Primary'] or "special://home/addons/plugin.video.emby/icon.png")
|
||||
listitem.setArt({'fanart': obj['Artwork']['Primary'] or "special://home/addons/plugin.video.emby/fanart.jpg"})
|
||||
|
||||
if obj['Type'] == 'Photo':
|
||||
metadata.update({
|
||||
'picturepath': obj['Artwork']['Primary'],
|
||||
'date': obj['FileDate'],
|
||||
'exif:width': str(obj.get('Width', 0)),
|
||||
'exif:height': str(obj.get('Height', 0)),
|
||||
'size': obj['Size'],
|
||||
'exif:cameramake': obj['CameraMake'],
|
||||
'exif:cameramodel': obj['CameraModel'],
|
||||
'exif:exposuretime': str(obj['ExposureTime']),
|
||||
'exif:focallength': str(obj['FocalLength'])
|
||||
})
|
||||
listitem.setProperty('plot', obj['Overview'])
|
||||
listitem.setProperty('IsFolder', 'false')
|
||||
else:
|
||||
if obj['Artwork']['Backdrop']:
|
||||
listitem.setArt({'fanart': obj['Artwork']['Backdrop'][0]})
|
||||
|
||||
listitem.setProperty('IsFolder', 'true')
|
||||
|
||||
listitem.setProperty('IsPlayable', 'false')
|
||||
listitem.setLabel(obj['Title'])
|
||||
listitem.setInfo('pictures', metadata)
|
||||
listitem.setContentLookup(False)
|
||||
|
||||
def set_artwork(self, artwork, listitem, media):
|
||||
|
||||
if media == 'Episode':
|
||||
|
||||
art = {
|
||||
'poster': "Series.Primary",
|
||||
'tvshow.poster': "Series.Primary",
|
||||
'clearart': "Art",
|
||||
'tvshow.clearart': "Art",
|
||||
'clearlogo': "Logo",
|
||||
'tvshow.clearlogo': "Logo",
|
||||
'discart': "Disc",
|
||||
'fanart_image': "Backdrop",
|
||||
'landscape': "Thumb",
|
||||
'tvshow.landscape': "Thumb",
|
||||
'thumb': "Primary",
|
||||
'fanart': "Backdrop"
|
||||
}
|
||||
elif media in ('Artist', 'Audio', 'MusicAlbum'):
|
||||
|
||||
art = {
|
||||
'clearlogo': "Logo",
|
||||
'discart': "Disc",
|
||||
'fanart': "Backdrop",
|
||||
'fanart_image': "Backdrop", # in case
|
||||
'thumb': "Primary"
|
||||
}
|
||||
else:
|
||||
art = {
|
||||
'poster': "Primary",
|
||||
'clearart': "Art",
|
||||
'clearlogo': "Logo",
|
||||
'discart': "Disc",
|
||||
'fanart_image': "Backdrop",
|
||||
'landscape': "Thumb",
|
||||
'thumb': "Primary",
|
||||
'fanart': "Backdrop"
|
||||
}
|
||||
|
||||
for k_art, e_art in art.items():
|
||||
|
||||
if e_art == "Backdrop":
|
||||
self._set_art(listitem, k_art, artwork[e_art][0] if artwork[e_art] else " ")
|
||||
else:
|
||||
self._set_art(listitem, k_art, artwork.get(e_art, " "))
|
||||
|
||||
def _set_art(self, listitem, art, path):
|
||||
LOG.debug(" [ art/%s ] %s", art, path)
|
||||
|
||||
if art in ('fanart_image', 'small_poster', 'tiny_poster',
|
||||
'medium_landscape', 'medium_poster', 'small_fanartimage',
|
||||
'medium_fanartimage', 'fanart_noindicators', 'discart',
|
||||
'tvshow.poster'):
|
||||
|
||||
listitem.setProperty(art, path)
|
||||
else:
|
||||
listitem.setArt({art: path})
|
||||
|
||||
def resume_dialog(self, seektime):
|
||||
|
||||
''' Base resume dialog based on Kodi settings.
|
||||
'''
|
||||
LOG.info("Resume dialog called.")
|
||||
XML_PATH = (xbmcaddon.Addon('plugin.video.emby').getAddonInfo('path'), "default", "1080i")
|
||||
|
||||
dialog = resume.ResumeDialog("script-emby-resume.xml", *XML_PATH)
|
||||
dialog.set_resume_point("Resume from %s" % str(timedelta(seconds=seektime)).split(".")[0])
|
||||
dialog.doModal()
|
||||
|
||||
if dialog.is_selected():
|
||||
if not dialog.get_selected(): # Start from beginning selected.
|
||||
return False
|
||||
else: # User backed out
|
||||
LOG.info("User exited without a selection.")
|
||||
return
|
||||
|
||||
return True
|
||||
|
||||
def detect_widgets(self, item):
|
||||
|
||||
kodi_version = xbmc.getInfoLabel('System.BuildVersion')
|
||||
|
||||
if kodi_version and "Git:" in kodi_version and kodi_version.split('Git:')[1].split("-")[0] in ('20171119', 'a9a7a20'):
|
||||
LOG.info("Build does not require workaround for widgets?")
|
||||
|
||||
return False
|
||||
|
||||
if (not xbmc.getCondVisibility('Window.IsMedia') and
|
||||
((item['Type'] == 'Audio' and not xbmc.getCondVisibility('Integer.IsGreater(Playlist.Length(music),1)')) or
|
||||
not xbmc.getCondVisibility('Integer.IsGreater(Playlist.Length(video),1)'))):
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class PlaylistWorker(threading.Thread):
|
||||
|
||||
def __init__(self, server_id, items, *args):
|
||||
|
||||
self.server_id = server_id
|
||||
self.items = items
|
||||
self.args = args
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def run(self):
|
||||
Actions(self.server_id).play_playlist(self.items, *self.args)
|
||||
|
||||
|
||||
def on_update(data, server):
|
||||
|
||||
''' Only for manually marking as watched/unwatched
|
||||
'''
|
||||
try:
|
||||
kodi_id = data['item']['id']
|
||||
media = data['item']['type']
|
||||
playcount = int(data['playcount'])
|
||||
LOG.info(" [ update/%s ] kodi_id: %s media: %s", playcount, kodi_id, media)
|
||||
except (KeyError, TypeError):
|
||||
LOG.debug("Invalid playstate update")
|
||||
|
||||
return
|
||||
|
||||
item = database.get_item(kodi_id, media)
|
||||
|
||||
if item:
|
||||
|
||||
if not window('emby.skip.%s.bool' % item[0]):
|
||||
server['api'].item_played(item[0], playcount)
|
||||
|
||||
window('emby.skip.%s' % item[0], clear=True)
|
||||
|
||||
def on_play(data, server):
|
||||
|
||||
''' Setup progress for emby playback.
|
||||
'''
|
||||
player = xbmc.Player()
|
||||
|
||||
try:
|
||||
kodi_id = None
|
||||
|
||||
if player.isPlayingVideo():
|
||||
|
||||
''' Seems to misbehave when playback is not terminated prior to playing new content.
|
||||
The kodi id remains that of the previous title. Maybe onPlay happens before
|
||||
this information is updated. Added a failsafe further below.
|
||||
'''
|
||||
item = player.getVideoInfoTag()
|
||||
kodi_id = item.getDbId()
|
||||
media = item.getMediaType()
|
||||
|
||||
if kodi_id is None or int(kodi_id) == -1 or 'item' in data and 'id' in data['item'] and data['item']['id'] != kodi_id:
|
||||
|
||||
item = data['item']
|
||||
kodi_id = item['id']
|
||||
media = item['type']
|
||||
|
||||
LOG.info(" [ play ] kodi_id: %s media: %s", kodi_id, media)
|
||||
|
||||
except (KeyError, TypeError):
|
||||
LOG.debug("Invalid playstate update")
|
||||
|
||||
return
|
||||
|
||||
if settings('useDirectPaths') == '1' or media == 'song':
|
||||
item = database.get_item(kodi_id, media)
|
||||
|
||||
if item:
|
||||
|
||||
try:
|
||||
file = player.getPlayingFile()
|
||||
except Exception as error:
|
||||
LOG.error(error)
|
||||
|
||||
return
|
||||
|
||||
item = server['api'].get_item(item[0])
|
||||
item['PlaybackInfo'] = {'Path': file}
|
||||
playutils.set_properties(item, 'DirectStream' if settings('useDirectPaths') == '0' else 'DirectPlay')
|
||||
|
||||
def special_listener():
|
||||
|
||||
''' Corner cases that needs to be listened to.
|
||||
This is run in a loop within monitor.py
|
||||
'''
|
||||
player = xbmc.Player()
|
||||
isPlaying = player.isPlaying()
|
||||
count = int(window('emby.external_count') or 0)
|
||||
|
||||
if (not isPlaying and xbmc.getCondVisibility('Window.IsVisible(DialogContextMenu.xml)') and
|
||||
xbmc.getInfoLabel('Control.GetLabel(1002)') == xbmc.getLocalizedString(12021)):
|
||||
|
||||
control = int(xbmcgui.Window(10106).getFocusId())
|
||||
|
||||
if control == 1002: # Start from beginning
|
||||
|
||||
LOG.info("Resume dialog: Start from beginning selected.")
|
||||
window('emby.resume', clear=True)
|
||||
else:
|
||||
LOG.info("Resume dialog: Resume selected.")
|
||||
window('emby.resume.bool', True)
|
||||
|
||||
elif isPlaying and not window('emby.external_check'):
|
||||
time = player.getTime()
|
||||
|
||||
if time > 1: # Not external player.
|
||||
|
||||
window('emby.external_check', value="true")
|
||||
window('emby.external_count', value="0")
|
||||
elif count == 120:
|
||||
|
||||
LOG.info("External player detected.")
|
||||
window('emby.external.bool', True)
|
||||
window('emby.external_check.bool', True)
|
||||
window('emby.external_count', value="0")
|
||||
|
||||
elif time == 0:
|
||||
window('emby.external_count', value=str(count + 1))
|
6
resources/lib/objects/kodi/__init__.py
Normal file
6
resources/lib/objects/kodi/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from kodi import Kodi
|
||||
from movies import Movies
|
||||
from musicvideos import MusicVideos
|
||||
from tvshows import TVShows
|
||||
from music import Music
|
||||
from artwork import Artwork
|
386
resources/lib/objects/kodi/artwork.py
Normal file
386
resources/lib/objects/kodi/artwork.py
Normal file
|
@ -0,0 +1,386 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
#################################################################################################
|
||||
|
||||
import logging
|
||||
import urllib
|
||||
import Queue
|
||||
import threading
|
||||
|
||||
import xbmc
|
||||
import xbmcvfs
|
||||
|
||||
import queries as QU
|
||||
import queries_texture as QUTEX
|
||||
from helper import window, settings
|
||||
from libraries import requests
|
||||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Artwork(object):
|
||||
|
||||
def __init__(self, cursor):
|
||||
|
||||
self.cursor = cursor
|
||||
self.enable_cache = settings('enableTextureCache.bool')
|
||||
self.queue = Queue.Queue()
|
||||
self.threads = []
|
||||
self.kodi = {
|
||||
'username': settings('webServerUser'),
|
||||
'password': settings('webServerPass'),
|
||||
'host': "localhost",
|
||||
'port': settings('webServerPort')
|
||||
}
|
||||
|
||||
|
||||
def update(self, image_url, kodi_id, media, image):
|
||||
|
||||
''' Update artwork in the video database.
|
||||
Only cache artwork if it changed for the main backdrop, poster.
|
||||
Delete current entry before updating with the new one.
|
||||
Cache fanart and poster in Kodi texture cache.
|
||||
'''
|
||||
if not image_url or image == 'poster' and media in ('song', 'artist', 'album'):
|
||||
return
|
||||
|
||||
cache = False
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_art, (kodi_id, media, image,))
|
||||
url = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
|
||||
cache = True
|
||||
LOG.debug("ADD to kodi_id %s art: %s", kodi_id, image_url)
|
||||
self.cursor.execute(QU.add_art, (kodi_id, media, image, image_url))
|
||||
else:
|
||||
if url != image_url:
|
||||
cache = True
|
||||
|
||||
if image in ('fanart', 'poster'):
|
||||
self.delete_cache(url)
|
||||
|
||||
LOG.info("UPDATE to kodi_id %s art: %s", kodi_id, image_url)
|
||||
self.cursor.execute(QU.update_art, (image_url, kodi_id, media, image))
|
||||
|
||||
if cache and image in ('fanart', 'poster'):
|
||||
self.cache(image_url)
|
||||
|
||||
def add(self, artwork, *args):
|
||||
|
||||
''' Add all artworks.
|
||||
'''
|
||||
KODI = {
|
||||
'Primary': ['thumb', 'poster'],
|
||||
'Banner': "banner",
|
||||
'Logo': "clearlogo",
|
||||
'Art': "clearart",
|
||||
'Thumb': "landscape",
|
||||
'Disc': "discart",
|
||||
'Backdrop': "fanart"
|
||||
}
|
||||
|
||||
for art in KODI:
|
||||
|
||||
if art == 'Backdrop':
|
||||
self.cursor.execute(QU.get_backdrops, args + ("fanart%",))
|
||||
|
||||
if len(self.cursor.fetchall()) > len(artwork['Backdrop']):
|
||||
self.cursor.execute(QU.delete_backdrops, args + ("fanart_",))
|
||||
|
||||
for index, backdrop in enumerate(artwork['Backdrop']):
|
||||
|
||||
if index:
|
||||
self.update(*(backdrop,) + args + ("%s%s" % ("fanart", index),))
|
||||
else:
|
||||
self.update(*(backdrop,) + args + ("fanart",))
|
||||
|
||||
elif art == 'Primary':
|
||||
for kodi_image in KODI['Primary']:
|
||||
self.update(*(artwork['Primary'],) + args + (kodi_image,))
|
||||
|
||||
elif artwork.get(art):
|
||||
self.update(*(artwork[art],) + args + (KODI[art],))
|
||||
|
||||
def delete(self, *args):
|
||||
|
||||
''' Delete artwork from kodi database and remove cache for backdrop/posters.
|
||||
'''
|
||||
self.cursor.execute(QU.get_art_url, args)
|
||||
|
||||
for row in self.cursor.fetchall():
|
||||
if row[1] in ('poster', 'fanart'):
|
||||
self.delete_cache(row[0])
|
||||
|
||||
def cache(self, url):
|
||||
|
||||
''' Cache a single image to texture cache.
|
||||
'''
|
||||
if not url or not self.enable_cache:
|
||||
return
|
||||
|
||||
url = self.double_urlencode(url)
|
||||
self.queue.put(url)
|
||||
self.add_worker()
|
||||
|
||||
def double_urlencode(self, text):
|
||||
|
||||
text = self.single_urlencode(text)
|
||||
text = self.single_urlencode(text)
|
||||
|
||||
return text
|
||||
|
||||
def single_urlencode(self, text):
|
||||
|
||||
''' urlencode needs a utf-string.
|
||||
return the result as unicode
|
||||
'''
|
||||
text = urllib.urlencode({'blahblahblah': text.encode('utf-8')})
|
||||
text = text[13:]
|
||||
|
||||
return text.decode('utf-8')
|
||||
|
||||
def add_worker(self):
|
||||
|
||||
for thread in self.threads:
|
||||
if thread.is_done:
|
||||
self.threads.remove(thread)
|
||||
|
||||
if self.queue.qsize() and len(self.threads) < 3:
|
||||
|
||||
new_thread = GetArtworkWorker(self.kodi, self.queue)
|
||||
new_thread.start()
|
||||
LOG.info("-->[ q:artwork/%s ]", id(new_thread))
|
||||
self.threads.append(new_thread)
|
||||
|
||||
def delete_cache(self, url):
|
||||
|
||||
''' Delete cached artwork.
|
||||
'''
|
||||
from database import Database
|
||||
|
||||
with Database('texture') as texturedb:
|
||||
|
||||
try:
|
||||
texturedb.cursor.execute(QUTEX.get_cache, (url,))
|
||||
cached = texturedb.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
LOG.debug("Could not find cached url: %s", url)
|
||||
else:
|
||||
thumbnails = xbmc.translatePath("special://thumbnails/%s" % cached).decode('utf-8')
|
||||
xbmcvfs.delete(thumbnails)
|
||||
texturedb.cursor.execute(QUTEX.delete_cache, (url,))
|
||||
LOG.info("DELETE cached %s", cached)
|
||||
|
||||
|
||||
class GetArtworkWorker(threading.Thread):
|
||||
|
||||
is_done = False
|
||||
|
||||
def __init__(self, kodi, queue):
|
||||
|
||||
self.kodi = kodi
|
||||
self.queue = queue
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def run(self):
|
||||
|
||||
''' Prepare the request. Request removes the urlencode which is required in this case.
|
||||
Use a session allows to use a pool of connections.
|
||||
'''
|
||||
with requests.Session() as s:
|
||||
while True:
|
||||
|
||||
try:
|
||||
url = self.queue.get(timeout=2)
|
||||
except Queue.Empty:
|
||||
|
||||
self.is_done = True
|
||||
LOG.info("--<[ q:artwork/%s ]", id(self))
|
||||
|
||||
return
|
||||
|
||||
try:
|
||||
req = requests.Request(method='HEAD',
|
||||
url="http://%s:%s/image/image://%s" % (self.kodi['host'], self.kodi['port'], url),
|
||||
auth=(self.kodi['username'], self.kodi['password']))
|
||||
prep = req.prepare()
|
||||
prep.url = "http://%s:%s/image/image://%s" % (self.kodi['host'], self.kodi['port'], url)
|
||||
s.send(prep, timeout=(0.01, 0.01))
|
||||
s.content # release the connection
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.queue.task_done()
|
||||
|
||||
if xbmc.Monitor().abortRequested():
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#################################################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
import urllib
|
||||
from sqlite3 import OperationalError
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
import xbmcvfs
|
||||
import requests
|
||||
|
||||
import resources.lib.image_cache_thread as image_cache_thread
|
||||
from resources.lib.helper import _, window, settings, JSONRPC
|
||||
from resources.lib.database import Database
|
||||
from __objs__ import QU
|
||||
|
||||
##################################################################################################
|
||||
|
||||
log = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Artwork(object):
|
||||
|
||||
xbmc_host = 'localhost'
|
||||
xbmc_port = None
|
||||
xbmc_username = None
|
||||
xbmc_password = None
|
||||
|
||||
image_cache_threads = []
|
||||
image_cache_limit = 0
|
||||
|
||||
|
||||
def __init__(self, server):
|
||||
|
||||
self.server = server
|
||||
self.enable_texture_cache = settings('enableTextureCache') == "true"
|
||||
self.image_cache_limit = int(settings('imageCacheLimit')) * 5
|
||||
log.debug("image cache thread count: %s", self.image_cache_limit)
|
||||
|
||||
if not self.xbmc_port and self.enable_texture_cache:
|
||||
self._set_webserver_details()
|
||||
|
||||
|
||||
def texture_cache_sync(self):
|
||||
# This method will sync all Kodi artwork to textures13.db
|
||||
# and cache them locally. This takes diskspace!
|
||||
if not dialog(type_="yesno",
|
||||
heading="{emby}",
|
||||
line1=_(33042)):
|
||||
return
|
||||
|
||||
log.info("Doing Image Cache Sync")
|
||||
|
||||
pdialog = xbmcgui.DialogProgress()
|
||||
pdialog.create(_(29999), _(33043))
|
||||
|
||||
# ask to rest all existing or not
|
||||
if dialog(type_="yesno", heading="{emby}", line1=_(33044)):
|
||||
log.info("Resetting all cache data first")
|
||||
self.delete_cache()
|
||||
|
||||
# Cache all entries in video DB
|
||||
self._cache_all_video_entries(pdialog)
|
||||
# Cache all entries in music DB
|
||||
self._cache_all_music_entries(pdialog)
|
||||
|
||||
pdialog.update(100, "%s %s" % (_(33046), len(self.image_cache_threads)))
|
||||
log.info("Waiting for all threads to exit")
|
||||
|
||||
while len(self.image_cache_threads):
|
||||
for thread in self.image_cache_threads:
|
||||
if thread.is_finished:
|
||||
self.image_cache_threads.remove(thread)
|
||||
pdialog.update(100, "%s %s" % (_(33046), len(self.image_cache_threads)))
|
||||
log.info("Waiting for all threads to exit: %s", len(self.image_cache_threads))
|
||||
xbmc.sleep(500)
|
||||
|
||||
pdialog.close()
|
||||
|
||||
@classmethod
|
||||
def delete_cache(cls):
|
||||
# Remove all existing textures first
|
||||
path = xbmc.translatePath('special://thumbnails/').decode('utf-8')
|
||||
if xbmcvfs.exists(path):
|
||||
dirs, ignore_files = xbmcvfs.listdir(path)
|
||||
for directory in dirs:
|
||||
ignore_dirs, files = xbmcvfs.listdir(path + directory)
|
||||
for file_ in files:
|
||||
|
||||
if os.path.supports_unicode_filenames:
|
||||
filename = os.path.join(path + directory.decode('utf-8'),
|
||||
file_.decode('utf-8'))
|
||||
else:
|
||||
filename = os.path.join(path.encode('utf-8') + directory, file_)
|
||||
|
||||
xbmcvfs.delete(filename)
|
||||
log.debug("deleted: %s", filename)
|
||||
|
||||
# remove all existing data from texture DB
|
||||
with DatabaseConn('texture') as cursor_texture:
|
||||
cursor_texture.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
|
||||
rows = cursor_texture.fetchall()
|
||||
for row in rows:
|
||||
table_name = row[0]
|
||||
if table_name != "version":
|
||||
cursor_texture.execute("DELETE FROM " + table_name)
|
||||
|
||||
def _cache_all_video_entries(self, pdialog):
|
||||
|
||||
with Database('video') as cursor_video:
|
||||
|
||||
cursor_video.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors
|
||||
result = cursor_video.fetchall()
|
||||
total = len(result)
|
||||
log.info("Image cache sync about to process %s images", total)
|
||||
cursor_video.close()
|
||||
|
||||
count = 0
|
||||
for url in result:
|
||||
|
||||
if pdialog.iscanceled():
|
||||
break
|
||||
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
message = "%s of %s (%s)" % (count, total, len(self.image_cache_threads))
|
||||
pdialog.update(percentage, "%s %s" % (_(33045), message))
|
||||
self.cache_texture(url[0])
|
||||
count += 1
|
||||
|
||||
def _cache_all_music_entries(self, pdialog):
|
||||
|
||||
with Database('music') as cursor_music:
|
||||
|
||||
cursor_music.execute("SELECT url FROM art")
|
||||
result = cursor_music.fetchall()
|
||||
total = len(result)
|
||||
|
||||
log.info("Image cache sync about to process %s images", total)
|
||||
|
||||
count = 0
|
||||
for url in result:
|
||||
|
||||
if pdialog.iscanceled():
|
||||
break
|
||||
|
||||
percentage = int((float(count) / float(total))*100)
|
||||
message = "%s of %s" % (count, total)
|
||||
pdialog.update(percentage, "%s %s" % (_(33045), message))
|
||||
self.cache_texture(url[0])
|
||||
count += 1
|
||||
|
||||
"""
|
||||
|
297
resources/lib/objects/kodi/kodi.py
Normal file
297
resources/lib/objects/kodi/kodi.py
Normal file
|
@ -0,0 +1,297 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
import xbmc
|
||||
|
||||
import artwork
|
||||
import queries as QU
|
||||
from helper import values
|
||||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Kodi(object):
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.artwork = artwork.Artwork(self.cursor)
|
||||
|
||||
def create_entry_path(self):
|
||||
self.cursor.execute(QU.create_path)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_file(self):
|
||||
self.cursor.execute(QU.create_file)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_person(self):
|
||||
self.cursor.execute(QU.create_person)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_genre(self):
|
||||
self.cursor.execute(QU.create_genre)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_studio(self):
|
||||
self.cursor.execute(QU.create_studio)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_bookmark(self):
|
||||
self.cursor.execute(QU.create_bookmark)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_tag(self):
|
||||
self.cursor.execute(QU.create_tag)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def add_path(self, *args):
|
||||
path_id = self.get_path(*args)
|
||||
|
||||
if path_id is None:
|
||||
|
||||
path_id = self.create_entry_path()
|
||||
self.cursor.execute(QU.add_path, (path_id,) + args)
|
||||
|
||||
return path_id
|
||||
|
||||
def get_path(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_path, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def update_path(self, *args):
|
||||
self.cursor.execute(QU.update_path, args)
|
||||
|
||||
def remove_path(self, *args):
|
||||
self.cursor.execute(QU.delete_path, args)
|
||||
|
||||
def add_file(self, filename, path_id):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_file, (filename, path_id,))
|
||||
file_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
|
||||
file_id = self.create_entry_file()
|
||||
self.cursor.execute(QU.add_file, (file_id, path_id, filename))
|
||||
|
||||
return file_id
|
||||
|
||||
def update_file(self, *args):
|
||||
self.cursor.execute(QU.update_file, args)
|
||||
|
||||
def remove_file(self, path, *args):
|
||||
path_id = self.get_path(path)
|
||||
|
||||
if path_id is not None:
|
||||
self.cursor.execute(QU.delete_file_by_path, (path_id,) + args)
|
||||
|
||||
def get_filename(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_filename, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return ""
|
||||
|
||||
def add_people(self, people, *args):
|
||||
|
||||
def add_thumbnail(person_id, person, person_type):
|
||||
|
||||
if person['imageurl']:
|
||||
|
||||
art = person_type.lower()
|
||||
if "writing" in art:
|
||||
art = "writer"
|
||||
|
||||
self.artwork.update(person['imageurl'], person_id, art, "thumb")
|
||||
|
||||
def add_link(link, person_id):
|
||||
self.cursor.execute(QU.update_link.replace("{LinkType}", link), (person_id,) + args)
|
||||
|
||||
cast_order = 1
|
||||
|
||||
for person in people:
|
||||
person_id = self.get_person(person['Name'])
|
||||
|
||||
if person['Type'] == 'Actor':
|
||||
|
||||
role = person.get('Role')
|
||||
self.cursor.execute(QU.update_actor, (person_id,) + args + (role, cast_order,))
|
||||
cast_order += 1
|
||||
|
||||
elif person['Type'] == 'Director':
|
||||
add_link('director_link', person_id)
|
||||
|
||||
elif person['Type'] == 'Writer':
|
||||
add_link('writer_link', person_id)
|
||||
|
||||
elif person['Type'] == 'Artist':
|
||||
add_link('actor_link', person_id)
|
||||
|
||||
add_thumbnail(person_id, person, person['Type'])
|
||||
|
||||
def add_person(self, *args):
|
||||
|
||||
person_id = self.create_entry_person()
|
||||
self.cursor.execute(QU.add_person, (person_id,) + args)
|
||||
|
||||
return person_id
|
||||
|
||||
def get_person(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_person, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return self.add_person(*args)
|
||||
|
||||
def add_genres(self, genres, *args):
|
||||
|
||||
''' Delete current genres first for clean slate.
|
||||
'''
|
||||
self.cursor.execute(QU.delete_genres, args)
|
||||
|
||||
for genre in genres:
|
||||
self.cursor.execute(QU.update_genres, (self.get_genre(genre),) + args)
|
||||
|
||||
def add_genre(self, *args):
|
||||
|
||||
genre_id = self.create_entry_genre()
|
||||
self.cursor.execute(QU.add_genre, (genre_id,) + args)
|
||||
|
||||
return genre_id
|
||||
|
||||
def get_genre(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_genre, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return self.add_genre(*args)
|
||||
|
||||
def add_studios(self, studios, *args):
|
||||
|
||||
for studio in studios:
|
||||
|
||||
studio_id = self.get_studio(studio)
|
||||
self.cursor.execute(QU.update_studios, (studio_id,) + args)
|
||||
|
||||
def add_studio(self, *args):
|
||||
|
||||
studio_id = self.create_entry_studio()
|
||||
self.cursor.execute(QU.add_studio, (studio_id,) + args)
|
||||
|
||||
return studio_id
|
||||
|
||||
def get_studio(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_studio, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return self.add_studio(*args)
|
||||
|
||||
def add_streams(self, file_id, streams, runtime):
|
||||
|
||||
''' First remove any existing entries
|
||||
Then re-add video, audio and subtitles.
|
||||
'''
|
||||
self.cursor.execute(QU.delete_streams, (file_id,))
|
||||
|
||||
if streams:
|
||||
for track in streams['video']:
|
||||
|
||||
track['FileId'] = file_id
|
||||
track['Runtime'] = runtime
|
||||
self.add_stream_video(*values(track, QU.add_stream_video_obj))
|
||||
|
||||
for track in streams['audio']:
|
||||
|
||||
track['FileId'] = file_id
|
||||
self.add_stream_audio(*values(track, QU.add_stream_audio_obj))
|
||||
|
||||
for track in streams['subtitle']:
|
||||
self.add_stream_sub(*values({'language': track, 'FileId': file_id}, QU.add_stream_sub_obj))
|
||||
|
||||
def add_stream_video(self, *args):
|
||||
self.cursor.execute(QU.add_stream_video, args)
|
||||
|
||||
def add_stream_audio(self, *args):
|
||||
self.cursor.execute(QU.add_stream_audio, args)
|
||||
|
||||
def add_stream_sub(self, *args):
|
||||
self.cursor.execute(QU.add_stream_sub, args)
|
||||
|
||||
def add_playstate(self, file_id, playcount, date_played, resume, *args):
|
||||
|
||||
''' Delete the existing resume point.
|
||||
Set the watched count.
|
||||
'''
|
||||
self.cursor.execute(QU.delete_bookmark, (file_id,))
|
||||
self.set_playcount(playcount, date_played, file_id)
|
||||
|
||||
if resume:
|
||||
|
||||
bookmark_id = self.create_entry_bookmark()
|
||||
self.cursor.execute(QU.add_bookmark, (bookmark_id, file_id, resume,) + args)
|
||||
|
||||
def set_playcount(self, *args):
|
||||
self.cursor.execute(QU.update_playcount, args)
|
||||
|
||||
def add_tags(self, tags, *args):
|
||||
self.cursor.execute(QU.delete_tags, args)
|
||||
|
||||
for tag in tags:
|
||||
tag_id = self.get_tag(tag, *args)
|
||||
|
||||
def add_tag(self, *args):
|
||||
|
||||
tag_id = self.create_entry_tag()
|
||||
self.cursor.execute(QU.add_tag, (tag_id,) + args)
|
||||
|
||||
return tag_id
|
||||
|
||||
def get_tag(self, tag, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_tag, (tag,))
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
tag_id = self.add_tag(tag)
|
||||
|
||||
self.cursor.execute(QU.update_tag, (tag_id,) + args)
|
||||
|
||||
return tag_id
|
||||
|
||||
def remove_tag(self, tag, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_tag, (tag,))
|
||||
tag_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
self.cursor.execute(QU.delete_tag, (tag,) + args)
|
149
resources/lib/objects/kodi/movies.py
Normal file
149
resources/lib/objects/kodi/movies.py
Normal file
|
@ -0,0 +1,149 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
from kodi import Kodi
|
||||
import queries as QU
|
||||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Movies(Kodi):
|
||||
|
||||
|
||||
def __init__(self, cursor):
|
||||
|
||||
self.cursor = cursor
|
||||
Kodi.__init__(self)
|
||||
|
||||
def create_entry_unique_id(self):
|
||||
self.cursor.execute(QU.create_unique_id)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_rating(self):
|
||||
self.cursor.execute(QU.create_rating)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry(self):
|
||||
self.cursor.execute(QU.create_movie)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_set(self):
|
||||
self.cursor.execute(QU.create_set)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_country(self):
|
||||
self.cursor.execute(QU.create_country)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def get(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_movie, args)
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def add(self, *args):
|
||||
self.cursor.execute(QU.add_movie, args)
|
||||
|
||||
def update(self, *args):
|
||||
self.cursor.execute(QU.update_movie, args)
|
||||
|
||||
def delete(self, kodi_id, file_id):
|
||||
|
||||
self.cursor.execute(QU.delete_movie, (kodi_id,))
|
||||
self.cursor.execute(QU.delete_file, (file_id,))
|
||||
|
||||
def get_rating_id(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_rating, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
def add_ratings(self, *args):
|
||||
|
||||
''' Add ratings, rating type and votes.
|
||||
'''
|
||||
self.cursor.execute(QU.add_rating, args)
|
||||
|
||||
def update_ratings(self, *args):
|
||||
|
||||
''' Update rating by rating_id.
|
||||
'''
|
||||
self.cursor.execute(QU.update_rating, args)
|
||||
|
||||
def get_unique_id(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_unique_id, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def add_unique_id(self, *args):
|
||||
|
||||
''' Add the provider id, imdb, tvdb.
|
||||
'''
|
||||
self.cursor.execute(QU.add_unique_id, args)
|
||||
|
||||
def update_unique_id(self, *args):
|
||||
|
||||
''' Update the provider id, imdb, tvdb.
|
||||
'''
|
||||
self.cursor.execute(QU.update_unique_id, args)
|
||||
|
||||
def add_countries(self, countries, *args):
|
||||
|
||||
for country in countries:
|
||||
self.cursor.execute(QU.update_country, (self.get_country(country),) + args)
|
||||
|
||||
def add_country(self, *args):
|
||||
|
||||
country_id = self.create_entry_country()
|
||||
self.cursor.execute(QU.add_country, (country_id,) + args)
|
||||
|
||||
return country_id
|
||||
|
||||
def get_country(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_country, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return self.add_country(*args)
|
||||
|
||||
def add_boxset(self, *args):
|
||||
|
||||
set_id = self.create_entry_set()
|
||||
self.cursor.execute(QU.add_set, (set_id,) + args)
|
||||
|
||||
return set_id
|
||||
|
||||
def update_boxset(self, *args):
|
||||
self.cursor.execute(QU.update_set, args)
|
||||
|
||||
def set_boxset(self, *args):
|
||||
self.cursor.execute(QU.update_movie_set, args)
|
||||
|
||||
def remove_from_boxset(self, *args):
|
||||
self.cursor.execute(QU.delete_movie_set, args)
|
||||
|
||||
def delete_boxset(self, *args):
|
||||
self.cursor.execute(QU.delete_set, args)
|
223
resources/lib/objects/kodi/music.py
Normal file
223
resources/lib/objects/kodi/music.py
Normal file
|
@ -0,0 +1,223 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
import queries_music as QU
|
||||
from kodi import Kodi
|
||||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Music(Kodi):
|
||||
|
||||
|
||||
def __init__(self, cursor):
|
||||
|
||||
self.cursor = cursor
|
||||
Kodi.__init__(self)
|
||||
|
||||
def create_entry(self):
|
||||
|
||||
''' Krypton has a dummy first entry
|
||||
idArtist: 1 strArtist: [Missing Tag] strMusicBrainzArtistID: Artist Tag Missing
|
||||
'''
|
||||
self.cursor.execute(QU.create_artist)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_album(self):
|
||||
self.cursor.execute(QU.create_album)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_song(self):
|
||||
self.cursor.execute(QU.create_song)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_genre(self):
|
||||
self.cursor.execute(QU.create_genre)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def update_path(self, *args):
|
||||
self.cursor.execute(QU.update_path, args)
|
||||
|
||||
def add_role(self, *args):
|
||||
self.cursor.execute(QU.update_role, args)
|
||||
|
||||
def get(self, artist_id, name, musicbrainz):
|
||||
|
||||
''' Get artist or create the entry.
|
||||
'''
|
||||
try:
|
||||
self.cursor.execute(QU.get_artist, (musicbrainz,))
|
||||
result = self.cursor.fetchone()
|
||||
artist_id = result[0]
|
||||
artist_name = result[1]
|
||||
except TypeError:
|
||||
artist_id = self.add_artist(artist_id, name, musicbrainz)
|
||||
else:
|
||||
if artist_name != name:
|
||||
self.update_artist_name(artist_id, name)
|
||||
|
||||
return artist_id
|
||||
|
||||
def add_artist(self, artist_id, name, *args):
|
||||
|
||||
''' Safety check, when musicbrainz does not exist
|
||||
'''
|
||||
try:
|
||||
self.cursor.execute(QU.get_artist_by_name, (name,))
|
||||
artist_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
artist_id = artist_id or self.create_entry()
|
||||
self.cursor.execute(QU.add_artist, (artist_id, name,) + args)
|
||||
|
||||
return artist_id
|
||||
|
||||
def update_artist_name(self, *args):
|
||||
self.cursor.execute(QU.update_artist_name, args)
|
||||
|
||||
def update(self, *args):
|
||||
self.cursor.execute(QU.update_artist, args)
|
||||
|
||||
def link(self, *args):
|
||||
self.cursor.execute(QU.update_link, args)
|
||||
|
||||
def add_discography(self, *args):
|
||||
self.cursor.execute(QU.update_discography, args)
|
||||
|
||||
def validate_artist(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_artist_by_id, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def validate_album(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_album_by_id, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def validate_song(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_song_by_id, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def get_album(self, album_id, name, musicbrainz, *args):
|
||||
|
||||
if musicbrainz is not None:
|
||||
self.cursor.execute(QU.get_album, (musicbrainz,))
|
||||
else:
|
||||
self.cursor.execute(QU.get_album_by_name, (name,))
|
||||
|
||||
try:
|
||||
album_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
album_id = self.add_album(*(album_id, name, musicbrainz,) + args)
|
||||
|
||||
return album_id
|
||||
|
||||
def add_album(self, album_id, *args):
|
||||
|
||||
album_id = album_id or self.create_entry_album()
|
||||
self.cursor.execute(QU.add_album, (album_id,) + args)
|
||||
|
||||
return album_id
|
||||
|
||||
def update_album(self, *args):
|
||||
self.cursor.execute(QU.update_album, args)
|
||||
|
||||
def get_album_artist(self, album_id, artists):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_album_artist, (album_id,))
|
||||
curr_artists = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
if curr_artists != artists:
|
||||
self.update_album_artist(artists, album_id)
|
||||
|
||||
def update_album_artist(self, *args):
|
||||
self.cursor.execute(QU.update_album_artist, args)
|
||||
|
||||
def add_single(self, *args):
|
||||
self.cursor.execute(QU.add_single, args)
|
||||
|
||||
def add_song(self, *args):
|
||||
self.cursor.execute(QU.add_song, args)
|
||||
|
||||
def update_song(self, *args):
|
||||
self.cursor.execute(QU.update_song, args)
|
||||
|
||||
def link_song_artist(self, *args):
|
||||
self.cursor.execute(QU.update_song_artist, args)
|
||||
|
||||
def link_song_album(self, *args):
|
||||
self.cursor.execute(QU.update_song_album, args)
|
||||
|
||||
def rate_song(self, *args):
|
||||
self.cursor.execute(QU.update_song_rating, args)
|
||||
|
||||
def add_genres(self, kodi_id, genres, media):
|
||||
|
||||
''' Add genres, but delete current genres first.
|
||||
'''
|
||||
if media == 'album':
|
||||
self.cursor.execute(QU.delete_genres_album, (kodi_id,))
|
||||
|
||||
for genre in genres:
|
||||
|
||||
genre_id = self.get_genre(genre)
|
||||
self.cursor.execute(QU.update_genre_album, (genre_id, kodi_id))
|
||||
|
||||
elif media == 'song':
|
||||
self.cursor.execute(QU.delete_genres_song, (kodi_id,))
|
||||
|
||||
for genre in genres:
|
||||
|
||||
genre_id = self.get_genre(genre)
|
||||
self.cursor.execute(QU.update_genre_song, (genre_id, kodi_id))
|
||||
|
||||
def get_genre(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_genre, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return self.add_genre(*args)
|
||||
|
||||
def add_genre(self, *args):
|
||||
|
||||
genre_id = self.create_entry_genre()
|
||||
self.cursor.execute(QU.add_genre, (genre_id,) + args)
|
||||
|
||||
return genre_id
|
||||
|
||||
def delete(self, *args):
|
||||
self.cursor.execute(QU.delete_artist, args)
|
||||
|
||||
def delete_album(self, *args):
|
||||
self.cursor.execute(QU.delete_album, args)
|
||||
|
||||
def delete_song(self, *args):
|
||||
self.cursor.execute(QU.delete_song, args)
|
48
resources/lib/objects/kodi/musicvideos.py
Normal file
48
resources/lib/objects/kodi/musicvideos.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
import queries as QU
|
||||
from kodi import Kodi
|
||||
|
||||
##################################################################################################
|
||||
|
||||
log = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class MusicVideos(Kodi):
|
||||
|
||||
|
||||
def __init__(self, cursor):
|
||||
|
||||
self.cursor = cursor
|
||||
Kodi.__init__(self)
|
||||
|
||||
def create_entry(self):
|
||||
self.cursor.execute(QU.create_musicvideo)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def get(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_musicvideo, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def add(self, *args):
|
||||
self.cursor.execute(QU.add_musicvideo, args)
|
||||
|
||||
def update(self, *args):
|
||||
self.cursor.execute(QU.update_musicvideo, args)
|
||||
|
||||
def delete(self, kodi_id, file_id):
|
||||
|
||||
self.cursor.execute(QU.delete_musicvideo, (kodi_id,))
|
||||
self.cursor.execute(QU.delete_file, (file_id,))
|
550
resources/lib/objects/kodi/queries.py
Normal file
550
resources/lib/objects/kodi/queries.py
Normal file
|
@ -0,0 +1,550 @@
|
|||
|
||||
''' Queries for the Kodi database. obj reflect key/value to retrieve from emby items.
|
||||
Some functions require additional information, therefore obj do not always reflect
|
||||
the Kodi database query values.
|
||||
'''
|
||||
create_path = """ SELECT coalesce(max(idPath), 0)
|
||||
FROM path
|
||||
"""
|
||||
create_file = """ SELECT coalesce(max(idFile), 0)
|
||||
FROM files
|
||||
"""
|
||||
create_person = """ SELECT coalesce(max(actor_id), 0)
|
||||
FROM actor
|
||||
"""
|
||||
create_genre = """ SELECT coalesce(max(genre_id), 0)
|
||||
FROM genre
|
||||
"""
|
||||
create_studio = """ SELECT coalesce(max(studio_id), 0)
|
||||
FROM studio
|
||||
"""
|
||||
create_bookmark = """ SELECT coalesce(max(idBookmark), 0)
|
||||
FROM bookmark
|
||||
"""
|
||||
create_tag = """ SELECT coalesce(max(tag_id), 0)
|
||||
FROM tag
|
||||
"""
|
||||
create_unique_id = """ SELECT coalesce(max(uniqueid_id), 0)
|
||||
FROM uniqueid
|
||||
"""
|
||||
create_rating = """ SELECT coalesce(max(rating_id), 0)
|
||||
FROM rating
|
||||
"""
|
||||
create_movie = """ SELECT coalesce(max(idMovie), 0)
|
||||
FROM movie
|
||||
"""
|
||||
create_set = """ SELECT coalesce(max(idSet), 0)
|
||||
FROM sets
|
||||
"""
|
||||
create_country = """ SELECT coalesce(max(country_id), 0)
|
||||
FROM country
|
||||
"""
|
||||
create_musicvideo = """ SELECT coalesce(max(idMVideo), 0)
|
||||
FROM musicvideo
|
||||
"""
|
||||
create_tvshow = """ SELECT coalesce(max(idShow), 0)
|
||||
FROM tvshow
|
||||
"""
|
||||
create_season = """ SELECT coalesce(max(idSeason), 0)
|
||||
FROM seasons
|
||||
"""
|
||||
create_episode = """ SELECT coalesce(max(idEpisode), 0)
|
||||
FROM episode
|
||||
"""
|
||||
|
||||
|
||||
get_path = """ SELECT idPath
|
||||
FROM path
|
||||
WHERE strPath = ?
|
||||
"""
|
||||
get_path_obj = [ "{Path}"
|
||||
]
|
||||
get_file = """ SELECT idFile
|
||||
FROM files
|
||||
WHERE idPath = ?
|
||||
AND strFilename = ?
|
||||
"""
|
||||
get_file_obj = [ "{FileId}"
|
||||
]
|
||||
get_filename = """ SELECT strFilename
|
||||
FROM files
|
||||
WHERE idFile = ?
|
||||
"""
|
||||
get_person = """ SELECT actor_id
|
||||
FROM actor
|
||||
WHERE name = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_genre = """ SELECT genre_id
|
||||
FROM genre
|
||||
WHERE name = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_studio = """ SELECT studio_id
|
||||
FROM studio
|
||||
WHERE name = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_tag = """ SELECT tag_id
|
||||
FROM tag
|
||||
WHERE name = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_tag_movie_obj = [ "{MovieId}", "Favorite movies", "movie"
|
||||
]
|
||||
get_tag_mvideo_obj = [ "{MvideoId}", "Favorite musicvideos", "musicvideo"
|
||||
]
|
||||
get_tag_episode_obj = [ "{KodiId}", "Favorite tvshows", "tvshow"
|
||||
]
|
||||
get_art = """ SELECT url
|
||||
FROM art
|
||||
WHERE media_id = ?
|
||||
AND media_type = ?
|
||||
AND type = ?
|
||||
"""
|
||||
get_movie = """ SELECT *
|
||||
FROM movie
|
||||
WHERE idMovie = ?
|
||||
"""
|
||||
get_movie_obj = [ "{MovieId}"
|
||||
]
|
||||
get_rating = """ SELECT rating_id
|
||||
FROM rating
|
||||
WHERE media_type = ?
|
||||
AND media_id = ?
|
||||
"""
|
||||
get_rating_movie_obj = [ "movie","{MovieId}"
|
||||
]
|
||||
get_rating_episode_obj = [ "episode","{EpisodeId}"
|
||||
]
|
||||
get_unique_id = """ SELECT uniqueid_id
|
||||
FROM uniqueid
|
||||
WHERE media_type = ?
|
||||
AND media_id = ?
|
||||
"""
|
||||
get_unique_id_movie_obj = [ "movie","{MovieId}"
|
||||
]
|
||||
get_unique_id_tvshow_obj = [ "tvshow","{ShowId}"
|
||||
]
|
||||
get_unique_id_episode_obj = [ "episode","{EpisodeId}"
|
||||
]
|
||||
get_country = """ SELECT country_id
|
||||
FROM country
|
||||
WHERE name = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_set = """ SELECT idSet
|
||||
FROM sets
|
||||
WHERE strSet = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_musicvideo = """ SELECT *
|
||||
FROM musicvideo
|
||||
WHERE idMVideo = ?
|
||||
"""
|
||||
get_musicvideo_obj = [ "{MvideoId}"
|
||||
]
|
||||
get_tvshow = """ SELECT *
|
||||
FROM tvshow
|
||||
WHERE idShow = ?
|
||||
"""
|
||||
get_tvshow_obj = [ "{ShowId}"
|
||||
]
|
||||
get_episode = """ SELECT *
|
||||
FROM episode
|
||||
WHERE idEpisode = ?
|
||||
"""
|
||||
get_episode_obj = [ "{EpisodeId}"
|
||||
]
|
||||
get_season = """ SELECT idSeason
|
||||
FROM seasons
|
||||
WHERE idShow = ?
|
||||
AND season = ?
|
||||
"""
|
||||
get_season_obj = [ "{Title}","{ShowId}","{Index}"
|
||||
]
|
||||
get_season_special_obj = [ None,"{ShowId}",-1
|
||||
]
|
||||
get_season_episode_obj = [ None,"{ShowId}","{Season}"
|
||||
]
|
||||
get_backdrops = """ SELECT url
|
||||
FROM art
|
||||
WHERE media_id = ?
|
||||
AND media_type = ?
|
||||
AND type LIKE ?
|
||||
"""
|
||||
get_art = """ SELECT url
|
||||
FROM art
|
||||
WHERE media_id = ?
|
||||
AND media_type = ?
|
||||
AND type = ?
|
||||
"""
|
||||
get_art_url = """ SELECT url, type
|
||||
FROM art
|
||||
WHERE media_id = ?
|
||||
AND media_type = ?
|
||||
"""
|
||||
get_show_by_unique_id = """ SELECT idShow
|
||||
FROM tvshow_view
|
||||
WHERE uniqueid_value = ?
|
||||
"""
|
||||
|
||||
get_total_episodes = """ SELECT totalCount
|
||||
FROM tvshowcounts
|
||||
WHERE idShow = ?
|
||||
"""
|
||||
get_total_episodes_obj = [ "{ParentId}"
|
||||
]
|
||||
|
||||
|
||||
|
||||
add_path = """ INSERT INTO path(idPath, strPath)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
add_path_obj = [ "{Path}"
|
||||
]
|
||||
add_file = """ INSERT INTO files(idFile, idPath, strFilename)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
add_file_obj = [ "{PathId}","{Filename}"
|
||||
]
|
||||
add_person = """ INSERT INTO actor(actor_id, name)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
add_people_movie_obj = [ "{People}","{MovieId}","movie"
|
||||
]
|
||||
add_people_mvideo_obj = [ "{People}","{MvideoId}","musicvideo"
|
||||
]
|
||||
add_people_tvshow_obj = [ "{People}","{ShowId}","tvshow"
|
||||
]
|
||||
add_people_episode_obj = [ "{People}","{EpisodeId}","episode"
|
||||
]
|
||||
add_actor_link = """ INSERT INTO actor_link(actor_id, media_id, media_type, role, cast_order)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_link = """ INSERT INTO {LinkType}(actor_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
add_genre = """ INSERT INTO genre(genre_id, name)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
add_genres_movie_obj = [ "{Genres}","{MovieId}","movie"
|
||||
]
|
||||
add_genres_mvideo_obj = [ "{Genres}","{MvideoId}","musicvideo"
|
||||
]
|
||||
add_genres_tvshow_obj = [ "{Genres}","{ShowId}","tvshow"
|
||||
]
|
||||
add_studio = """ INSERT INTO studio(studio_id, name)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
add_studios_movie_obj = [ "{Studios}","{MovieId}","movie"
|
||||
]
|
||||
add_studios_mvideo_obj = [ "{Studios}","{MvideoId}","musicvideo"
|
||||
]
|
||||
add_studios_tvshow_obj = [ "{Studios}","{ShowId}","tvshow"
|
||||
]
|
||||
add_bookmark = """ INSERT INTO bookmark(idBookmark, idFile, timeInSeconds, totalTimeInSeconds, player, type)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_bookmark_obj = [ "{FileId}","{PlayCount}","{DatePlayed}","{Resume}","{Runtime}","DVDPlayer",1
|
||||
]
|
||||
add_streams_obj = [ "{FileId}","{Streams}","{Runtime}"
|
||||
]
|
||||
add_stream_video = """ INSERT INTO streamdetails(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth,
|
||||
iVideoHeight, iVideoDuration, strStereoMode)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_stream_video_obj = [ "{FileId}",0,"{codec}","{aspect}","{width}","{height}","{Runtime}","{3d}"
|
||||
]
|
||||
add_stream_audio = """ INSERT INTO streamdetails(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_stream_audio_obj = [ "{FileId}",1,"{codec}","{channels}","{language}"
|
||||
]
|
||||
add_stream_sub = """ INSERT INTO streamdetails(idFile, iStreamType, strSubtitleLanguage)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
add_stream_sub_obj = [ "{FileId}",2,"{language}"
|
||||
]
|
||||
add_tag = """ INSERT INTO tag(tag_id, name)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
add_tags_movie_obj = [ "{Tags}","{MovieId}","movie"
|
||||
]
|
||||
add_tags_mvideo_obj = [ "{Tags}","{MvideoId}","musicvideo"
|
||||
]
|
||||
add_tags_tvshow_obj = [ "{Tags}","{ShowId}","tvshow"
|
||||
]
|
||||
add_art = """ INSERT INTO art(media_id, media_type, type, url)
|
||||
VALUES (?, ?, ?, ?)
|
||||
"""
|
||||
add_movie = """ 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_movie_obj = [ "{MovieId}","{FileId}","{Title}","{Plot}","{ShortPlot}","{Tagline}",
|
||||
"{Votes}","{RatingId}","{Writers}","{Year}","{Unique}","{SortTitle}",
|
||||
"{Runtime}","{Mpaa}","{Genre}","{Directors}","{Title}","{Studio}",
|
||||
"{Trailer}","{Country}","{Year}"
|
||||
]
|
||||
add_rating = """ INSERT INTO rating(rating_id, media_id, media_type, rating_type, rating, votes)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_rating_movie_obj = [ "{RatingId}","{MovieId}","movie","default","{Rating}","{Votes}"
|
||||
]
|
||||
add_rating_tvshow_obj = [ "{RatingId}","{ShowId}","tvshow","default","{Rating}","{Votes}"
|
||||
]
|
||||
add_rating_episode_obj = [ "{RatingId}","{EpisodeId}","episode","default","{Rating}","{Votes}"
|
||||
]
|
||||
add_unique_id = """ INSERT INTO uniqueid(uniqueid_id, media_id, media_type, value, type)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_unique_id_movie_obj = [ "{Unique}","{MovieId}","movie","{UniqueId}","{ProviderName}"
|
||||
]
|
||||
add_unique_id_tvshow_obj = [ "{Unique}","{ShowId}","tvshow","{UniqueId}","{ProviderName}"
|
||||
]
|
||||
add_unique_id_episode_obj = [ "{Unique}","{EpisodeId}","episode","{UniqueId}","{ProviderName}"
|
||||
]
|
||||
add_country = """ INSERT INTO country(country_id, name)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
add_set = """ INSERT INTO sets(idSet, strSet, strOverview)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
add_set_obj = [ "{Title}","{Overview}"
|
||||
]
|
||||
add_musicvideo = """ INSERT INTO musicvideo(idMVideo,idFile, c00, c04, c05, c06, c07, c08, c09, c10,
|
||||
c11, c12, premiered)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_musicvideo_obj = [ "{MvideoId}","{FileId}","{Title}","{Runtime}","{Directors}","{Studio}","{Year}",
|
||||
"{Plot}","{Album}","{Artists}","{Genre}","{Index}","{Premiere}"
|
||||
]
|
||||
add_tvshow = """ INSERT INTO tvshow(idShow, c00, c01, c04, c05, c08, c09, c12, c13, c14, c15)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_tvshow_obj = [ "{ShowId}","{Title}","{Plot}","{RatingId}","{Premiere}","{Genre}","{Title}",
|
||||
"{Unique}","{Mpaa}","{Studio}","{SortTitle}"
|
||||
]
|
||||
add_season = """ INSERT INTO seasons(idSeason, idShow, season)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
add_episode = """ INSERT INTO episode(idEpisode, idFile, c00, c01, c03, c04, c05, c09, c10, c12, c13, c14,
|
||||
idShow, c15, c16, idSeason)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_episode_obj = [ "{EpisodeId}","{FileId}","{Title}","{Plot}","{RatingId}","{Writers}","{Premiere}","{Runtime}",
|
||||
"{Directors}","{Season}","{Index}","{Title}","{ShowId}","{AirsBeforeSeason}",
|
||||
"{AirsBeforeEpisode}","{SeasonId}"
|
||||
]
|
||||
add_art = """ INSERT INTO art(media_id, media_type, type, url)
|
||||
VALUES (?, ?, ?, ?)
|
||||
"""
|
||||
|
||||
|
||||
|
||||
update_path = """ UPDATE path
|
||||
SET strPath = ?, strContent = ?, strScraper = ?, noUpdate = ?
|
||||
WHERE idPath = ?
|
||||
"""
|
||||
update_path_movie_obj = [ "{Path}","movies","metadata.local",1,"{PathId}"
|
||||
]
|
||||
update_path_toptvshow_obj = [ "{TopLevel}","tvshows","metadata.local",1,"{TopPathId}"
|
||||
]
|
||||
update_path_tvshow_obj = [ "{Path}",None,None,1,"{PathId}"
|
||||
]
|
||||
update_path_episode_obj = [ "{Path}",None,None,1,"{PathId}"
|
||||
]
|
||||
update_path_mvideo_obj = [ "{Path}","musicvideos","metadata.local",1,"{PathId}"
|
||||
]
|
||||
update_file = """ UPDATE files
|
||||
SET idPath = ?, strFilename = ?, dateAdded = ?
|
||||
WHERE idFile = ?
|
||||
"""
|
||||
update_file_obj = [ "{PathId}","{Filename}","{DateAdded}","{FileId}"
|
||||
]
|
||||
update_genres = """ INSERT OR REPLACE INTO genre_link(genre_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
update_studios = """ INSERT OR REPLACE INTO studio_link(studio_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
update_playcount = """ UPDATE files
|
||||
SET playCount = ?, lastPlayed = ?
|
||||
WHERE idFile = ?
|
||||
"""
|
||||
update_tag = """ INSERT OR REPLACE INTO tag_link(tag_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
update_art = """ UPDATE art
|
||||
SET url = ?
|
||||
WHERE media_id = ?
|
||||
AND media_type = ?
|
||||
AND type = ?
|
||||
"""
|
||||
update_actor = """ INSERT OR REPLACE INTO actor_link(actor_id, media_id, media_type, role, cast_order)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
|
||||
update_link = """ INSERT OR REPLACE INTO {LinkType}(actor_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
update_movie = """ UPDATE movie
|
||||
SET c00 = ?, c01 = ?, c02 = ?, c03 = ?, c04 = ?, c05 = ?, c06 = ?,
|
||||
c07 = ?, c09 = ?, c10 = ?, c11 = ?, c12 = ?, c14 = ?, c15 = ?,
|
||||
c16 = ?, c18 = ?, c19 = ?, c21 = ?, premiered = ?
|
||||
WHERE idMovie = ?
|
||||
"""
|
||||
update_movie_obj = [ "{Title}","{Plot}","{ShortPlot}","{Tagline}","{Votes}","{RatingId}",
|
||||
"{Writers}","{Year}","{Unique}","{SortTitle}","{Runtime}",
|
||||
"{Mpaa}","{Genre}","{Directors}","{Title}","{Studio}","{Trailer}",
|
||||
"{Country}","{Year}","{MovieId}"
|
||||
]
|
||||
update_rating = """ UPDATE rating
|
||||
SET media_id = ?, media_type = ?, rating_type = ?, rating = ?, votes = ?
|
||||
WHERE rating_id = ?
|
||||
"""
|
||||
update_rating_movie_obj = [ "{MovieId}","movie","default","{Rating}","{Votes}","{RatingId}"
|
||||
]
|
||||
update_rating_tvshow_obj = [ "{ShowId}","tvshow","default","{Rating}","{Votes}","{RatingId}"
|
||||
]
|
||||
update_rating_episode_obj = [ "{EpisodeId}","episode","default","{Rating}","{Votes}","{RatingId}"
|
||||
]
|
||||
update_unique_id = """ UPDATE uniqueid
|
||||
SET media_id = ?, media_type = ?, value = ?, type = ?
|
||||
WHERE uniqueid_id = ?
|
||||
"""
|
||||
update_unique_id_movie_obj = [ "{MovieId}","movie","{UniqueId}","{ProviderName}","{Unique}"
|
||||
]
|
||||
update_unique_id_tvshow_obj = [ "{ShowId}","tvshow","{UniqueId}","{ProviderName}","{Unique}"
|
||||
]
|
||||
update_unique_id_episode_obj = [ "{EpisodeId}","episode","{UniqueId}","{ProviderName}","{Unique}"
|
||||
]
|
||||
update_country = """ INSERT OR REPLACE INTO country_link(country_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
update_country_obj = [ "{Countries}","{MovieId}","movie"
|
||||
]
|
||||
update_set = """ UPDATE sets
|
||||
SET strSet = ?, strOverview = ?
|
||||
WHERE idSet = ?
|
||||
"""
|
||||
update_set_obj = [ "{Title}", "{Overview}", "{SetId}"
|
||||
]
|
||||
update_movie_set = """ UPDATE movie
|
||||
SET idSet = ?
|
||||
WHERE idMovie = ?
|
||||
"""
|
||||
update_movie_set_obj = [ "{SetId}","{MovieId}"
|
||||
]
|
||||
update_musicvideo = """ UPDATE musicvideo
|
||||
SET c00 = ?, c04 = ?, c05 = ?, c06 = ?, c07 = ?, c08 = ?, c09 = ?, c10 = ?,
|
||||
c11 = ?, c12 = ?, premiered = ?
|
||||
WHERE idMVideo = ?
|
||||
"""
|
||||
update_musicvideo_obj = [ "{Title}","{Runtime}","{Directors}","{Studio}","{Year}","{Plot}","{Album}",
|
||||
"{Artists}","{Genre}","{Index}","{Premiere}","{MvideoId}"
|
||||
]
|
||||
update_tvshow = """ UPDATE tvshow
|
||||
SET c00 = ?, c01 = ?, c04 = ?, c05 = ?, c08 = ?, c09 = ?,
|
||||
c12 = ?, c13 = ?, c14 = ?, c15 = ?
|
||||
WHERE idShow = ?
|
||||
"""
|
||||
update_tvshow_obj = [ "{ShowId}","{Title}","{Plot}","{RatingId}","{Premiere}","{Genre}","{Title}",
|
||||
"{Unique}","{Mpaa}","{Studio}","{SortTitle}"
|
||||
]
|
||||
update_tvshow_link = """ INSERT OR REPLACE INTO tvshowlinkpath(idShow, idPath)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
update_tvshow_link_obj = [ "{ShowId}","{PathId}"
|
||||
]
|
||||
update_season = """ UPDATE seasons
|
||||
SET name = ?
|
||||
WHERE idSeason = ?
|
||||
"""
|
||||
update_episode = """ UPDATE episode
|
||||
SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?, c10 = ?,
|
||||
c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?, idSeason = ?, idShow = ?
|
||||
WHERE idEpisode = ?
|
||||
"""
|
||||
update_episode_obj = [ "{Title}","{Plot}","{RatingId}","{Writers}","{Premiere}","{Runtime}","{Directors}",
|
||||
"{Season}","{Index}","{Title}","{AirsBeforeSeason}","{AirsBeforeEpisode}","{SeasonId}",
|
||||
"{ShowId}","{EpisodeId}"
|
||||
]
|
||||
|
||||
|
||||
|
||||
delete_path = """ DELETE FROM path
|
||||
WHERE idPath = ?
|
||||
"""
|
||||
delete_path_obj = [ "{PathId}"
|
||||
]
|
||||
delete_file = """ DELETE FROM files
|
||||
WHERE idFile = ?
|
||||
"""
|
||||
delete_file_obj = [ "{Path}","{Filename}"
|
||||
]
|
||||
delete_file_by_path = """ DELETE FROM files
|
||||
WHERE idPath = ?
|
||||
AND strFileName = ?
|
||||
"""
|
||||
delete_genres = """ DELETE FROM genre_link
|
||||
WHERE media_id = ?
|
||||
AND media_type = ?
|
||||
"""
|
||||
delete_bookmark = """ DELETE FROM bookmark
|
||||
WHERE idFile = ?
|
||||
"""
|
||||
delete_streams = """ DELETE FROM streamdetails
|
||||
WHERE idFile = ?
|
||||
"""
|
||||
delete_tags = """ DELETE FROM tag_link
|
||||
WHERE media_id = ?
|
||||
AND media_type = ?
|
||||
"""
|
||||
delete_tag = """ DELETE FROM tag_link
|
||||
WHERE tag_id = ?
|
||||
AND media_type = ?
|
||||
AND media_id = ?
|
||||
"""
|
||||
delete_tag_movie_obj = [ "{MovieId}","Favorite movies","movie"
|
||||
]
|
||||
delete_tag_mvideo_obj = [ "{MvideoId}","Favorite musicvideos","musicvideo"
|
||||
]
|
||||
delete_tag_episode_obj = [ "{KodiId}","Favorite tvshows","tvshow"
|
||||
]
|
||||
delete_movie = """ DELETE FROM movie
|
||||
WHERE idMovie = ?
|
||||
"""
|
||||
delete_movie_obj = [ "{KodiId}","{FileId}"
|
||||
]
|
||||
delete_set = """ DELETE FROM sets
|
||||
WHERE idSet = ?
|
||||
"""
|
||||
delete_set_obj = [ "{KodiId}"
|
||||
]
|
||||
delete_movie_set = """ UPDATE movie
|
||||
SET idSet = null
|
||||
WHERE idMovie = ?
|
||||
"""
|
||||
delete_movie_set_obj = [ "{MovieId}"
|
||||
]
|
||||
delete_musicvideo = """ DELETE FROM musicvideo
|
||||
WHERE idMVideo = ?
|
||||
"""
|
||||
delete_musicvideo_obj = [ "{MvideoId}", "{FileId}"
|
||||
]
|
||||
delete_tvshow = """ DELETE FROM tvshow
|
||||
WHERE idShow = ?
|
||||
"""
|
||||
delete_season = """ DELETE FROM seasons
|
||||
WHERE idSeason = ?
|
||||
"""
|
||||
delete_episode = """ DELETE FROM episode
|
||||
WHERE idEpisode = ?
|
||||
"""
|
||||
delete_backdrops = """ DELETE FROM art
|
||||
WHERE media_id = ?
|
||||
AND media_type = ?
|
||||
AND type LIKE ?
|
||||
"""
|
197
resources/lib/objects/kodi/queries_music.py
Normal file
197
resources/lib/objects/kodi/queries_music.py
Normal file
|
@ -0,0 +1,197 @@
|
|||
|
||||
create_artist = """ SELECT coalesce(max(idArtist), 1)
|
||||
FROM artist
|
||||
"""
|
||||
create_album = """ SELECT coalesce(max(idAlbum), 0)
|
||||
FROM album
|
||||
"""
|
||||
create_song = """ SELECT coalesce(max(idSong), 0)
|
||||
FROM song
|
||||
"""
|
||||
create_genre = """ SELECT coalesce(max(idGenre), 0)
|
||||
FROM genre
|
||||
"""
|
||||
|
||||
|
||||
|
||||
get_artist = """ SELECT idArtist, strArtist
|
||||
FROM artist
|
||||
WHERE strMusicBrainzArtistID = ?
|
||||
"""
|
||||
get_artist_obj = [ "{ArtistId}","{Name}","{UniqueId}"
|
||||
]
|
||||
get_artist_by_name = """ SELECT idArtist
|
||||
FROM artist
|
||||
WHERE strArtist = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_artist_by_id = """ SELECT *
|
||||
FROM artist
|
||||
WHERE idArtist = ?
|
||||
"""
|
||||
get_artist_by_id_obj = [ "{ArtistId}"
|
||||
]
|
||||
get_album_by_id = """ SELECT *
|
||||
FROM album
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
get_album_by_id_obj = [ "{AlbumId}"
|
||||
]
|
||||
get_song_by_id = """ SELECT *
|
||||
FROM song
|
||||
WHERE idSong = ?
|
||||
"""
|
||||
get_song_by_id_obj = [ "{SongId}"
|
||||
]
|
||||
get_album = """ SELECT idAlbum
|
||||
FROM album
|
||||
WHERE strMusicBrainzAlbumID = ?
|
||||
"""
|
||||
get_album_obj = [ "{AlbumId}","{Title}","{UniqueId}","album"
|
||||
]
|
||||
get_album_by_name = """ SELECT idAlbum
|
||||
FROM album
|
||||
WHERE strAlbum = ?
|
||||
"""
|
||||
get_album_artist = """ SELECT strArtists
|
||||
FROM album
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
get_album_artist_obj = [ "{AlbumId}","{strAlbumArtists}"
|
||||
]
|
||||
get_genre = """ SELECT idGenre
|
||||
FROM genre
|
||||
WHERE strGenre = ?
|
||||
COLLATE NOCASE
|
||||
"""
|
||||
get_total_episodes = """ SELECT totalCount
|
||||
FROM tvshowcounts
|
||||
WHERE idShow = ?
|
||||
"""
|
||||
|
||||
|
||||
|
||||
add_artist = """ INSERT INTO artist(idArtist, strArtist, strMusicBrainzArtistID)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
add_album = """ INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strReleaseType)
|
||||
VALUES (?, ?, ?, ?)
|
||||
"""
|
||||
add_single = """ INSERT INTO album(idAlbum, strGenres, iYear, strReleaseType)
|
||||
VALUES (?, ?, ?, ?)
|
||||
"""
|
||||
add_single_obj = [ "{AlbumId}","{Genre}","{Year}","single"
|
||||
]
|
||||
add_song = """ INSERT INTO song(idSong, idAlbum, idPath, strArtists, strGenres, strTitle, iTrack,
|
||||
iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed,
|
||||
rating, comment, dateAdded)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
add_song_obj = [ "{SongId}","{AlbumId}","{PathId}","{Artists}","{Genre}","{Title}","{Index}",
|
||||
"{Runtime}","{Year}","{Filename}","{UniqueId}","{PlayCount}","{DatePlayed}","{Rating}",
|
||||
"{Comment}","{DateAdded}"
|
||||
]
|
||||
add_genre = """ INSERT INTO genre(idGenre, strGenre)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
add_genres_obj = [ "{AlbumId}","{Genres}","album"
|
||||
]
|
||||
|
||||
|
||||
|
||||
update_path = """ UPDATE path
|
||||
SET strPath = ?
|
||||
WHERE idPath = ?
|
||||
"""
|
||||
update_path_obj = [ "{Path}","{PathId}"
|
||||
]
|
||||
update_role = """ INSERT OR REPLACE INTO role(idRole, strRole)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
update_role_obj = [ 1,"Composer"
|
||||
]
|
||||
update_artist_name = """ UPDATE artist
|
||||
SET strArtist = ?
|
||||
WHERE idArtist = ?
|
||||
"""
|
||||
update_artist_name_obj = [ "{Name}","{ArtistId}"
|
||||
]
|
||||
update_artist = """ UPDATE artist
|
||||
SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?, lastScraped = ?
|
||||
WHERE idArtist = ?
|
||||
"""
|
||||
update_link = """ INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
update_link_obj = [ "{ArtistId}","{AlbumId}","{Name}"
|
||||
]
|
||||
update_discography = """ INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
|
||||
VALUES (?, ?, ?)
|
||||
"""
|
||||
update_discography_obj = [ "{ArtistId}","{Title}","{Year}"
|
||||
]
|
||||
update_album = """ UPDATE album
|
||||
SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,
|
||||
iUserrating = ?, lastScraped = ?, strReleaseType = ?
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
update_album_obj = [ "{Artists}","{Year}","{Genre}","{Bio}","{Thumb}","{Rating}","{LastScraped}",
|
||||
"album","{AlbumId}"
|
||||
]
|
||||
update_album_artist = """ UPDATE album
|
||||
SET strArtists = ?
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
update_song = """ UPDATE song
|
||||
SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?,
|
||||
iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,
|
||||
rating = ?, comment = ?, dateAdded = ?
|
||||
WHERE idSong = ?
|
||||
"""
|
||||
update_song_obj = [ "{AlbumId}","{Artists}","{Genre}","{Title}","{Index}","{Runtime}","{Year}",
|
||||
"{Filename}","{PlayCount}","{DatePlayed}","{Rating}","{Comment}",
|
||||
"{DateAdded}","{SongId}"
|
||||
]
|
||||
update_song_artist = """ INSERT OR REPLACE INTO song_artist(idArtist, idSong, idRole, iOrder, strArtist)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
update_song_artist_obj = [ "{ArtistId}","{SongId}",1,"{Index}","{Name}"
|
||||
]
|
||||
update_song_album = """ INSERT OR REPLACE INTO albuminfosong(idAlbumInfoSong, idAlbumInfo, iTrack,
|
||||
strTitle, iDuration)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
update_song_album_obj = [ "{SongId}","{AlbumId}","{Index}","{Title}","{Runtime}"
|
||||
]
|
||||
update_song_rating = """ UPDATE song
|
||||
SET iTimesPlayed = ?, lastplayed = ?, rating = ?
|
||||
WHERE idSong = ?
|
||||
"""
|
||||
update_song_rating_obj = [ "{PlayCount}","{DatePlayed}","{Rating}","{KodiId}"
|
||||
]
|
||||
update_genre_album = """ INSERT OR REPLACE INTO album_genre(idGenre, idAlbum)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
update_genre_song = """ INSERT OR REPLACE INTO song_genre(idGenre, idSong)
|
||||
VALUES (?, ?)
|
||||
"""
|
||||
update_genre_song_obj = [ "{SongId}","{Genres}","song"
|
||||
]
|
||||
|
||||
|
||||
|
||||
delete_genres_album = """ DELETE FROM album_genre
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
delete_genres_song = """ DELETE FROM song_genre
|
||||
WHERE idSong = ?
|
||||
"""
|
||||
delete_artist = """ DELETE FROM artist
|
||||
WHERE idArtist = ?
|
||||
"""
|
||||
delete_album = """ DELETE FROM album
|
||||
WHERE idAlbum = ?
|
||||
"""
|
||||
delete_song = """ DELETE FROM song
|
||||
WHERE idSong = ?
|
||||
"""
|
11
resources/lib/objects/kodi/queries_texture.py
Normal file
11
resources/lib/objects/kodi/queries_texture.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
get_cache = """ SELECT cachedurl
|
||||
FROM texture
|
||||
WHERE url = ?
|
||||
"""
|
||||
|
||||
|
||||
|
||||
delete_cache = """ DELETE FROM texture
|
||||
WHERE url = ?
|
||||
"""
|
156
resources/lib/objects/kodi/tvshows.py
Normal file
156
resources/lib/objects/kodi/tvshows.py
Normal file
|
@ -0,0 +1,156 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
import queries as QU
|
||||
from kodi import Kodi
|
||||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class TVShows(Kodi):
|
||||
|
||||
|
||||
def __init__(self, cursor):
|
||||
|
||||
self.cursor = cursor
|
||||
Kodi.__init__(self)
|
||||
|
||||
def create_entry_unique_id(self):
|
||||
self.cursor.execute(QU.create_unique_id)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_rating(self):
|
||||
self.cursor.execute(QU.create_rating)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry(self):
|
||||
self.cursor.execute(QU.create_tvshow)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_season(self):
|
||||
self.cursor.execute(QU.create_season)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def create_entry_episode(self):
|
||||
self.cursor.execute(QU.create_episode)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def get(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_tvshow, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def get_episode(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_episode, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def get_rating_id(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_rating, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def add_ratings(self, *args):
|
||||
self.cursor.execute(QU.add_rating, args)
|
||||
|
||||
def update_ratings(self, *args):
|
||||
self.cursor.execute(QU.update_rating, args)
|
||||
|
||||
def get_total_episodes(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_total_episodes, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def get_unique_id(self, *args):
|
||||
|
||||
try:
|
||||
self.cursor.execute(QU.get_unique_id, args)
|
||||
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def add_unique_id(self, *args):
|
||||
self.cursor.execute(QU.add_unique_id, args)
|
||||
|
||||
def update_unique_id(self, *args):
|
||||
self.cursor.execute(QU.update_unique_id, args)
|
||||
|
||||
def add(self, *args):
|
||||
self.cursor.execute(QU.add_tvshow, args)
|
||||
|
||||
def update(self, *args):
|
||||
self.cursor.execute(QU.update_tvshow, args)
|
||||
|
||||
def link(self, *args):
|
||||
self.cursor.execute(QU.update_tvshow_link, args)
|
||||
|
||||
def get_season(self, name, *args):
|
||||
|
||||
self.cursor.execute(QU.get_season, args)
|
||||
try:
|
||||
season_id = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
season_id = self.add_season(*args)
|
||||
|
||||
if name:
|
||||
self.cursor.execute(QU.update_season, (name, season_id))
|
||||
|
||||
return season_id
|
||||
|
||||
def add_season(self, *args):
|
||||
|
||||
season_id = self.create_entry_season()
|
||||
self.cursor.execute(QU.add_season, (season_id,) + args)
|
||||
|
||||
return season_id
|
||||
|
||||
def get_by_unique_id(self, *args):
|
||||
self.cursor.execute(QU.get_show_by_unique_id, args)
|
||||
|
||||
return self.cursor.fetchall()
|
||||
|
||||
def add_episode(self, *args):
|
||||
self.cursor.execute(QU.add_episode, args)
|
||||
|
||||
def update_episode(self, *args):
|
||||
self.cursor.execute(QU.update_episode, args)
|
||||
|
||||
def delete_tvshow(self, *args):
|
||||
self.cursor.execute(QU.delete_tvshow, args)
|
||||
|
||||
def delete_season(self, *args):
|
||||
self.cursor.execute(QU.delete_season, args)
|
||||
|
||||
def delete_episode(self, kodi_id, file_id):
|
||||
|
||||
self.cursor.execute(QU.delete_episode, (kodi_id,))
|
||||
self.cursor.execute(QU.delete_file, (file_id,))
|
|
@ -2,466 +2,346 @@
|
|||
|
||||
##################################################################################################
|
||||
|
||||
import json
|
||||
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
|
||||
import downloader as server
|
||||
from obj import Objects
|
||||
from kodi import Movies as KodiDb, queries as QU
|
||||
from database import emby_db, queries as QUEM
|
||||
from helper import api, catch, stop, validate, emby_item, library_check, values
|
||||
|
||||
##################################################################################################
|
||||
|
||||
log = logging.getLogger("EMBY."+__name__)
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Movies(Items):
|
||||
class Movies(KodiDb):
|
||||
|
||||
def __init__(self, server, embydb, videodb, direct_path):
|
||||
|
||||
def __init__(self, embycursor, kodicursor, pdialog=None):
|
||||
self.server = server
|
||||
self.emby = embydb
|
||||
self.video = videodb
|
||||
self.direct_path = direct_path
|
||||
|
||||
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.emby_db = emby_db.EmbyDatabase(embydb.cursor)
|
||||
self.objects = Objects()
|
||||
|
||||
self.new_time = int(settings('newvideotime'))*1000
|
||||
KodiDb.__init__(self, videodb.cursor)
|
||||
|
||||
Items.__init__(self)
|
||||
def __getitem__(self, key):
|
||||
|
||||
def _get_func(self, item_type, action):
|
||||
if key == 'Movie':
|
||||
return self.movie
|
||||
elif key == 'BoxSet':
|
||||
return self.boxset
|
||||
elif key == 'UserData':
|
||||
return self.userdata
|
||||
elif key in 'Removed':
|
||||
return self.remove
|
||||
|
||||
if item_type == "Movie":
|
||||
actions = {
|
||||
'added': self.add_movies,
|
||||
'update': self.add_update,
|
||||
'userdata': self.updateUserdata,
|
||||
'remove': self.remove
|
||||
}
|
||||
elif item_type == "BoxSet":
|
||||
actions = {
|
||||
'added': self.add_boxsets,
|
||||
'update': self.add_updateBoxset,
|
||||
'remove': self.remove
|
||||
}
|
||||
else:
|
||||
log.info("Unsupported item_type: %s", item_type)
|
||||
actions = {}
|
||||
|
||||
return actions.get(action)
|
||||
|
||||
def force_refresh_boxsets(self):
|
||||
|
||||
if self.pdialog:
|
||||
self.pdialog.update(heading=lang(29999), message=lang(33018))
|
||||
|
||||
boxsets = self.emby.getBoxset(dialog=self.pdialog)
|
||||
self.add_all("BoxSet", boxsets)
|
||||
|
||||
log.debug("Boxsets finished.")
|
||||
return True
|
||||
|
||||
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))
|
||||
@stop()
|
||||
@emby_item()
|
||||
@library_check()
|
||||
def movie(self, item, e_item, library):
|
||||
|
||||
movies = dict(self.emby_db.get_checksum_by_view("Movie", view_id))
|
||||
emby_movies = self.emby.getMovies(view_id, basic=True, dialog=self.pdialog)
|
||||
''' If item does not exist, entry will be added.
|
||||
If item exists, entry will be updated.
|
||||
'''
|
||||
API = api.API(item, self.server['auth/server-address'])
|
||||
obj = self.objects.map(item, 'Movie')
|
||||
update = True
|
||||
|
||||
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 add_movies(self, items, total=None, view=None):
|
||||
|
||||
for item in self.added(items, total):
|
||||
if self.add_update(item, view):
|
||||
self.content_pop(item.get('Name', "unknown"))
|
||||
|
||||
def add_boxsets(self, items, total=None):
|
||||
|
||||
for item in 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()
|
||||
obj['MovieId'] = e_item[0]
|
||||
obj['FileId'] = e_item[1]
|
||||
obj['PathId'] = e_item[2]
|
||||
except TypeError as error:
|
||||
|
||||
update = False
|
||||
LOG.debug("MovieId %s not found", obj['Id'])
|
||||
obj['MovieId'] = self.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 self.get(*values(obj, QU.get_movie_obj)) is None:
|
||||
|
||||
if not view:
|
||||
# Get view tag from emby
|
||||
viewtag, viewid = emby_db.getView_embyId(itemid)
|
||||
log.debug("View tag found: %s", viewtag)
|
||||
update = False
|
||||
LOG.info("MovieId %s missing from kodi. repairing the entry.", obj['MovieId'])
|
||||
|
||||
|
||||
obj['Path'] = API.get_file_path(obj['Path'])
|
||||
obj['LibraryId'] = library['Id']
|
||||
obj['LibraryName'] = library['Name']
|
||||
obj['Genres'] = obj['Genres'] or []
|
||||
obj['Studios'] = [API.validate_studio(studio) for studio in (obj['Studios'] or [])]
|
||||
obj['People'] = obj['People'] or []
|
||||
obj['Genre'] = " / ".join(obj['Genres'])
|
||||
obj['Writers'] = " / ".join(obj['Writers'] or [])
|
||||
obj['Directors'] = " / ".join(obj['Directors'] or [])
|
||||
obj['Plot'] = API.get_overview(obj['Plot'])
|
||||
obj['Resume'] = API.adjust_resume((obj['Resume'] or 0) / 10000000.0)
|
||||
obj['Runtime'] = round(float((obj['Runtime'] or 0) / 10000000.0), 6)
|
||||
obj['People'] = API.get_people_artwork(obj['People'])
|
||||
obj['DateAdded'] = obj['DateAdded'].split('.')[0].replace('T', " ")
|
||||
obj['DatePlayed'] = (obj['DatePlayed'] or obj['DateAdded']).split('.')[0].replace('T', " ")
|
||||
obj['PlayCount'] = API.get_playcount(obj['Played'], obj['PlayCount'])
|
||||
obj['Artwork'] = API.get_all_artwork(self.objects.map(item, 'Artwork'))
|
||||
obj['Video'] = API.video_streams(obj['Video'] or [], obj['Container'])
|
||||
obj['Audio'] = API.audio_streams(obj['Audio'] or [])
|
||||
obj['Streams'] = API.media_streams(obj['Video'], obj['Audio'], obj['Subtitles'])
|
||||
|
||||
self.get_path_filename(obj)
|
||||
self.trailer(obj)
|
||||
|
||||
if obj['Countries']:
|
||||
self.add_countries(*values(obj, QU.update_country_obj))
|
||||
|
||||
tags = []
|
||||
tags.extend(obj['Tags'] or [])
|
||||
tags.append(obj['LibraryName'])
|
||||
|
||||
if obj['Favorite']:
|
||||
tags.append('Favorite movies')
|
||||
|
||||
obj['Tags'] = tags
|
||||
|
||||
|
||||
if update:
|
||||
self.movie_update(obj)
|
||||
else:
|
||||
viewtag = view['name']
|
||||
viewid = view['id']
|
||||
self.movie_add(obj)
|
||||
|
||||
# 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()
|
||||
self.update_path(*values(obj, QU.update_path_movie_obj))
|
||||
self.update_file(*values(obj, QU.update_file_obj))
|
||||
self.add_tags(*values(obj, QU.add_tags_movie_obj))
|
||||
self.add_genres(*values(obj, QU.add_genres_movie_obj))
|
||||
self.add_studios(*values(obj, QU.add_studios_movie_obj))
|
||||
self.add_playstate(*values(obj, QU.add_bookmark_obj))
|
||||
self.add_people(*values(obj, QU.add_people_movie_obj))
|
||||
self.add_streams(*values(obj, QU.add_streams_obj))
|
||||
self.artwork.add(obj['Artwork'], obj['MovieId'], "movie")
|
||||
|
||||
def movie_add(self, obj):
|
||||
|
||||
''' Add object to kodi.
|
||||
'''
|
||||
obj['RatingId'] = self.create_entry_rating()
|
||||
self.add_ratings(*values(obj, QU.add_rating_movie_obj))
|
||||
|
||||
obj['Unique'] = self.create_entry_unique_id()
|
||||
self.add_unique_id(*values(obj, QU.add_unique_id_movie_obj))
|
||||
|
||||
obj['PathId'] = self.add_path(*values(obj, QU.add_path_obj))
|
||||
obj['FileId'] = self.add_file(*values(obj, QU.add_file_obj))
|
||||
|
||||
self.add(*values(obj, QU.add_movie_obj))
|
||||
self.emby_db.add_reference(*values(obj, QUEM.add_reference_movie_obj))
|
||||
LOG.info("ADD movie [%s/%s/%s] %s: %s", obj['PathId'], obj['FileId'], obj['MovieId'], obj['Id'], obj['Title'])
|
||||
|
||||
def movie_update(self, obj):
|
||||
|
||||
''' Update object to kodi.
|
||||
'''
|
||||
obj['RatingId'] = self.get_rating_id(*values(obj, QU.get_rating_movie_obj))
|
||||
self.update_ratings(*values(obj, QU.update_rating_movie_obj))
|
||||
|
||||
obj['Unique'] = self.get_unique_id(*values(obj, QU.get_unique_id_movie_obj))
|
||||
self.update_unique_id(*values(obj, QU.update_unique_id_movie_obj))
|
||||
|
||||
self.update(*values(obj, QU.update_movie_obj))
|
||||
self.emby_db.update_reference(*values(obj, QUEM.update_reference_obj))
|
||||
LOG.info("UPDATE movie [%s/%s/%s] %s: %s", obj['PathId'], obj['FileId'], obj['MovieId'], obj['Id'], obj['Title'])
|
||||
|
||||
def trailer(self, obj):
|
||||
|
||||
try:
|
||||
studio = studios[0]
|
||||
except IndexError:
|
||||
studio = None
|
||||
if obj['LocalTrailer']:
|
||||
|
||||
if int(item.get('LocalTrailerCount', 0)) > 0:
|
||||
# There's a local trailer
|
||||
url = (
|
||||
"{server}/emby/Users/{UserId}/Items/%s/LocalTrailers?format=json"
|
||||
% itemid
|
||||
)
|
||||
try:
|
||||
result = self.do_url(url)
|
||||
trailer = "plugin://plugin.video.emby/trailer/?id=%s&mode=play" % result[0]['Id']
|
||||
except Exception as error:
|
||||
log.info("Failed to process local trailer: " + str(error))
|
||||
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
|
||||
trailer = self.server['api'].get_local_trailers(obj['Id'])
|
||||
obj['Trailer'] = "plugin://plugin.video.emby/trailer?id=%s&mode=play" % trailer[0]['Id']
|
||||
|
||||
elif obj['Trailer']:
|
||||
obj['Trailer'] = "plugin://plugin.video.youtube/play/?video_id=%s" % obj['Trailer'].rsplit('=', 1)[1]
|
||||
except Exception as error:
|
||||
|
||||
##### GET THE FILE AND PATH #####
|
||||
playurl = API.get_file_path()
|
||||
LOG.error("Failed to get trailer: %s", error)
|
||||
obj['Trailer'] = None
|
||||
|
||||
if "\\" in playurl:
|
||||
# Local path
|
||||
filename = playurl.rsplit("\\", 1)[1]
|
||||
else: # Network share
|
||||
filename = playurl.rsplit("/", 1)[1]
|
||||
def get_path_filename(self, obj):
|
||||
|
||||
''' Get the path and filename and build it into protocol://path
|
||||
'''
|
||||
obj['Filename'] = obj['Path'].rsplit('\\', 1)[1] if '\\' in obj['Path'] else obj['Path'].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")
|
||||
if not validate(obj['Path']):
|
||||
raise Exception("Failed to validate path. User stopped.")
|
||||
|
||||
obj['Path'] = obj['Path'].replace(obj['Filename'], "")
|
||||
|
||||
else:
|
||||
# Set plugin path and media flags using real filename
|
||||
path = "plugin://plugin.video.emby.movies/"
|
||||
obj['Path'] = "plugin://plugin.video.emby.movies/"
|
||||
params = {
|
||||
|
||||
'filename': filename.encode('utf-8'),
|
||||
'id': itemid,
|
||||
'dbid': movieid,
|
||||
'filename': obj['Filename'].encode('utf-8'),
|
||||
'id': obj['Id'],
|
||||
'dbid': obj['MovieId'],
|
||||
'mode': "play"
|
||||
}
|
||||
filename = "%s?%s" % (path, urllib.urlencode(params))
|
||||
obj['Filename'] = "%s?%s" % (obj['Path'], urllib.urlencode(params))
|
||||
|
||||
|
||||
##### UPDATE THE MOVIE #####
|
||||
if update_item:
|
||||
log.info("UPDATE movie itemid: %s - Title: %s", itemid, title)
|
||||
@stop()
|
||||
@emby_item()
|
||||
def boxset(self, item, e_item):
|
||||
|
||||
''' If item does not exist, entry will be added.
|
||||
If item exists, entry will be updated.
|
||||
|
||||
# update ratings
|
||||
ratingid = self.kodi_db.get_ratingid(movieid)
|
||||
self.kodi_db.update_ratings(movieid, "movie", "default", rating, votecount, ratingid)
|
||||
Process movies inside boxset.
|
||||
Process removals from boxset.
|
||||
'''
|
||||
API = api.API(item, self.server['auth/server-address'])
|
||||
obj = self.objects.map(item, 'Boxset')
|
||||
|
||||
# update uniqueid
|
||||
uniqueid = self.kodi_db.get_uniqueid(movieid)
|
||||
self.kodi_db.update_uniqueid(movieid, "movie", imdb, "imdb", uniqueid)
|
||||
obj['Overview'] = API.get_overview(obj['Overview'])
|
||||
|
||||
# Update the movie entry
|
||||
self.kodi_db.update_movie(title, plot, shortplot, tagline, votecount, uniqueid,
|
||||
writer, year, uniqueid, sorttitle, runtime, mpaa, genre,
|
||||
director, title, studio, trailer, country, year, 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 ratings
|
||||
ratingid = self.kodi_db.create_entry_rating()
|
||||
self.kodi_db.add_ratings(ratingid, movieid, "movie", "default", rating, votecount)
|
||||
|
||||
# Add uniqueid
|
||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||
self.kodi_db.add_uniqueid(uniqueid, movieid, "movie", imdb, "imdb")
|
||||
|
||||
# Add path
|
||||
pathid = self.kodi_db.add_path(path)
|
||||
# Add the file
|
||||
fileid = self.kodi_db.add_file(filename, pathid)
|
||||
|
||||
# Create the movie entry
|
||||
self.kodi_db.add_movie(movieid, fileid, title, plot, shortplot, tagline,
|
||||
votecount, uniqueid, writer, year, uniqueid, sorttitle,
|
||||
runtime, mpaa, genre, director, title, studio, trailer,
|
||||
country, year)
|
||||
|
||||
# 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")
|
||||
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]
|
||||
self.kodi_db.update_boxset(setid, title)
|
||||
except TypeError:
|
||||
setid = self.kodi_db.add_boxset(title)
|
||||
obj['SetId'] = e_item[0]
|
||||
self.update_boxset(*values(obj, QU.update_set_obj))
|
||||
except TypeError as error:
|
||||
|
||||
# Process artwork
|
||||
artwork.add_artwork(artwork.get_all_artwork(boxset), setid, "set", self.kodicursor)
|
||||
LOG.debug("SetId %s not found", obj['Id'])
|
||||
obj['SetId'] = self.add_boxset(*values(obj, QU.add_set_obj))
|
||||
|
||||
# Process movies inside boxset
|
||||
current_movies = emby_db.getItemId_byParentId(setid, "movie")
|
||||
process = []
|
||||
self.boxset_current(obj)
|
||||
obj['Artwork'] = API.get_all_artwork(self.objects.map(item, 'Artwork'))
|
||||
|
||||
for movie in obj['Current']:
|
||||
|
||||
temp_obj = dict(obj)
|
||||
temp_obj['Movie'] = movie
|
||||
temp_obj['MovieId'] = obj['Current'][temp_obj['Movie']]
|
||||
self.remove_from_boxset(*values(temp_obj, QU.delete_movie_set_obj))
|
||||
self.emby_db.update_parent_id(*values(temp_obj, QUEM.delete_parent_boxset_obj))
|
||||
LOG.info("DELETE from boxset [%s] %s: %s", temp_obj['SetId'], temp_obj['Title'], temp_obj['MovieId'])
|
||||
|
||||
self.artwork.add(obj['Artwork'], obj['SetId'], "set")
|
||||
self.emby_db.add_reference(*values(obj, QUEM.add_reference_boxset_obj))
|
||||
LOG.info("UPDATE boxset [%s] %s", obj['SetId'], obj['Title'])
|
||||
|
||||
def boxset_current(self, obj):
|
||||
|
||||
''' Add or removes movies based on the current movies found in the boxset.
|
||||
'''
|
||||
obj['Current'] = []
|
||||
try:
|
||||
# Try to convert tuple to dictionary
|
||||
current = dict(current_movies)
|
||||
movies = dict(self.emby_db.get_item_id_by_parent_id(*values(obj, QUEM.get_item_id_by_parent_boxset_obj)))
|
||||
except ValueError:
|
||||
current = {}
|
||||
movies = {}
|
||||
|
||||
# Sort current titles
|
||||
for current_movie in current:
|
||||
process.append(current_movie)
|
||||
for movie in movies:
|
||||
obj['Current'].append(movie)
|
||||
|
||||
# New list to compare
|
||||
for movie in emby.getMovies_byBoxset(boxsetid)['Items']:
|
||||
|
||||
itemid = movie['Id']
|
||||
for all_movies in server.get_movies_by_boxset(obj['Id']):
|
||||
for movie in all_movies['Items']:
|
||||
|
||||
temp_obj = dict(obj)
|
||||
temp_obj['Title'] = movie['Name']
|
||||
temp_obj['Id'] = movie['Id']
|
||||
|
||||
if not current.get(itemid):
|
||||
# Assign boxset to movie
|
||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
||||
try:
|
||||
movieid = emby_dbitem[0]
|
||||
temp_obj['MovieId'] = self.emby_db.get_item_by_id(*values(temp_obj, QUEM.get_item_obj))[0]
|
||||
except TypeError:
|
||||
log.info("Failed to add: %s to boxset", movie['Name'])
|
||||
LOG.info("Failed to process %s to boxset.", temp_obj['Title'])
|
||||
|
||||
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)
|
||||
if temp_obj['Id'] not in movies:
|
||||
|
||||
# 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)
|
||||
self.set_boxset(*values(temp_obj, QU.update_movie_set_obj))
|
||||
self.emby_db.update_parent_id(*values(temp_obj, QUEM.update_parent_movie_obj))
|
||||
LOG.info("ADD to boxset [%s/%s] %s: %s to boxset", temp_obj['SetId'], temp_obj['MovieId'], temp_obj['Title'], temp_obj['Id'])
|
||||
else:
|
||||
obj['Current'].remove(temp_obj['Id'])
|
||||
|
||||
# Update the reference in the emby table
|
||||
emby_db.addReference(boxsetid, setid, "BoxSet", mediatype="set", checksum=checksum)
|
||||
def boxsets_reset(self):
|
||||
|
||||
def updateUserdata(self, item):
|
||||
# This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
|
||||
# Poster with progress bar
|
||||
emby_db = self.emby_db
|
||||
API = api.API(item)
|
||||
''' Special function to remove all existing boxsets.
|
||||
'''
|
||||
boxsets = self.emby_db.get_items_by_media('set')
|
||||
for boxset in boxsets:
|
||||
self.remove(boxset[0])
|
||||
|
||||
# Get emby information
|
||||
itemid = item['Id']
|
||||
checksum = API.get_checksum()
|
||||
userdata = API.get_userdata()
|
||||
runtime = API.get_runtime()
|
||||
@stop()
|
||||
@emby_item()
|
||||
def userdata(self, item, e_item):
|
||||
|
||||
''' This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
|
||||
Poster with progress bar
|
||||
'''
|
||||
API = api.API(item, self.server['auth/server-address'])
|
||||
obj = self.objects.map(item, 'MovieUserData')
|
||||
|
||||
# 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)
|
||||
obj['MovieId'] = e_item[0]
|
||||
obj['FileId'] = e_item[1]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
# Process favorite tags
|
||||
if userdata['Favorite']:
|
||||
self.kodi_db.get_tag(movieid, "Favorite movies", "movie")
|
||||
obj['Resume'] = API.adjust_resume((obj['Resume'] or 0) / 10000000.0)
|
||||
obj['Runtime'] = round(float((obj['Runtime'] or 0) / 10000000.0), 6)
|
||||
obj['PlayCount'] = API.get_playcount(obj['Played'], obj['PlayCount'])
|
||||
|
||||
if obj['DatePlayed']:
|
||||
obj['DatePlayed'] = obj['DatePlayed'].split('.')[0].replace('T', " ")
|
||||
|
||||
if obj['Favorite']:
|
||||
self.get_tag(*values(obj, QU.get_tag_movie_obj))
|
||||
else:
|
||||
self.kodi_db.remove_tag(movieid, "Favorite movies", "movie")
|
||||
self.remove_tag(*values(obj, QU.delete_tag_movie_obj))
|
||||
|
||||
# Process playstates
|
||||
playcount = userdata['PlayCount']
|
||||
dateplayed = userdata['LastPlayedDate']
|
||||
resume = API.adjust_resume(userdata['Resume'])
|
||||
total = round(float(runtime), 6)
|
||||
LOG.debug("New resume point %s: %s", obj['Id'], obj['Resume'])
|
||||
self.add_playstate(*values(obj, QU.add_bookmark_obj))
|
||||
self.emby_db.update_reference(*values(obj, QUEM.update_reference_obj))
|
||||
LOG.info("USERDATA movie [%s/%s] %s: %s", obj['FileId'], obj['MovieId'], obj['Id'], obj['Title'])
|
||||
|
||||
log.debug("%s New resume point: %s", itemid, resume)
|
||||
@stop()
|
||||
@emby_item()
|
||||
def remove(self, item_id, e_item):
|
||||
|
||||
self.kodi_db.add_playstate(fileid, resume, total, playcount, dateplayed)
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
''' Remove movieid, fileid, emby reference.
|
||||
Remove artwork, boxset
|
||||
'''
|
||||
obj = {'Id': item_id}
|
||||
|
||||
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)
|
||||
obj['KodiId'] = e_item[0]
|
||||
obj['FileId'] = e_item[1]
|
||||
obj['Media'] = e_item[4]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
# Remove the emby reference
|
||||
emby_db.removeItem(itemid)
|
||||
# Remove artwork
|
||||
artwork.delete_artwork(kodiid, mediatype, self.kodicursor)
|
||||
self.artwork.delete(obj['KodiId'], obj['Media'])
|
||||
|
||||
if mediatype == "movie":
|
||||
self.kodi_db.remove_movie(kodiid, fileid)
|
||||
if obj['Media'] == 'movie':
|
||||
self.delete(*values(obj, QU.delete_movie_obj))
|
||||
elif obj['Media'] == 'set':
|
||||
|
||||
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)
|
||||
for movie in self.emby_db.get_item_by_parent_id(*values(obj, QUEM.get_item_by_parent_movie_obj)):
|
||||
|
||||
temp_obj = dict(obj)
|
||||
temp_obj['MovieId'] = movie[1]
|
||||
temp_obj['Movie'] = movie[0]
|
||||
self.remove_from_boxset(*values(temp_obj, QU.delete_movie_set_obj))
|
||||
self.emby_db.update_parent_id(*values(temp_obj, QUEM.delete_parent_boxset_obj))
|
||||
|
||||
self.kodi_db.remove_boxset(kodiid)
|
||||
self.delete_boxset(*values(obj, QU.delete_set_obj))
|
||||
|
||||
log.info("Deleted %s %s from kodi database", mediatype, itemid)
|
||||
self.emby_db.remove_item(*values(obj, QUEM.delete_item_obj))
|
||||
LOG.info("DELETE %s [%s/%s] %s", obj['Media'], obj['FileId'], obj['KodiId'], obj['Id'])
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,321 +2,236 @@
|
|||
|
||||
##################################################################################################
|
||||
|
||||
import logging
|
||||
import urllib
|
||||
import datetime
|
||||
import logging
|
||||
import re
|
||||
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
|
||||
from obj import Objects
|
||||
from kodi import MusicVideos as KodiDb, queries as QU
|
||||
from database import emby_db, queries as QUEM
|
||||
from helper import api, catch, stop, validate, library_check, emby_item, values
|
||||
|
||||
##################################################################################################
|
||||
|
||||
log = logging.getLogger("EMBY."+__name__)
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class MusicVideos(Items):
|
||||
class MusicVideos(KodiDb):
|
||||
|
||||
def __init__(self, server, embydb, videodb, direct_path):
|
||||
|
||||
def __init__(self, embycursor, kodicursor, pdialog=None):
|
||||
self.server = server
|
||||
self.emby = embydb
|
||||
self.video = videodb
|
||||
self.direct_path = direct_path
|
||||
|
||||
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.emby_db = emby_db.EmbyDatabase(embydb.cursor)
|
||||
self.objects = Objects()
|
||||
|
||||
self.new_time = int(settings('newvideotime'))*1000
|
||||
KodiDb.__init__(self, videodb.cursor)
|
||||
|
||||
Items.__init__(self)
|
||||
def __getitem__(self, key):
|
||||
|
||||
def _get_func(self, item_type, action):
|
||||
if key == 'MusicVideo':
|
||||
return self.musicvideo
|
||||
elif key == 'UserData':
|
||||
return self.userdata
|
||||
elif key in 'Removed':
|
||||
return self.remove
|
||||
|
||||
if item_type == "MusicVideo":
|
||||
actions = {
|
||||
'added': self.add_mvideos,
|
||||
'update': self.add_update,
|
||||
'userdata': self.updateUserdata,
|
||||
'remove': self.remove
|
||||
}
|
||||
else:
|
||||
log.info("Unsupported item_type: %s", item_type)
|
||||
actions = {}
|
||||
@stop()
|
||||
@emby_item()
|
||||
@library_check()
|
||||
def musicvideo(self, item, e_item, library):
|
||||
|
||||
return actions.get(action)
|
||||
''' If item does not exist, entry will be added.
|
||||
If item exists, entry will be updated.
|
||||
|
||||
def compare_all(self):
|
||||
# Pull the list of musicvideos in Kodi
|
||||
views = self.emby_db.getView_byType('musicvideos')
|
||||
log.info("Media folders: %s", views)
|
||||
If we don't get the track number from Emby, see if we can infer it
|
||||
from the sortname attribute.
|
||||
'''
|
||||
API = api.API(item, self.server['auth/server-address'])
|
||||
obj = self.objects.map(item, 'MusicVideo')
|
||||
update = True
|
||||
|
||||
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 add_mvideos(self, items, total=None, view=None):
|
||||
|
||||
for item in 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()
|
||||
obj['MvideoId'] = e_item[0]
|
||||
obj['FileId'] = e_item[1]
|
||||
obj['PathId'] = e_item[2]
|
||||
except TypeError as error:
|
||||
|
||||
update = False
|
||||
LOG.debug("MvideoId for %s not found", obj['Id'])
|
||||
obj['MvideoId'] = self.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 self.get(*values(obj, QU.get_musicvideo_obj)) is None:
|
||||
|
||||
if not view:
|
||||
# Get view tag from emby
|
||||
viewtag, viewid = emby_db.getView_embyId(itemid)
|
||||
log.debug("View tag found: %s", viewtag)
|
||||
update = False
|
||||
LOG.info("MvideoId %s missing from kodi. repairing the entry.", obj['MvideoId'])
|
||||
|
||||
obj['Path'] = API.get_file_path(obj['Path'])
|
||||
obj['LibraryId'] = library['Id']
|
||||
obj['LibraryName'] = library['Name']
|
||||
obj['Genres'] = obj['Genres'] or []
|
||||
obj['ArtistItems'] = obj['ArtistItems'] or []
|
||||
obj['Studios'] = [API.validate_studio(studio) for studio in (obj['Studios'] or [])]
|
||||
obj['Plot'] = API.get_overview(obj['Plot'])
|
||||
obj['DateAdded'] = obj['DateAdded'].split('.')[0].replace('T', " ")
|
||||
obj['DatePlayed'] = (obj['DatePlayed'] or obj['DateAdded']).split('.')[0].replace('T', " ")
|
||||
obj['PlayCount'] = API.get_playcount(obj['Played'], obj['PlayCount'])
|
||||
obj['Resume'] = API.adjust_resume((obj['Resume'] or 0) / 10000000.0)
|
||||
obj['Runtime'] = round(float((obj['Runtime'] or 0) / 10000000.0), 6)
|
||||
obj['Premiere'] = obj['Premiere'] or datetime.date(obj['Year'] or 2021, 1, 1)
|
||||
obj['Genre'] = " / ".join(obj['Genres'])
|
||||
obj['Studio'] = " / ".join(obj['Studios'])
|
||||
obj['Artists'] = " / ".join(obj['Artists'] or [])
|
||||
obj['Directors'] = " / ".join(obj['Directors'] or [])
|
||||
obj['Video'] = API.video_streams(obj['Video'] or [], obj['Container'])
|
||||
obj['Audio'] = API.audio_streams(obj['Audio'] or [])
|
||||
obj['Streams'] = API.media_streams(obj['Video'], obj['Audio'], obj['Subtitles'])
|
||||
obj['Artwork'] = API.get_all_artwork(self.objects.map(item, 'Artwork'))
|
||||
|
||||
self.get_path_filename(obj)
|
||||
|
||||
if obj['Premiere']:
|
||||
obj['Premiere'] = str(obj['Premiere']).split('.')[0].replace('T', " ")
|
||||
|
||||
for artist in obj['ArtistItems']:
|
||||
artist['Type'] = "Artist"
|
||||
|
||||
obj['People'] = obj['People'] or [] + obj['ArtistItems']
|
||||
obj['People'] = API.get_people_artwork(obj['People'])
|
||||
|
||||
if obj['Index'] is None and obj['SortTitle'] is not None:
|
||||
search = re.search(r'^\d+\s?', obj['SortTitle'])
|
||||
|
||||
if search:
|
||||
obj['Index'] = search.group()
|
||||
|
||||
tags = []
|
||||
tags.extend(obj['Tags'] or [])
|
||||
tags.append(obj['LibraryName'])
|
||||
|
||||
if obj['Favorite']:
|
||||
tags.append('Favorite musicvideos')
|
||||
|
||||
obj['Tags'] = tags
|
||||
|
||||
|
||||
if update:
|
||||
self.musicvideo_update(obj)
|
||||
else:
|
||||
viewtag = view['name']
|
||||
viewid = view['id']
|
||||
self.musicvideo_add(obj)
|
||||
|
||||
# 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')
|
||||
# Some kodi views/skins rely on the "premiered" field to display by year.
|
||||
# So, if we don't get the premiere date from Emby, just set it to Jan 1st
|
||||
# of the video's "year", so that Kodi has a year to work with.
|
||||
premiered = item.get('PremiereDate')
|
||||
if premiered is None and year is not None:
|
||||
premiered = datetime.date(year, 1, 1)
|
||||
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')
|
||||
# If we don't get the track number from Emby, see if we can infer it
|
||||
# from the sortname attribute.
|
||||
if track is None:
|
||||
sortname = item.get('SortName')
|
||||
if sortname is not None:
|
||||
search = re.search(r'^\d+\s?', sortname)
|
||||
if search is not None:
|
||||
track = search.group()
|
||||
people = API.get_people()
|
||||
director = " / ".join(people['Director'])
|
||||
self.update_path(*values(obj, QU.update_path_mvideo_obj))
|
||||
self.update_file(*values(obj, QU.update_file_obj))
|
||||
self.add_tags(*values(obj, QU.add_tags_mvideo_obj))
|
||||
self.add_genres(*values(obj, QU.add_genres_mvideo_obj))
|
||||
self.add_studios(*values(obj, QU.add_studios_mvideo_obj))
|
||||
self.add_playstate(*values(obj, QU.add_bookmark_obj))
|
||||
self.add_people(*values(obj, QU.add_people_mvideo_obj))
|
||||
self.add_streams(*values(obj, QU.add_streams_obj))
|
||||
self.artwork.add(obj['Artwork'], obj['MvideoId'], "musicvideo")
|
||||
|
||||
##### GET THE FILE AND PATH #####
|
||||
playurl = API.get_file_path()
|
||||
def musicvideo_add(self, obj):
|
||||
|
||||
''' Add object to kodi.
|
||||
'''
|
||||
obj['PathId'] = self.add_path(*values(obj, QU.add_path_obj))
|
||||
obj['FileId'] = self.add_file(*values(obj, QU.add_file_obj))
|
||||
|
||||
if "\\" in playurl:
|
||||
# Local path
|
||||
filename = playurl.rsplit("\\", 1)[1]
|
||||
else: # Network share
|
||||
filename = playurl.rsplit("/", 1)[1]
|
||||
self.add(*values(obj, QU.add_musicvideo_obj))
|
||||
self.emby_db.add_reference(*values(obj, QUEM.add_reference_mvideo_obj))
|
||||
LOG.info("ADD mvideo [%s/%s/%s] %s: %s", obj['PathId'], obj['FileId'], obj['MvideoId'], obj['Id'], obj['Title'])
|
||||
|
||||
def musicvideo_update(self, obj):
|
||||
|
||||
''' Update object to kodi.
|
||||
'''
|
||||
self.update(*values(obj, QU.update_musicvideo_obj))
|
||||
self.emby_db.update_reference(*values(obj, QUEM.update_reference_obj))
|
||||
LOG.info("UPDATE mvideo [%s/%s/%s] %s: %s", obj['PathId'], obj['FileId'], obj['MvideoId'], obj['Id'], obj['Title'])
|
||||
|
||||
def get_path_filename(self, obj):
|
||||
|
||||
''' Get the path and filename and build it into protocol://path
|
||||
'''
|
||||
obj['Filename'] = obj['Path'].rsplit('\\', 1)[1] if '\\' in obj['Path'] else obj['Path'].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")
|
||||
if not validate(obj['Path']):
|
||||
raise Exception("Failed to validate path. User stopped.")
|
||||
|
||||
obj['Path'] = obj['Path'].replace(obj['Filename'], "")
|
||||
|
||||
else:
|
||||
# Set plugin path and media flags using real filename
|
||||
path = "plugin://plugin.video.emby.musicvideos/"
|
||||
obj['Path'] = "plugin://plugin.video.emby.musicvideos/"
|
||||
params = {
|
||||
|
||||
'filename': filename.encode('utf-8'),
|
||||
'id': itemid,
|
||||
'dbid': mvideoid,
|
||||
'filename': obj['Filename'].encode('utf-8'),
|
||||
'id': obj['Id'],
|
||||
'dbid': obj['MvideoId'],
|
||||
'mode': "play"
|
||||
}
|
||||
filename = "%s?%s" % (path, urllib.urlencode(params))
|
||||
obj['Filename'] = "%s?%s" % (obj['Path'], urllib.urlencode(params))
|
||||
|
||||
|
||||
##### UPDATE THE MUSIC VIDEO #####
|
||||
if update_item:
|
||||
log.info("UPDATE mvideo itemid: %s - Title: %s", itemid, title)
|
||||
@stop()
|
||||
@emby_item()
|
||||
def userdata(self, item, e_item):
|
||||
|
||||
''' This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
|
||||
Poster with progress bar
|
||||
'''
|
||||
API = api.API(item, self.server['auth/server-address'])
|
||||
obj = self.objects.map(item, 'MusicVideoUserData')
|
||||
|
||||
# Update the music video entry
|
||||
if self.kodi_version > 16: #Krypton and later
|
||||
self.kodi_db.update_musicvideo(title, runtime, director, studio, year, plot, album,
|
||||
artist, genre, track, premiered, mvideoid)
|
||||
else:
|
||||
self.kodi_db.update_musicvideo_16(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
|
||||
if self.kodi_version > 16: #Krypton and later
|
||||
self.kodi_db.add_musicvideo(mvideoid, fileid, title, runtime, director, studio,
|
||||
year, plot, album, artist, genre, track, premiered)
|
||||
else:
|
||||
self.kodi_db.add_musicvideo_16(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)
|
||||
obj['MvideoId'] = e_item[0]
|
||||
obj['FileId'] = e_item[1]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
# Process favorite tags
|
||||
if userdata['Favorite']:
|
||||
self.kodi_db.get_tag(mvideoid, "Favorite musicvideos", "musicvideo")
|
||||
obj['Resume'] = API.adjust_resume((obj['Resume'] or 0) / 10000000.0)
|
||||
obj['Runtime'] = round(float((obj['Runtime'] or 0) / 10000000.0), 6)
|
||||
obj['PlayCount'] = API.get_playcount(obj['Played'], obj['PlayCount'])
|
||||
|
||||
if obj['DatePlayed']:
|
||||
obj['DatePlayed'] = obj['DatePlayed'].split('.')[0].replace('T', " ")
|
||||
|
||||
if obj['Favorite']:
|
||||
self.get_tag(*values(obj, QU.get_tag_mvideo_obj))
|
||||
else:
|
||||
self.kodi_db.remove_tag(mvideoid, "Favorite musicvideos", "musicvideo")
|
||||
self.remove_tag(*values(obj, QU.delete_tag_mvideo_obj))
|
||||
|
||||
# Process playstates
|
||||
playcount = userdata['PlayCount']
|
||||
dateplayed = userdata['LastPlayedDate']
|
||||
resume = API.adjust_resume(userdata['Resume'])
|
||||
total = round(float(runtime), 6)
|
||||
self.add_playstate(*values(obj, QU.add_bookmark_obj))
|
||||
self.emby_db.update_reference(*values(obj, QUEM.update_reference_obj))
|
||||
LOG.info("USERDATA mvideo [%s/%s] %s: %s", obj['FileId'], obj['MvideoId'], obj['Id'], obj['Title'])
|
||||
|
||||
self.kodi_db.add_playstate(fileid, resume, total, playcount, dateplayed)
|
||||
emby_db.updateReference(itemid, checksum)
|
||||
@stop()
|
||||
@emby_item()
|
||||
def remove(self, item_id, e_item):
|
||||
|
||||
def remove(self, itemid):
|
||||
# Remove mvideoid, fileid, pathid, emby reference
|
||||
emby_db = self.emby_db
|
||||
kodicursor = self.kodicursor
|
||||
artwork = self.artwork
|
||||
''' Remove mvideoid, fileid, pathid, emby reference.
|
||||
'''
|
||||
obj = {'Id': item_id}
|
||||
|
||||
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)
|
||||
obj['MvideoId'] = e_item[0]
|
||||
obj['FileId'] = e_item[1]
|
||||
obj['PathId'] = e_item[2]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
# Remove the emby reference
|
||||
emby_db.removeItem(itemid)
|
||||
# Remove artwork
|
||||
artwork.delete_artwork(mvideoid, "musicvideo", self.kodicursor)
|
||||
self.artwork.delete(obj['MvideoId'], "musicvideo")
|
||||
self.delete(*values(obj, QU.delete_musicvideo_obj))
|
||||
|
||||
self.kodi_db.remove_musicvideo(mvideoid, fileid)
|
||||
if self.direct_path:
|
||||
self.kodi_db.remove_path(pathid)
|
||||
self.remove_path(*values(obj, QU.delete_path_obj))
|
||||
|
||||
log.info("Deleted musicvideo %s from kodi database", itemid)
|
||||
self.emby_db.remove_item(*values(obj, QUEM.delete_item_obj))
|
||||
LOG.info("DELETE musicvideo %s [%s/%s] %s", obj['MvideoId'], obj['PathId'], obj['FileId'], obj['Id'])
|
||||
|
|
161
resources/lib/objects/obj.py
Normal file
161
resources/lib/objects/obj.py
Normal file
|
@ -0,0 +1,161 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##################################################################################################
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
||||
class Objects(object):
|
||||
|
||||
# Borg - multiple instances, shared state
|
||||
_shared_state = {}
|
||||
|
||||
def __init__(self):
|
||||
|
||||
''' Hold all persistent data here.
|
||||
'''
|
||||
|
||||
self.__dict__ = self._shared_state
|
||||
|
||||
def mapping(self):
|
||||
|
||||
''' Load objects mapping.
|
||||
'''
|
||||
with open(os.path.join(os.path.dirname(__file__), 'obj_map.json')) as infile:
|
||||
self.objects = json.load(infile)
|
||||
|
||||
def map(self, item, mapping_name):
|
||||
|
||||
''' Syntax to traverse the item dictionary.
|
||||
This of the query almost as a url.
|
||||
|
||||
Item is the Emby item json object structure
|
||||
|
||||
",": each element will be used as a fallback until a value is found.
|
||||
"?": split filters and key name from the query part, i.e. MediaSources/0?$Name
|
||||
"$": lead the key name with $. Only one key value can be requested per element.
|
||||
":": indicates it's a list of elements [], i.e. MediaSources/0/MediaStreams:?$Name
|
||||
MediaStreams is a list.
|
||||
"/": indicates where to go directly
|
||||
'''
|
||||
self.mapped_item = {}
|
||||
|
||||
if not mapping_name:
|
||||
raise Exception("execute mapping() first")
|
||||
|
||||
mapping = self.objects[mapping_name]
|
||||
|
||||
for key, value in mapping.iteritems():
|
||||
|
||||
self.mapped_item[key] = None
|
||||
params = value.split(',')
|
||||
|
||||
for param in params:
|
||||
|
||||
obj = item
|
||||
obj_param = param
|
||||
obj_key = ""
|
||||
obj_filters = {}
|
||||
|
||||
if '?' in obj_param:
|
||||
|
||||
if '$' in obj_param:
|
||||
obj_param, obj_key = obj_param.rsplit('$', 1)
|
||||
|
||||
obj_param, filters = obj_param.rsplit('?', 1)
|
||||
|
||||
if filters:
|
||||
for filter in filters.split('&'):
|
||||
filter_key, filter_value = filter.split('=')
|
||||
obj_filters[filter_key] = filter_value
|
||||
|
||||
if ':' in obj_param:
|
||||
result = []
|
||||
|
||||
for d in self.__recursiveloop__(obj, obj_param):
|
||||
|
||||
if obj_filters and self.__filters__(d, obj_filters):
|
||||
result.append(d)
|
||||
elif not obj_filters:
|
||||
result.append(d)
|
||||
|
||||
obj = result
|
||||
obj_filters = {}
|
||||
|
||||
elif '/' in obj_param:
|
||||
obj = self.__recursive__(obj, obj_param)
|
||||
|
||||
elif obj is item:
|
||||
obj = item.get(obj_param)
|
||||
|
||||
if obj_filters and obj:
|
||||
if not self.__filters__(obj, obj_filters):
|
||||
obj = None
|
||||
|
||||
if not obj and len(params) != params.index(param):
|
||||
continue
|
||||
|
||||
if obj_key:
|
||||
obj = [d[obj_key] for d in obj if d.get(obj_key)] if type(obj) == list else obj.get(obj_key)
|
||||
|
||||
self.mapped_item[key] = obj
|
||||
break
|
||||
|
||||
self.mapped_item['ProviderName'] = self.objects.get('%sProviderName' % mapping_name)
|
||||
|
||||
if not mapping_name.startswith('Browse'):
|
||||
self.mapped_item['Checksum'] = json.dumps(item['UserData'])
|
||||
|
||||
return self.mapped_item
|
||||
|
||||
def __recursiveloop__(self, obj, keys):
|
||||
|
||||
first, rest = keys.split(':', 1)
|
||||
obj = self.__recursive__(obj, first)
|
||||
|
||||
if obj:
|
||||
if rest:
|
||||
for item in obj:
|
||||
self.__recursiveloop__(item, rest)
|
||||
else:
|
||||
for item in obj:
|
||||
yield item
|
||||
|
||||
def __recursive__(self, obj, keys):
|
||||
|
||||
for string in keys.split('/'):
|
||||
|
||||
if not obj:
|
||||
return
|
||||
|
||||
obj = obj[int(string)] if string.isdigit() else obj.get(string)
|
||||
|
||||
return obj
|
||||
|
||||
def __filters__(self, obj, filters):
|
||||
|
||||
result = False
|
||||
|
||||
for key, value in filters.iteritems():
|
||||
|
||||
inverse = False
|
||||
|
||||
if value.startswith('!'):
|
||||
|
||||
inverse = True
|
||||
value = value.split('!', 1)[1]
|
||||
|
||||
if value.lower() == "null":
|
||||
value = None
|
||||
|
||||
result = obj.get(key) != value if inverse else obj.get(key) == value
|
||||
|
||||
return result
|
328
resources/lib/objects/obj_map.json
Normal file
328
resources/lib/objects/obj_map.json
Normal file
|
@ -0,0 +1,328 @@
|
|||
{
|
||||
"video": "special://database/MyVideos107.db",
|
||||
"music": "special://database/MyMusic60.db",
|
||||
"texture": "special://database/Textures13.db",
|
||||
"emby": "special://database/emby.db",
|
||||
"MovieProviderName": "imdb",
|
||||
"Movie": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"SortTitle": "SortName",
|
||||
"Path": "Path",
|
||||
"Genres": "Genres",
|
||||
"UniqueId": "ProviderIds/Imdb",
|
||||
"Rating": "CommunityRating",
|
||||
"Year": "ProductionYear",
|
||||
"Votes": "VoteCount",
|
||||
"Plot": "Overview",
|
||||
"ShortPlot": "ShortOverview",
|
||||
"People": "People",
|
||||
"Writers": "People:?Type=Writer$Name",
|
||||
"Directors": "People:?Type=Director$Name",
|
||||
"Cast": "People:?Type=Actor$Name",
|
||||
"Tagline": "Taglines/0",
|
||||
"Mpaa": "OfficialRating",
|
||||
"Country": "ProductionLocations/0",
|
||||
"Countries": "ProductionLocations",
|
||||
"Studios": "Studios:?$Name",
|
||||
"Studio": "Studios/0/Name",
|
||||
"Runtime": "RunTimeTicks,CumulativeRunTimeTicks",
|
||||
"LocalTrailer": "LocalTrailerCount",
|
||||
"Trailer": "RemoteTrailers/0/Url",
|
||||
"DateAdded": "DateCreated",
|
||||
"Played": "UserData/Played",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"Favorite": "UserData/IsFavorite",
|
||||
"Resume": "UserData/PlaybackPositionTicks",
|
||||
"Tags": "Tags",
|
||||
"Subtitles": "MediaSources/0/MediaStreams:?Type=Subtitle$Language",
|
||||
"Audio": "MediaSources/0/MediaStreams:?Type=Audio",
|
||||
"Video": "MediaSources/0/MediaStreams:?Type=Video",
|
||||
"Container": "MediaSources/0/Container"
|
||||
},
|
||||
"MovieUserData": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"Runtime": "RunTimeTicks,CumulativeRunTimeTicks",
|
||||
"Resume": "UserData/PlaybackPositionTicks",
|
||||
"Favorite": "UserData/IsFavorite",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"Played": "UserData/Played"
|
||||
},
|
||||
"Boxset": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"Overview": "Overview"
|
||||
},
|
||||
"SeriesProviderName": "tvdb",
|
||||
"Series": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"SortTitle": "SortName",
|
||||
"People": "People",
|
||||
"Path": "Path",
|
||||
"Genres": "Genres",
|
||||
"Plot": "Overview",
|
||||
"Rating": "CommunityRating",
|
||||
"Year": "ProductionYear",
|
||||
"Votes": "VoteCount",
|
||||
"Premiere": "PremiereDate",
|
||||
"UniqueId": "ProviderIds/Tvdb",
|
||||
"Mpaa": "OfficialRating",
|
||||
"Studios": "Studios:?$Name",
|
||||
"Tags": "Tags",
|
||||
"Favorite": "UserData/IsFavorite",
|
||||
"RecursiveCount": "RecursiveItemCount"
|
||||
},
|
||||
"Season": {
|
||||
"Id": "Id",
|
||||
"Index": "IndexNumber",
|
||||
"SeriesId": "SeriesId",
|
||||
"Location": "LocationType",
|
||||
"Title": "Name"
|
||||
},
|
||||
"EpisodeProviderName": "tvdb",
|
||||
"Episode": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"Path": "Path",
|
||||
"Plot": "Overview",
|
||||
"People": "People",
|
||||
"Rating": "CommunityRating",
|
||||
"Writers": "People:?Type=Writer$Name",
|
||||
"Directors": "People:?Type=Director$Name",
|
||||
"Runtime": "RunTimeTicks,CumulativeRunTimeTicks",
|
||||
"Premiere": "PremiereDate",
|
||||
"Votes": "VoteCount",
|
||||
"UniqueId": "ProviderIds/Tvdb",
|
||||
"SeriesId": "SeriesId",
|
||||
"Season": "ParentIndexNumber",
|
||||
"Index": "IndexNumber",
|
||||
"AbsoluteNumber": "AbsoluteEpisodeNumber",
|
||||
"AirsAfterSeason": "AirsAfterSeasonNumber",
|
||||
"AirsBeforeSeason": "AirsBeforeSeasonNumber",
|
||||
"AirsBeforeEpisode": "AirsBeforeEpisodeNumber",
|
||||
"MultiEpisode": "IndexNumberEnd",
|
||||
"Played": "UserData/Played",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"DateAdded": "DateCreated",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"Resume": "UserData/PlaybackPositionTicks",
|
||||
"Subtitles": "MediaSources/0/MediaStreams:?Type=Subtitle$Language",
|
||||
"Audio": "MediaSources/0/MediaStreams:?Type=Audio",
|
||||
"Video": "MediaSources/0/MediaStreams:?Type=Video",
|
||||
"Container": "MediaSources/0/Container",
|
||||
"Location": "LocationType"
|
||||
},
|
||||
"EpisodeUserData": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"Runtime": "RunTimeTicks,CumulativeRunTimeTicks",
|
||||
"Resume": "UserData/PlaybackPositionTicks",
|
||||
"Favorite": "UserData/IsFavorite",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"DateAdded": "DateCreated",
|
||||
"Played": "UserData/Played"
|
||||
},
|
||||
"MusicVideo": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"Path": "Path",
|
||||
"DateAdded": "DateCreated",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"Resume": "UserData/PlaybackPositionTicks",
|
||||
"SortTitle": "SortName",
|
||||
"Runtime": "RunTimeTicks,CumulativeRunTimeTicks",
|
||||
"Plot": "Overview",
|
||||
"Year": "ProductionYear",
|
||||
"Premiere": "PremiereDate",
|
||||
"Genres": "Genres",
|
||||
"Studios": "Studios?$Name",
|
||||
"Artists": "Artists",
|
||||
"ArtistItems": "ArtistItems",
|
||||
"Album": "Album",
|
||||
"Index": "Track",
|
||||
"People": "People",
|
||||
"Subtitles": "MediaSources/0/MediaStreams:?Type=Subtitle$Language",
|
||||
"Audio": "MediaSources/0/MediaStreams:?Type=Audio",
|
||||
"Video": "MediaSources/0/MediaStreams:?Type=Video",
|
||||
"Container": "MediaSources/0/Container",
|
||||
"Tags": "Tags",
|
||||
"Played": "UserData/Played",
|
||||
"Favorite": "UserData/IsFavorite",
|
||||
"Directors": "People:?Type=Director$Name"
|
||||
},
|
||||
"MusicVideoUserData": {
|
||||
"Id": "Id",
|
||||
"Runtime": "RunTimeTicks,CumulativeRunTimeTicks",
|
||||
"Resume": "UserData/PlaybackPositionTicks",
|
||||
"Favorite": "UserData/IsFavorite",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"Played": "UserData/Played"
|
||||
},
|
||||
"Artist": {
|
||||
"Id": "Id",
|
||||
"Name": "Name",
|
||||
"UniqueId": "ProviderIds/MusicBrainzArtist",
|
||||
"Genres": "Genres",
|
||||
"Bio": "Overview"
|
||||
},
|
||||
"Album": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"UniqueId": "ProviderIds/MusicBrainzAlbum",
|
||||
"Year": "ProductionYear",
|
||||
"Genres": "Genres",
|
||||
"Bio": "Overview",
|
||||
"AlbumArtists": "AlbumArtists",
|
||||
"Artists": "AlbumArtists:?$Name",
|
||||
"ArtistItems": "ArtistItems"
|
||||
},
|
||||
"Song": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"Path": "Path",
|
||||
"DateAdded": "DateCreated",
|
||||
"Played": "UserData/Played",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"UniqueId": "ProviderIds/MusicBrainzTrackId",
|
||||
"Genres": "Genres",
|
||||
"Artists": "Artists",
|
||||
"Index": "IndexNumber",
|
||||
"Disc": "ParentIndexNumber",
|
||||
"Year": "ProductionYear",
|
||||
"Runtime": "RunTimeTicks,CumulativeRunTimeTicks",
|
||||
"Comment": "Overview",
|
||||
"ArtistItems": "ArtistItems",
|
||||
"AlbumArtists": "AlbumArtists",
|
||||
"Album": "Album",
|
||||
"SongAlbumId": "AlbumId",
|
||||
"Container": "MediaSources/0/Container"
|
||||
},
|
||||
"SongUserData": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"DateAdded": "DateCreated"
|
||||
},
|
||||
"Artwork": {
|
||||
"Id": "Id",
|
||||
"Tags": "ImageTags",
|
||||
"BackdropTags": "BackdropImageTags"
|
||||
},
|
||||
"ArtworkParent": {
|
||||
"Id": "Id",
|
||||
"Tags": "ImageTags",
|
||||
"BackdropTags": "BackdropImageTags",
|
||||
"ParentBackdropId": "ParentBackdropItemId",
|
||||
"ParentBackdropTags": "ParentBackdropImageTags",
|
||||
"ParentLogoId": "ParentLogoItemId",
|
||||
"ParentLogoTag": "ParentLogoImageTag",
|
||||
"ParentArtId": "ParentArtItemId",
|
||||
"ParentArtTag": "ParentArtImageTag",
|
||||
"ParentThumbId": "ParentThumbItemId",
|
||||
"ParentThumbTag": "ParentThumbTag",
|
||||
"SeriesTag": "SeriesPrimaryImageTag",
|
||||
"SeriesId": "SeriesId"
|
||||
},
|
||||
"ArtworkMusic": {
|
||||
"Id": "Id",
|
||||
"Tags": "ImageTags",
|
||||
"BackdropTags": "BackdropImageTags",
|
||||
"ParentBackdropId": "ParentBackdropItemId",
|
||||
"ParentBackdropTags": "ParentBackdropImageTags",
|
||||
"ParentLogoId": "ParentLogoItemId",
|
||||
"ParentLogoTag": "ParentLogoImageTag",
|
||||
"ParentArtId": "ParentArtItemId",
|
||||
"ParentArtTag": "ParentArtImageTag",
|
||||
"ParentThumbId": "ParentThumbItemId",
|
||||
"ParentThumbTag": "ParentThumbTag",
|
||||
"AlbumId": "AlbumId",
|
||||
"AlbumTag": "AlbumPrimaryImageTag"
|
||||
},
|
||||
"BrowseVideo": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"Type": "Type",
|
||||
"Plot": "Overview",
|
||||
"Year": "ProductionYear",
|
||||
"Writers": "People:?Type=Writer$Name",
|
||||
"Directors": "People:?Type=Director$Name",
|
||||
"Cast": "People:?Type=Actor$Name",
|
||||
"Mpaa": "OfficialRating",
|
||||
"Genres": "Genres",
|
||||
"Studios": "Studios:?$Name,SeriesStudio",
|
||||
"Premiere": "PremiereDate,DateCreated",
|
||||
"Rating": "CommunityRating",
|
||||
"Votes": "VoteCount",
|
||||
"Season": "ParentIndexNumber",
|
||||
"Index": "IndexNumber,AbsoluteEpisodeNumber",
|
||||
"SeriesName": "SeriesName",
|
||||
"Countries": "ProductionLocations",
|
||||
"Played": "UserData/Played",
|
||||
"People": "People",
|
||||
"ShortPlot": "ShortOverview",
|
||||
"Runtime": "RunTimeTicks,CumulativeRunTimeTicks",
|
||||
"Tagline": "Taglines/0",
|
||||
"UniqueId": "ProviderIds/Imdb",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"Artists": "Artists",
|
||||
"Album": "Album",
|
||||
"Votes": "VoteCount",
|
||||
"Path": "Path",
|
||||
"LocalTrailer": "LocalTrailerCount",
|
||||
"Trailer": "RemoteTrailers/0/Url",
|
||||
"DateAdded": "DateCreated",
|
||||
"SortTitle": "SortName",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"Resume": "UserData/PlaybackPositionTicks",
|
||||
"Subtitles": "MediaStreams:?Type=Subtitle$Language",
|
||||
"Audio": "MediaStreams:?Type=Audio",
|
||||
"Video": "MediaStreams:?Type=Video",
|
||||
"Container": "Container",
|
||||
"Unwatched": "UserData/UnplayedItemCount",
|
||||
"ChildCount": "ChildCount",
|
||||
"RecursiveCount": "RecursiveItemCount",
|
||||
"MediaType": "MediaType"
|
||||
},
|
||||
"BrowseAudio": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"Type": "Type",
|
||||
"Index": "IndexNumber",
|
||||
"Disc": "ParentIndexNumber",
|
||||
"Runtime": "RunTimeTicks,CumulativeRunTimeTicks",
|
||||
"Year": "ProductionYear",
|
||||
"Genre": "Genres/0",
|
||||
"Album": "Album",
|
||||
"Artists": "Artists/0",
|
||||
"Rating": "CommunityRating",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"UniqueId": "ProviderIds/MusicBrainzTrackId,ProviderIds/MusicBrainzAlbum,ProviderIds/MusicBrainzArtist",
|
||||
"Comment": "Overview",
|
||||
"FileDate": "DateCreated",
|
||||
"Played": "UserData/Played"
|
||||
},
|
||||
"BrowsePhoto": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
"Type": "Type",
|
||||
"FileDate": "DateCreated",
|
||||
"Width": "Width",
|
||||
"Height": "Height",
|
||||
"Size": "Size",
|
||||
"Overview": "Overview",
|
||||
"CameraMake": "CameraMake",
|
||||
"CameraModel": "CameraModel",
|
||||
"ExposureTime": "ExposureTime",
|
||||
"FocalLength": "FocalLength"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
37
resources/lib/objects/utils.py
Normal file
37
resources/lib/objects/utils.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
#################################################################################################
|
||||
|
||||
import logging
|
||||
|
||||
from helper import JSONRPC
|
||||
|
||||
#################################################################################################
|
||||
|
||||
LOG = logging.getLogger("EMBY."+__name__)
|
||||
|
||||
#################################################################################################
|
||||
|
||||
def get_play_action():
|
||||
|
||||
''' I could not figure out a way to listen to kodi setting changes?
|
||||
For now, verify the play action every time play is called.
|
||||
'''
|
||||
options = ['Choose', 'Play', 'Resume', 'Show information']
|
||||
result = JSONRPC('Settings.GetSettingValue').execute({'setting': "myvideos.selectaction"})
|
||||
try:
|
||||
return options[result['result']['value']]
|
||||
except Exception as error:
|
||||
log.error("Returning play action due to error: %s", error)
|
||||
|
||||
return options[1]
|
||||
|
||||
def get_grouped_set():
|
||||
|
||||
''' Get if boxsets should be grouped
|
||||
'''
|
||||
result = JSONRPC('Settings.GetSettingValue').execute({'setting': "videolibrary.groupmoviesets"})
|
||||
try:
|
||||
return result['result']['value']
|
||||
except Exception as error:
|
||||
return False
|
Loading…
Add table
Add a link
Reference in a new issue