New hybrid method

This commit is contained in:
angelblue05 2018-09-06 03:36:32 -05:00
parent 7f5084c62e
commit ace50b34dc
279 changed files with 39526 additions and 19994 deletions

View file

@ -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()

View file

@ -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

View file

@ -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,))

View file

@ -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,))

View file

@ -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,))

View file

@ -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,))

View file

@ -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,))

View 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))

View 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

View 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
"""

View 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)

View 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)

View 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)

View 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,))

View 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 ?
"""

View 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 = ?
"""

View file

@ -0,0 +1,11 @@
get_cache = """ SELECT cachedurl
FROM texture
WHERE url = ?
"""
delete_cache = """ DELETE FROM texture
WHERE url = ?
"""

View 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,))

View file

@ -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

View file

@ -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'])

View 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

View 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

View 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